Global key binding on X using Python gtk3

I was looking for some example of global python xlib babe that will work with gtk3, as is done for gtk2 at http://www.siafoo.net/snippet/239 , very similar code here:

from Xlib.display import Display from Xlib import X import gtk.gdk import threading import gobject class GlobalKeyBinding (gobject.GObject, threading.Thread): __gsignals__ = { 'activate': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), } def __init__ (self): gobject.GObject.__init__ (self) threading.Thread.__init__ (self) self.setDaemon (True) self.keymap = gtk.gdk.keymap_get_default () self.display = Display () self.screen = self.display.screen () self.root = self.screen.root self.map_modifiers () self.keybindings={} self.current_signal=None def map_modifiers (self): gdk_modifiers = (gtk.gdk.CONTROL_MASK, gtk.gdk.SHIFT_MASK, gtk.gdk.MOD1_MASK, gtk.gdk.MOD3_MASK, gtk.gdk.MOD4_MASK, gtk.gdk.MOD5_MASK, gtk.gdk.SUPER_MASK, gtk.gdk.HYPER_MASK) self.known_modifiers_mask = 0 for modifier in gdk_modifiers: self.known_modifiers_mask |= modifier def add_grab_key(self,accelerator,signal): if not accelerator: return keyval,modifiers=gtk.accelerator_parse(accelerator) if not keyval or not modifiers: return keycode=self.keymap.get_entries_for_keyval(keyval)[0][0] self.keybindings[signal]=[accelerator, keycode, int (modifiers)] #grab_key operation forces X to exclusivelly send given keycode (like apostrophe char) to current X client (unless other X client grabbed it before). #`X.AnyModifier' parameter tells to register the keycode for all modifiers, thus Ctrl-', Alt-', Shift-', ' will all be sent to this X client. # given keyval is grabbed by current X client until `ungrab_key' is called. return self.root.grab_key (keycode, X.AnyModifier, True, X.GrabModeAsync, X.GrabModeSync) def ungrab (self): for signal in self.keybindings: # ungrab_key ungrabs given keycode, that was grabbed by `grab_key'. self.root.ungrab_key (self.keybindings[signal][1],X.AnyModifier, self.root) self.keybindings={} def idle (self): if self.current_signal: gtk.gdk.threads_enter () self.emit (self.current_signal) self.current_signal=None gtk.gdk.threads_leave () return False #threading.Thread.start() method invokes this method. def run (self): self.running = True wait_for_release = False while self.running: event = self.display.next_event () # registered keycode(or probably rather event) has been received. if self.current_signal: self.display.allow_events (X.ReplayKeyboard, event.time) continue try: if not wait_for_release and event.type == X.KeyPress: modifiers = event.state & self.known_modifiers_mask print modifiers, event.detail for signal in self.keybindings: if self.keybindings[signal][1] == event.detail and self.keybindings[signal][2] == modifiers: self.this_signal=signal this_keycode = self.keybindings[signal][1] wait_for_release=True break if wait_for_release: self.display.allow_events (X.AsyncKeyboard, event.time) else: self.display.allow_events (X.ReplayKeyboard, event.time) continue elif wait_for_release and event.detail == this_keycode and event.type == X.KeyRelease: wait_for_release = False self.current_signal=self.this_signal self.event_window=event.window gobject.idle_add (self.idle) self.display.allow_events (X.AsyncKeyboard, event.time) else: self.display.allow_events (X.ReplayKeyboard, event.time) except: self.display.allow_events (X.ReplayKeyboard, event.time) def stop (self): print "stopping keybindings thread..." self.running = False self.ungrab () self.display.close () # SAMPLE USAGE def callback (keybinding): print 'Callback!' keybinding.stop() gtk.main_quit () def main(): print "starting..." gtk.gdk.threads_init () keybindings=GlobalKeyBinding() keybindings.add_grab_key('<Control>apostrophe','activate') keybindings.connect('activate',callback) keybindings.start () # let thart the thread gtk.main () main() 

