Blame ui/gtk3/keybindingmanager.vala

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