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
This commit is contained in:
Matthias Clasen 2013-04-14 00:05:39 -04:00
parent f358bb1a96
commit 96994721ef
6 changed files with 149 additions and 0 deletions

View File

@ -105,6 +105,7 @@ nobase_dist_js_DATA = \
ui/workspacesView.js \ ui/workspacesView.js \
ui/workspaceSwitcherPopup.js \ ui/workspaceSwitcherPopup.js \
ui/xdndHandler.js \ ui/xdndHandler.js \
ui/xkbHandler.js \
ui/components/__init__.js \ ui/components/__init__.js \
ui/components/autorunManager.js \ ui/components/autorunManager.js \
ui/components/automountManager.js \ ui/components/automountManager.js \

View File

@ -36,6 +36,7 @@ const ShellMountOperation = imports.ui.shellMountOperation;
const WindowManager = imports.ui.windowManager; const WindowManager = imports.ui.windowManager;
const Magnifier = imports.ui.magnifier; const Magnifier = imports.ui.magnifier;
const XdndHandler = imports.ui.xdndHandler; const XdndHandler = imports.ui.xdndHandler;
const XkbHandler = imports.ui.xkbHandler;
const Util = imports.misc.util; const Util = imports.misc.util;
const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides'; const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
@ -63,6 +64,7 @@ let modalActorFocusStack = [];
let uiGroup = null; let uiGroup = null;
let magnifier = null; let magnifier = null;
let xdndHandler = null; let xdndHandler = null;
let xkbHandler = null;
let keyboard = null; let keyboard = null;
let layoutManager = null; let layoutManager = null;
let _startDate; let _startDate;
@ -139,6 +141,7 @@ function _initializeUI() {
uiGroup = layoutManager.uiGroup; uiGroup = layoutManager.uiGroup;
xdndHandler = new XdndHandler.XdndHandler(); xdndHandler = new XdndHandler.XdndHandler();
xkbHandler = new XkbHandler.XkbHandler();
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager(); ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
osdWindow = new OsdWindow.OsdWindow(); osdWindow = new OsdWindow.OsdWindow();
overview = new Overview.Overview(); overview = new Overview.Overview();

72
js/ui/xkbHandler.js Normal file
View File

@ -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);
}
});

View File

@ -35,6 +35,7 @@
#include <gjs/gjs.h> #include <gjs/gjs.h>
#include <meta/display.h> #include <meta/display.h>
#include <meta/meta-plugin.h> #include <meta/meta-plugin.h>
#include <X11/XKBlib.h>
#include "shell-global-private.h" #include "shell-global-private.h"
#include "shell-perf-log.h" #include "shell-perf-log.h"
@ -101,6 +102,9 @@ struct _GnomeShellPlugin
guint have_swap_event : 1; guint have_swap_event : 1;
CoglContext *cogl_context; CoglContext *cogl_context;
int xkb_event_base;
XkbDescPtr xkb;
ShellGlobal *global; ShellGlobal *global;
}; };
@ -187,6 +191,10 @@ gnome_shell_plugin_start (MetaPlugin *plugin)
int status; int status;
GjsContext *gjs_context; GjsContext *gjs_context;
ClutterBackend *backend; ClutterBackend *backend;
MetaScreen *screen;
MetaDisplay *display;
Display *xdisplay;
int xkb_base_error_type, xkb_opcode;
backend = clutter_get_default_backend (); backend = clutter_get_default_backend ();
shell_plugin->cogl_context = clutter_backend_get_cogl_context (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)", "GL buffer swap complete event received (with timestamp of completion)",
"x"); "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_plugin->global = shell_global_get ();
_shell_global_set_plugin (shell_plugin->global, META_PLUGIN (shell_plugin)); _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)) if (_shell_global_check_xdnd_event (shell_plugin->global, xev))
return TRUE; 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; return clutter_x11_handle_event (xev) != CLUTTER_X11_FILTER_CONTINUE;
} }

View File

@ -16,4 +16,7 @@ GjsContext *_shell_global_get_gjs_context (ShellGlobal *global);
gboolean _shell_global_check_xdnd_event (ShellGlobal *global, gboolean _shell_global_check_xdnd_event (ShellGlobal *global,
XEvent *xev); XEvent *xev);
gboolean _shell_global_check_xkb_event (ShellGlobal *global,
XEvent *xev);
#endif /* __SHELL_GLOBAL_PRIVATE_H__ */ #endif /* __SHELL_GLOBAL_PRIVATE_H__ */

View File

@ -16,6 +16,7 @@
#include <locale.h> #include <locale.h>
#include <X11/extensions/Xfixes.h> #include <X11/extensions/Xfixes.h>
#include <X11/XKBlib.h>
#include <canberra.h> #include <canberra.h>
#include <canberra-gtk.h> #include <canberra-gtk.h>
#include <clutter/glx/clutter-glx.h> #include <clutter/glx/clutter-glx.h>
@ -122,6 +123,7 @@ enum
XDND_POSITION_CHANGED, XDND_POSITION_CHANGED,
XDND_LEAVE, XDND_LEAVE,
XDND_ENTER, XDND_ENTER,
XKB_STATE_CHANGED,
NOTIFY_ERROR, NOTIFY_ERROR,
LAST_SIGNAL LAST_SIGNAL
}; };
@ -338,6 +340,15 @@ shell_global_class_init (ShellGlobalClass *klass)
NULL, NULL, NULL, NULL, NULL, NULL,
G_TYPE_NONE, 0); 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] = shell_global_signals[NOTIFY_ERROR] =
g_signal_new ("notify-error", g_signal_new ("notify-error",
G_TYPE_FROM_CLASS (klass), G_TYPE_FROM_CLASS (klass),
@ -1779,3 +1790,30 @@ shell_global_get_session_mode (ShellGlobal *global)
return global->session_mode; 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;
}