Unfortunately, I did not find it, so I decided to override it to use gtk3 (ubuntu 12.04). Below is the result. It does not have runtime errors, but unfortunately it does not receive any data.

 from Xlib.display import Display from Xlib import X from gi.repository import Gtk, Gdk, GObject import threading class GlobalKeyBinding (GObject.GObject, threading.Thread): __gsignals__ = { 'activate': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__ (self): GObject.GObject.__init__ (self) threading.Thread.__init__ (self) self.setDaemon (True) self.keymap = Gdk.Keymap.get_default() self.display = Display () self.screen = self.display.screen () self.root = self.screen.root self.map_modifiers () self.keybindings={} self.current_signal=None def map_modifiers (self): gdk_modifiers = (Gdk.ModifierType.CONTROL_MASK, Gdk.ModifierType.SHIFT_MASK, Gdk.ModifierType.MOD1_MASK, Gdk.ModifierType.MOD3_MASK, Gdk.ModifierType.MOD4_MASK, Gdk.ModifierType.MOD5_MASK, Gdk.ModifierType.SUPER_MASK, Gdk.ModifierType.HYPER_MASK) self.known_modifiers_mask = 0 for modifier in gdk_modifiers: #print modifier,modifier+0 self.known_modifiers_mask |= modifier def add_grab_key(self,accelerator,signal): if not accelerator: return keyval,modifiers=Gtk.accelerator_parse(accelerator) if not keyval or not modifiers: return #keycode=self.keymap.get_entries_for_keyval(keyval)[0][0] success, entries = self.keymap.get_entries_for_keyval(keyval) entry = [(int(i.keycode), i.group, i.level) for i in entries] if not entry: raise TypeError("Invalid key name") keycode=entry[0][0] self.keybindings[signal]=[accelerator, keycode, int (modifiers)] return self.root.grab_key (keycode, X.AnyModifier, True, X.GrabModeAsync, X.GrabModeSync) def ungrab (self): for signal in self.keybindings: self.root.ungrab_key (self.keybindings[signal][1],X.AnyModifier, self.root) self.keybindings={} def idle (self): if self.current_signal: Gdk.threads_enter () self.emit (self.current_signal) self.current_signal=None Gdk.threads_leave () return False def run (self): self.running = True wait_for_release = False while self.running: event = self.display.next_event () if self.current_signal: self.display.allow_events (X.ReplayKeyboard, event.time) continue try: if not wait_for_release and event.type == X.KeyPress: modifiers = event.get_state() & self.known_modifiers_mask print modifiers,event.get_state() for signal in self.keybindings: if self.keybindings[signal][1] == event.detail and self.keybindings[signal][2] == modifiers: self.this_signal=signal this_keycode = self.keybindings[signal][1] wait_for_release=True break if wait_for_release: self.display.allow_events (X.AsyncKeyboard, event.time) else: self.display.allow_events (X.ReplayKeyboard, event.time) continue elif wait_for_release and event.detail == this_keycode and event.type == X.KeyRelease: wait_for_release = False self.current_signal=self.this_signal self.event_window=event.window GObject.idle_add (self.idle) self.display.allow_events (X.AsyncKeyboard, event.time) else: self.display.allow_events (X.ReplayKeyboard, event.time) except: self.display.allow_events (X.ReplayKeyboard, event.time) def stop (self): self.running = False self.ungrab () self.display.close () # SAMPLE USAGE def callback (keybinding): print 'Callback!' keybinding.stop() Gtk.main_quit () def main(): print "starting..." Gdk.threads_init () keybindings=GlobalKeyBinding() keybindings.add_grab_key('<Control>apostrophe','activate') keybindings.connect('activate',callback) print "keybindings go" keybindings.start () # let thart the thread print "gtk go" Gtk.main () main() 

Perhaps you have ideas on how to make it work?

Regards Paul

+2
python linux gtk3 xlib
source share
1 answer

Hey, I implemented the same code and worked fine, you can try this. but there are no guarantees. If you find the missing parts, please tell me.

 # -*- coding: utf-8; -*- # Copyright (C) 2013 Özcan Esen <ozcanesen@gmail.com> # Copyright (C) 2008 Luca Bruno <lethalman88@gmail.com> # # This a slightly modified version of the globalkeybinding.py file which is part of FreeSpeak. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. from Xlib.display import Display from Xlib import X, error #import GObject #import gtk.gdk from gi.repository import Gtk, Gdk, GObject, GLib import threading from config import ConfigManager class GlobalKeyBinding(GObject.GObject, threading.Thread): __gsignals__ = { 'activate':(GObject.SIGNAL_RUN_LAST, None,()), } def __init__(self): GObject.GObject.__init__(self) threading.Thread.__init__(self) self.setDaemon(True) self.keymap = Gdk.Keymap.get_default() self.display = Display() self.screen = self.display.screen() self.root = self.screen.root self.ignored_masks = self.get_mask_combinations(X.LockMask | X.Mod2Mask | X.Mod5Mask) self.map_modifiers() def map_modifiers(self): gdk_modifiers =(Gdk.ModifierType.CONTROL_MASK, Gdk.ModifierType.SHIFT_MASK, Gdk.ModifierType.MOD1_MASK, Gdk.ModifierType.MOD2_MASK, Gdk.ModifierType.MOD3_MASK, Gdk.ModifierType.MOD4_MASK, Gdk.ModifierType.MOD5_MASK, Gdk.ModifierType.SUPER_MASK, Gdk.ModifierType.HYPER_MASK) self.known_modifiers_mask = 0 for modifier in gdk_modifiers: if "Mod" not in Gtk.accelerator_name(0, modifier): self.known_modifiers_mask |= modifier def grab(self): Gdk.threads_enter() accelerator = ConfigManager.get_conf('global-key') Gdk.threads_leave() keyval, modifiers = Gtk.accelerator_parse(accelerator) if not accelerator or(not keyval and not modifiers): self.keycode = None self.modifiers = None return self.keycode= self.keymap.get_entries_for_keyval(keyval)[1][0].keycode self.modifiers = int(modifiers) catch = error.CatchError(error.BadAccess) for ignored_mask in self.ignored_masks: mod = modifiers | ignored_mask result = self.root.grab_key(self.keycode, mod, True, X.GrabModeAsync, X.GrabModeSync, onerror=catch) self.display.sync() if catch.get_error(): return False return True def ungrab(self): if self.keycode: self.root.ungrab_key(self.keycode, X.AnyModifier, self.root) def get_mask_combinations(self, mask): return [x for x in xrange(mask+1) if not (x & ~mask)] def idle(self): Gdk.threads_enter() self.emit("activate") Gdk.threads_leave() return False def run(self): self.running = True wait_for_release = False while self.running: event = self.display.next_event() self.current_event_time = event.time if event.detail == self.keycode and event.type == X.KeyPress and not wait_for_release: modifiers = event.state & self.known_modifiers_mask if modifiers == self.modifiers: wait_for_release = True self.display.allow_events(X.AsyncKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) elif event.detail == self.keycode and wait_for_release: if event.type == X.KeyRelease: wait_for_release = False GLib.idle_add(self.idle) self.display.allow_events(X.AsyncKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) def stop(self): self.running = False self.ungrab() self.display.close() 
+2
source share

All Articles