Compare commits
	
		
			23 Commits
		
	
	
		
			wip/exalm/
			...
			gbsneto/ne
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 6fd0aafada | ||
|   | e5091ff17c | ||
|   | 22534c4c64 | ||
|   | cd1c45731d | ||
|   | 5380b06eb5 | ||
|   | be714f7401 | ||
|   | e71c249ea5 | ||
|   | 5b6deff977 | ||
|   | 6cd0c3f965 | ||
|   | d529e222d7 | ||
|   | 2e23a0e6b8 | ||
|   | 64aaa46333 | ||
|   | d1f61e884d | ||
|   | fe3d55eb80 | ||
|   | 7a90b2d908 | ||
|   | 137df4bc26 | ||
|   | 23abe4eb22 | ||
|   | b7feb71490 | ||
|   | 9aa9431a32 | ||
|   | c9e2afcf65 | ||
|   | be5ab24e97 | ||
|   | caf0c8dd7d | ||
|   | 10a194e6ed | 
| @@ -1929,6 +1929,7 @@ StScrollBar { | ||||
|  | ||||
|   StEntry { | ||||
|     @extend %search_entry; | ||||
|     width: -1px; | ||||
|     border-radius: $button_radius; | ||||
|     @if $variant=='dark' { | ||||
|       $_gdm_entry_bg: transparentize(lighten(desaturate(#241f31, 20%), 2%), 0.5); | ||||
| @@ -1986,6 +1987,15 @@ StScrollBar { | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .cancel-button { | ||||
|     padding: 0; | ||||
|     border-radius: 16px; | ||||
|     width: 32px; | ||||
|     height: 32px; | ||||
|  | ||||
| 	  StIcon { icon-size: 16px; } | ||||
|   } | ||||
| } | ||||
|  | ||||
|   .login-dialog-logo-bin { padding: 24px 0px; } | ||||
| @@ -2069,42 +2079,28 @@ StScrollBar { | ||||
|  | ||||
| $_screenshield_shadow: 0px 0px 6px rgba(0, 0, 0, 0.726); | ||||
|  | ||||
| .screen-shield-arrows { | ||||
|     padding-bottom: 3em; | ||||
| } | ||||
|  | ||||
| .screen-shield-arrows Gjs_Arrow { | ||||
|     color: white; | ||||
|     width: 80px; | ||||
|     height: 48px; | ||||
|     -arrow-thickness: 12px; | ||||
|     -arrow-shadow: $_screenshield_shadow; | ||||
| } | ||||
|  | ||||
| .screen-shield-clock { | ||||
|   color: white; | ||||
|   text-shadow: $_screenshield_shadow; | ||||
|   font-weight: bold; | ||||
|   text-align: center; | ||||
|   padding-bottom: 1.5em; | ||||
|   padding-bottom: 2.5em; | ||||
| } | ||||
|  | ||||
| .screen-shield-clock-time { | ||||
|   font-size: 72pt; | ||||
|   text-shadow: $_screenshield_shadow; | ||||
|   font-size: 64pt; | ||||
|   font-weight: 200; | ||||
|   padding-bottom: 24px; | ||||
|   font-feature-settings: "tnum"; | ||||
| } | ||||
|  | ||||
| .screen-shield-clock-date {  | ||||
|   font-size: 28pt; | ||||
|   font-weight: normal; | ||||
|   font-size: 16pt; | ||||
|   font-weight: bold; | ||||
| } | ||||
|  | ||||
| .screen-shield-notifications-container { | ||||
|   spacing: 6px; | ||||
|   width: 30em; | ||||
|   background-color: transparent; | ||||
|   max-height: 500px; | ||||
|   padding: 24px 0; | ||||
|   .summary-notification-stack-scrollview { | ||||
|     padding-top: 0; | ||||
|     padding-bottom: 0; | ||||
| @@ -2112,7 +2108,7 @@ $_screenshield_shadow: 0px 0px 6px rgba(0, 0, 0, 0.726); | ||||
|  | ||||
|   .notification, | ||||
|   .screen-shield-notification-source { | ||||
|     padding: 12px 6px; | ||||
|     padding: 12px; | ||||
|     border: 1px solid $osd_outer_borders_color; | ||||
|     background-color: transparentize($osd_bg_color,0.5); | ||||
|     color: $osd_fg_color; | ||||
|   | ||||
| @@ -16,6 +16,10 @@ var DEFAULT_BUTTON_WELL_ANIMATION_TIME = 300; | ||||
|  | ||||
| var MESSAGE_FADE_OUT_ANIMATION_TIME = 500; | ||||
|  | ||||
| const WIGGLE_OFFSET = 6; | ||||
| const WIGGLE_DURATION = 65; | ||||
| const N_WIGGLES = 3; | ||||
|  | ||||
| var AuthPromptMode = { | ||||
|     UNLOCK_ONLY: 0, | ||||
|     UNLOCK_OR_LOG_IN: 1 | ||||
| @@ -90,40 +94,14 @@ var AuthPrompt = class { | ||||
|                          x_fill: false, | ||||
|                          y_fill: true, | ||||
|                          x_align: St.Align.START }); | ||||
|         this._entry = new St.Entry({ style_class: 'login-dialog-prompt-entry', | ||||
|                                      can_focus: true }); | ||||
|         ShellEntry.addContextMenu(this._entry, { isPassword: true, actionMode: Shell.ActionMode.NONE }); | ||||
|  | ||||
|         this.actor.add(this._entry, | ||||
|                        { expand: true, | ||||
|                          x_fill: true, | ||||
|                          y_fill: false, | ||||
|                          x_align: St.Align.START }); | ||||
|  | ||||
|         this._entry.grab_key_focus(); | ||||
|         this._initEntryRow(); | ||||
|  | ||||
|         this._message = new St.Label({ opacity: 0, | ||||
|                                        styleClass: 'login-dialog-message' }); | ||||
|         this._message.clutter_text.line_wrap = true; | ||||
|         this._message.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|         this.actor.add(this._message, { x_fill: false, x_align: St.Align.START, y_align: St.Align.START }); | ||||
|  | ||||
|         this._buttonBox = new St.BoxLayout({ style_class: 'login-dialog-button-box', | ||||
|                                              vertical: false }); | ||||
|         this.actor.add(this._buttonBox, | ||||
|                        { expand: true, | ||||
|                          x_align: St.Align.MIDDLE, | ||||
|                          y_align: St.Align.END }); | ||||
|  | ||||
|         this._defaultButtonWell = new St.Widget({ layout_manager: new Clutter.BinLayout() }); | ||||
|         this._defaultButtonWellActor = null; | ||||
|  | ||||
|         this._initButtons(); | ||||
|  | ||||
|         this._spinner = new Animation.Spinner(DEFAULT_BUTTON_WELL_ICON_SIZE); | ||||
|         this._spinner.actor.opacity = 0; | ||||
|         this._spinner.actor.show(); | ||||
|         this._defaultButtonWell.add_child(this._spinner.actor); | ||||
|     } | ||||
|  | ||||
|     _onDestroy() { | ||||
| @@ -131,52 +109,47 @@ var AuthPrompt = class { | ||||
|         this._userVerifier = null; | ||||
|     } | ||||
|  | ||||
|     _initButtons() { | ||||
|         this.cancelButton = new St.Button({ style_class: 'modal-dialog-button button', | ||||
|                                             button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE, | ||||
|                                             reactive: true, | ||||
|                                             can_focus: true, | ||||
|                                             label: _("Cancel") }); | ||||
|     _initEntryRow() { | ||||
|         let mainBox = new St.BoxLayout({ | ||||
|             style_class: 'login-dialog-button-box', | ||||
|             vertical: false, | ||||
|         }); | ||||
|         this.actor.add_child(mainBox); | ||||
|  | ||||
|         this.cancelButton = new St.Button({ | ||||
|             style_class: 'modal-dialog-button button cancel-button', | ||||
|             button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE, | ||||
|             reactive: true, | ||||
|             can_focus: true, | ||||
|             child: new St.Icon({ icon_name: 'go-previous-symbolic' }), | ||||
|         }); | ||||
|         this.cancelButton.connect('clicked', () => this.cancel()); | ||||
|         this._buttonBox.add(this.cancelButton, | ||||
|                             { expand: false, | ||||
|                               x_fill: false, | ||||
|                               y_fill: false, | ||||
|                               x_align: St.Align.START, | ||||
|                               y_align: St.Align.END }); | ||||
|         mainBox.add_child(this.cancelButton); | ||||
|  | ||||
|         this._buttonBox.add(this._defaultButtonWell, | ||||
|                             { expand: true, | ||||
|                               x_fill: false, | ||||
|                               y_fill: false, | ||||
|                               x_align: St.Align.END, | ||||
|                               y_align: St.Align.MIDDLE }); | ||||
|         this.nextButton = new St.Button({ style_class: 'modal-dialog-button button', | ||||
|                                           button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE, | ||||
|                                           reactive: true, | ||||
|                                           can_focus: true, | ||||
|                                           label: _("Next") }); | ||||
|         this.nextButton.connect('clicked', () => this.emit('next')); | ||||
|         this.nextButton.add_style_pseudo_class('default'); | ||||
|         this._buttonBox.add(this.nextButton, | ||||
|                             { expand: false, | ||||
|                               x_fill: false, | ||||
|                               y_fill: false, | ||||
|                               x_align: St.Align.END, | ||||
|                               y_align: St.Align.END }); | ||||
|         this._entry = new St.Entry({ | ||||
|             style_class: 'login-dialog-prompt-entry', | ||||
|             can_focus: true, | ||||
|             x_expand: true, | ||||
|         }); | ||||
|         ShellEntry.addContextMenu(this._entry, { isPassword: true, actionMode: Shell.ActionMode.NONE }); | ||||
|  | ||||
|         this._updateNextButtonSensitivity(this._entry.text.length > 0); | ||||
|         mainBox.add_child(this._entry); | ||||
|  | ||||
|         this._entry.grab_key_focus(); | ||||
|         this._entry.clutter_text.connect('activate', () => this.emit('next')); | ||||
|         this._entry.clutter_text.connect('text-changed', () => { | ||||
|             if (!this._userVerifier.hasPendingMessages) | ||||
|                 this._fadeOutMessage(); | ||||
|         }); | ||||
|  | ||||
|             this._updateNextButtonSensitivity(this._entry.text.length > 0 || this.verificationStatus == AuthPromptStatus.VERIFYING); | ||||
|         }); | ||||
|         this._entry.clutter_text.connect('activate', () => { | ||||
|             if (this.nextButton.reactive) | ||||
|                 this.emit('next'); | ||||
|         }); | ||||
|         this._defaultButtonWell = new St.Widget({ layout_manager: new Clutter.BinLayout() }); | ||||
|         this._defaultButtonWellActor = null; | ||||
|         mainBox.add_child(this._defaultButtonWell); | ||||
|  | ||||
|         this._spinner = new Animation.Spinner(DEFAULT_BUTTON_WELL_ICON_SIZE); | ||||
|         this._spinner.actor.opacity = 0; | ||||
|         this._spinner.actor.show(); | ||||
|         this._defaultButtonWell.add_child(this._spinner.actor); | ||||
|     } | ||||
|  | ||||
|     _onAskQuestion(verifier, serviceName, question, passwordChar) { | ||||
| @@ -191,16 +164,6 @@ var AuthPrompt = class { | ||||
|         } | ||||
|         this.setPasswordChar(passwordChar); | ||||
|         this.setQuestion(question); | ||||
|  | ||||
|         if (passwordChar) { | ||||
|             if (this._userVerifier.reauthenticating) | ||||
|                 this.nextButton.label = _("Unlock"); | ||||
|             else | ||||
|                 this.nextButton.label = C_("button", "Sign In"); | ||||
|         } else { | ||||
|             this.nextButton.label = _("Next"); | ||||
|         } | ||||
|  | ||||
|         this.updateSensitivity(true); | ||||
|         this.emit('prompted'); | ||||
|     } | ||||
| @@ -241,6 +204,35 @@ var AuthPrompt = class { | ||||
|         this.updateSensitivity(canRetry); | ||||
|         this.setActorInDefaultButtonWell(null); | ||||
|         this.verificationStatus = AuthPromptStatus.VERIFICATION_FAILED; | ||||
|  | ||||
|         this._wiggle(); | ||||
|     } | ||||
|  | ||||
|     _wiggle() { | ||||
|         // Accelerate before wiggling | ||||
|         this._entry.ease({ | ||||
|             translation_x: -WIGGLE_OFFSET, | ||||
|             duration: WIGGLE_DURATION, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             onComplete: () => { | ||||
|                 // Wiggle | ||||
|                 this._entry.ease({ | ||||
|                     translation_x: WIGGLE_OFFSET, | ||||
|                     duration: WIGGLE_DURATION, | ||||
|                     mode: Clutter.AnimationMode.LINEAR, | ||||
|                     repeat_count: N_WIGGLES, | ||||
|                     auto_reverse: true, | ||||
|                     onComplete: () => { | ||||
|                         // Decelerate and return to the original position | ||||
|                         this._entry.ease({ | ||||
|                             translation_x: 0, | ||||
|                             duration: WIGGLE_DURATION, | ||||
|                             mode: Clutter.AnimationMode.EASE_IN_QUAD, | ||||
|                         }); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     _onVerificationComplete() { | ||||
| @@ -393,13 +385,7 @@ var AuthPrompt = class { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _updateNextButtonSensitivity(sensitive) { | ||||
|         this.nextButton.reactive = sensitive; | ||||
|         this.nextButton.can_focus = sensitive; | ||||
|     } | ||||
|  | ||||
|     updateSensitivity(sensitive) { | ||||
|         this._updateNextButtonSensitivity(sensitive && (this._entry.text.length > 0 || this.verificationStatus == AuthPromptStatus.VERIFYING)); | ||||
|         this._entry.reactive = sensitive; | ||||
|         this._entry.clutter_text.editable = sensitive; | ||||
|     } | ||||
| @@ -430,7 +416,6 @@ var AuthPrompt = class { | ||||
|         let oldStatus = this.verificationStatus; | ||||
|         this.verificationStatus = AuthPromptStatus.NOT_VERIFYING; | ||||
|         this.cancelButton.reactive = true; | ||||
|         this.nextButton.label = _("Next"); | ||||
|         this._preemptiveAnswer = null; | ||||
|  | ||||
|         if (this._userVerifier) | ||||
|   | ||||
| @@ -105,6 +105,16 @@ function _easeActor(actor, params) { | ||||
|         actor.set_easing_delay(params.delay); | ||||
|     delete params.delay; | ||||
|  | ||||
|     let repeat_count = 0; | ||||
|     if (params.repeat_count != undefined) | ||||
|         repeat_count = params.repeat_count; | ||||
|     delete params.repeat_count; | ||||
|  | ||||
|     let auto_reverse = false; | ||||
|     if (params.auto_reverse != undefined) | ||||
|         auto_reverse = params.auto_reverse; | ||||
|     delete params.auto_reverse; | ||||
|  | ||||
|     if (params.mode != undefined) | ||||
|         actor.set_easing_mode(params.mode); | ||||
|     delete params.mode; | ||||
| @@ -124,10 +134,14 @@ function _easeActor(actor, params) { | ||||
|     let transition = animatedProps.map(p => actor.get_transition(p)) | ||||
|         .find(t => t !== null); | ||||
|  | ||||
|     if (transition) | ||||
|     if (transition) { | ||||
|         transition.repeat_count = repeat_count; | ||||
|         transition.auto_reverse = auto_reverse; | ||||
|  | ||||
|         transition.connect('stopped', (t, finished) => callback(finished)); | ||||
|     else | ||||
|     } else { | ||||
|         callback(true); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function _easeActorProperty(actor, propName, target, params) { | ||||
| @@ -140,6 +154,16 @@ function _easeActorProperty(actor, propName, target, params) { | ||||
|         params.duration = adjustAnimationTime(params.duration); | ||||
|     let duration = Math.floor(params.duration || 0); | ||||
|  | ||||
|     let repeat_count = 0; | ||||
|     if (params.repeat_count != undefined) | ||||
|         repeat_count = params.repeat_count; | ||||
|     delete params.repeat_count; | ||||
|  | ||||
|     let auto_reverse = false; | ||||
|     if (params.auto_reverse != undefined) | ||||
|         auto_reverse = params.auto_reverse; | ||||
|     delete params.auto_reverse; | ||||
|  | ||||
|     // Copy Clutter's behavior for implicit animations, see | ||||
|     // should_skip_implicit_transition() | ||||
|     if (actor instanceof Clutter.Actor && !actor.mapped) | ||||
| @@ -166,7 +190,9 @@ function _easeActorProperty(actor, propName, target, params) { | ||||
|     let transition = new Clutter.PropertyTransition(Object.assign({ | ||||
|         property_name: propName, | ||||
|         interval: new Clutter.Interval({ value_type: pspec.value_type }), | ||||
|         remove_on_complete: true | ||||
|         remove_on_complete: true, | ||||
|         repeat_count: repeat_count, | ||||
|         auto_reverse: auto_reverse, | ||||
|     }, params)); | ||||
|     actor.add_transition(propName, transition); | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const { AccountsService, Clutter, Cogl, Gio, GLib, | ||||
|         GnomeDesktop, GObject, Meta, Shell, St } = imports.gi; | ||||
|         GObject, Meta, Shell, St } = imports.gi; | ||||
| const Cairo = imports.cairo; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| @@ -16,6 +16,7 @@ const Overview = imports.ui.overview; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
| const ShellDBus = imports.ui.shellDBus; | ||||
| const SmartcardManager = imports.misc.smartcardManager; | ||||
| const Params = imports.misc.params; | ||||
|  | ||||
| const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver'; | ||||
| const LOCK_ENABLED_KEY = 'lock-enabled'; | ||||
| @@ -25,16 +26,14 @@ const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown'; | ||||
| const DISABLE_LOCK_KEY = 'disable-lock-screen'; | ||||
|  | ||||
| const LOCKED_STATE_STR = 'screenShield.locked'; | ||||
|  | ||||
| const BLUR_BRIGHTNESS = 0.55; | ||||
| const BLUR_RADIUS = 200; | ||||
|  | ||||
| // fraction of screen height the arrow must reach before completing | ||||
| // the slide up automatically | ||||
| var ARROW_DRAG_THRESHOLD = 0.1; | ||||
|  | ||||
| // Parameters for the arrow animation | ||||
| var N_ARROWS = 3; | ||||
| var ARROW_ANIMATION_TIME = 600; | ||||
| var ARROW_ANIMATION_PEAK_OPACITY = 0.4; | ||||
| var ARROW_IDLE_TIME = 30000; // ms | ||||
|  | ||||
| var SUMMARY_ICON_SIZE = 48; | ||||
|  | ||||
| // ScreenShield animation time | ||||
| @@ -46,373 +45,6 @@ var STANDARD_FADE_TIME = 10000; | ||||
| var MANUAL_FADE_TIME = 300; | ||||
| var CURTAIN_SLIDE_TIME = 300; | ||||
|  | ||||
| var Clock = class { | ||||
|     constructor() { | ||||
|         this.actor = new St.BoxLayout({ style_class: 'screen-shield-clock', | ||||
|                                         vertical: true }); | ||||
|  | ||||
|         this._time = new St.Label({ style_class: 'screen-shield-clock-time' }); | ||||
|         this._date = new St.Label({ style_class: 'screen-shield-clock-date' }); | ||||
|  | ||||
|         this.actor.add(this._time, { x_align: St.Align.MIDDLE }); | ||||
|         this.actor.add(this._date, { x_align: St.Align.MIDDLE }); | ||||
|  | ||||
|         this._wallClock = new GnomeDesktop.WallClock({ time_only: true }); | ||||
|         this._wallClock.connect('notify::clock', this._updateClock.bind(this)); | ||||
|  | ||||
|         this._updateClock(); | ||||
|     } | ||||
|  | ||||
|     _updateClock() { | ||||
|         this._time.text = this._wallClock.clock; | ||||
|  | ||||
|         let date = new Date(); | ||||
|         /* Translators: This is a time format for a date in | ||||
|            long format */ | ||||
|         let dateFormat = Shell.util_translate_time_string(N_("%A, %B %d")); | ||||
|         this._date.text = date.toLocaleFormat(dateFormat); | ||||
|     } | ||||
|  | ||||
|     destroy() { | ||||
|         this.actor.destroy(); | ||||
|         this._wallClock.run_dispose(); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| var NotificationsBox = class { | ||||
|     constructor() { | ||||
|         this.actor = new St.BoxLayout({ vertical: true, | ||||
|                                         name: 'screenShieldNotifications', | ||||
|                                         style_class: 'screen-shield-notifications-container' }); | ||||
|  | ||||
|         this._scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.START, | ||||
|                                                hscrollbar_policy: St.PolicyType.NEVER }); | ||||
|         this._notificationBox = new St.BoxLayout({ vertical: true, | ||||
|                                                    style_class: 'screen-shield-notifications-container' }); | ||||
|         this._scrollView.add_actor(this._notificationBox); | ||||
|  | ||||
|         this.actor.add(this._scrollView, { x_fill: true, x_align: St.Align.START }); | ||||
|  | ||||
|         this._sources = new Map(); | ||||
|         Main.messageTray.getSources().forEach(source => { | ||||
|             this._sourceAdded(Main.messageTray, source, true); | ||||
|         }); | ||||
|         this._updateVisibility(); | ||||
|  | ||||
|         this._sourceAddedId = Main.messageTray.connect('source-added', this._sourceAdded.bind(this)); | ||||
|     } | ||||
|  | ||||
|     destroy() { | ||||
|         if (this._sourceAddedId) { | ||||
|             Main.messageTray.disconnect(this._sourceAddedId); | ||||
|             this._sourceAddedId = 0; | ||||
|         } | ||||
|  | ||||
|         let items = this._sources.entries(); | ||||
|         for (let [source, obj] of items) { | ||||
|             this._removeSource(source, obj); | ||||
|         } | ||||
|  | ||||
|         this.actor.destroy(); | ||||
|     } | ||||
|  | ||||
|     _updateVisibility() { | ||||
|         this._notificationBox.visible = | ||||
|             this._notificationBox.get_children().some(a => a.visible); | ||||
|  | ||||
|         this.actor.visible = this._notificationBox.visible; | ||||
|     } | ||||
|  | ||||
|     _makeNotificationCountText(count, isChat) { | ||||
|         if (isChat) | ||||
|             return ngettext("%d new message", "%d new messages", count).format(count); | ||||
|         else | ||||
|             return ngettext("%d new notification", "%d new notifications", count).format(count); | ||||
|     } | ||||
|  | ||||
|     _makeNotificationSource(source, box) { | ||||
|         let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE); | ||||
|         box.add(sourceActor, { y_fill: true }); | ||||
|  | ||||
|         let textBox = new St.BoxLayout({ vertical: true }); | ||||
|         box.add(textBox, { y_fill: false, y_align: St.Align.START }); | ||||
|  | ||||
|         let title = new St.Label({ text: source.title, | ||||
|                                    style_class: 'screen-shield-notification-label' }); | ||||
|         textBox.add(title); | ||||
|  | ||||
|         let count = source.unseenCount; | ||||
|         let countLabel = new St.Label({ text: this._makeNotificationCountText(count, source.isChat), | ||||
|                                         style_class: 'screen-shield-notification-count-text' }); | ||||
|         textBox.add(countLabel); | ||||
|  | ||||
|         box.visible = count != 0; | ||||
|         return [title, countLabel]; | ||||
|     } | ||||
|  | ||||
|     _makeNotificationDetailedSource(source, box) { | ||||
|         let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE); | ||||
|         let sourceBin = new St.Bin({ y_align: St.Align.START, | ||||
|                                      x_align: St.Align.START, | ||||
|                                      child: sourceActor }); | ||||
|         box.add(sourceBin); | ||||
|  | ||||
|         let textBox = new St.BoxLayout({ vertical: true }); | ||||
|         box.add(textBox, { y_fill: false, y_align: St.Align.START }); | ||||
|  | ||||
|         let title = new St.Label({ text: source.title, | ||||
|                                    style_class: 'screen-shield-notification-label' }); | ||||
|         textBox.add(title); | ||||
|  | ||||
|         let visible = false; | ||||
|         for (let i = 0; i < source.notifications.length; i++) { | ||||
|             let n = source.notifications[i]; | ||||
|  | ||||
|             if (n.acknowledged) | ||||
|                 continue; | ||||
|  | ||||
|             let body = ''; | ||||
|             if (n.bannerBodyText) { | ||||
|                 body = n.bannerBodyMarkup | ||||
|                     ? n.bannerBodyText | ||||
|                     : GLib.markup_escape_text(n.bannerBodyText, -1); | ||||
|             } | ||||
|  | ||||
|             let label = new St.Label({ style_class: 'screen-shield-notification-count-text' }); | ||||
|             label.clutter_text.set_markup('<b>' + n.title + '</b> ' + body); | ||||
|             textBox.add(label); | ||||
|  | ||||
|             visible = true; | ||||
|         } | ||||
|  | ||||
|         box.visible = visible; | ||||
|         return [title, null]; | ||||
|     } | ||||
|  | ||||
|     _shouldShowDetails(source) { | ||||
|         return source.policy.detailsInLockScreen || | ||||
|                source.narrowestPrivacyScope == MessageTray.PrivacyScope.SYSTEM; | ||||
|     } | ||||
|  | ||||
|     _showSource(source, obj, box) { | ||||
|         if (obj.detailed) { | ||||
|             [obj.titleLabel, obj.countLabel] = this._makeNotificationDetailedSource(source, box); | ||||
|         } else { | ||||
|             [obj.titleLabel, obj.countLabel] = this._makeNotificationSource(source, box); | ||||
|         } | ||||
|  | ||||
|         box.visible = obj.visible && (source.unseenCount > 0); | ||||
|     } | ||||
|  | ||||
|     _sourceAdded(tray, source, initial) { | ||||
|         let obj = { | ||||
|             visible: source.policy.showInLockScreen, | ||||
|             detailed: this._shouldShowDetails(source), | ||||
|             sourceDestroyId: 0, | ||||
|             sourceCountChangedId: 0, | ||||
|             sourceTitleChangedId: 0, | ||||
|             sourceUpdatedId: 0, | ||||
|             sourceBox: null, | ||||
|             titleLabel: null, | ||||
|             countLabel: null, | ||||
|         }; | ||||
|  | ||||
|         obj.sourceBox = new St.BoxLayout({ style_class: 'screen-shield-notification-source', | ||||
|                                            x_expand: true }); | ||||
|         this._showSource(source, obj, obj.sourceBox); | ||||
|         this._notificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START }); | ||||
|  | ||||
|         obj.sourceCountChangedId = source.connect('count-updated', source => { | ||||
|             this._countChanged(source, obj); | ||||
|         }); | ||||
|         obj.sourceTitleChangedId = source.connect('title-changed', source => { | ||||
|             this._titleChanged(source, obj); | ||||
|         }); | ||||
|         obj.policyChangedId = source.policy.connect('policy-changed', (policy, key) => { | ||||
|             if (key == 'show-in-lock-screen') | ||||
|                 this._visibleChanged(source, obj); | ||||
|             else | ||||
|                 this._detailedChanged(source, obj); | ||||
|         }); | ||||
|         obj.sourceDestroyId = source.connect('destroy', source => { | ||||
|             this._onSourceDestroy(source, obj); | ||||
|         }); | ||||
|  | ||||
|         this._sources.set(source, obj); | ||||
|  | ||||
|         if (!initial) { | ||||
|             // block scrollbars while animating, if they're not needed now | ||||
|             let boxHeight = this._notificationBox.height; | ||||
|             if (this._scrollView.height >= boxHeight) | ||||
|                 this._scrollView.vscrollbar_policy = St.PolicyType.NEVER; | ||||
|  | ||||
|             let widget = obj.sourceBox; | ||||
|             let [, natHeight] = widget.get_preferred_height(-1); | ||||
|             widget.height = 0; | ||||
|             widget.ease({ | ||||
|                 height: natHeight, | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|                 duration: 250, | ||||
|                 onComplete: () => { | ||||
|                     this._scrollView.vscrollbar_policy = St.PolicyType.AUTOMATIC; | ||||
|                     widget.set_height(-1); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             this._updateVisibility(); | ||||
|             if (obj.sourceBox.visible) | ||||
|                 this.emit('wake-up-screen'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _titleChanged(source, obj) { | ||||
|         obj.titleLabel.text = source.title; | ||||
|     } | ||||
|  | ||||
|     _countChanged(source, obj) { | ||||
|         // A change in the number of notifications may change whether we show | ||||
|         // details. | ||||
|         let newDetailed = this._shouldShowDetails(source); | ||||
|         let oldDetailed = obj.detailed; | ||||
|  | ||||
|         obj.detailed = newDetailed; | ||||
|  | ||||
|         if (obj.detailed || oldDetailed != newDetailed) { | ||||
|             // A new notification was pushed, or a previous notification was destroyed. | ||||
|             // Give up, and build the list again. | ||||
|  | ||||
|             obj.sourceBox.destroy_all_children(); | ||||
|             obj.titleLabel = obj.countLabel = null; | ||||
|             this._showSource(source, obj, obj.sourceBox); | ||||
|         } else { | ||||
|             let count = source.unseenCount; | ||||
|             obj.countLabel.text = this._makeNotificationCountText(count, source.isChat); | ||||
|         } | ||||
|  | ||||
|         obj.sourceBox.visible = obj.visible && (source.unseenCount > 0); | ||||
|  | ||||
|         this._updateVisibility(); | ||||
|         if (obj.sourceBox.visible) | ||||
|             this.emit('wake-up-screen'); | ||||
|     } | ||||
|  | ||||
|     _visibleChanged(source, obj) { | ||||
|         if (obj.visible == source.policy.showInLockScreen) | ||||
|             return; | ||||
|  | ||||
|         obj.visible = source.policy.showInLockScreen; | ||||
|         obj.sourceBox.visible = obj.visible && source.unseenCount > 0; | ||||
|  | ||||
|         this._updateVisibility(); | ||||
|         if (obj.sourceBox.visible) | ||||
|             this.emit('wake-up-screen'); | ||||
|     } | ||||
|  | ||||
|     _detailedChanged(source, obj) { | ||||
|         let newDetailed = this._shouldShowDetails(source); | ||||
|         if (obj.detailed == newDetailed) | ||||
|             return; | ||||
|  | ||||
|         obj.detailed = newDetailed; | ||||
|  | ||||
|         obj.sourceBox.destroy_all_children(); | ||||
|         obj.titleLabel = obj.countLabel = null; | ||||
|         this._showSource(source, obj, obj.sourceBox); | ||||
|     } | ||||
|  | ||||
|     _onSourceDestroy(source, obj) { | ||||
|         this._removeSource(source, obj); | ||||
|         this._updateVisibility(); | ||||
|     } | ||||
|  | ||||
|     _removeSource(source, obj) { | ||||
|         obj.sourceBox.destroy(); | ||||
|         obj.sourceBox = obj.titleLabel = obj.countLabel = null; | ||||
|  | ||||
|         source.disconnect(obj.sourceDestroyId); | ||||
|         source.disconnect(obj.sourceCountChangedId); | ||||
|         source.disconnect(obj.sourceTitleChangedId); | ||||
|         source.policy.disconnect(obj.policyChangedId); | ||||
|  | ||||
|         this._sources.delete(source); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(NotificationsBox.prototype); | ||||
|  | ||||
| var Arrow = GObject.registerClass( | ||||
| class ScreenShieldArrow extends St.Bin { | ||||
|     _init(params) { | ||||
|         super._init(params); | ||||
|         this.x_fill = this.y_fill = true; | ||||
|  | ||||
|         this._drawingArea = new St.DrawingArea(); | ||||
|         this._drawingArea.connect('repaint', this._drawArrow.bind(this)); | ||||
|         this.child = this._drawingArea; | ||||
|  | ||||
|         this._shadowHelper = null; | ||||
|         this._shadowWidth = this._shadowHeight = 0; | ||||
|     } | ||||
|  | ||||
|     _drawArrow(arrow) { | ||||
|         let cr = arrow.get_context(); | ||||
|         let [w, h] = arrow.get_surface_size(); | ||||
|         let node = this.get_theme_node(); | ||||
|         let thickness = node.get_length('-arrow-thickness'); | ||||
|  | ||||
|         Clutter.cairo_set_source_color(cr, node.get_foreground_color()); | ||||
|  | ||||
|         cr.setLineCap(Cairo.LineCap.ROUND); | ||||
|         cr.setLineWidth(thickness); | ||||
|  | ||||
|         cr.moveTo(thickness / 2, h - thickness / 2); | ||||
|         cr.lineTo(w / 2, thickness); | ||||
|         cr.lineTo(w - thickness / 2, h - thickness / 2); | ||||
|         cr.stroke(); | ||||
|         cr.$dispose(); | ||||
|     } | ||||
|  | ||||
|     vfunc_get_paint_volume(volume) { | ||||
|         if (!super.vfunc_get_paint_volume(volume)) | ||||
|             return false; | ||||
|  | ||||
|         if (!this._shadow) | ||||
|             return true; | ||||
|  | ||||
|         let shadowBox = new Clutter.ActorBox(); | ||||
|         this._shadow.get_box(this._drawingArea.get_allocation_box(), shadowBox); | ||||
|  | ||||
|         volume.set_width(Math.max(shadowBox.x2 - shadowBox.x1, volume.get_width())); | ||||
|         volume.set_height(Math.max(shadowBox.y2 - shadowBox.y1, volume.get_height())); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     vfunc_style_changed() { | ||||
|         let node = this.get_theme_node(); | ||||
|         this._shadow = node.get_shadow('-arrow-shadow'); | ||||
|         if (this._shadow) | ||||
|             this._shadowHelper = St.ShadowHelper.new(this._shadow); | ||||
|         else | ||||
|             this._shadowHelper = null; | ||||
|  | ||||
|         super.vfunc_style_changed(); | ||||
|     } | ||||
|  | ||||
|     vfunc_paint() { | ||||
|         if (this._shadowHelper) { | ||||
|             this._shadowHelper.update(this._drawingArea); | ||||
|  | ||||
|             let allocation = this._drawingArea.get_allocation_box(); | ||||
|             let paintOpacity = this._drawingArea.get_paint_opacity(); | ||||
|             let framebuffer = Cogl.get_draw_framebuffer(); | ||||
|  | ||||
|             this._shadowHelper.paint(framebuffer, allocation, paintOpacity); | ||||
|         } | ||||
|  | ||||
|         this._drawingArea.paint(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| function clamp(value, min, max) { | ||||
|     return Math.max(min, Math.min(max, value)); | ||||
| } | ||||
| @@ -430,67 +62,24 @@ var ScreenShield = class { | ||||
|         this.actor = Main.layoutManager.screenShieldGroup; | ||||
|  | ||||
|         this._lockScreenState = MessageTray.State.HIDDEN; | ||||
|         this._lockScreenGroup = new St.Widget({ | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|             reactive: true, | ||||
|             can_focus: true, | ||||
|             name: 'lockScreenGroup', | ||||
|             visible: false, | ||||
|         }); | ||||
|         this._lockScreenGroup.connect('key-press-event', | ||||
|                                       this._onLockScreenKeyPress.bind(this)); | ||||
|         this._lockScreenGroup.connect('scroll-event', | ||||
|                                       this._onLockScreenScroll.bind(this)); | ||||
|         Main.ctrlAltTabManager.addGroup(this._lockScreenGroup, _("Lock"), 'changes-prevent-symbolic'); | ||||
|  | ||||
|         this._lockScreenContents = new St.Widget({ layout_manager: new Clutter.BinLayout(), | ||||
|                                                    name: 'lockScreenContents' }); | ||||
|         this._lockScreenContents.add_constraint(new Layout.MonitorConstraint({ primary: true })); | ||||
|  | ||||
|         this._lockScreenGroup.add_actor(this._lockScreenContents); | ||||
|  | ||||
|         this._backgroundGroup = new Clutter.Actor(); | ||||
|  | ||||
|         this._lockScreenGroup.add_actor(this._backgroundGroup); | ||||
|         this._backgroundGroup.lower_bottom(); | ||||
|         this._bgManagers = []; | ||||
|  | ||||
|         this._updateBackgrounds(); | ||||
|         Main.layoutManager.connect('monitors-changed', this._updateBackgrounds.bind(this)); | ||||
|  | ||||
|         this._arrowAnimationId = 0; | ||||
|         this._arrowWatchId = 0; | ||||
|         this._arrowActiveWatchId = 0; | ||||
|         this._arrowContainer = new St.BoxLayout({ style_class: 'screen-shield-arrows', | ||||
|                                                   vertical: true, | ||||
|                                                   x_align: Clutter.ActorAlign.CENTER, | ||||
|                                                   y_align: Clutter.ActorAlign.END, | ||||
|                                                   // HACK: without these, ClutterBinLayout | ||||
|                                                   // ignores alignment properties on the actor | ||||
|                                                   x_expand: true, | ||||
|                                                   y_expand: true }); | ||||
|  | ||||
|         for (let i = 0; i < N_ARROWS; i++) { | ||||
|             let arrow = new Arrow({ opacity: 0 }); | ||||
|             this._arrowContainer.add_actor(arrow); | ||||
|         } | ||||
|         this._lockScreenContents.add_actor(this._arrowContainer); | ||||
|  | ||||
|         this._dragAction = new Clutter.GestureAction(); | ||||
|         this._dragAction.connect('gesture-begin', this._onDragBegin.bind(this)); | ||||
|         this._dragAction.connect('gesture-progress', this._onDragMotion.bind(this)); | ||||
|         this._dragAction.connect('gesture-end', this._onDragEnd.bind(this)); | ||||
|         this._lockScreenGroup.add_action(this._dragAction); | ||||
|  | ||||
|         this._lockDialogGroup = new St.Widget({ x_expand: true, | ||||
|                                                 y_expand: true, | ||||
|                                                 reactive: true, | ||||
|                                                 pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }), | ||||
|                                                 name: 'lockDialogGroup' }); | ||||
|         this._lockDialogGroup.connect('key-press-event', | ||||
|                                       this._onLockScreenKeyPress.bind(this)); | ||||
|  | ||||
|         this.actor.add_actor(this._lockDialogGroup); | ||||
|         this.actor.add_actor(this._lockScreenGroup); | ||||
|  | ||||
|         this._backgroundGroup = new Clutter.Actor(); | ||||
|         this._lockDialogGroup.add_actor(this._backgroundGroup); | ||||
|         this._backgroundGroup.lower_bottom(); | ||||
|         this._bgManagers = []; | ||||
|  | ||||
|         this._updateBackgrounds(); | ||||
|         Main.layoutManager.connect('monitors-changed', this._updateBackgrounds.bind(this)); | ||||
|  | ||||
|         this._presence = new GnomeSession.Presence((proxy, error) => { | ||||
|             if (error) { | ||||
| @@ -510,14 +99,14 @@ var ScreenShield = class { | ||||
|         this._smartcardManager.connect('smartcard-inserted', | ||||
|                                        (manager, token) => { | ||||
|                                            if (this._isLocked && token.UsedToLogin) | ||||
|                                                this._liftShield(true, 0); | ||||
|                                                this._liftShield(); | ||||
|                                        }); | ||||
|  | ||||
|         this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager(); | ||||
|         this._oVirtCredentialsManager.connect('user-authenticated', | ||||
|                                               () => { | ||||
|                                                   if (this._isLocked) | ||||
|                                                       this._liftShield(true, 0); | ||||
|                                                       this._liftShield(); | ||||
|                                               }); | ||||
|  | ||||
|         this._loginManager = LoginManager.getLoginManager(); | ||||
| @@ -542,7 +131,6 @@ var ScreenShield = class { | ||||
|         this._lockSettings.connect(`changed::${DISABLE_LOCK_KEY}`, this._syncInhibitor.bind(this)); | ||||
|  | ||||
|         this._isModal = false; | ||||
|         this._hasLockScreen = false; | ||||
|         this._isGreeter = false; | ||||
|         this._isActive = false; | ||||
|         this._isLocked = false; | ||||
| @@ -585,6 +173,7 @@ var ScreenShield = class { | ||||
|     _createBackground(monitorIndex) { | ||||
|         let monitor = Main.layoutManager.monitors[monitorIndex]; | ||||
|         let widget = new St.Widget({ style_class: 'screen-shield-background', | ||||
|                                      clip_to_allocation: true, | ||||
|                                      x: monitor.x, | ||||
|                                      y: monitor.y, | ||||
|                                      width: monitor.width, | ||||
| @@ -598,12 +187,27 @@ var ScreenShield = class { | ||||
|         this._bgManagers.push(bgManager); | ||||
|  | ||||
|         this._backgroundGroup.add_child(widget); | ||||
|  | ||||
|         widget.add_effect(new Shell.BlurEffect({ | ||||
|             blur_radius: BLUR_RADIUS, | ||||
|             vertical: true, | ||||
|         })); | ||||
|         widget.add_effect(new Shell.BlurEffect({ | ||||
|             blur_radius: BLUR_RADIUS, | ||||
|             brightness: BLUR_BRIGHTNESS, | ||||
|         })); | ||||
|     } | ||||
|  | ||||
|     _updateBackgrounds() { | ||||
|         let isGreeter = Main.sessionMode.isGreeter; | ||||
|         this._backgroundGroup.visible = !isGreeter; | ||||
|  | ||||
|         for (let i = 0; i < this._bgManagers.length; i++) | ||||
|             this._bgManagers[i].destroy(); | ||||
|  | ||||
|         if (isGreeter) | ||||
|             return; | ||||
|  | ||||
|         this._bgManagers = []; | ||||
|         this._backgroundGroup.destroy_all_children(); | ||||
|  | ||||
| @@ -611,28 +215,17 @@ var ScreenShield = class { | ||||
|             this._createBackground(i); | ||||
|     } | ||||
|  | ||||
|     _liftShield(onPrimary, velocity) { | ||||
|     _liftShield() { | ||||
|         if (this._isLocked) { | ||||
|             if (this._ensureUnlockDialog(onPrimary, true /* allowCancel */)) | ||||
|                 this._hideLockScreen(true /* animate */, velocity); | ||||
|             if (this._ensureUnlockDialog(true /* allowCancel */)) | ||||
|                 this._hideLockScreen(); | ||||
|         } else { | ||||
|             this.deactivate(true /* animate */); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _maybeCancelDialog() { | ||||
|         if (!this._dialog) | ||||
|             return; | ||||
|  | ||||
|         this._dialog.cancel(); | ||||
|         if (this._isGreeter) { | ||||
|             // LoginDialog.cancel() will grab the key focus | ||||
|             // on its own, so ensure it stays on lock screen | ||||
|             // instead | ||||
|             this._lockScreenGroup.grab_key_focus(); | ||||
|         } else { | ||||
|             this._dialog = null; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _becomeModal() { | ||||
| @@ -672,31 +265,10 @@ var ScreenShield = class { | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         if (this._isLocked && | ||||
|             this._ensureUnlockDialog(true, true) && | ||||
|             this._ensureUnlockDialog(true) && | ||||
|             GLib.unichar_isgraph(unichar)) | ||||
|             this._dialog.addCharacter(unichar); | ||||
|  | ||||
|         this._liftShield(true, 0); | ||||
|         return Clutter.EVENT_STOP; | ||||
|     } | ||||
|  | ||||
|     _onLockScreenScroll(actor, event) { | ||||
|         if (this._lockScreenState != MessageTray.State.SHOWN) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         let delta = 0; | ||||
|         if (event.get_scroll_direction() == Clutter.ScrollDirection.SMOOTH) | ||||
|             delta = Math.abs(event.get_scroll_delta()[0]); | ||||
|         else | ||||
|             delta = 5; | ||||
|  | ||||
|         this._lockScreenScrollCounter += delta; | ||||
|  | ||||
|         // 7 standard scrolls to lift up | ||||
|         if (this._lockScreenScrollCounter > 35) { | ||||
|             this._liftShield(true, 0); | ||||
|         } | ||||
|  | ||||
|         return Clutter.EVENT_STOP; | ||||
|     } | ||||
|  | ||||
| @@ -728,79 +300,6 @@ var ScreenShield = class { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _animateArrows() { | ||||
|         let arrows = this._arrowContainer.get_children(); | ||||
|         let unitaryDelay = ARROW_ANIMATION_TIME / (arrows.length + 1); | ||||
|         let maxOpacity = 255 * ARROW_ANIMATION_PEAK_OPACITY; | ||||
|         for (let i = 0; i < arrows.length; i++) { | ||||
|             arrows[i].opacity = 0; | ||||
|             arrows[i].ease({ | ||||
|                 opacity: maxOpacity, | ||||
|                 delay: unitaryDelay * (N_ARROWS - (i + 1)), | ||||
|                 duration: ARROW_ANIMATION_TIME / 2, | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|                 onComplete: () => { | ||||
|                     arrows[i].ease({ | ||||
|                         opacity: 0, | ||||
|                         duration: ARROW_ANIMATION_TIME / 2, | ||||
|                         mode: Clutter.AnimationMode.EASE_IN_QUAD | ||||
|                     }); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         return GLib.SOURCE_CONTINUE; | ||||
|     } | ||||
|  | ||||
|     _onDragBegin() { | ||||
|         this._lockScreenGroup.remove_all_transitions(); | ||||
|         this._lockScreenState = MessageTray.State.HIDING; | ||||
|  | ||||
|         if (this._isLocked) | ||||
|             this._ensureUnlockDialog(false, false); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     _onDragMotion() { | ||||
|         let [, origY] = this._dragAction.get_press_coords(0); | ||||
|         let [, currentY] = this._dragAction.get_motion_coords(0); | ||||
|  | ||||
|         let newY = currentY - origY; | ||||
|         newY = clamp(newY, -global.stage.height, 0); | ||||
|  | ||||
|         this._lockScreenGroup.y = newY; | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     _onDragEnd(_action, _actor, _eventX, _eventY, _modifiers) { | ||||
|         if (this._lockScreenState != MessageTray.State.HIDING) | ||||
|             return; | ||||
|         if (this._lockScreenGroup.y < -(ARROW_DRAG_THRESHOLD * global.stage.height)) { | ||||
|             // Complete motion automatically | ||||
|             let [velocity_, velocityX_, velocityY] = this._dragAction.get_velocity(0); | ||||
|             this._liftShield(true, -velocityY); | ||||
|         } else { | ||||
|             // restore the lock screen to its original place | ||||
|             // try to use the same speed as the normal animation | ||||
|             let h = global.stage.height; | ||||
|             let duration = MANUAL_FADE_TIME * (-this._lockScreenGroup.y) / h; | ||||
|             this._lockScreenGroup.remove_all_transitions(); | ||||
|             this._lockScreenGroup.ease({ | ||||
|                 y: 0, | ||||
|                 duration, | ||||
|                 mode: Clutter.AnimationMode.EASE_IN_QUAD, | ||||
|                 onComplete: () => { | ||||
|                     this._lockScreenGroup.fixed_position_set = false; | ||||
|                     this._lockScreenState = MessageTray.State.SHOWN; | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             this._maybeCancelDialog(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _onStatusChanged(status) { | ||||
|         if (status != GnomeSession.PresenceStatus.IDLE) | ||||
|             return; | ||||
| @@ -904,57 +403,19 @@ var ScreenShield = class { | ||||
|         this.actor.show(); | ||||
|         this._isGreeter = Main.sessionMode.isGreeter; | ||||
|         this._isLocked = true; | ||||
|         if (this._ensureUnlockDialog(true, true)) | ||||
|             this._hideLockScreen(false, 0); | ||||
|         if (this._ensureUnlockDialog(true)) | ||||
|             this._hideLockScreen(); | ||||
|     } | ||||
|  | ||||
|     _hideLockScreenComplete() { | ||||
|         if (Main.sessionMode.currentMode == 'lock-screen') | ||||
|             Main.sessionMode.popMode('lock-screen'); | ||||
|  | ||||
|         this._lockScreenState = MessageTray.State.HIDDEN; | ||||
|         this._lockScreenGroup.hide(); | ||||
|  | ||||
|         if (this._dialog) { | ||||
|             this._dialog.grab_key_focus(); | ||||
|             this._dialog.navigate_focus(null, St.DirectionType.TAB_FORWARD, false); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _hideLockScreen(animate, velocity) { | ||||
|     _hideLockScreen() { | ||||
|         if (this._lockScreenState == MessageTray.State.HIDDEN) | ||||
|             return; | ||||
|  | ||||
|         this._lockScreenState = MessageTray.State.HIDING; | ||||
|  | ||||
|         this._lockScreenGroup.remove_all_transitions(); | ||||
|  | ||||
|         if (animate) { | ||||
|             // Tween the lock screen out of screen | ||||
|             // if velocity is not specified (i.e. we come here from pressing ESC), | ||||
|             // use the same speed regardless of original position | ||||
|             // if velocity is specified, it's in pixels per milliseconds | ||||
|             let h = global.stage.height; | ||||
|             let delta = (h + this._lockScreenGroup.y); | ||||
|             let minVelocity = global.stage.height / CURTAIN_SLIDE_TIME; | ||||
|  | ||||
|             velocity = Math.max(minVelocity, velocity); | ||||
|             let duration = delta / velocity; | ||||
|  | ||||
|             this._lockScreenGroup.ease({ | ||||
|                 y: -h, | ||||
|                 duration, | ||||
|                 mode: Clutter.AnimationMode.EASE_IN_QUAD, | ||||
|                 onComplete: () => this._hideLockScreenComplete() | ||||
|             }); | ||||
|         } else { | ||||
|             this._hideLockScreenComplete(); | ||||
|         } | ||||
|  | ||||
|         this._lockScreenState = MessageTray.State.HIDDEN; | ||||
|         this._cursorTracker.set_pointer_visible(true); | ||||
|     } | ||||
|  | ||||
|     _ensureUnlockDialog(onPrimary, allowCancel) { | ||||
|     _ensureUnlockDialog(allowCancel) { | ||||
|         if (!this._dialog) { | ||||
|             let constructor = Main.sessionMode.unlockDialog; | ||||
|             if (!constructor) { | ||||
| @@ -967,7 +428,7 @@ var ScreenShield = class { | ||||
|  | ||||
|  | ||||
|             let time = global.get_current_time(); | ||||
|             if (!this._dialog.open(time, onPrimary)) { | ||||
|             if (!this._dialog.open(time)) { | ||||
|                 // This is kind of an impossible error: we're already modal | ||||
|                 // by the time we reach this... | ||||
|                 log('Could not open login dialog: failed to acquire grab'); | ||||
| @@ -995,20 +456,17 @@ var ScreenShield = class { | ||||
|         if (this._lockScreenState != MessageTray.State.HIDDEN) | ||||
|             return; | ||||
|  | ||||
|         this._ensureLockScreen(); | ||||
|         this._lockDialogGroup.scale_x = 1; | ||||
|         this._lockDialogGroup.scale_y = 1; | ||||
|         this._ensureUnlockDialog(false); | ||||
|  | ||||
|         this._lockScreenGroup.show(); | ||||
|         this._lockScreenState = MessageTray.State.SHOWING; | ||||
|  | ||||
|         let fadeToBlack = params.fadeToBlack; | ||||
|  | ||||
|         if (params.animateLockScreen) { | ||||
|             this._lockScreenGroup.y = -global.screen_height; | ||||
|             this._lockScreenGroup.remove_all_transitions(); | ||||
|             this._lockScreenGroup.ease({ | ||||
|                 y: 0, | ||||
|             this._lockDialogGroup.translation_y = -global.screen_height; | ||||
|             this._lockDialogGroup.remove_all_transitions(); | ||||
|             this._lockDialogGroup.ease({ | ||||
|                 translation_y: 0, | ||||
|                 duration: MANUAL_FADE_TIME, | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|                 onComplete: () => { | ||||
| @@ -1016,73 +474,17 @@ var ScreenShield = class { | ||||
|                 } | ||||
|             }); | ||||
|         } else { | ||||
|             this._lockScreenGroup.fixed_position_set = false; | ||||
|             this._lockScreenShown({ fadeToBlack: fadeToBlack, | ||||
|                                     animateFade: false }); | ||||
|         } | ||||
|  | ||||
|         this._lockScreenGroup.grab_key_focus(); | ||||
|         this._lockDialogGroup.grab_key_focus(); | ||||
|  | ||||
|         if (Main.sessionMode.currentMode != 'lock-screen') | ||||
|             Main.sessionMode.pushMode('lock-screen'); | ||||
|     } | ||||
|  | ||||
|     _startArrowAnimation() { | ||||
|         this._arrowActiveWatchId = 0; | ||||
|  | ||||
|         if (!this._arrowAnimationId) { | ||||
|             this._arrowAnimationId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 6000, this._animateArrows.bind(this)); | ||||
|             GLib.Source.set_name_by_id(this._arrowAnimationId, '[gnome-shell] this._animateArrows'); | ||||
|             this._animateArrows(); | ||||
|         } | ||||
|  | ||||
|         if (!this._arrowWatchId) | ||||
|             this._arrowWatchId = this.idleMonitor.add_idle_watch(ARROW_IDLE_TIME, | ||||
|                                                                  this._pauseArrowAnimation.bind(this)); | ||||
|     } | ||||
|  | ||||
|     _pauseArrowAnimation() { | ||||
|         if (this._arrowAnimationId) { | ||||
|             GLib.source_remove(this._arrowAnimationId); | ||||
|             this._arrowAnimationId = 0; | ||||
|         } | ||||
|  | ||||
|         if (!this._arrowActiveWatchId) | ||||
|             this._arrowActiveWatchId = this.idleMonitor.add_user_active_watch(this._startArrowAnimation.bind(this)); | ||||
|     } | ||||
|  | ||||
|     _stopArrowAnimation() { | ||||
|         if (this._arrowAnimationId) { | ||||
|             GLib.source_remove(this._arrowAnimationId); | ||||
|             this._arrowAnimationId = 0; | ||||
|         } | ||||
|         if (this._arrowActiveWatchId) { | ||||
|             this.idleMonitor.remove_watch(this._arrowActiveWatchId); | ||||
|             this._arrowActiveWatchId = 0; | ||||
|         } | ||||
|         if (this._arrowWatchId) { | ||||
|             this.idleMonitor.remove_watch(this._arrowWatchId); | ||||
|             this._arrowWatchId = 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _checkArrowAnimation() { | ||||
|         let idleTime = this.idleMonitor.get_idletime(); | ||||
|  | ||||
|         if (idleTime < ARROW_IDLE_TIME) | ||||
|             this._startArrowAnimation(); | ||||
|         else | ||||
|             this._pauseArrowAnimation(); | ||||
|         if (Main.sessionMode.currentMode != 'unlock-dialog') | ||||
|             Main.sessionMode.pushMode('unlock-dialog'); | ||||
|     } | ||||
|  | ||||
|     _lockScreenShown(params) { | ||||
|         if (this._dialog && !this._isGreeter) { | ||||
|             this._dialog.destroy(); | ||||
|             this._dialog = null; | ||||
|         } | ||||
|  | ||||
|         this._checkArrowAnimation(); | ||||
|  | ||||
|         let motionId = global.stage.connect('captured-event', (stage, event) => { | ||||
|             if (event.type() == Clutter.EventType.MOTION) { | ||||
|                 this._cursorTracker.set_pointer_visible(true); | ||||
| @@ -1094,8 +496,6 @@ var ScreenShield = class { | ||||
|         this._cursorTracker.set_pointer_visible(false); | ||||
|  | ||||
|         this._lockScreenState = MessageTray.State.SHOWN; | ||||
|         this._lockScreenGroup.fixed_position_set = false; | ||||
|         this._lockScreenScrollCounter = 0; | ||||
|  | ||||
|         if (params.fadeToBlack && params.animateFade) { | ||||
|             // Take a beat | ||||
| @@ -1118,54 +518,6 @@ var ScreenShield = class { | ||||
|         this.emit('lock-screen-shown'); | ||||
|     } | ||||
|  | ||||
|     // Some of the actors in the lock screen are heavy in | ||||
|     // resources, so we only create them when needed | ||||
|     _ensureLockScreen() { | ||||
|         if (this._hasLockScreen) | ||||
|             return; | ||||
|  | ||||
|         this._lockScreenContentsBox = new St.BoxLayout({ x_align: Clutter.ActorAlign.CENTER, | ||||
|                                                          y_align: Clutter.ActorAlign.CENTER, | ||||
|                                                          x_expand: true, | ||||
|                                                          y_expand: true, | ||||
|                                                          vertical: true, | ||||
|                                                          style_class: 'screen-shield-contents-box' }); | ||||
|         this._clock = new Clock(); | ||||
|         this._lockScreenContentsBox.add(this._clock.actor, { x_fill: true, | ||||
|                                                              y_fill: true }); | ||||
|  | ||||
|         this._lockScreenContents.add_actor(this._lockScreenContentsBox); | ||||
|  | ||||
|         this._notificationsBox = new NotificationsBox(); | ||||
|         this._wakeUpScreenId = this._notificationsBox.connect('wake-up-screen', this._wakeUpScreen.bind(this)); | ||||
|         this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true, | ||||
|                                                                         y_fill: true, | ||||
|                                                                         expand: true }); | ||||
|  | ||||
|         this._hasLockScreen = true; | ||||
|     } | ||||
|  | ||||
|     _wakeUpScreen() { | ||||
|         this._onUserBecameActive(); | ||||
|         this.emit('wake-up-screen'); | ||||
|     } | ||||
|  | ||||
|     _clearLockScreen() { | ||||
|         this._clock.destroy(); | ||||
|         this._clock = null; | ||||
|  | ||||
|         if (this._notificationsBox) { | ||||
|             this._notificationsBox.disconnect(this._wakeUpScreenId); | ||||
|             this._notificationsBox.destroy(); | ||||
|             this._notificationsBox = null; | ||||
|         } | ||||
|  | ||||
|         this._stopArrowAnimation(); | ||||
|  | ||||
|         this._lockScreenContentsBox.destroy(); | ||||
|  | ||||
|         this._hasLockScreen = false; | ||||
|     } | ||||
|  | ||||
|     get locked() { | ||||
|         return this._isLocked; | ||||
| @@ -1180,20 +532,12 @@ var ScreenShield = class { | ||||
|     } | ||||
|  | ||||
|     deactivate(animate) { | ||||
|         if (this._dialog) | ||||
|             this._dialog.finish(() => this._continueDeactivate(animate)); | ||||
|         else | ||||
|             this._continueDeactivate(animate); | ||||
|         this._dialog.finish(() => this._continueDeactivate(animate)); | ||||
|     } | ||||
|  | ||||
|     _continueDeactivate(animate) { | ||||
|         this._hideLockScreen(animate, 0); | ||||
|         this._hideLockScreen(); | ||||
|  | ||||
|         if (this._hasLockScreen) | ||||
|             this._clearLockScreen(); | ||||
|  | ||||
|         if (Main.sessionMode.currentMode == 'lock-screen') | ||||
|             Main.sessionMode.popMode('lock-screen'); | ||||
|         if (Main.sessionMode.currentMode == 'unlock-dialog') | ||||
|             Main.sessionMode.popMode('unlock-dialog'); | ||||
|  | ||||
| @@ -1210,7 +554,7 @@ var ScreenShield = class { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (this._dialog && !this._isGreeter) | ||||
|         if (!this._isGreeter) | ||||
|             this._dialog.popModal(); | ||||
|  | ||||
|         if (this._isModal) { | ||||
| @@ -1219,9 +563,8 @@ var ScreenShield = class { | ||||
|         } | ||||
|  | ||||
|         this._lockDialogGroup.ease({ | ||||
|             scale_x: 0, | ||||
|             scale_y: 0, | ||||
|             duration: animate ? Overview.ANIMATION_TIME : 0, | ||||
|             translation_y: -global.screen_height, | ||||
|             duration: CURTAIN_SLIDE_TIME, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             onComplete: () => this._completeDeactivate() | ||||
|         }); | ||||
| @@ -1260,8 +603,7 @@ var ScreenShield = class { | ||||
|  | ||||
|         this.actor.show(); | ||||
|  | ||||
|         if (Main.sessionMode.currentMode != 'unlock-dialog' && | ||||
|             Main.sessionMode.currentMode != 'lock-screen') { | ||||
|         if (Main.sessionMode.currentMode != 'unlock-dialog') { | ||||
|             this._isGreeter = Main.sessionMode.isGreeter; | ||||
|             if (!this._isGreeter) | ||||
|                 Main.sessionMode.pushMode('unlock-dialog'); | ||||
|   | ||||
| @@ -53,19 +53,6 @@ const _modes = { | ||||
|         panelStyle: 'login-screen' | ||||
|     }, | ||||
|  | ||||
|     'lock-screen': { | ||||
|         isLocked: true, | ||||
|         isGreeter: undefined, | ||||
|         unlockDialog: undefined, | ||||
|         components: ['polkitAgent', 'telepathyClient'], | ||||
|         panel: { | ||||
|             left: [], | ||||
|             center: [], | ||||
|             right: ['aggregateMenu'] | ||||
|         }, | ||||
|         panelStyle: 'lock-screen' | ||||
|     }, | ||||
|  | ||||
|     'unlock-dialog': { | ||||
|         isLocked: true, | ||||
|         unlockDialog: undefined, | ||||
|   | ||||
| @@ -1,17 +1,376 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported UnlockDialog */ | ||||
|  | ||||
| const { AccountsService, Atk, Clutter, | ||||
|         Gdm, Gio, GLib, GObject, Meta, Shell, St } = imports.gi; | ||||
| const { AccountsService, Atk, Clutter, Gdm, Gio, GLib, | ||||
|         GnomeDesktop, GObject, Meta, Shell, St } = imports.gi; | ||||
|  | ||||
| const Layout = imports.ui.layout; | ||||
| const Main = imports.ui.main; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const AuthPrompt = imports.gdm.authPrompt; | ||||
|  | ||||
| // The timeout before going back automatically to the lock screen (in seconds) | ||||
| const IDLE_TIMEOUT = 2 * 60; | ||||
|  | ||||
| var SUMMARY_ICON_SIZE = 24; | ||||
|  | ||||
| var Clock = class { | ||||
|     constructor() { | ||||
|         this.actor = new St.BoxLayout({ | ||||
|             style_class: 'screen-shield-clock', | ||||
|             vertical: true, | ||||
|         }); | ||||
|  | ||||
|         this._time = new St.Label({ style_class: 'screen-shield-clock-time' }); | ||||
|         this._date = new St.Label({ style_class: 'screen-shield-clock-date' }); | ||||
|  | ||||
|         this.actor.add(this._time, { x_align: St.Align.MIDDLE }); | ||||
|         this.actor.add(this._date, { x_align: St.Align.MIDDLE }); | ||||
|  | ||||
|         this._wallClock = new GnomeDesktop.WallClock({ time_only: true }); | ||||
|         this._wallClock.connect('notify::clock', this._updateClock.bind(this)); | ||||
|  | ||||
|         this._updateClock(); | ||||
|     } | ||||
|  | ||||
|     _updateClock() { | ||||
|         this._time.text = this._wallClock.clock; | ||||
|  | ||||
|         let date = new Date(); | ||||
|         /* Translators: This is a time format for a date in | ||||
|            long format */ | ||||
|         let dateFormat = Shell.util_translate_time_string(N_("%A, %B %d")); | ||||
|         this._date.text = date.toLocaleFormat(dateFormat); | ||||
|     } | ||||
|  | ||||
|     destroy() { | ||||
|         this.actor.destroy(); | ||||
|         this._wallClock.run_dispose(); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| var NotificationsBox = class { | ||||
|     constructor() { | ||||
|         this.actor = new St.BoxLayout({ vertical: true, | ||||
|                                         name: 'screenShieldNotifications', | ||||
|                                         style_class: 'screen-shield-notifications-container' }); | ||||
|  | ||||
|         this._scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.START, | ||||
|                                                hscrollbar_policy: St.PolicyType.NEVER }); | ||||
|         this._notificationBox = new St.BoxLayout({ vertical: true, | ||||
|                                                    style_class: 'screen-shield-notifications-container' }); | ||||
|         this._scrollView.add_actor(this._notificationBox); | ||||
|  | ||||
|         this.actor.add(this._scrollView, { x_fill: true, x_align: St.Align.START }); | ||||
|  | ||||
|         this._sources = new Map(); | ||||
|         Main.messageTray.getSources().forEach(source => { | ||||
|             this._sourceAdded(Main.messageTray, source, true); | ||||
|         }); | ||||
|         this._updateVisibility(); | ||||
|  | ||||
|         this._sourceAddedId = Main.messageTray.connect('source-added', this._sourceAdded.bind(this)); | ||||
|     } | ||||
|  | ||||
|     destroy() { | ||||
|         if (this._sourceAddedId) { | ||||
|             Main.messageTray.disconnect(this._sourceAddedId); | ||||
|             this._sourceAddedId = 0; | ||||
|         } | ||||
|  | ||||
|         let items = this._sources.entries(); | ||||
|         for (let [source, obj] of items) { | ||||
|             this._removeSource(source, obj); | ||||
|         } | ||||
|  | ||||
|         this.actor.destroy(); | ||||
|     } | ||||
|  | ||||
|     _updateVisibility() { | ||||
|         this._notificationBox.visible = | ||||
|             this._notificationBox.get_children().some(a => a.visible); | ||||
|  | ||||
|         this.actor.visible = this._notificationBox.visible; | ||||
|     } | ||||
|  | ||||
|     _makeNotificationSource(source, box) { | ||||
|         let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE); | ||||
|         box.add(sourceActor, { y_fill: true }); | ||||
|  | ||||
|         let title = new St.Label({ | ||||
|             text: source.title, | ||||
|             style_class: 'screen-shield-notification-label', | ||||
|             x_expand: true, | ||||
|             y_align: Clutter.ActorAlign.START, | ||||
|             y_expand: true, | ||||
|             y_align: Clutter.ActorAlign.CENTER, | ||||
|         }); | ||||
|         box.add_child(title); | ||||
|  | ||||
|         let count = source.unseenCount; | ||||
|         let countLabel = new St.Label({ | ||||
|             text: '%d'.format(count), | ||||
|             style_class: 'screen-shield-notification-count-text', | ||||
|             y_expand: true, | ||||
|             y_align: Clutter.ActorAlign.CENTER, | ||||
|         }); | ||||
|         box.add_child(countLabel); | ||||
|  | ||||
|         box.visible = count != 0; | ||||
|         return [title, countLabel]; | ||||
|     } | ||||
|  | ||||
|     _makeNotificationDetailedSource(source, box) { | ||||
|         let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE); | ||||
|         let sourceBin = new St.Bin({ y_align: St.Align.START, | ||||
|                                      x_align: St.Align.START, | ||||
|                                      child: sourceActor }); | ||||
|         box.add(sourceBin); | ||||
|  | ||||
|         let textBox = new St.BoxLayout({ vertical: true }); | ||||
|         box.add(textBox, { y_fill: false, y_align: St.Align.START }); | ||||
|  | ||||
|         let title = new St.Label({ text: source.title, | ||||
|                                    style_class: 'screen-shield-notification-label' }); | ||||
|         textBox.add(title); | ||||
|  | ||||
|         let visible = false; | ||||
|         for (let i = 0; i < source.notifications.length; i++) { | ||||
|             let n = source.notifications[i]; | ||||
|  | ||||
|             if (n.acknowledged) | ||||
|                 continue; | ||||
|  | ||||
|             let body = ''; | ||||
|             if (n.bannerBodyText) { | ||||
|                 body = n.bannerBodyMarkup | ||||
|                     ? n.bannerBodyText | ||||
|                     : GLib.markup_escape_text(n.bannerBodyText, -1); | ||||
|             } | ||||
|  | ||||
|             let label = new St.Label({ style_class: 'screen-shield-notification-count-text' }); | ||||
|             label.clutter_text.set_markup('<b>' + n.title + '</b> ' + body); | ||||
|             textBox.add(label); | ||||
|  | ||||
|             visible = true; | ||||
|         } | ||||
|  | ||||
|         box.visible = visible; | ||||
|         return [title, null]; | ||||
|     } | ||||
|  | ||||
|     _shouldShowDetails(source) { | ||||
|         return source.policy.detailsInLockScreen || | ||||
|                source.narrowestPrivacyScope == MessageTray.PrivacyScope.SYSTEM; | ||||
|     } | ||||
|  | ||||
|     _showSource(source, obj, box) { | ||||
|         if (obj.detailed) { | ||||
|             [obj.titleLabel, obj.countLabel] = this._makeNotificationDetailedSource(source, box); | ||||
|         } else { | ||||
|             [obj.titleLabel, obj.countLabel] = this._makeNotificationSource(source, box); | ||||
|         } | ||||
|  | ||||
|         box.visible = obj.visible && (source.unseenCount > 0); | ||||
|     } | ||||
|  | ||||
|     _sourceAdded(tray, source, initial) { | ||||
|         let obj = { | ||||
|             visible: source.policy.showInLockScreen, | ||||
|             detailed: this._shouldShowDetails(source), | ||||
|             sourceDestroyId: 0, | ||||
|             sourceCountChangedId: 0, | ||||
|             sourceTitleChangedId: 0, | ||||
|             sourceUpdatedId: 0, | ||||
|             sourceBox: null, | ||||
|             titleLabel: null, | ||||
|             countLabel: null, | ||||
|         }; | ||||
|  | ||||
|         obj.sourceBox = new St.BoxLayout({ style_class: 'screen-shield-notification-source', | ||||
|                                            x_expand: true }); | ||||
|         this._showSource(source, obj, obj.sourceBox); | ||||
|         this._notificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START }); | ||||
|  | ||||
|         obj.sourceCountChangedId = source.connect('count-updated', source => { | ||||
|             this._countChanged(source, obj); | ||||
|         }); | ||||
|         obj.sourceTitleChangedId = source.connect('title-changed', source => { | ||||
|             this._titleChanged(source, obj); | ||||
|         }); | ||||
|         obj.policyChangedId = source.policy.connect('policy-changed', (policy, key) => { | ||||
|             if (key == 'show-in-lock-screen') | ||||
|                 this._visibleChanged(source, obj); | ||||
|             else | ||||
|                 this._detailedChanged(source, obj); | ||||
|         }); | ||||
|         obj.sourceDestroyId = source.connect('destroy', source => { | ||||
|             this._onSourceDestroy(source, obj); | ||||
|         }); | ||||
|  | ||||
|         this._sources.set(source, obj); | ||||
|  | ||||
|         if (!initial) { | ||||
|             // block scrollbars while animating, if they're not needed now | ||||
|             let boxHeight = this._notificationBox.height; | ||||
|             if (this._scrollView.height >= boxHeight) | ||||
|                 this._scrollView.vscrollbar_policy = St.PolicyType.NEVER; | ||||
|  | ||||
|             let widget = obj.sourceBox; | ||||
|             let [, natHeight] = widget.get_preferred_height(-1); | ||||
|             widget.height = 0; | ||||
|             widget.ease({ | ||||
|                 height: natHeight, | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|                 duration: 250, | ||||
|                 onComplete: () => { | ||||
|                     this._scrollView.vscrollbar_policy = St.PolicyType.AUTOMATIC; | ||||
|                     widget.set_height(-1); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             this._updateVisibility(); | ||||
|             if (obj.sourceBox.visible) | ||||
|                 this.emit('wake-up-screen'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _titleChanged(source, obj) { | ||||
|         obj.titleLabel.text = source.title; | ||||
|     } | ||||
|  | ||||
|     _countChanged(source, obj) { | ||||
|         // A change in the number of notifications may change whether we show | ||||
|         // details. | ||||
|         let newDetailed = this._shouldShowDetails(source); | ||||
|         let oldDetailed = obj.detailed; | ||||
|  | ||||
|         obj.detailed = newDetailed; | ||||
|  | ||||
|         if (obj.detailed || oldDetailed != newDetailed) { | ||||
|             // A new notification was pushed, or a previous notification was destroyed. | ||||
|             // Give up, and build the list again. | ||||
|  | ||||
|             obj.sourceBox.destroy_all_children(); | ||||
|             obj.titleLabel = obj.countLabel = null; | ||||
|             this._showSource(source, obj, obj.sourceBox); | ||||
|         } else { | ||||
|             let count = source.unseenCount; | ||||
|             obj.countLabel.text = '%d'.format(count); | ||||
|         } | ||||
|  | ||||
|         obj.sourceBox.visible = obj.visible && (source.unseenCount > 0); | ||||
|  | ||||
|         this._updateVisibility(); | ||||
|         if (obj.sourceBox.visible) | ||||
|             this.emit('wake-up-screen'); | ||||
|     } | ||||
|  | ||||
|     _visibleChanged(source, obj) { | ||||
|         if (obj.visible == source.policy.showInLockScreen) | ||||
|             return; | ||||
|  | ||||
|         obj.visible = source.policy.showInLockScreen; | ||||
|         obj.sourceBox.visible = obj.visible && source.unseenCount > 0; | ||||
|  | ||||
|         this._updateVisibility(); | ||||
|         if (obj.sourceBox.visible) | ||||
|             this.emit('wake-up-screen'); | ||||
|     } | ||||
|  | ||||
|     _detailedChanged(source, obj) { | ||||
|         let newDetailed = this._shouldShowDetails(source); | ||||
|         if (obj.detailed == newDetailed) | ||||
|             return; | ||||
|  | ||||
|         obj.detailed = newDetailed; | ||||
|  | ||||
|         obj.sourceBox.destroy_all_children(); | ||||
|         obj.titleLabel = obj.countLabel = null; | ||||
|         this._showSource(source, obj, obj.sourceBox); | ||||
|     } | ||||
|  | ||||
|     _onSourceDestroy(source, obj) { | ||||
|         this._removeSource(source, obj); | ||||
|         this._updateVisibility(); | ||||
|     } | ||||
|  | ||||
|     _removeSource(source, obj) { | ||||
|         obj.sourceBox.destroy(); | ||||
|         obj.sourceBox = obj.titleLabel = obj.countLabel = null; | ||||
|  | ||||
|         source.disconnect(obj.sourceDestroyId); | ||||
|         source.disconnect(obj.sourceCountChangedId); | ||||
|         source.disconnect(obj.sourceTitleChangedId); | ||||
|         source.policy.disconnect(obj.policyChangedId); | ||||
|  | ||||
|         this._sources.delete(source); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(NotificationsBox.prototype); | ||||
|  | ||||
| var UnlockDialogLayout = GObject.registerClass( | ||||
| class UnlockDialogLayout extends Clutter.LayoutManager { | ||||
|     _init(clockStack, notifications) { | ||||
|         super._init(); | ||||
|  | ||||
|         this._clockStack = clockStack; | ||||
|         this._notifications = notifications; | ||||
|     } | ||||
|  | ||||
|     vfunc_get_preferred_width(container, forHeight) { | ||||
|         return this._clockStack.get_preferred_width(forHeight); | ||||
|     } | ||||
|  | ||||
|     vfunc_get_preferred_height(container, forWidth) { | ||||
|         return this._clockStack.get_preferred_height(forWidth); | ||||
|     } | ||||
|  | ||||
|     vfunc_allocate(container, box, flags) { | ||||
|         let [width, height] = box.get_size(); | ||||
|  | ||||
|         let tenthOfHeight = height / 10.0; | ||||
|         let thirdOfHeight = height / 3.0; | ||||
|  | ||||
|         let [clockStackWidth, clockStackHeight] = | ||||
|             this._clockStack.get_preferred_size(); | ||||
|  | ||||
|         let [, , notificationsWidth, notificationsHeight] = | ||||
|             this._notifications.get_preferred_size(); | ||||
|  | ||||
|         let columnWidth = Math.max(clockStackWidth, notificationsWidth); | ||||
|  | ||||
|         let columnX1 = Math.floor(width / 2.0 - columnWidth / 2.0); | ||||
|         let actorBox = new Clutter.ActorBox(); | ||||
|  | ||||
|         // Notifications | ||||
|         let maxNotificationsHeight = Math.min( | ||||
|             notificationsHeight, | ||||
|             height - tenthOfHeight - clockStackHeight); | ||||
|  | ||||
|         actorBox.x1 = columnX1; | ||||
|         actorBox.y1 = height - maxNotificationsHeight; | ||||
|         actorBox.x2 = columnX1 + columnWidth; | ||||
|         actorBox.y2 = actorBox.y1 + maxNotificationsHeight; | ||||
|  | ||||
|         this._notifications.allocate(actorBox, flags); | ||||
|  | ||||
|         // Clock Stack | ||||
|         let clockStackY = Math.min( | ||||
|             thirdOfHeight, | ||||
|             height - clockStackHeight - maxNotificationsHeight); | ||||
|  | ||||
|         actorBox.x1 = columnX1; | ||||
|         actorBox.y1 = clockStackY; | ||||
|         actorBox.x2 = columnX1 + columnWidth; | ||||
|         actorBox.y2 = clockStackY + clockStackHeight; | ||||
|  | ||||
|         this._clockStack.allocate(actorBox, flags); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| var UnlockDialog = GObject.registerClass({ | ||||
|     Signals: { 'failed': {} }, | ||||
| }, class UnlockDialog extends St.Widget { | ||||
| @@ -19,10 +378,16 @@ var UnlockDialog = GObject.registerClass({ | ||||
|         super._init({ | ||||
|             accessible_role: Atk.Role.WINDOW, | ||||
|             style_class: 'login-dialog', | ||||
|             layout_manager: new Clutter.BoxLayout(), | ||||
|             reactive: true, | ||||
|             visible: false, | ||||
|         }); | ||||
|  | ||||
|         let tapAction = new Clutter.TapAction(); | ||||
|         tapAction.connect('tap', () => { | ||||
|             this._showAuth(); | ||||
|         }) | ||||
|         this.add_action(tapAction); | ||||
|  | ||||
|         this.add_constraint(new Layout.MonitorConstraint({ primary: true })); | ||||
|         parentActor.add_child(this); | ||||
|  | ||||
| @@ -30,21 +395,31 @@ var UnlockDialog = GObject.registerClass({ | ||||
|         this._userName = GLib.get_user_name(); | ||||
|         this._user = this._userManager.get_user(this._userName); | ||||
|  | ||||
|         this._promptBox = new St.BoxLayout({ vertical: true, | ||||
|                                              x_align: Clutter.ActorAlign.CENTER, | ||||
|                                              y_align: Clutter.ActorAlign.CENTER, | ||||
|                                              x_expand: true, | ||||
|                                              y_expand: true }); | ||||
|         this.add_child(this._promptBox); | ||||
|         let clockStack = new Shell.Stack(); | ||||
|         this.add_child(clockStack); | ||||
|  | ||||
|         this._clock = new Clock(); | ||||
|         clockStack.add_child(this._clock.actor); | ||||
|  | ||||
|         this._activePage  = this._clock.actor; | ||||
|  | ||||
|         this._authBox = new St.BoxLayout({ | ||||
|             x_align: Clutter.ActorAlign.CENTER, | ||||
|             y_align: Clutter.ActorAlign.CENTER, | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|             vertical: true, | ||||
|             visible: false, | ||||
|         }); | ||||
|         clockStack.add_child(this._authBox); | ||||
|  | ||||
|         this._authPrompt = new AuthPrompt.AuthPrompt(new Gdm.Client(), AuthPrompt.AuthPromptMode.UNLOCK_ONLY); | ||||
|         this._authPrompt.connect('failed', this._fail.bind(this)); | ||||
|         this._authPrompt.connect('cancelled', this._fail.bind(this)); | ||||
|         this._authPrompt.connect('reset', this._onReset.bind(this)); | ||||
|         this._authPrompt.setPasswordChar('\u25cf'); | ||||
|         this._authPrompt.nextButton.label = _("Unlock"); | ||||
|  | ||||
|         this._promptBox.add_child(this._authPrompt.actor); | ||||
|         this._authBox.add_child(this._authPrompt.actor); | ||||
|  | ||||
|         this.allowCancel = false; | ||||
|  | ||||
| @@ -59,11 +434,15 @@ var UnlockDialog = GObject.registerClass({ | ||||
|                                                     x_align: St.Align.START, | ||||
|                                                     x_fill: false }); | ||||
|             this._otherUserButton.connect('clicked', this._otherUserClicked.bind(this)); | ||||
|             this._promptBox.add_child(this._otherUserButton); | ||||
|             this._authBox.add_child(this._otherUserButton); | ||||
|         } else { | ||||
|             this._otherUserButton = null; | ||||
|         } | ||||
|  | ||||
|         this._notificationsBox = new NotificationsBox(); | ||||
|         this._wakeUpScreenId = this._notificationsBox.connect('wake-up-screen', this._wakeUpScreen.bind(this)); | ||||
|         this.add_child(this._notificationsBox.actor); | ||||
|  | ||||
|         this._authPrompt.reset(); | ||||
|         this._updateSensitivity(true); | ||||
|  | ||||
| @@ -72,9 +451,81 @@ var UnlockDialog = GObject.registerClass({ | ||||
|         this._idleMonitor = Meta.IdleMonitor.get_core(); | ||||
|         this._idleWatchId = this._idleMonitor.add_idle_watch(IDLE_TIMEOUT * 1000, this._escape.bind(this)); | ||||
|  | ||||
|         this.layout_manager = new UnlockDialogLayout( | ||||
|             clockStack, | ||||
|             this._notificationsBox.actor); | ||||
|  | ||||
|         this.connect('destroy', this._onDestroy.bind(this)); | ||||
|     } | ||||
|  | ||||
|     _showClock() { | ||||
|         if (this._activePage == this._clock.actor) | ||||
|             return; | ||||
|  | ||||
|         this._activePage = this._clock.actor; | ||||
|         this._clock.actor.show(); | ||||
|  | ||||
|         this._authBox.ease({ | ||||
|             opacity: 0, | ||||
|             duration: 300, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             onComplete: () => this._authBox.hide(), | ||||
|         }); | ||||
|  | ||||
|         this._clock.actor.ease({ | ||||
|             opacity: 255, | ||||
|             duration: 300, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     _showAuth() { | ||||
|         if (this._activePage == this._authBox) | ||||
|             return; | ||||
|  | ||||
|         this._activePage = this._authBox; | ||||
|         this._authBox.show(); | ||||
|  | ||||
|         this._clock.actor.ease({ | ||||
|             opacity: 0, | ||||
|             duration: 300, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             onComplete: () => this._clock.actor.hide(), | ||||
|         }); | ||||
|  | ||||
|         this._authBox.ease({ | ||||
|             opacity: 255, | ||||
|             duration: 300, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     vfunc_captured_event(event) { | ||||
|         if (event.type() != Clutter.EventType.KEY_PRESS) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         if (this._activePage == this._authBox) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         let symbol = event.get_key_symbol(); | ||||
|         let unichar = event.get_key_unicode(); | ||||
|  | ||||
|         let isEnter = (symbol == Clutter.KEY_Return || | ||||
|                        symbol == Clutter.KEY_KP_Enter || | ||||
|                        symbol == Clutter.KEY_ISO_Enter); | ||||
|         let isEscape = (symbol == Clutter.KEY_Escape); | ||||
|         let isLiftChar = (GLib.unichar_isprint(unichar) && | ||||
|                           (this._activePage == this._clock.actor || | ||||
|                           !GLib.unichar_isgraph(unichar))); | ||||
|  | ||||
|         if (!isEnter && !isEscape && !isLiftChar) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         this._showAuth(); | ||||
|  | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
|  | ||||
|     _updateSensitivity(sensitive) { | ||||
|         this._authPrompt.updateSensitivity(sensitive); | ||||
|  | ||||
| @@ -85,6 +536,7 @@ var UnlockDialog = GObject.registerClass({ | ||||
|     } | ||||
|  | ||||
|     _fail() { | ||||
|         this._showClock(); | ||||
|         this.emit('failed'); | ||||
|     } | ||||
|  | ||||
| @@ -111,9 +563,24 @@ var UnlockDialog = GObject.registerClass({ | ||||
|         this._authPrompt.cancel(); | ||||
|     } | ||||
|  | ||||
|     _wakeUpScreen() { | ||||
|         // FIXME | ||||
|         //this._onUserBecameActive(); | ||||
|         this.emit('wake-up-screen'); | ||||
|     } | ||||
|  | ||||
|     _onDestroy() { | ||||
|         this.popModal(); | ||||
|  | ||||
|         if (this._notificationsBox) { | ||||
|             this._notificationsBox.disconnect(this._wakeUpScreenId); | ||||
|             this._notificationsBox.destroy(); | ||||
|             this._notificationsBox = null; | ||||
|         } | ||||
|  | ||||
|         this._clock.destroy(); | ||||
|         this._clock = null; | ||||
|  | ||||
|         if (this._idleWatchId) { | ||||
|             this._idleMonitor.remove_watch(this._idleWatchId); | ||||
|             this._idleWatchId = 0; | ||||
| @@ -122,11 +589,10 @@ var UnlockDialog = GObject.registerClass({ | ||||
|  | ||||
|     cancel() { | ||||
|         this._authPrompt.cancel(); | ||||
|  | ||||
|         this.destroy(); | ||||
|     } | ||||
|  | ||||
|     addCharacter(unichar) { | ||||
|         this._showAuth(); | ||||
|         this._authPrompt.addCharacter(unichar); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -95,6 +95,7 @@ libshell_public_headers = [ | ||||
|   'shell-app.h', | ||||
|   'shell-app-system.h', | ||||
|   'shell-app-usage.h', | ||||
|   'shell-blur-effect.h', | ||||
|   'shell-embedded-window.h', | ||||
|   'shell-glsl-effect.h', | ||||
|   'shell-gtk-embed.h', | ||||
| @@ -129,6 +130,7 @@ libshell_sources = [ | ||||
|   'shell-app.c', | ||||
|   'shell-app-system.c', | ||||
|   'shell-app-usage.c', | ||||
|   'shell-blur-effect.c', | ||||
|   'shell-embedded-window.c', | ||||
|   'shell-embedded-window-private.h', | ||||
|   'shell-global.c', | ||||
|   | ||||
							
								
								
									
										506
									
								
								src/shell-blur-effect.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										506
									
								
								src/shell-blur-effect.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,506 @@ | ||||
| /* shell-blur-effect.c | ||||
|  * | ||||
|  * Copyright 2019 Georges Basile Stavracas Neto <georges.stavracas@gmail.com> | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  */ | ||||
|  | ||||
| #include "shell-blur-effect.h" | ||||
|  | ||||
| static const gchar *gaussian_blur_glsl_declarations = | ||||
| "uniform float blur_radius;                                        \n" | ||||
| "uniform float brightness;                                         \n" | ||||
| "uniform float pixel_step;                                         \n" | ||||
| "uniform int vertical;                                             \n" | ||||
| "                                                                  \n" | ||||
| "float gaussian (float sigma, float x) {                           \n" | ||||
| "  return exp ( - (x * x) / (2.0 * sigma * sigma));                \n" | ||||
| "}                                                                 \n" | ||||
| "                                                                  \n"; | ||||
|  | ||||
| static const gchar *gaussian_blur_glsl = | ||||
| "  float radius = blur_radius * 2.30348;                           \n" | ||||
| "  float total = 0.0;                                              \n" | ||||
| "  vec4 ret = vec4 (0);                                            \n" | ||||
| "  vec2 uv = vec2(cogl_tex_coord.st);                              \n" | ||||
| "                                                                  \n" | ||||
| "  int half_radius = max(int(radius / 2.0), 1);                    \n" | ||||
| "                                                                  \n" | ||||
| "  if (vertical != 0) {                                            \n" | ||||
| "    for (int y = -half_radius; y < half_radius; y++) {            \n" | ||||
| "      float fy = gaussian (radius / 2.0, float(y));               \n" | ||||
| "      float offset_y = float(y) * pixel_step;                     \n" | ||||
| "                                                                  \n" | ||||
| "      vec4 c = texture2D(cogl_sampler, uv + vec2(0.0, offset_y)); \n" | ||||
| "      total += fy;                                                \n" | ||||
| "      ret += c * fy;                                              \n" | ||||
| "    }                                                             \n" | ||||
| "  } else {                                                        \n" | ||||
| "    for (int x = -half_radius; x < half_radius; x++) {            \n" | ||||
| "      float fx = gaussian (radius / 2.0, float(x));               \n" | ||||
| "      float offset_x = float(x) * pixel_step;                     \n" | ||||
| "                                                                  \n" | ||||
| "      vec4 c = texture2D(cogl_sampler, uv + vec2(offset_x, 0.0)); \n" | ||||
| "      total += fx;                                                \n" | ||||
| "      ret += c * fx;                                              \n" | ||||
| "    }                                                             \n" | ||||
| "  }                                                               \n" | ||||
| "                                                                  \n" | ||||
| "  cogl_texel = vec4 (ret / total);                                \n" | ||||
| "  cogl_texel.rgb *= brightness;                                   \n"; | ||||
|  | ||||
| #define DOWNSCALE_FACTOR 4.0 | ||||
|  | ||||
| struct _ShellBlurEffect | ||||
| { | ||||
|   ClutterOffscreenEffect parent_instance; | ||||
|  | ||||
|   CoglPipeline *pipeline; | ||||
|   int blur_radius_uniform; | ||||
|   int brightness_uniform; | ||||
|   int pixel_step_uniform; | ||||
|   int vertical_uniform; | ||||
|  | ||||
|   unsigned int tex_width; | ||||
|   unsigned int tex_height; | ||||
|  | ||||
|   gboolean vertical; | ||||
|   float brightness; | ||||
|   int blur_radius; | ||||
| }; | ||||
|  | ||||
| G_DEFINE_TYPE (ShellBlurEffect, shell_blur_effect, CLUTTER_TYPE_OFFSCREEN_EFFECT) | ||||
|  | ||||
| enum { | ||||
|   PROP_0, | ||||
|   PROP_BLUR_RADIUS, | ||||
|   PROP_BRIGHTNESS, | ||||
|   PROP_VERTICAL, | ||||
|   N_PROPS | ||||
| }; | ||||
|  | ||||
| static GParamSpec *properties [N_PROPS] = { NULL, }; | ||||
|  | ||||
| static CoglPipeline* | ||||
| create_pipeline (void) | ||||
| { | ||||
|   static CoglPipeline *base_pipeline = NULL; | ||||
|  | ||||
|   if (G_UNLIKELY (base_pipeline == NULL)) | ||||
|     { | ||||
|       CoglSnippet *snippet; | ||||
|       CoglContext *ctx = | ||||
|         clutter_backend_get_cogl_context (clutter_get_default_backend ()); | ||||
|  | ||||
|       base_pipeline = cogl_pipeline_new (ctx); | ||||
|  | ||||
|       snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP, | ||||
|                                   gaussian_blur_glsl_declarations, | ||||
|                                   NULL); | ||||
|       cogl_snippet_set_replace (snippet, gaussian_blur_glsl); | ||||
|       cogl_pipeline_add_layer_snippet (base_pipeline, 0, snippet); | ||||
|       cogl_object_unref (snippet); | ||||
|  | ||||
|       cogl_pipeline_set_layer_null_texture (base_pipeline, 0); | ||||
|       cogl_pipeline_set_layer_filters (base_pipeline, | ||||
|                                        0, | ||||
|                                        COGL_PIPELINE_FILTER_LINEAR, | ||||
|                                        COGL_PIPELINE_FILTER_LINEAR); | ||||
|       cogl_pipeline_set_layer_wrap_mode (base_pipeline, | ||||
|                                          0, | ||||
|                                          COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE); | ||||
|     } | ||||
|  | ||||
|   return cogl_pipeline_copy (base_pipeline); | ||||
| } | ||||
|  | ||||
| static void | ||||
| queue_actor_repaint (ShellBlurEffect *self) | ||||
| { | ||||
|   ClutterActor *actor = | ||||
|     clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (self)); | ||||
|  | ||||
|   if (actor) | ||||
|     clutter_actor_queue_redraw (actor); | ||||
| } | ||||
|  | ||||
| static void | ||||
| update_uniforms (ShellBlurEffect *self) | ||||
| { | ||||
|   ClutterOffscreenEffect *offscreen_effect = | ||||
|     CLUTTER_OFFSCREEN_EFFECT (self); | ||||
|   CoglHandle texture; | ||||
|  | ||||
|   texture = clutter_offscreen_effect_get_texture (offscreen_effect); | ||||
|   self->tex_width = cogl_texture_get_width (texture) * DOWNSCALE_FACTOR; | ||||
|   self->tex_height = cogl_texture_get_height (texture) * DOWNSCALE_FACTOR; | ||||
|  | ||||
|   if (self->pixel_step_uniform > -1) | ||||
|     { | ||||
|       ClutterRect rect; | ||||
|       float pixel_step; | ||||
|  | ||||
|       clutter_offscreen_effect_get_target_rect (CLUTTER_OFFSCREEN_EFFECT (self), | ||||
|                                                 &rect); | ||||
|  | ||||
|       if (self->vertical) | ||||
|         pixel_step = 1.f / rect.size.height; | ||||
|       else | ||||
|         pixel_step = 1.f / rect.size.width; | ||||
|  | ||||
|       cogl_pipeline_set_uniform_1f (self->pipeline, | ||||
|                                     self->pixel_step_uniform, | ||||
|                                     pixel_step / DOWNSCALE_FACTOR); | ||||
|     } | ||||
|  | ||||
|   if (self->blur_radius_uniform > -1) | ||||
|     { | ||||
|       cogl_pipeline_set_uniform_1f (self->pipeline, | ||||
|                                     self->blur_radius_uniform, | ||||
|                                     self->blur_radius / DOWNSCALE_FACTOR); | ||||
|     } | ||||
|  | ||||
|   if (self->brightness_uniform > -1) | ||||
|     { | ||||
|       cogl_pipeline_set_uniform_1f (self->pipeline, | ||||
|                                     self->brightness_uniform, | ||||
|                                     self->brightness); | ||||
|     } | ||||
|  | ||||
|   if (self->vertical_uniform > -1) | ||||
|     { | ||||
|       cogl_pipeline_set_uniform_1i (self->pipeline, | ||||
|                                     self->vertical_uniform, | ||||
|                                     self->vertical); | ||||
|     } | ||||
|  | ||||
|   cogl_pipeline_set_layer_texture (self->pipeline, 0, texture); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| shell_blur_effect_pre_paint (ClutterEffect *effect) | ||||
| { | ||||
|   gboolean success; | ||||
|  | ||||
|   success = CLUTTER_EFFECT_CLASS (shell_blur_effect_parent_class)->pre_paint (effect); | ||||
|  | ||||
|   if (success) | ||||
|     { | ||||
|       CoglFramebuffer *offscreen = cogl_get_draw_framebuffer (); | ||||
|  | ||||
|       /* Texture is downscaled, draw downscaled as well */ | ||||
|       cogl_framebuffer_scale (offscreen, | ||||
|                               1.0 / DOWNSCALE_FACTOR, | ||||
|                               1.0 / DOWNSCALE_FACTOR, | ||||
|                               1.0); | ||||
|     } | ||||
|  | ||||
|   return success; | ||||
| } | ||||
|  | ||||
| static void | ||||
| shell_blur_effect_paint_target (ClutterOffscreenEffect *effect) | ||||
| { | ||||
|   ShellBlurEffect *self = SHELL_BLUR_EFFECT (effect); | ||||
|   CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer (); | ||||
|   ClutterActor *actor; | ||||
|   float blur_radius_offset_h; | ||||
|   float blur_radius_offset_v; | ||||
|   guint8 paint_opacity; | ||||
|  | ||||
|   update_uniforms (self); | ||||
|  | ||||
|   actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (self)); | ||||
|   paint_opacity = clutter_actor_get_paint_opacity (actor); | ||||
|  | ||||
|   cogl_pipeline_set_color4ub (self->pipeline, | ||||
|                               paint_opacity, | ||||
|                               paint_opacity, | ||||
|                               paint_opacity, | ||||
|                               paint_opacity); | ||||
|  | ||||
|   blur_radius_offset_h = | ||||
|     self->blur_radius / (float) self->tex_width / DOWNSCALE_FACTOR; | ||||
|   blur_radius_offset_v = | ||||
|     self->blur_radius / (float) self->tex_height / DOWNSCALE_FACTOR; | ||||
|  | ||||
|   cogl_framebuffer_draw_textured_rectangle (framebuffer, | ||||
|                                             self->pipeline, | ||||
|                                             0, 0, | ||||
|                                             self->tex_width, | ||||
|                                             self->tex_height, | ||||
|                                             blur_radius_offset_h, | ||||
|                                             blur_radius_offset_v, | ||||
|                                             1.f - blur_radius_offset_h, | ||||
|                                             1.f - blur_radius_offset_v); | ||||
| } | ||||
|  | ||||
| static void | ||||
| shell_blur_effect_post_paint (ClutterEffect *effect) | ||||
| { | ||||
|   CoglFramebuffer *offscreen = cogl_get_draw_framebuffer (); | ||||
|  | ||||
|   cogl_framebuffer_scale (offscreen, | ||||
|                           DOWNSCALE_FACTOR, | ||||
|                           DOWNSCALE_FACTOR, | ||||
|                           1.f); | ||||
|  | ||||
|   CLUTTER_EFFECT_CLASS (shell_blur_effect_parent_class)->post_paint (effect); | ||||
| } | ||||
|  | ||||
| static CoglTexture* | ||||
| shell_blur_effect_create_texture (ClutterOffscreenEffect *effect, | ||||
|                                   float                   width, | ||||
|                                   float                   height) | ||||
| { | ||||
|   CoglContext *ctx = | ||||
|     clutter_backend_get_cogl_context (clutter_get_default_backend ()); | ||||
|  | ||||
|   return cogl_texture_2d_new_with_size (ctx, | ||||
|                                         width / DOWNSCALE_FACTOR, | ||||
|                                         height / DOWNSCALE_FACTOR); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| shell_blur_effect_modify_paint_volume (ClutterEffect      *effect, | ||||
|                                        ClutterPaintVolume *volume) | ||||
| { | ||||
|   ShellBlurEffect *self = SHELL_BLUR_EFFECT (effect); | ||||
|   ClutterVertex origin; | ||||
|   float width; | ||||
|   float height; | ||||
|  | ||||
|   clutter_paint_volume_get_origin (volume, &origin); | ||||
|   width = clutter_paint_volume_get_width (volume); | ||||
|   height = clutter_paint_volume_get_height (volume); | ||||
|  | ||||
|   if (self->vertical) | ||||
|     { | ||||
|       origin.y -= self->blur_radius; | ||||
|       height += 2 * self->blur_radius; | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       origin.x -= self->blur_radius; | ||||
|       width += 2 * self->blur_radius; | ||||
|     } | ||||
|  | ||||
|   clutter_paint_volume_set_origin (volume, &origin); | ||||
|   clutter_paint_volume_set_width (volume, width); | ||||
|   clutter_paint_volume_set_height (volume, height); | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| static void | ||||
| shell_blur_effect_finalize (GObject *object) | ||||
| { | ||||
|   ShellBlurEffect *self = (ShellBlurEffect *)object; | ||||
|  | ||||
|   g_clear_pointer (&self->pipeline, cogl_object_unref); | ||||
|  | ||||
|   G_OBJECT_CLASS (shell_blur_effect_parent_class)->finalize (object); | ||||
| } | ||||
|  | ||||
| static void | ||||
| shell_blur_effect_get_property (GObject    *object, | ||||
|                                 guint       prop_id, | ||||
|                                 GValue     *value, | ||||
|                                 GParamSpec *pspec) | ||||
| { | ||||
|   ShellBlurEffect *self = SHELL_BLUR_EFFECT (object); | ||||
|  | ||||
|   switch (prop_id) | ||||
|     { | ||||
|     case PROP_BLUR_RADIUS: | ||||
|       g_value_set_int (value, self->blur_radius); | ||||
|       break; | ||||
|  | ||||
|     case PROP_BRIGHTNESS: | ||||
|       g_value_set_int (value, self->brightness); | ||||
|       break; | ||||
|  | ||||
|     case PROP_VERTICAL: | ||||
|       g_value_set_boolean (value, self->vertical); | ||||
|       break; | ||||
|  | ||||
|     default: | ||||
|       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| shell_blur_effect_set_property (GObject      *object, | ||||
|                                 guint         prop_id, | ||||
|                                 const GValue *value, | ||||
|                                 GParamSpec   *pspec) | ||||
| { | ||||
|   ShellBlurEffect *self = SHELL_BLUR_EFFECT (object); | ||||
|  | ||||
|   switch (prop_id) | ||||
|     { | ||||
|     case PROP_BLUR_RADIUS: | ||||
|       shell_blur_effect_set_blur_radius (self, g_value_get_int (value)); | ||||
|       break; | ||||
|  | ||||
|     case PROP_BRIGHTNESS: | ||||
|       shell_blur_effect_set_brightness (self, g_value_get_float (value)); | ||||
|       break; | ||||
|  | ||||
|     case PROP_VERTICAL: | ||||
|       shell_blur_effect_set_vertical (self, g_value_get_boolean (value)); | ||||
|       break; | ||||
|  | ||||
|     default: | ||||
|       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| shell_blur_effect_class_init (ShellBlurEffectClass *klass) | ||||
| { | ||||
|   GObjectClass *object_class = G_OBJECT_CLASS (klass); | ||||
|   ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass); | ||||
|   ClutterOffscreenEffectClass *offscreen_class = | ||||
|     CLUTTER_OFFSCREEN_EFFECT_CLASS (klass); | ||||
|  | ||||
|   object_class->finalize = shell_blur_effect_finalize; | ||||
|   object_class->get_property = shell_blur_effect_get_property; | ||||
|   object_class->set_property = shell_blur_effect_set_property; | ||||
|  | ||||
|   effect_class->pre_paint = shell_blur_effect_pre_paint; | ||||
|   effect_class->post_paint = shell_blur_effect_post_paint; | ||||
|   effect_class->modify_paint_volume = shell_blur_effect_modify_paint_volume; | ||||
|  | ||||
|   offscreen_class->paint_target = shell_blur_effect_paint_target; | ||||
|   offscreen_class->create_texture = shell_blur_effect_create_texture; | ||||
|  | ||||
|   properties[PROP_BLUR_RADIUS] = | ||||
|     g_param_spec_int ("blur-radius", | ||||
|                       "Blur radius", | ||||
|                       "Blur radius", | ||||
|                       0, G_MAXINT, 0, | ||||
|                       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); | ||||
|  | ||||
|   properties[PROP_BRIGHTNESS] = | ||||
|     g_param_spec_float ("brightness", | ||||
|                         "Brightness", | ||||
|                         "Brightness", | ||||
|                         0.f, 1.f, 1.f, | ||||
|                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); | ||||
|  | ||||
|   properties[PROP_VERTICAL] = | ||||
|     g_param_spec_boolean ("vertical", | ||||
|                           "Vertical", | ||||
|                           "Whether the blur is vertical or horizontal", | ||||
|                           FALSE, | ||||
|                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); | ||||
|  | ||||
|   g_object_class_install_properties (object_class, N_PROPS, properties); | ||||
| } | ||||
|  | ||||
| static void | ||||
| shell_blur_effect_init (ShellBlurEffect *self) | ||||
| { | ||||
|   self->blur_radius = 0; | ||||
|   self->brightness = 1.f; | ||||
|   self->vertical = FALSE; | ||||
|   self->pipeline = create_pipeline (); | ||||
|  | ||||
|   self->blur_radius_uniform = | ||||
|     cogl_pipeline_get_uniform_location (self->pipeline, "blur_radius"); | ||||
|   self->brightness_uniform = | ||||
|     cogl_pipeline_get_uniform_location (self->pipeline, "brightness"); | ||||
|   self->pixel_step_uniform = | ||||
|     cogl_pipeline_get_uniform_location (self->pipeline, "pixel_step"); | ||||
|   self->vertical_uniform = | ||||
|     cogl_pipeline_get_uniform_location (self->pipeline, "vertical"); | ||||
| } | ||||
|  | ||||
| ShellBlurEffect * | ||||
| shell_blur_effect_new (void) | ||||
| { | ||||
|   return g_object_new (SHELL_TYPE_BLUR_EFFECT, NULL); | ||||
| } | ||||
|  | ||||
| int | ||||
| shell_blur_effect_get_blur_radius (ShellBlurEffect *self) | ||||
| { | ||||
|   g_return_val_if_fail (SHELL_IS_BLUR_EFFECT (self), -1); | ||||
|  | ||||
|   return self->blur_radius; | ||||
| } | ||||
|  | ||||
| void | ||||
| shell_blur_effect_set_blur_radius (ShellBlurEffect *self, | ||||
|                                    int              radius) | ||||
| { | ||||
|   g_return_if_fail (SHELL_IS_BLUR_EFFECT (self)); | ||||
|  | ||||
|   if (self->blur_radius == radius) | ||||
|     return; | ||||
|  | ||||
|   self->blur_radius = radius; | ||||
|   queue_actor_repaint (self); | ||||
|  | ||||
|   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BLUR_RADIUS]); | ||||
| } | ||||
|  | ||||
| float | ||||
| shell_blur_effect_get_brightness (ShellBlurEffect *self) | ||||
| { | ||||
|   g_return_val_if_fail (SHELL_IS_BLUR_EFFECT (self), FALSE); | ||||
|  | ||||
|   return self->brightness; | ||||
| } | ||||
|  | ||||
| void | ||||
| shell_blur_effect_set_brightness (ShellBlurEffect *self, | ||||
|                                   float            brightness) | ||||
| { | ||||
|   g_return_if_fail (SHELL_IS_BLUR_EFFECT (self)); | ||||
|  | ||||
|   if (self->brightness == brightness) | ||||
|     return; | ||||
|  | ||||
|   self->brightness = brightness; | ||||
|   queue_actor_repaint (self); | ||||
|  | ||||
|   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BRIGHTNESS]); | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| shell_blur_effect_get_vertical (ShellBlurEffect *self) | ||||
| { | ||||
|   g_return_val_if_fail (SHELL_IS_BLUR_EFFECT (self), FALSE); | ||||
|  | ||||
|   return self->vertical; | ||||
| } | ||||
|  | ||||
| void | ||||
| shell_blur_effect_set_vertical (ShellBlurEffect *self, | ||||
|                                 gboolean         vertical) | ||||
| { | ||||
|   g_return_if_fail (SHELL_IS_BLUR_EFFECT (self)); | ||||
|  | ||||
|   if (self->vertical == vertical) | ||||
|     return; | ||||
|  | ||||
|   self->vertical = vertical; | ||||
|   queue_actor_repaint (self); | ||||
|  | ||||
|   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VERTICAL]); | ||||
| } | ||||
							
								
								
									
										45
									
								
								src/shell-blur-effect.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/shell-blur-effect.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| /* shell-blur-effect.h | ||||
|  * | ||||
|  * Copyright 2019 Georges Basile Stavracas Neto <georges.stavracas@gmail.com> | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <clutter/clutter.h> | ||||
|  | ||||
| G_BEGIN_DECLS | ||||
|  | ||||
| #define SHELL_TYPE_BLUR_EFFECT (shell_blur_effect_get_type()) | ||||
|  | ||||
| G_DECLARE_FINAL_TYPE (ShellBlurEffect, shell_blur_effect, SHELL, BLUR_EFFECT, ClutterOffscreenEffect) | ||||
|  | ||||
| ShellBlurEffect *shell_blur_effect_new (void); | ||||
|  | ||||
| int shell_blur_effect_get_blur_radius (ShellBlurEffect *self); | ||||
| void shell_blur_effect_set_blur_radius (ShellBlurEffect *self, | ||||
|                                         int              radius); | ||||
|  | ||||
| float shell_blur_effect_get_brightness (ShellBlurEffect *self); | ||||
| void shell_blur_effect_set_brightness (ShellBlurEffect *self, | ||||
|                                        float            brightness); | ||||
|  | ||||
| gboolean shell_blur_effect_get_vertical (ShellBlurEffect *self); | ||||
| void shell_blur_effect_set_vertical (ShellBlurEffect *self, | ||||
|                                      gboolean         vertical); | ||||
|  | ||||
| G_END_DECLS | ||||
		Reference in New Issue
	
	Block a user