/* Copyright (c) 2011 by Simon Schneegans This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ namespace GnomePie { ///////////////////////////////////////////////////////////////////////// /// Globally binds key stroke to given ID's. When one of the bound /// strokes is invoked, a signal with the according ID is emitted. ///////////////////////////////////////////////////////////////////////// public class BindingManager : GLib.Object { ///////////////////////////////////////////////////////////////////// /// Called when a stored binding is invoked. The according ID is /// passed as argument. ///////////////////////////////////////////////////////////////////// public signal void on_press(string id); ///////////////////////////////////////////////////////////////////// /// A list storing bindings, which are invoked even if Gnome-Pie /// doesn't have the current focus ///////////////////////////////////////////////////////////////////// private Gee.List bindings = new Gee.ArrayList(); ///////////////////////////////////////////////////////////////////// /// Ignored modifier masks, used to grab all keys even if these locks /// are active. ///////////////////////////////////////////////////////////////////// private static uint[] lock_modifiers = { 0, Gdk.ModifierType.MOD2_MASK, Gdk.ModifierType.LOCK_MASK, Gdk.ModifierType.MOD5_MASK, 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 }; ///////////////////////////////////////////////////////////////////// /// Helper class to store keybinding ///////////////////////////////////////////////////////////////////// private class Keybinding { public Keybinding(string accelerator, int keycode, Gdk.ModifierType modifiers, string id) { this.accelerator = accelerator; this.keycode = keycode; this.modifiers = modifiers; this.id = id; } public string accelerator { get; set; } public int keycode { get; set; } public Gdk.ModifierType modifiers { get; set; } public string id { get; set; } } ///////////////////////////////////////////////////////////////////// /// C'tor adds the event filter to the root window. ///////////////////////////////////////////////////////////////////// public BindingManager() { // init filter to retrieve X.Events Gdk.Window rootwin = Gdk.get_default_root_window(); if(rootwin != null) { rootwin.add_filter(event_filter); } } ///////////////////////////////////////////////////////////////////// /// Binds the ID to the given accelerator. ///////////////////////////////////////////////////////////////////// public void bind(string accelerator, string id) { uint keysym; Gdk.ModifierType modifiers; Gtk.accelerator_parse(accelerator, out keysym, out modifiers); if (keysym == 0) { warning("Invalid keystroke: " + accelerator); return; } Gdk.Window rootwin = Gdk.get_default_root_window(); X.Display display = Gdk.x11_drawable_get_xdisplay(rootwin); X.ID xid = Gdk.x11_drawable_get_xid(rootwin); int keycode = display.keysym_to_keycode(keysym); if(keycode != 0) { Gdk.error_trap_push(); foreach(uint lock_modifier in lock_modifiers) { display.grab_key(keycode, modifiers|lock_modifier, xid, false, X.GrabMode.Async, X.GrabMode.Async); } Gdk.flush(); Keybinding binding = new Keybinding(accelerator, keycode, modifiers, id); bindings.add(binding); } } ///////////////////////////////////////////////////////////////////// /// Unbinds the accelerator of the given ID. ///////////////////////////////////////////////////////////////////// public void unbind(string id) { Gdk.Window rootwin = Gdk.get_default_root_window(); X.Display display = Gdk.x11_drawable_get_xdisplay(rootwin); X.ID xid = Gdk.x11_drawable_get_xid(rootwin); Gee.List remove_bindings = new Gee.ArrayList(); foreach(var binding in bindings) { if(id == binding.id) { foreach(uint lock_modifier in lock_modifiers) { display.ungrab_key(binding.keycode, binding.modifiers, xid); } remove_bindings.add(binding); } } bindings.remove_all(remove_bindings); } ///////////////////////////////////////////////////////////////////// /// Returns a human readable accelerator for the given ID. ///////////////////////////////////////////////////////////////////// public string get_accelerator_label_of(string id) { string accelerator = this.get_accelerator_of(id); if (accelerator == "") return _("Not bound"); uint key = 0; Gdk.ModifierType mods; Gtk.accelerator_parse(accelerator, out key, out mods); return Gtk.accelerator_get_label(key, mods); } ///////////////////////////////////////////////////////////////////// /// Returns the accelerator to which the given ID is bound. ///////////////////////////////////////////////////////////////////// public string get_accelerator_of(string id) { foreach (var binding in bindings) { if (binding.id == id) { return binding.accelerator; } } return ""; } ///////////////////////////////////////////////////////////////////// /// 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; void* pointer = &gdk_xevent; X.Event* xevent = (X.Event*) pointer; if(xevent->type == X.EventType.KeyPress) { foreach(var 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) { on_press(binding.id); } } } return filter_return; } } }