From 4b01bb6f9995f3012fcf6033e633ba10572a68f6 Mon Sep 17 00:00:00 2001 From: Olivier Fourdan Date: Thu, 21 Feb 2019 08:44:34 +0100 Subject: [PATCH 1/3] ripples: Add a new class ripples So we can use the same code for both the ripples in overview and the pointer location. https://gitlab.gnome.org/GNOME/gnome-shell/issues/981 https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/413 https://gitlab.gnome.org/GNOME/mutter/merge_requests/453 https://gitlab.gnome.org/GNOME/gsettings-desktop-schemas/merge_requests/19 https://gitlab.gnome.org/GNOME/gnome-settings-daemon/merge_requests/86 --- js/js-resources.gresource.xml | 1 + js/ui/ripples.js | 97 +++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 js/ui/ripples.js diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml index 836d1c6746..135b8e885b 100644 --- a/js/js-resources.gresource.xml +++ b/js/js-resources.gresource.xml @@ -84,6 +84,7 @@ ui/pointerWatcher.js ui/popupMenu.js ui/remoteSearch.js + ui/ripples.js ui/runDialog.js ui/screenShield.js ui/screencast.js diff --git a/js/ui/ripples.js b/js/ui/ripples.js new file mode 100644 index 0000000000..37d7362dee --- /dev/null +++ b/js/ui/ripples.js @@ -0,0 +1,97 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + +const { Clutter, St } = imports.gi; +const Tweener = imports.ui.tweener; + +// Shamelessly copied from the layout "hotcorner" ripples implementation +var Ripples = class Ripples { + constructor(px, py, styleClass) { + this._x = 0; + this._y = 0; + + this._px = px; + this._py = py; + + this._ripple1 = new St.BoxLayout({ style_class: styleClass, + opacity: 0, + can_focus: false, + reactive: false, + visible: false }); + this._ripple1.set_pivot_point(px, py); + + this._ripple2 = new St.BoxLayout({ style_class: styleClass, + opacity: 0, + can_focus: false, + reactive: false, + visible: false }); + this._ripple2.set_pivot_point(px, py); + + this._ripple3 = new St.BoxLayout({ style_class: styleClass, + opacity: 0, + can_focus: false, + reactive: false, + visible: false }); + this._ripple3.set_pivot_point(px, py); + } + + _animRipple(ripple, delay, time, startScale, startOpacity, finalScale) { + // We draw a ripple by using a source image and animating it scaling + // outwards and fading away. We want the ripples to move linearly + // or it looks unrealistic, but if the opacity of the ripple goes + // linearly to zero it fades away too quickly, so we use Tweener's + // 'onUpdate' to give a non-linear curve to the fade-away and make + // it more visible in the middle section. + + ripple.x = this._x; + ripple.y = this._y; + ripple._opacity = startOpacity; + ripple.visible = true; + ripple.opacity = 255 * Math.sqrt(startOpacity); + ripple.scale_x = ripple.scale_y = startScale; + ripple.set_translation( - this._px * ripple.width, - this._py * ripple.height, 0.0); + + Tweener.addTween(ripple, { _opacity: 0, + scale_x: finalScale, + scale_y: finalScale, + delay: delay, + time: time, + transition: 'linear', + onUpdate() { ripple.opacity = 255 * Math.sqrt(ripple._opacity); }, + onComplete() { ripple.visible = false; } }); + } + + addTo(stage) { + if (this._stage !== undefined) { + throw new Error('Ripples already added'); + return; + } + + this._stage = stage; + this._stage.add_actor(this._ripple1); + this._stage.add_actor(this._ripple2); + this._stage.add_actor(this._ripple3); + } + + playAnimation(x, y) { + if (this._stage === undefined) { + throw new Error('Ripples not added'); + return; + } + + this._x = x; + this._y = y; + + this._stage.set_child_above_sibling(this._ripple1, null); + this._stage.set_child_above_sibling(this._ripple2, this._ripple1); + this._stage.set_child_above_sibling(this._ripple3, this._ripple2); + + // Show three concentric ripples expanding outwards; the exact + // parameters were found by trial and error, so don't look + // for them to make perfect sense mathematically + + // delay time scale opacity => scale + this._animRipple(this._ripple1, 0.0, 0.83, 0.25, 1.0, 1.5); + this._animRipple(this._ripple2, 0.05, 1.0, 0.0, 0.7, 1.25); + this._animRipple(this._ripple3, 0.35, 1.0, 0.0, 0.3, 1); + } +}; -- GitLab From ee3f52c097375099c85011cfa2b83c37298186e1 Mon Sep 17 00:00:00 2001 From: Olivier Fourdan Date: Thu, 21 Feb 2019 08:45:17 +0100 Subject: [PATCH 2/3] layout: Use the ripples for overview As we moved the ripples implementation different class of its own, use that for switching to overview. https://gitlab.gnome.org/GNOME/gnome-shell/issues/981 https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/413 https://gitlab.gnome.org/GNOME/mutter/merge_requests/453 https://gitlab.gnome.org/GNOME/gsettings-desktop-schemas/merge_requests/19 https://gitlab.gnome.org/GNOME/gnome-settings-daemon/merge_requests/86 --- js/ui/layout.js | 59 +++++++++---------------------------------------- 1 file changed, 10 insertions(+), 49 deletions(-) diff --git a/js/ui/layout.js b/js/ui/layout.js index 6f6677d642..90385e82cb 100644 --- a/js/ui/layout.js +++ b/js/ui/layout.js @@ -11,6 +11,7 @@ const DND = imports.ui.dnd; const Main = imports.ui.main; const Params = imports.misc.params; const Tweener = imports.ui.tweener; +const Ripples = imports.ui.ripples; var STARTUP_ANIMATION_TIME = 0.5; var KEYBOARD_ANIMATION_TIME = 0.15; @@ -1117,14 +1118,15 @@ var HotCorner = class HotCorner { Shell.ActionMode.OVERVIEW); this._pressureBarrier.connect('trigger', this._toggleOverview.bind(this)); - // Cache the three ripples instead of dynamically creating and destroying them. - this._ripple1 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0, visible: false }); - this._ripple2 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0, visible: false }); - this._ripple3 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0, visible: false }); + let px = 0.0; + let py = 0.0; + if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) { + px = 1.0; + py = 0.0; + } - layoutManager.uiGroup.add_actor(this._ripple1); - layoutManager.uiGroup.add_actor(this._ripple2); - layoutManager.uiGroup.add_actor(this._ripple3); + this._ripples = new Ripples.Ripples(px, py, 'ripple-box'); + this._ripples.addTo(layoutManager.uiGroup); } setBarrierSize(size) { @@ -1206,53 +1208,12 @@ var HotCorner = class HotCorner { this.actor.destroy(); } - _animRipple(ripple, delay, time, startScale, startOpacity, finalScale) { - // We draw a ripple by using a source image and animating it scaling - // outwards and fading away. We want the ripples to move linearly - // or it looks unrealistic, but if the opacity of the ripple goes - // linearly to zero it fades away too quickly, so we use Tweener's - // 'onUpdate' to give a non-linear curve to the fade-away and make - // it more visible in the middle section. - - ripple._opacity = startOpacity; - - if (ripple.get_text_direction() == Clutter.TextDirection.RTL) - ripple.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST); - - ripple.visible = true; - ripple.opacity = 255 * Math.sqrt(startOpacity); - ripple.scale_x = ripple.scale_y = startScale; - - ripple.x = this._x; - ripple.y = this._y; - - Tweener.addTween(ripple, { _opacity: 0, - scale_x: finalScale, - scale_y: finalScale, - delay: delay, - time: time, - transition: 'linear', - onUpdate() { ripple.opacity = 255 * Math.sqrt(ripple._opacity); }, - onComplete() { ripple.visible = false; } }); - } - - _rippleAnimation() { - // Show three concentric ripples expanding outwards; the exact - // parameters were found by trial and error, so don't look - // for them to make perfect sense mathematically - - // delay time scale opacity => scale - this._animRipple(this._ripple1, 0.0, 0.83, 0.25, 1.0, 1.5); - this._animRipple(this._ripple2, 0.05, 1.0, 0.0, 0.7, 1.25); - this._animRipple(this._ripple3, 0.35, 1.0, 0.0, 0.3, 1); - } - _toggleOverview() { if (this._monitor.inFullscreen && !Main.overview.visible) return; if (Main.overview.shouldToggleByCornerOrButton()) { - this._rippleAnimation(); + this._ripples.playAnimation(this._x, this._y); Main.overview.toggle(); } } -- GitLab From 443c8347eaa6be143bb07a0f1960539d247bccff Mon Sep 17 00:00:00 2001 From: Olivier Fourdan Date: Wed, 20 Feb 2019 10:12:36 +0100 Subject: [PATCH 3/3] locatePointer: Add implementation in gnome-shell The "locate pointer" functionality was implemented in gnome settings daemon using X11 protocols and would fail when run under Wayland. With Wayland, there is no global coordinate space exposed to the clients so this functionality cannot be implemented as a separate program. Instead, add the "locate pointer" functionality in gnome-shell so that it works in both X11 and Wayland. https://gitlab.gnome.org/GNOME/gnome-shell/issues/981 https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/413 https://gitlab.gnome.org/GNOME/mutter/merge_requests/453 https://gitlab.gnome.org/GNOME/gsettings-desktop-schemas/merge_requests/19 https://gitlab.gnome.org/GNOME/gnome-settings-daemon/merge_requests/86 --- data/theme/gnome-shell-sass/_common.scss | 9 +++++++++ js/js-resources.gresource.xml | 1 + js/ui/locatePointer.js | 24 ++++++++++++++++++++++++ js/ui/main.js | 10 ++++++++++ src/gnome-shell-plugin.c | 9 +++++++++ src/shell-global-private.h | 2 ++ src/shell-global.c | 14 ++++++++++++++ 7 files changed, 69 insertions(+) create mode 100644 js/ui/locatePointer.js diff --git a/data/theme/gnome-shell-sass/_common.scss b/data/theme/gnome-shell-sass/_common.scss index 64cc612aa3..c8c53d557f 100644 --- a/data/theme/gnome-shell-sass/_common.scss +++ b/data/theme/gnome-shell-sass/_common.scss @@ -1159,6 +1159,15 @@ StScrollBar { border: 1px solid $selected_bg_color; } +// Pointer location +.ripple-pointer-location { + width: 50px; + height: 50px; + border-radius: 25px 25px 25px 25px; // radius the size of the box give us the curve + background-color: lighten(transparentize($selected_bg_color, 0.7), 30%); + box-shadow: 0 0 2px 2px lighten($selected_bg_color, 20%); +} + // not really top bar only .popup-menu-arrow { icon-size: 1.09em; } .popup-menu-icon { icon-size: 1.09em; } diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml index 135b8e885b..7807cce9db 100644 --- a/js/js-resources.gresource.xml +++ b/js/js-resources.gresource.xml @@ -64,6 +64,7 @@ ui/keyboard.js ui/layout.js ui/lightbox.js + ui/locatePointer.js ui/lookingGlass.js ui/magnifier.js ui/magnifierDBus.js diff --git a/js/ui/locatePointer.js b/js/ui/locatePointer.js new file mode 100644 index 0000000000..52a0bc4865 --- /dev/null +++ b/js/ui/locatePointer.js @@ -0,0 +1,24 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + +const { Clutter, Gio, GLib, St } = imports.gi; +const Ripples = imports.ui.ripples; +const Main = imports.ui.main; + +const LOCATE_POINTER_KEY = "locate-pointer"; +const LOCATE_POINTER_SCHEMA = "org.gnome.desktop.interface" + +var locatePointer = class { + constructor() { + this._settings = new Gio.Settings({schema_id: LOCATE_POINTER_SCHEMA}); + this._ripples = new Ripples.Ripples(0.5, 0.5, 'ripple-pointer-location'); + this._ripples.addTo(Main.uiGroup); + } + + show() { + if (!this._settings.get_boolean("locate-pointer")) + return; + + let [x, y, mods] = global.get_pointer(); + this._ripples.playAnimation(x, y); + } +}; diff --git a/js/ui/main.js b/js/ui/main.js index d9f287cd9d..b3cd69283a 100644 --- a/js/ui/main.js +++ b/js/ui/main.js @@ -37,6 +37,7 @@ const WindowManager = imports.ui.windowManager; const Magnifier = imports.ui.magnifier; const XdndHandler = imports.ui.xdndHandler; const KbdA11yDialog = imports.ui.kbdA11yDialog; +const LocatePointer = imports.ui.locatePointer; const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard'; const STICKY_KEYS_ENABLE = 'stickykeys-enable'; @@ -74,6 +75,7 @@ var layoutManager = null; var kbdA11yDialog = null; var inputMethod = null; var introspectService = null; +var locatePointer = null; let _startDate; let _defaultCssStylesheet = null; let _cssStylesheet = null; @@ -92,6 +94,8 @@ function _sessionUpdated() { wm.allowKeybinding('overlay-key', Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW); + wm.allowKeybinding('locate-pointer-key', Shell.ActionMode.ALL); + wm.setCustomKeybindingHandler('panel-run-dialog', Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW, @@ -165,6 +169,8 @@ function _initializeUI() { kbdA11yDialog = new KbdA11yDialog.KbdA11yDialog(); wm = new WindowManager.WindowManager(); magnifier = new Magnifier.Magnifier(); + locatePointer = new LocatePointer.locatePointer(); + if (LoginManager.canLock()) screenShield = new ScreenShield.ScreenShield(); @@ -190,6 +196,10 @@ function _initializeUI() { overview.toggle(); }); + global.connect('locate-pointer', () => { + locatePointer.show(); + }); + global.display.connect('show-restart-message', (display, message) => { showRestartMessage(message); return true; diff --git a/src/gnome-shell-plugin.c b/src/gnome-shell-plugin.c index 35a9a8bcb4..2e491646ca 100644 --- a/src/gnome-shell-plugin.c +++ b/src/gnome-shell-plugin.c @@ -346,6 +346,13 @@ gnome_shell_plugin_create_inhibit_shortcuts_dialog (MetaPlugin *plugin, return _shell_wm_create_inhibit_shortcuts_dialog (get_shell_wm (), window); } +static void +gnome_shell_plugin_locate_pointer (MetaPlugin *plugin) +{ + GnomeShellPlugin *shell_plugin = GNOME_SHELL_PLUGIN (plugin); + _shell_global_locate_pointer (shell_plugin->global); +} + static void gnome_shell_plugin_class_init (GnomeShellPluginClass *klass) { @@ -378,6 +385,8 @@ gnome_shell_plugin_class_init (GnomeShellPluginClass *klass) plugin_class->create_close_dialog = gnome_shell_plugin_create_close_dialog; plugin_class->create_inhibit_shortcuts_dialog = gnome_shell_plugin_create_inhibit_shortcuts_dialog; + + plugin_class->locate_pointer = gnome_shell_plugin_locate_pointer; } static void diff --git a/src/shell-global-private.h b/src/shell-global-private.h index df53236aac..9969691cb4 100644 --- a/src/shell-global-private.h +++ b/src/shell-global-private.h @@ -18,4 +18,6 @@ GjsContext *_shell_global_get_gjs_context (ShellGlobal *global); gboolean _shell_global_check_xdnd_event (ShellGlobal *global, XEvent *xev); +void _shell_global_locate_pointer (ShellGlobal *global); + #endif /* __SHELL_GLOBAL_PRIVATE_H__ */ diff --git a/src/shell-global.c b/src/shell-global.c index 293053d0b5..70120c2da8 100644 --- a/src/shell-global.c +++ b/src/shell-global.c @@ -115,6 +115,7 @@ enum { enum { NOTIFY_ERROR, + LOCATE_POINTER, LAST_SIGNAL }; @@ -357,6 +358,13 @@ shell_global_class_init (ShellGlobalClass *klass) G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); + shell_global_signals[LOCATE_POINTER] = + g_signal_new ("locate-pointer", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); g_object_class_install_property (gobject_class, PROP_SESSION_MODE, @@ -1732,3 +1740,9 @@ shell_global_get_persistent_state (ShellGlobal *global, { return load_variant (global->userdatadir_path, property_type, property_name); } + +void +_shell_global_locate_pointer (ShellGlobal *global) +{ + g_signal_emit (global, shell_global_signals[LOCATE_POINTER], 0); +} -- GitLab