2019-11-07 11:50:44 +01:00
|
|
|
/* vim: set tabstop=2:softtabstop=2:shiftwidth=2:noexpandtab */
|
|
|
|
// modules: X
|
|
|
|
|
2011-10-16 19:09:33 +02:00
|
|
|
using X;
|
|
|
|
|
2013-09-18 20:32:40 +02:00
|
|
|
namespace NeoLayoutViewer {
|
|
|
|
|
2011-10-14 22:10:11 +02:00
|
|
|
/**
|
|
|
|
* Modification of http://www.linux-nantes.org/~fmonnier/ocaml/Xlib/doc/Xlib.html by
|
|
|
|
* Oliver Sauder <os@esite.ch>
|
|
|
|
*/
|
2013-09-18 20:32:40 +02:00
|
|
|
public class KeybindingManager : GLib.Object {
|
|
|
|
|
2011-10-14 22:10:11 +02:00
|
|
|
private NeoWindow neo_win;
|
2013-09-18 20:32:40 +02:00
|
|
|
|
2011-10-14 22:10:11 +02:00
|
|
|
/**
|
|
|
|
* list of binded keybindings
|
|
|
|
*/
|
|
|
|
private Gee.List<Keybinding> bindings = new Gee.ArrayList<Keybinding>();
|
2013-09-18 20:32:40 +02:00
|
|
|
|
2011-10-14 22:10:11 +02:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
};
|
|
|
|
|
2011-10-16 19:09:33 +02:00
|
|
|
private int modifier_keycodes[10];
|
|
|
|
private int modifier_pressed[10];
|
2011-10-14 22:10:11 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper class to store keybinding
|
|
|
|
*/
|
2013-09-18 20:32:40 +02:00
|
|
|
private class Keybinding {
|
|
|
|
|
|
|
|
public Keybinding(string accelerator, int keycode, Gdk.ModifierType modifiers, KeybindingHandlerFunc handler) {
|
2011-10-14 22:10:11 +02:00
|
|
|
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; }
|
2012-11-01 20:41:15 +01:00
|
|
|
public unowned KeybindingHandlerFunc handler { get; set; }
|
2011-10-14 22:10:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Keybinding func needed to bind key to handler
|
|
|
|
*
|
|
|
|
* @param event passing on gdk event
|
|
|
|
*/
|
|
|
|
public delegate void KeybindingHandlerFunc(Gdk.Event event);
|
|
|
|
|
2013-09-18 20:32:40 +02:00
|
|
|
public KeybindingManager(NeoWindow neo_win) {
|
2011-10-14 22:10:11 +02:00
|
|
|
this.neo_win = neo_win;
|
2012-11-01 20:41:15 +01:00
|
|
|
|
2011-10-14 22:10:11 +02:00
|
|
|
// init filter to retrieve X.Events
|
2012-11-01 20:41:15 +01:00
|
|
|
unowned Gdk.Window rootwin = Gdk.get_default_root_window();
|
2013-09-18 20:32:40 +02:00
|
|
|
|
|
|
|
if (rootwin != null) {
|
|
|
|
|
2011-10-14 22:10:11 +02:00
|
|
|
rootwin.add_filter(event_filter);
|
2011-10-16 19:09:33 +02:00
|
|
|
|
2017-11-12 12:59:17 +01:00
|
|
|
unowned X.Display display = Gdk.X11.get_default_xdisplay();
|
2012-11-01 20:41:15 +01:00
|
|
|
|
|
|
|
modifier_keycodes[0] = display.keysym_to_keycode(XK_Shift_L);
|
|
|
|
modifier_keycodes[1] = display.keysym_to_keycode(XK_Shift_R);
|
2011-10-16 19:09:33 +02:00
|
|
|
modifier_keycodes[2] = 66; //Mod3L can differ?!
|
|
|
|
modifier_keycodes[3] = 51; //Mod3R
|
|
|
|
modifier_keycodes[4] = 94; //Mod4L
|
|
|
|
modifier_keycodes[5] = 108;//Mod4R
|
2012-11-01 20:41:15 +01:00
|
|
|
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);
|
2011-10-16 19:09:33 +02:00
|
|
|
|
2013-09-18 20:32:40 +02:00
|
|
|
Timeout.add(100, modifier_timer);
|
2011-10-14 22:10:11 +02:00
|
|
|
|
2011-10-16 19:09:33 +02:00
|
|
|
}
|
2011-10-14 22:10:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Bind accelerator to given handler
|
|
|
|
*
|
|
|
|
* @param accelerator accelerator parsable by Gtk.accelerator_parse
|
|
|
|
* @param handler handler called when given accelerator is pressed
|
|
|
|
*/
|
2013-09-18 20:32:40 +02:00
|
|
|
public void bind(string accelerator, KeybindingHandlerFunc handler) {
|
|
|
|
|
2011-10-16 20:52:57 +02:00
|
|
|
debug("Binding key " + accelerator);
|
2011-10-14 22:10:11 +02:00
|
|
|
|
|
|
|
// convert accelerator
|
|
|
|
uint keysym;
|
|
|
|
Gdk.ModifierType modifiers;
|
|
|
|
Gtk.accelerator_parse(accelerator, out keysym, out modifiers);
|
2012-11-01 20:41:15 +01:00
|
|
|
|
2017-11-12 12:59:17 +01:00
|
|
|
unowned X.Display display = Gdk.X11.get_default_xdisplay();
|
2012-11-01 20:41:15 +01:00
|
|
|
int keycode = display.keysym_to_keycode(keysym);
|
2011-10-14 22:10:11 +02:00
|
|
|
|
2013-09-18 20:32:40 +02:00
|
|
|
if (keycode != 0) {
|
2017-11-12 12:59:17 +01:00
|
|
|
X.Window root_window = Gdk.X11.get_default_root_xwindow();
|
2012-11-01 20:41:15 +01:00
|
|
|
|
2011-10-14 22:10:11 +02:00
|
|
|
// 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
|
2013-09-18 20:32:40 +02:00
|
|
|
foreach (uint lock_modifier in lock_modifiers) {
|
|
|
|
display.grab_key(keycode, modifiers|lock_modifier, root_window, false, X.GrabMode.Async, X.GrabMode.Async);
|
2011-10-14 22:10:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// wait until all X request have been processed
|
|
|
|
Gdk.flush();
|
|
|
|
|
|
|
|
// store binding
|
|
|
|
Keybinding binding = new Keybinding(accelerator, keycode, modifiers, handler);
|
|
|
|
bindings.add(binding);
|
|
|
|
|
2013-09-18 20:32:40 +02:00
|
|
|
debug("Successfully bound key " + accelerator);
|
2011-10-14 22:10:11 +02:00
|
|
|
}
|
|
|
|
}
|
2013-09-18 20:32:40 +02:00
|
|
|
|
2011-10-14 22:10:11 +02:00
|
|
|
/**
|
|
|
|
* Unbind given accelerator.
|
|
|
|
*
|
|
|
|
* @param accelerator accelerator parsable by Gtk.accelerator_parse
|
|
|
|
*/
|
|
|
|
|
2013-09-18 20:32:40 +02:00
|
|
|
public void unbind(string accelerator) {
|
2011-10-16 20:52:57 +02:00
|
|
|
debug("Unbinding key " + accelerator);
|
2011-10-14 22:10:11 +02:00
|
|
|
|
2017-11-12 12:59:17 +01:00
|
|
|
unowned X.Display display = Gdk.X11.get_default_xdisplay();
|
|
|
|
X.Window root_window = Gdk.X11.get_default_root_xwindow();
|
2011-10-13 19:15:00 +02:00
|
|
|
|
2011-10-14 22:10:11 +02:00
|
|
|
// unbind all keys with given accelerator
|
|
|
|
Gee.List<Keybinding> remove_bindings = new Gee.ArrayList<Keybinding>();
|
2013-09-18 20:32:40 +02:00
|
|
|
|
|
|
|
foreach (Keybinding binding in bindings) {
|
|
|
|
|
|
|
|
if (str_equal(accelerator, binding.accelerator)) {
|
|
|
|
|
|
|
|
foreach (uint lock_modifier in lock_modifiers) {
|
2012-11-01 20:41:15 +01:00
|
|
|
display.ungrab_key(binding.keycode, binding.modifiers, root_window);
|
2011-10-14 22:10:11 +02:00
|
|
|
}
|
2013-09-18 20:32:40 +02:00
|
|
|
|
2011-10-14 22:10:11 +02:00
|
|
|
remove_bindings.add(binding);
|
2011-10-13 19:15:00 +02:00
|
|
|
}
|
2011-10-14 22:10:11 +02:00
|
|
|
}
|
2012-11-01 20:41:15 +01:00
|
|
|
|
2011-10-14 22:10:11 +02:00
|
|
|
// remove unbinded keys
|
|
|
|
bindings.remove_all(remove_bindings);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Event filter method needed to fetch X.Events
|
|
|
|
*/
|
2013-09-18 20:32:40 +02:00
|
|
|
private Gdk.FilterReturn event_filter(Gdk.XEvent gdk_xevent, Gdk.Event gdk_event) {
|
2011-10-14 22:10:11 +02:00
|
|
|
Gdk.FilterReturn filter_return = Gdk.FilterReturn.CONTINUE;
|
2013-09-17 22:59:44 +02:00
|
|
|
#if VALA_0_16 || VALA_0_17
|
2012-11-01 20:41:15 +01:00
|
|
|
X.Event* xevent = (X.Event*) gdk_xevent;
|
2013-09-17 22:59:44 +02:00
|
|
|
#else
|
|
|
|
void* pointer = &gdk_xevent;
|
|
|
|
X.Event* xevent = (X.Event*) pointer;
|
|
|
|
#endif
|
2011-10-14 22:10:11 +02:00
|
|
|
|
2013-09-18 20:32:40 +02:00
|
|
|
if (xevent->type == X.EventType.KeyPress) {
|
|
|
|
|
2019-11-12 16:10:04 +01:00
|
|
|
foreach (Keybinding binding in bindings) {
|
2011-10-14 22:10:11 +02:00
|
|
|
// remove NumLock, CapsLock and ScrollLock from key state
|
|
|
|
uint event_mods = xevent.xkey.state & ~ (lock_modifiers[7]);
|
2013-09-18 20:32:40 +02:00
|
|
|
|
|
|
|
if (xevent->xkey.keycode == binding.keycode && event_mods == binding.modifiers) {
|
2011-10-14 22:10:11 +02:00
|
|
|
// call all handlers with pressed key and modifiers
|
|
|
|
binding.handler(gdk_event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return filter_return;
|
|
|
|
}
|
|
|
|
|
2011-10-16 20:52:57 +02:00
|
|
|
/*
|
2019-11-07 11:50:44 +01:00
|
|
|
Checks periodically which modifier are pressed.
|
|
|
|
*/
|
2019-11-12 16:10:04 +01:00
|
|
|
private bool modifier_timer() {
|
2017-11-12 12:59:17 +01:00
|
|
|
unowned X.Display display = Gdk.X11.get_default_xdisplay();
|
2011-10-16 19:09:33 +02:00
|
|
|
|
2013-09-18 20:32:40 +02:00
|
|
|
checkModifier(display, &modifier_keycodes[0], modifier_keycodes.length, &modifier_pressed[0]);
|
2011-10-16 19:09:33 +02:00
|
|
|
|
|
|
|
// Convert modifier keys to modifier/layer
|
2013-09-18 20:32:40 +02:00
|
|
|
neo_win.change_active_modifier(1, true, (int)((modifier_pressed[0] | modifier_pressed[1])
|
2019-11-07 11:50:44 +01:00
|
|
|
!= (checkCapsLock(display) ? 1 : 0)) );
|
2013-09-18 20:32:40 +02:00
|
|
|
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]));
|
2011-10-16 19:09:33 +02:00
|
|
|
|
|
|
|
neo_win.redraw();
|
|
|
|
|
2011-10-16 20:52:57 +02:00
|
|
|
return true;
|
2011-10-16 19:09:33 +02:00
|
|
|
}
|
2011-10-16 20:52:57 +02:00
|
|
|
}// End KeybindingManager class
|
|
|
|
|
|
|
|
|
|
|
|
}// End Namespace
|