[gnome-shell] Add an OSD for sticky modifiers



commit 96994721ef38d330dae2ffc0101a83322b312117
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun Apr 14 00:05:39 2013 -0400

    Add an OSD for sticky modifiers
    
    This commit adds an OSD that displays which modifiers are
    currently latched or locked. This is commonly used together
    with sticky keys.
    https://bugzilla.gnome.org/show_bug.cgi?id=647711

 js/Makefile.am             |  1 +
 js/ui/main.js              |  3 ++
 js/ui/xkbHandler.js        | 72 ++++++++++++++++++++++++++++++++++++++++++++++
 src/gnome-shell-plugin.c   | 32 +++++++++++++++++++++
 src/shell-global-private.h |  3 ++
 src/shell-global.c         | 38 ++++++++++++++++++++++++
 6 files changed, 149 insertions(+)
---
diff --git a/js/Makefile.am b/js/Makefile.am
index 62c8323..b294dbe 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -105,6 +105,7 @@ nobase_dist_js_DATA =       \
        ui/workspacesView.js    \
        ui/workspaceSwitcherPopup.js    \
        ui/xdndHandler.js       \
+       ui/xkbHandler.js        \
        ui/components/__init__.js               \
        ui/components/autorunManager.js         \
        ui/components/automountManager.js       \
diff --git a/js/ui/main.js b/js/ui/main.js
index f438114..3ca0901 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -36,6 +36,7 @@ const ShellMountOperation = imports.ui.shellMountOperation;
 const WindowManager = imports.ui.windowManager;
 const Magnifier = imports.ui.magnifier;
 const XdndHandler = imports.ui.xdndHandler;
+const XkbHandler = imports.ui.xkbHandler;
 const Util = imports.misc.util;
 
 const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
@@ -63,6 +64,7 @@ let modalActorFocusStack = [];
 let uiGroup = null;
 let magnifier = null;
 let xdndHandler = null;
+let xkbHandler = null;
 let keyboard = null;
 let layoutManager = null;
 let _startDate;
@@ -139,6 +141,7 @@ function _initializeUI() {
     uiGroup = layoutManager.uiGroup;
 
     xdndHandler = new XdndHandler.XdndHandler();
+    xkbHandler = new XkbHandler.XkbHandler();
     ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
     osdWindow = new OsdWindow.OsdWindow();
     overview = new Overview.Overview();
diff --git a/js/ui/xkbHandler.js b/js/ui/xkbHandler.js
new file mode 100644
index 0000000..82d9e23
--- /dev/null
+++ b/js/ui/xkbHandler.js
@@ -0,0 +1,72 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Clutter = imports.gi.Clutter;
+const St = imports.gi.St;
+
+const Lang = imports.lang;
+const Layout = imports.ui.layout;
+const Main = imports.ui.main;
+const Shell = imports.gi.Shell;
+const Tweener = imports.ui.tweener;
+
+const FADE_TIME = 0.1;
+
+const XkbHandler = new Lang.Class({
+    Name: 'XkbHandler',
+
+    _init: function() {
+
+        global.connect('xkb-state-changed', Lang.bind(this, this._onStateChange));
+        this.actor = new St.Widget({ x_expand: true,
+                                     y_expand: true,
+                                     x_align: Clutter.ActorAlign.END,
+                                     y_align: Clutter.ActorAlign.END,
+                                     margin_left: 100,
+                                     margin_right: 100,
+                                     margin_bottom: 100});
+        this.actor.add_constraint(new Layout.MonitorConstraint({ primary: true }));
+        this._box = new St.BoxLayout({ style_class: 'osd-window',
+                                       vertical: true });
+        this.actor.add_actor(this._box);
+
+        this._label = new St.Label();
+        this._box.add(this._label);
+
+        Main.layoutManager.addChrome(this.actor, { affectsInputRegion: false });
+
+        this.actor.hide();
+    },
+
+    _onStateChange: function(obj, latched, locked) {
+      mods = [ 'Shift', 'Caps', 'Ctrl', 'Alt', 'Num Lock', '', 'Super', '' ];
+      markup = '';
+      for (let i = 0; i < 8; i++) {
+        if (locked & (1 << i))
+          {
+            if (markup != '') markup += ' ';
+            markup += '<b>' + mods[i] + '</b>';
+          }
+        else if (latched & (1 << i))
+          {
+            if (markup != '') markup += ' ';
+            markup += mods[i];
+          }
+      }
+
+      this._label.clutter_text.set_markup (markup);
+      if (latched != 0 || locked != 0)
+        {
+           this.actor.show();
+           this.actor.opacity = 0;
+           Tweener.addTween(this.actor,
+                            { opacity: 255,
+                              time: FADE_TIME,
+                              transition: 'easeOutQuad' });
+        }
+      else
+        {
+          this.actor.hide();
+        }
+      log ('xkb state changed, latched: ' + latched + ' locked: ' + locked);
+    }
+});
diff --git a/src/gnome-shell-plugin.c b/src/gnome-shell-plugin.c
index 3470637..96ac9cc 100644
--- a/src/gnome-shell-plugin.c
+++ b/src/gnome-shell-plugin.c
@@ -35,6 +35,7 @@
 #include <gjs/gjs.h>
 #include <meta/display.h>
 #include <meta/meta-plugin.h>
+#include <X11/XKBlib.h>
 
 #include "shell-global-private.h"
 #include "shell-perf-log.h"
@@ -101,6 +102,9 @@ struct _GnomeShellPlugin
   guint have_swap_event : 1;
   CoglContext *cogl_context;
 
+  int xkb_event_base;
+  XkbDescPtr xkb;
+
   ShellGlobal *global;
 };
 
