You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

217 lines
6.4 KiB

/* vim: set tabstop=2:softtabstop=2:shiftwidth=2:noexpandtab */
// modules: X
using X;
namespace NeoLayoutViewer {
/**
* Modification of http://www.linux-nantes.org/~fmonnier/ocaml/Xlib/doc/Xlib.html by
* Oliver Sauder <os@esite.ch>
*/
public class KeybindingManager : GLib.Object {
private NeoWindow neo_win;
/**
* list of binded keybindings
*/
private Gee.List<Keybinding> bindings = new Gee.ArrayList<Keybinding>();
/**
* locked modifiers used to grab all keys whatever lock key
* is pressed.
*/
private static uint[] lock_modifiers = {
0,
Gdk.ModifierType.MOD2_MASK, // NUM_LOCK
Gdk.ModifierType.LOCK_MASK, // CAPS_LOCK
Gdk.ModifierType.MOD5_MASK, // SCROLL_LOCK
Gdk.ModifierType.MOD2_MASK|Gdk.ModifierType.LOCK_MASK,
Gdk.ModifierType.MOD2_MASK|Gdk.ModifierType.MOD5_MASK,
Gdk.ModifierType.LOCK_MASK|Gdk.ModifierType.MOD5_MASK,
Gdk.ModifierType.MOD2_MASK|Gdk.ModifierType.LOCK_MASK|Gdk.ModifierType.MOD5_MASK
};
private int modifier_keycodes[10];
private int modifier_pressed[10];
/**
* Helper class to store keybinding
*/
private class Keybinding {
public Keybinding(string accelerator, int keycode, Gdk.ModifierType modifiers, KeybindingHandlerFunc handler) {
this.accelerator = accelerator;
this.keycode = keycode;
this.modifiers = modifiers;
this.handler = handler;
}
public string accelerator { get; set; }
public int keycode { get; set; }
public Gdk.ModifierType modifiers { get; set; }
public unowned KeybindingHandlerFunc handler { get; set; }
}
/**
* Keybinding func needed to bind key to handler
*
* @param event passing on gdk event
*/
public delegate void KeybindingHandlerFunc(Gdk.Event event);
public KeybindingManager(NeoWindow neo_win) {
this.neo_win = neo_win;
// init filter to retrieve X.Events
unowned Gdk.Window rootwin = Gdk.get_default_root_window();
if (rootwin != null) {
rootwin.add_filter(event_filter);
unowned X.Display display = Gdk.X11.get_default_xdisplay();
modifier_keycodes[0] = display.keysym_to_keycode(XK_Shift_L);
modifier_keycodes[1] = display.keysym_to_keycode(XK_Shift_R);
modifier_keycodes[2] = 66; //Mod3L can differ?!
modifier_keycodes[3] = 51; //Mod3R
modifier_keycodes[4] = 94; //Mod4L
modifier_keycodes[5] = 108;//Mod4R
modifier_keycodes[6] = display.keysym_to_keycode(XK_Control_L);
modifier_keycodes[7] = display.keysym_to_keycode(XK_Control_R);
modifier_keycodes[8] = display.keysym_to_keycode(XK_Alt_L);
modifier_keycodes[9] = display.keysym_to_keycode(XK_Alt_R);
Timeout.add(100, modifier_timer);
}
}
/**
* Bind accelerator to given handler
*
* @param accelerator accelerator parsable by Gtk.accelerator_parse
* @param handler handler called when given accelerator is pressed
*/
public void bind(string accelerator, KeybindingHandlerFunc handler) {
debug("Binding key " + accelerator);
// convert accelerator
uint keysym;
Gdk.ModifierType modifiers;
Gtk.accelerator_parse(accelerator, out keysym, out modifiers);
unowned X.Display display = Gdk.X11.get_default_xdisplay();
int keycode = display.keysym_to_keycode(keysym);
if (keycode != 0) {
X.Window root_window = Gdk.X11.get_default_root_xwindow();
// trap XErrors to avoid closing of application
// even when grabing of key fails
Gdk.error_trap_push();
// grab key finally
// also grab all keys which are combined with a lock key such NumLock
foreach (uint lock_modifier in lock_modifiers) {
display.grab_key(keycode, modifiers|lock_modifier, root_window, false, X.GrabMode.Async, X.GrabMode.Async);
}
// wait until all X request have been processed
Gdk.flush();
// store binding
Keybinding binding = new Keybinding(accelerator, keycode, modifiers, handler);
bindings.add(binding);
debug("Successfully bound key " + accelerator);
}
}
/**
* Unbind given accelerator.
*
* @param accelerator accelerator parsable by Gtk.accelerator_parse
*/
public void unbind(string accelerator) {
debug("Unbinding key " + accelerator);
unowned X.Display display = Gdk.X11.get_default_xdisplay();
X.Window root_window = Gdk.X11.get_default_root_xwindow();
// unbind all keys with given accelerator
Gee.List<Keybinding> remove_bindings = new Gee.ArrayList<Keybinding>();
foreach (Keybinding binding in bindings) {
if (str_equal(accelerator, binding.accelerator)) {
foreach (uint lock_modifier in lock_modifiers) {
display.ungrab_key(binding.keycode, binding.modifiers, root_window);
}
remove_bindings.add(binding);
}
}
// remove unbinded keys
bindings.remove_all(remove_bindings);
}
/**
* Event filter method needed to fetch X.Events
*/
private Gdk.FilterReturn event_filter(Gdk.XEvent gdk_xevent, Gdk.Event gdk_event) {
Gdk.FilterReturn filter_return = Gdk.FilterReturn.CONTINUE;
#if VALA_0_16 || VALA_0_17
X.Event* xevent = (X.Event*) gdk_xevent;
#else
void* pointer = &gdk_xevent;
X.Event* xevent = (X.Event*) pointer;
#endif
if (xevent->type == X.EventType.KeyPress) {
foreach (Keybinding binding in bindings) {
// remove NumLock, CapsLock and ScrollLock from key state
uint event_mods = xevent.xkey.state & ~ (lock_modifiers[7]);
if (xevent->xkey.keycode == binding.keycode && event_mods == binding.modifiers) {
// call all handlers with pressed key and modifiers
binding.handler(gdk_event);
}
}
}
return filter_return;
}
/*
Checks periodically which modifier are pressed.
*/
private bool modifier_timer() {
unowned X.Display display = Gdk.X11.get_default_xdisplay();
checkModifier(display, &modifier_keycodes[0], modifier_keycodes.length, &modifier_pressed[0]);
// Convert modifier keys to modifier/layer
neo_win.change_active_modifier(1, true, (int)((modifier_pressed[0] | modifier_pressed[1])
!= (checkCapsLock(display) ? 1 : 0)) );
neo_win.change_active_modifier(2, true, (int)(modifier_pressed[2] | modifier_pressed[3]));
neo_win.change_active_modifier(3, true, (int)(modifier_pressed[4] | modifier_pressed[5]));
neo_win.change_active_modifier(4, true, (int)(modifier_pressed[6] | modifier_pressed[7]));
neo_win.change_active_modifier(5, true, (int)(modifier_pressed[8] | modifier_pressed[9]));
neo_win.redraw();
return true;
}
}// End KeybindingManager class
}// End Namespace