|
Packit |
3ff832 |
/* vim:set et sts=4 sw=4:
|
|
Packit |
3ff832 |
valac --pkg gtk+-2.0 --pkg x11 --pkg gdk-x11-2.0 --pkg gee-1.0 keybinding-manager.vala
|
|
Packit |
3ff832 |
*/
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
/**
|
|
Packit |
3ff832 |
* This class is in charge to grab keybindings on the X11 display
|
|
Packit |
3ff832 |
* and filter X11-events and passing on such events to the registed
|
|
Packit |
3ff832 |
* handler methods.
|
|
Packit |
3ff832 |
*
|
|
Packit |
3ff832 |
* @author Oliver Sauder <os@esite.ch>
|
|
Packit |
3ff832 |
*/
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
public class KeybindingManager : GLib.Object {
|
|
Packit |
3ff832 |
/**
|
|
Packit |
3ff832 |
* list of binded keybindings
|
|
Packit |
3ff832 |
*/
|
|
Packit |
3ff832 |
private GLib.List<Keybinding> m_bindings = new GLib.List<Keybinding>();
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
private static KeybindingManager m_instance = null;
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
public const uint MODIFIER_FILTER =
|
|
Packit |
3ff832 |
Gdk.ModifierType.MODIFIER_MASK & ~(
|
|
Packit |
3ff832 |
Gdk.ModifierType.LOCK_MASK | // Caps Lock
|
|
Packit |
3ff832 |
// Gdk.ModifierType.MOD1_MASK | // Alt
|
|
Packit |
3ff832 |
Gdk.ModifierType.MOD2_MASK | // Num Lock
|
|
Packit |
3ff832 |
// Gdk.ModifierType.MOD3_MASK |
|
|
Packit |
3ff832 |
// Gdk.ModifierType.MOD4_MASK | // Super, Hyper
|
|
Packit |
3ff832 |
// Gdk.ModifierType.MOD5_MASK | //
|
|
Packit |
3ff832 |
Gdk.ModifierType.BUTTON1_MASK |
|
|
Packit |
3ff832 |
Gdk.ModifierType.BUTTON2_MASK |
|
|
Packit |
3ff832 |
Gdk.ModifierType.BUTTON3_MASK |
|
|
Packit |
3ff832 |
Gdk.ModifierType.BUTTON4_MASK |
|
|
Packit |
3ff832 |
Gdk.ModifierType.BUTTON5_MASK |
|
|
Packit |
3ff832 |
Gdk.ModifierType.SUPER_MASK |
|
|
Packit |
3ff832 |
Gdk.ModifierType.HYPER_MASK |
|
|
Packit |
3ff832 |
Gdk.ModifierType.META_MASK);
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
/**
|
|
Packit |
3ff832 |
* Helper class to store keybinding
|
|
Packit |
3ff832 |
*/
|
|
Packit |
3ff832 |
private class Keybinding {
|
|
Packit |
3ff832 |
public Keybinding(uint keysym,
|
|
Packit |
3ff832 |
Gdk.ModifierType modifiers,
|
|
Packit |
3ff832 |
KeybindingHandlerFunc handler) {
|
|
Packit |
3ff832 |
this.keysym = keysym;
|
|
Packit |
3ff832 |
this.modifiers = modifiers;
|
|
Packit |
3ff832 |
this.handler = handler;
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
public uint keysym { get; set; }
|
|
Packit |
3ff832 |
public Gdk.ModifierType modifiers { get; set; }
|
|
Packit |
3ff832 |
public unowned KeybindingHandlerFunc handler { get; set; }
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
/**
|
|
Packit |
3ff832 |
* Keybinding func needed to bind key to handler
|
|
Packit |
3ff832 |
*
|
|
Packit |
3ff832 |
* @param event passing on gdk event
|
|
Packit |
3ff832 |
*/
|
|
Packit |
3ff832 |
public delegate void KeybindingHandlerFunc(Gdk.Event event);
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
private KeybindingManager() {
|
|
Packit |
3ff832 |
Gdk.Event.handler_set(event_handler);
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
/**
|
|
Packit |
3ff832 |
* Bind accelerator to given handler
|
|
Packit |
3ff832 |
*
|
|
Packit |
3ff832 |
* @param keysym
|
|
Packit |
3ff832 |
* @param modifiers
|
|
Packit |
3ff832 |
* @param handler handler called when given accelerator is pressed
|
|
Packit |
3ff832 |
*/
|
|
Packit |
3ff832 |
public bool bind(uint keysym,
|
|
Packit |
3ff832 |
Gdk.ModifierType modifiers,
|
|
Packit |
3ff832 |
KeybindingHandlerFunc handler) {
|
|
Packit |
3ff832 |
#if VALA_0_24
|
|
Packit |
3ff832 |
unowned X.Display display = Gdk.X11.get_default_xdisplay();
|
|
Packit |
3ff832 |
#else
|
|
Packit |
3ff832 |
unowned X.Display display = Gdk.x11_get_default_xdisplay();
|
|
Packit |
3ff832 |
#endif
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
int keycode = display.keysym_to_keycode(keysym);
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
if (keycode == 0)
|
|
Packit |
3ff832 |
return false;
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
grab_keycode (Gdk.Display.get_default(), keysym, modifiers);
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
// store binding
|
|
Packit |
3ff832 |
Keybinding binding = new Keybinding(keysym, modifiers, handler);
|
|
Packit |
3ff832 |
m_bindings.append(binding);
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
return true;
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
/**
|
|
Packit |
3ff832 |
* Unbind given accelerator.
|
|
Packit |
3ff832 |
*
|
|
Packit |
3ff832 |
* @param keysym
|
|
Packit |
3ff832 |
* @param modifiers
|
|
Packit |
3ff832 |
*/
|
|
Packit |
3ff832 |
public void unbind(uint keysym,
|
|
Packit |
3ff832 |
Gdk.ModifierType modifiers) {
|
|
Packit |
3ff832 |
// unbind all keys with given accelerator
|
|
Packit |
3ff832 |
GLib.List<Keybinding> remove_bindings = new GLib.List<Keybinding>();
|
|
Packit |
3ff832 |
foreach(Keybinding binding in m_bindings) {
|
|
Packit |
3ff832 |
if (binding.keysym == keysym && binding.modifiers == modifiers) {
|
|
Packit |
3ff832 |
ungrab_keycode (Gdk.Display.get_default(),
|
|
Packit |
3ff832 |
binding.keysym,
|
|
Packit |
3ff832 |
binding.modifiers);
|
|
Packit |
3ff832 |
remove_bindings.append(binding);
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
// remove unbinded keys
|
|
Packit |
3ff832 |
foreach (Keybinding binding in remove_bindings)
|
|
Packit |
3ff832 |
m_bindings.remove (binding);
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
public static KeybindingManager get_instance () {
|
|
Packit |
3ff832 |
if (m_instance == null)
|
|
Packit |
3ff832 |
m_instance = new KeybindingManager ();
|
|
Packit |
3ff832 |
return m_instance;
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
public static Gdk.ModifierType get_primary_modifier (uint binding_mask) {
|
|
Packit |
3ff832 |
const Gdk.ModifierType[] masks = {
|
|
Packit |
3ff832 |
Gdk.ModifierType.MOD5_MASK,
|
|
Packit |
3ff832 |
Gdk.ModifierType.MOD4_MASK,
|
|
Packit |
3ff832 |
Gdk.ModifierType.MOD3_MASK,
|
|
Packit |
3ff832 |
Gdk.ModifierType.MOD2_MASK,
|
|
Packit |
3ff832 |
Gdk.ModifierType.MOD1_MASK,
|
|
Packit |
3ff832 |
Gdk.ModifierType.CONTROL_MASK,
|
|
Packit |
3ff832 |
Gdk.ModifierType.SHIFT_MASK,
|
|
Packit |
3ff832 |
Gdk.ModifierType.LOCK_MASK
|
|
Packit |
3ff832 |
};
|
|
Packit |
3ff832 |
for (int i = 0; i < masks.length; i++) {
|
|
Packit |
3ff832 |
Gdk.ModifierType mask = masks[i];
|
|
Packit |
3ff832 |
if ((binding_mask & mask) == mask)
|
|
Packit |
3ff832 |
return mask;
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
return 0;
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
public static bool primary_modifier_still_pressed(Gdk.Event event,
|
|
Packit |
3ff832 |
uint primary_modifier) {
|
|
Packit |
3ff832 |
Gdk.EventKey keyevent = event.key;
|
|
Packit |
3ff832 |
if (primary_modifier == 0)
|
|
Packit |
3ff832 |
return false;
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
Gdk.Device device = event.get_device();
|
|
Packit |
3ff832 |
Gdk.Device pointer;
|
|
Packit |
3ff832 |
if (device.get_source() == Gdk.InputSource.KEYBOARD)
|
|
Packit |
3ff832 |
pointer = device.get_associated_device();
|
|
Packit |
3ff832 |
else
|
|
Packit |
3ff832 |
pointer = device;
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
double[] axes = null;
|
|
Packit |
3ff832 |
uint modifier = 0;
|
|
Packit |
3ff832 |
pointer.get_state(keyevent.window, axes, out modifier);
|
|
Packit |
3ff832 |
if ((primary_modifier & modifier) == primary_modifier)
|
|
Packit |
3ff832 |
return true;
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
return false;
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
public static uint keyval_to_modifier (uint keyval) {
|
|
Packit |
3ff832 |
switch(keyval) {
|
|
Packit |
3ff832 |
case 0xffe3: /* Control_L */
|
|
Packit |
3ff832 |
case 0xffe4: /* Control_R */
|
|
Packit |
3ff832 |
return Gdk.ModifierType.CONTROL_MASK;
|
|
Packit |
3ff832 |
case 0xffe1: /* Shift_L */
|
|
Packit |
3ff832 |
case 0xffe2: /* Shift_R */
|
|
Packit |
3ff832 |
return Gdk.ModifierType.SHIFT_MASK;
|
|
Packit |
3ff832 |
case 0xffe5: /* Caps_Lock */
|
|
Packit |
3ff832 |
return Gdk.ModifierType.LOCK_MASK;
|
|
Packit |
3ff832 |
case 0xffe9: /* Alt_L */
|
|
Packit |
3ff832 |
case 0xffea: /* Alt_R */
|
|
Packit |
3ff832 |
return Gdk.ModifierType.MOD1_MASK;
|
|
Packit |
3ff832 |
case 0xffe7: /* Meta_L */
|
|
Packit |
3ff832 |
case 0xffe8: /* Meta_R */
|
|
Packit |
3ff832 |
return Gdk.ModifierType.META_MASK;
|
|
Packit |
3ff832 |
case 0xffeb: /* Super_L */
|
|
Packit |
3ff832 |
case 0xffec: /* Super_R */
|
|
Packit |
3ff832 |
return Gdk.ModifierType.SUPER_MASK;
|
|
Packit |
3ff832 |
case 0xffed: /* Hyper_L */
|
|
Packit |
3ff832 |
case 0xffee: /* Hyper_R */
|
|
Packit |
3ff832 |
return Gdk.ModifierType.HYPER_MASK;
|
|
Packit |
3ff832 |
default:
|
|
Packit |
3ff832 |
return 0;
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
private void event_handler(Gdk.Event event) {
|
|
Packit |
3ff832 |
do {
|
|
Packit |
3ff832 |
if (event.any.window != Gdk.get_default_root_window()) {
|
|
Packit |
3ff832 |
break;
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
if (event.type == Gdk.EventType.KEY_PRESS) {
|
|
Packit |
3ff832 |
uint modifiers = event.key.state & MODIFIER_FILTER;
|
|
Packit |
3ff832 |
uint keyval = event.key.keyval;
|
|
Packit |
3ff832 |
if (keyval >= IBus.KEY_A && keyval <= IBus.KEY_Z &&
|
|
Packit |
3ff832 |
(modifiers & Gdk.ModifierType.SHIFT_MASK) != 0) {
|
|
Packit |
3ff832 |
keyval = keyval - IBus.KEY_A + IBus.KEY_a;
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
foreach (var binding in m_bindings) {
|
|
Packit |
3ff832 |
if (keyval != binding.keysym ||
|
|
Packit |
3ff832 |
modifiers != binding.modifiers)
|
|
Packit |
3ff832 |
continue;
|
|
Packit |
3ff832 |
binding.handler(event);
|
|
Packit |
3ff832 |
return;
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
} while (false);
|
|
Packit |
3ff832 |
Gtk.main_do_event(event);
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
// Get union of given modifiers and all the combination of the
|
|
Packit |
3ff832 |
// modifiers in ignored_modifiers.
|
|
Packit |
3ff832 |
XI.GrabModifiers[] get_grab_modifiers(uint modifiers) {
|
|
Packit |
3ff832 |
const int[] ignored_modifiers = {
|
|
Packit |
3ff832 |
X.KeyMask.LockMask,
|
|
Packit |
3ff832 |
X.KeyMask.Mod2Mask,
|
|
Packit |
3ff832 |
X.KeyMask.Mod5Mask
|
|
Packit |
3ff832 |
};
|
|
Packit |
3ff832 |
int[] masks = {};
|
|
Packit |
3ff832 |
for (int i = 0; i < ignored_modifiers.length; i++) {
|
|
Packit |
3ff832 |
int modifier = ignored_modifiers[i];
|
|
Packit |
3ff832 |
masks += modifier;
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
int length = masks.length;
|
|
Packit |
3ff832 |
for (int j = 0; j < length - 1; j++) {
|
|
Packit |
3ff832 |
masks += masks[j] | modifier;
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
masks += 0;
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
XI.GrabModifiers[] ximodifiers = {};
|
|
Packit |
3ff832 |
foreach (var mask in masks) {
|
|
Packit |
3ff832 |
ximodifiers += XI.GrabModifiers() {
|
|
Packit |
3ff832 |
modifiers = mask | modifiers,
|
|
Packit |
3ff832 |
status = 0
|
|
Packit |
3ff832 |
};
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
return ximodifiers;
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
bool grab_keycode(Gdk.Display display, uint keyval, uint modifiers) {
|
|
Packit |
3ff832 |
#if VALA_0_24
|
|
Packit |
3ff832 |
unowned X.Display xdisplay =
|
|
Packit |
3ff832 |
(display as Gdk.X11.Display).get_xdisplay();
|
|
Packit |
3ff832 |
#else
|
|
Packit |
3ff832 |
unowned X.Display xdisplay = Gdk.X11Display.get_xdisplay(display);
|
|
Packit |
3ff832 |
#endif
|
|
Packit |
3ff832 |
int keycode = xdisplay.keysym_to_keycode(keyval);
|
|
Packit |
3ff832 |
if (keycode == 0) {
|
|
Packit |
3ff832 |
warning("Can not convert keyval=%u to keycode!", keyval);
|
|
Packit |
3ff832 |
return false;
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
XI.EventMask evmask = XI.EventMask() {
|
|
Packit |
3ff832 |
deviceid = XI.AllMasterDevices,
|
|
Packit |
3ff832 |
mask = new uchar[(XI.LASTEVENT + 7) / 8]
|
|
Packit |
3ff832 |
};
|
|
Packit |
3ff832 |
XI.set_mask(evmask.mask, XI.EventType.KeyPress);
|
|
Packit |
3ff832 |
XI.set_mask(evmask.mask, XI.EventType.KeyRelease);
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
int retval = XI.grab_keycode (xdisplay,
|
|
Packit |
3ff832 |
XI.AllMasterDevices,
|
|
Packit |
3ff832 |
keycode,
|
|
Packit |
3ff832 |
xdisplay.default_root_window(),
|
|
Packit |
3ff832 |
X.GrabMode.Async,
|
|
Packit |
3ff832 |
X.GrabMode.Async,
|
|
Packit |
3ff832 |
true,
|
|
Packit |
3ff832 |
evmask,
|
|
Packit |
3ff832 |
get_grab_modifiers(modifiers));
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
return retval == 0;
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
bool ungrab_keycode(Gdk.Display display, uint keyval, uint modifiers) {
|
|
Packit |
3ff832 |
#if VALA_0_24
|
|
Packit |
3ff832 |
unowned X.Display xdisplay =
|
|
Packit |
3ff832 |
(display as Gdk.X11.Display).get_xdisplay();
|
|
Packit |
3ff832 |
#else
|
|
Packit |
3ff832 |
unowned X.Display xdisplay = Gdk.X11Display.get_xdisplay(display);
|
|
Packit |
3ff832 |
#endif
|
|
Packit |
3ff832 |
int keycode = xdisplay.keysym_to_keycode(keyval);
|
|
Packit |
3ff832 |
if (keycode == 0) {
|
|
Packit |
3ff832 |
warning("Can not convert keyval=%u to keycode!", keyval);
|
|
Packit |
3ff832 |
return false;
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
int retval = XI.ungrab_keycode (xdisplay,
|
|
Packit |
3ff832 |
XI.AllMasterDevices,
|
|
Packit |
3ff832 |
keycode,
|
|
Packit |
3ff832 |
xdisplay.default_root_window(),
|
|
Packit |
3ff832 |
get_grab_modifiers(modifiers));
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
return retval == 0;
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
/*
|
|
Packit |
3ff832 |
public static int main (string[] args)
|
|
Packit |
3ff832 |
{
|
|
Packit |
3ff832 |
Gtk.init (ref args);
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
KeybindingManager manager = new KeybindingManager();
|
|
Packit |
3ff832 |
manager.bind("<Ctrl><Alt>V", test);
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
Gtk.main ();
|
|
Packit |
3ff832 |
return 0;
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
|
|
Packit |
3ff832 |
private static void test()
|
|
Packit |
3ff832 |
{
|
|
Packit |
3ff832 |
debug("hotkey pressed");
|
|
Packit |
3ff832 |
}
|
|
Packit |
3ff832 |
*/
|