@@ -187,6 +191,10 @@ gnome_shell_plugin_start (MetaPlugin *plugin)
   int status;
   GjsContext *gjs_context;
   ClutterBackend *backend;
+  MetaScreen *screen;
+  MetaDisplay *display;
+  Display *xdisplay;
+  int xkb_base_error_type, xkb_opcode;
 
   backend = clutter_get_default_backend ();
   shell_plugin->cogl_context = clutter_backend_get_cogl_context (backend);
@@ -199,6 +207,26 @@ gnome_shell_plugin_start (MetaPlugin *plugin)
                                "GL buffer swap complete event received (with timestamp of completion)",
                                "x");
 
+  screen = meta_plugin_get_screen (META_PLUGIN (shell_plugin));
+  display = meta_screen_get_display (screen);
+  xdisplay = meta_display_get_xdisplay (display);
+  if (!XkbQueryExtension (xdisplay, &xkb_opcode,
+                          &shell_plugin->xkb_event_base,
+                          &xkb_base_error_type,
+                          NULL, NULL))
+    {
+      shell_plugin->xkb_event_base = -1;
+      g_message ("could not find XKB extension.");
+    }
+  else
+    {
+      shell_plugin->xkb = XkbGetMap (xdisplay,
+                                     XkbAllComponentsMask,
+                                     XkbUseCoreKbd);
+      XkbSelectEvents (xdisplay, XkbUseCoreKbd,
+                       XkbAllEventsMask, XkbAllEventsMask);
+    }
+
   shell_plugin->global = shell_global_get ();
   _shell_global_set_plugin (shell_plugin->global, META_PLUGIN (shell_plugin));
 
@@ -389,6 +417,10 @@ gnome_shell_plugin_xevent_filter (MetaPlugin *plugin,
   if (_shell_global_check_xdnd_event (shell_plugin->global, xev))
     return TRUE;
 
+  if (xev->type == shell_plugin->xkb_event_base &&
+      _shell_global_check_xkb_event (shell_plugin->global, xev))
+    return TRUE;
+
   return clutter_x11_handle_event (xev) != CLUTTER_X11_FILTER_CONTINUE;
 }
 
diff --git a/src/shell-global-private.h b/src/shell-global-private.h
index 786719f..f681228 100644
--- a/src/shell-global-private.h
+++ b/src/shell-global-private.h
@@ -16,4 +16,7 @@ GjsContext *_shell_global_get_gjs_context (ShellGlobal  *global);
 gboolean _shell_global_check_xdnd_event (ShellGlobal  *global,
                                          XEvent       *xev);
 
+gboolean _shell_global_check_xkb_event  (ShellGlobal  *global,
+                                         XEvent       *xev);
+
 #endif /* __SHELL_GLOBAL_PRIVATE_H__ */
diff --git a/src/shell-global.c b/src/shell-global.c
index 889e146..c80df4f 100644
--- a/src/shell-global.c
+++ b/src/shell-global.c
@@ -16,6 +16,7 @@
 #include <locale.h>
 
 #include <X11/extensions/Xfixes.h>
+#include <X11/XKBlib.h>
 #include <canberra.h>
 #include <canberra-gtk.h>
 #include <clutter/glx/clutter-glx.h>
@@ -122,6 +123,7 @@ enum
  XDND_POSITION_CHANGED,
  XDND_LEAVE,
  XDND_ENTER,
+ XKB_STATE_CHANGED,
  NOTIFY_ERROR,
  LAST_SIGNAL
 };
@@ -338,6 +340,15 @@ shell_global_class_init (ShellGlobalClass *klass)
                     NULL, NULL, NULL,
                     G_TYPE_NONE, 0);
 
+  shell_global_signals[XKB_STATE_CHANGED] =
+      g_signal_new ("xkb-state-changed",
+                    G_TYPE_FROM_CLASS (klass),
+                    G_SIGNAL_RUN_LAST,
+                    0,
+                    NULL, NULL, NULL,
+                    G_TYPE_NONE, 2,
+                    G_TYPE_UINT, G_TYPE_UINT);
+
   shell_global_signals[NOTIFY_ERROR] =
       g_signal_new ("notify-error",
                     G_TYPE_FROM_CLASS (klass),
@@ -1779,3 +1790,30 @@ shell_global_get_session_mode (ShellGlobal *global)
 
   return global->session_mode;
 }
+
+static void
+notify_xkb_state (ShellGlobal         *global,
+                  XkbStateNotifyEvent *event)
+{
+  if (event->changed & (XkbModifierLatchMask | XkbModifierLockMask))
+    g_signal_emit_by_name (G_OBJECT (global), "xkb-state-changed",
+                           event->latched_mods, event->locked_mods);
+}
+
+gboolean
+_shell_global_check_xkb_event (ShellGlobal *global,
+                               XEvent      *event)
+{
+  XkbEvent *xkb_event = (XkbEvent *)event;
+
+  switch (xkb_event->any.xkb_type)
+    {
+    case XkbStateNotify:
+      notify_xkb_state (global, &xkb_event->state);
+      break;
+    default:
+      break;
+    }
+
+  return FALSE;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]