diff options
| author | Tim Redfern <tim@eclectronics.org> | 2013-11-18 14:40:41 +0000 |
|---|---|---|
| committer | Tim Redfern <tim@eclectronics.org> | 2013-11-18 14:40:41 +0000 |
| commit | a36735b4585521218268da5fed2214ba239b4c2a (patch) | |
| tree | 77f951acb62d61c88b0b01d4bcb34367e44abc91 /lyricstimer/pyxhook.py | |
| parent | 6c5ba0ff0a4301b932c70aee6b4c87b2092d8bff (diff) | |
simple lyrics script
Diffstat (limited to 'lyricstimer/pyxhook.py')
| -rw-r--r-- | lyricstimer/pyxhook.py | 359 |
1 files changed, 359 insertions, 0 deletions
diff --git a/lyricstimer/pyxhook.py b/lyricstimer/pyxhook.py new file mode 100644 index 0000000..387eb86 --- /dev/null +++ b/lyricstimer/pyxhook.py @@ -0,0 +1,359 @@ +#!/usr/bin/python +# from pykeylogger http://pykeylogger.sourceforge.net/ +# +# pyxhook -- an extension to emulate some of the PyHook library on linux. +# +# Copyright (C) 2008 Tim Alexander <dragonfyre13@gmail.com> +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Thanks to Alex Badea <vamposdecampos@gmail.com> for writing the Record +# demo for the xlib libraries. It helped me immensely working with these +# in this library. +# +# Thanks to the python-xlib team. This wouldn't have been possible without +# your code. +# +# This requires: +# at least python-xlib 1.4 +# xwindows must have the "record" extension present, and active. +# +# This file has now been somewhat extensively modified by +# Daniel Folkinshteyn <nanotube@users.sf.net> +# So if there are any bugs, they are probably my fault. :) + +import sys +import os +import re +import time +import threading + +from Xlib import X, XK, display, error +from Xlib.ext import record +from Xlib.protocol import rq + +####################################################################### +########################START CLASS DEF################################ +####################################################################### + +class HookManager(threading.Thread): + """This is the main class. Instantiate it, and you can hand it KeyDown and KeyUp (functions in your own code) which execute to parse the pyxhookkeyevent class that is returned. + + This simply takes these two values for now: + KeyDown = The function to execute when a key is pressed, if it returns anything. It hands the function an argument that is the pyxhookkeyevent class. + KeyUp = The function to execute when a key is released, if it returns anything. It hands the function an argument that is the pyxhookkeyevent class. + """ + + def __init__(self): + threading.Thread.__init__(self) + self.finished = threading.Event() + + # Give these some initial values + self.mouse_position_x = 0 + self.mouse_position_y = 0 + self.ison = {"shift":False, "caps":False} + + # Compile our regex statements. + self.isshift = re.compile('^Shift') + self.iscaps = re.compile('^Caps_Lock') + self.shiftablechar = re.compile('^[a-z0-9]$|^minus$|^equal$|^bracketleft$|^bracketright$|^semicolon$|^backslash$|^apostrophe$|^comma$|^period$|^slash$|^grave$') + self.logrelease = re.compile('.*') + self.isspace = re.compile('^space$') + + # Assign default function actions (do nothing). + self.KeyDown = lambda x: True + self.KeyUp = lambda x: True + self.MouseAllButtonsDown = lambda x: True + self.MouseAllButtonsUp = lambda x: True + + self.contextEventMask = [X.KeyPress,X.MotionNotify] + + # Hook to our display. + self.local_dpy = display.Display() + self.record_dpy = display.Display() + + def run(self): + # Check if the extension is present + if not self.record_dpy.has_extension("RECORD"): + print "RECORD extension not found" + sys.exit(1) + r = self.record_dpy.record_get_version(0, 0) + print "RECORD extension version %d.%d" % (r.major_version, r.minor_version) + + # Create a recording context; we only want key and mouse events + self.ctx = self.record_dpy.record_create_context( + 0, + [record.AllClients], + [{ + 'core_requests': (0, 0), + 'core_replies': (0, 0), + 'ext_requests': (0, 0, 0, 0), + 'ext_replies': (0, 0, 0, 0), + 'delivered_events': (0, 0), + 'device_events': tuple(self.contextEventMask), #(X.KeyPress, X.ButtonPress), + 'errors': (0, 0), + 'client_started': False, + 'client_died': False, + }]) + + # Enable the context; this only returns after a call to record_disable_context, + # while calling the callback function in the meantime + self.record_dpy.record_enable_context(self.ctx, self.processevents) + # Finally free the context + self.record_dpy.record_free_context(self.ctx) + + def cancel(self): + self.finished.set() + self.local_dpy.record_disable_context(self.ctx) + self.local_dpy.flush() + + def printevent(self, event): + print event + + def HookKeyboard(self): + pass + # We don't need to do anything here anymore, since the default mask + # is now set to contain X.KeyPress + #self.contextEventMask[0] = X.KeyPress + + def HookMouse(self): + pass + # We don't need to do anything here anymore, since the default mask + # is now set to contain X.MotionNotify + + # need mouse motion to track pointer position, since ButtonPress events + # don't carry that info. + #self.contextEventMask[1] = X.MotionNotify + + def processevents(self, reply): + if reply.category != record.FromServer: + return + if reply.client_swapped: + print "* received swapped protocol data, cowardly ignored" + return + if not len(reply.data) or ord(reply.data[0]) < 2: + # not an event + return + data = reply.data + while len(data): + event, data = rq.EventField(None).parse_binary_value(data, self.record_dpy.display, None, None) + if event.type == X.KeyPress: + hookevent = self.keypressevent(event) + self.KeyDown(hookevent) + elif event.type == X.KeyRelease: + hookevent = self.keyreleaseevent(event) + self.KeyUp(hookevent) + elif event.type == X.ButtonPress: + hookevent = self.buttonpressevent(event) + self.MouseAllButtonsDown(hookevent) + elif event.type == X.ButtonRelease: + hookevent = self.buttonreleaseevent(event) + self.MouseAllButtonsUp(hookevent) + elif event.type == X.MotionNotify: + # use mouse moves to record mouse position, since press and release events + # do not give mouse position info (event.root_x and event.root_y have + # bogus info). + self.mousemoveevent(event) + + #print "processing events...", event.type + + def keypressevent(self, event): + matchto = self.lookup_keysym(self.local_dpy.keycode_to_keysym(event.detail, 0)) + if self.shiftablechar.match(self.lookup_keysym(self.local_dpy.keycode_to_keysym(event.detail, 0))): ## This is a character that can be typed. + if self.ison["shift"] == False: + keysym = self.local_dpy.keycode_to_keysym(event.detail, 0) + return self.makekeyhookevent(keysym, event) + else: + keysym = self.local_dpy.keycode_to_keysym(event.detail, 1) + return self.makekeyhookevent(keysym, event) + else: ## Not a typable character. + keysym = self.local_dpy.keycode_to_keysym(event.detail, 0) + if self.isshift.match(matchto): + self.ison["shift"] = self.ison["shift"] + 1 + elif self.iscaps.match(matchto): + if self.ison["caps"] == False: + self.ison["shift"] = self.ison["shift"] + 1 + self.ison["caps"] = True + if self.ison["caps"] == True: + self.ison["shift"] = self.ison["shift"] - 1 + self.ison["caps"] = False + return self.makekeyhookevent(keysym, event) + + def keyreleaseevent(self, event): + if self.shiftablechar.match(self.lookup_keysym(self.local_dpy.keycode_to_keysym(event.detail, 0))): + if self.ison["shift"] == False: + keysym = self.local_dpy.keycode_to_keysym(event.detail, 0) + else: + keysym = self.local_dpy.keycode_to_keysym(event.detail, 1) + else: + keysym = self.local_dpy.keycode_to_keysym(event.detail, 0) + matchto = self.lookup_keysym(keysym) + if self.isshift.match(matchto): + self.ison["shift"] = self.ison["shift"] - 1 + return self.makekeyhookevent(keysym, event) + + def buttonpressevent(self, event): + #self.clickx = self.rootx + #self.clicky = self.rooty + return self.makemousehookevent(event) + + def buttonreleaseevent(self, event): + #if (self.clickx == self.rootx) and (self.clicky == self.rooty): + ##print "ButtonClick " + str(event.detail) + " x=" + str(self.rootx) + " y=" + str(self.rooty) + #if (event.detail == 1) or (event.detail == 2) or (event.detail == 3): + #self.captureclick() + #else: + #pass + + return self.makemousehookevent(event) + + # sys.stdout.write("ButtonDown " + str(event.detail) + " x=" + str(self.clickx) + " y=" + str(self.clicky) + "\n") + # sys.stdout.write("ButtonUp " + str(event.detail) + " x=" + str(self.rootx) + " y=" + str(self.rooty) + "\n") + #sys.stdout.flush() + + def mousemoveevent(self, event): + self.mouse_position_x = event.root_x + self.mouse_position_y = event.root_y + + # need the following because XK.keysym_to_string() only does printable chars + # rather than being the correct inverse of XK.string_to_keysym() + def lookup_keysym(self, keysym): + for name in dir(XK): + if name.startswith("XK_") and getattr(XK, name) == keysym: + return name.lstrip("XK_") + return "[%d]" % keysym + + def asciivalue(self, keysym): + asciinum = XK.string_to_keysym(self.lookup_keysym(keysym)) + if asciinum < 256: + return asciinum + else: + return 0 + + def makekeyhookevent(self, keysym, event): + storewm = self.xwindowinfo() + if event.type == X.KeyPress: + MessageName = "key down" + elif event.type == X.KeyRelease: + MessageName = "key up" + return pyxhookkeyevent(storewm["handle"], storewm["name"], storewm["class"], self.lookup_keysym(keysym), self.asciivalue(keysym), False, event.detail, MessageName) + + def makemousehookevent(self, event): + storewm = self.xwindowinfo() + if event.detail == 1: + MessageName = "mouse left " + elif event.detail == 3: + MessageName = "mouse right " + elif event.detail == 2: + MessageName = "mouse middle " + elif event.detail == 5: + MessageName = "mouse wheel down " + elif event.detail == 4: + MessageName = "mouse wheel up " + else: + MessageName = "mouse " + str(event.detail) + " " + + if event.type == X.ButtonPress: + MessageName = MessageName + "down" + elif event.type == X.ButtonRelease: + MessageName = MessageName + "up" + return pyxhookmouseevent(storewm["handle"], storewm["name"], storewm["class"], (self.mouse_position_x, self.mouse_position_y), MessageName) + + def xwindowinfo(self): + try: + windowvar = self.local_dpy.get_input_focus().focus + wmname = windowvar.get_wm_name() + wmclass = windowvar.get_wm_class() + wmhandle = str(windowvar)[20:30] + except: + ## This is to keep things running smoothly. It almost never happens, but still... + return {"name":None, "class":None, "handle":None} + if (wmname == None) and (wmclass == None): + try: + windowvar = windowvar.query_tree().parent + wmname = windowvar.get_wm_name() + wmclass = windowvar.get_wm_class() + wmhandle = str(windowvar)[20:30] + except: + ## This is to keep things running smoothly. It almost never happens, but still... + return {"name":None, "class":None, "handle":None} + if wmclass == None: + return {"name":wmname, "class":wmclass, "handle":wmhandle} + else: + return {"name":wmname, "class":wmclass[0], "handle":wmhandle} + +class pyxhookkeyevent: + """This is the class that is returned with each key event.f + It simply creates the variables below in the class. + + Window = The handle of the window. + WindowName = The name of the window. + WindowProcName = The backend process for the window. + Key = The key pressed, shifted to the correct caps value. + Ascii = An ascii representation of the key. It returns 0 if the ascii value is not between 31 and 256. + KeyID = This is just False for now. Under windows, it is the Virtual Key Code, but that's a windows-only thing. + ScanCode = Please don't use this. It differs for pretty much every type of keyboard. X11 abstracts this information anyway. + MessageName = "key down", "key up". + """ + + def __init__(self, Window, WindowName, WindowProcName, Key, Ascii, KeyID, ScanCode, MessageName): + self.Window = Window + self.WindowName = WindowName + self.WindowProcName = WindowProcName + self.Key = Key + self.Ascii = Ascii + self.KeyID = KeyID + self.ScanCode = ScanCode + self.MessageName = MessageName + + def __str__(self): + return "Window Handle: " + str(self.Window) + "\nWindow Name: " + str(self.WindowName) + "\nWindow's Process Name: " + str(self.WindowProcName) + "\nKey Pressed: " + str(self.Key) + "\nAscii Value: " + str(self.Ascii) + "\nKeyID: " + str(self.KeyID) + "\nScanCode: " + str(self.ScanCode) + "\nMessageName: " + str(self.MessageName) + "\n" + +class pyxhookmouseevent: + """This is the class that is returned with each key event.f + It simply creates the variables below in the class. + + Window = The handle of the window. + WindowName = The name of the window. + WindowProcName = The backend process for the window. + Position = 2-tuple (x,y) coordinates of the mouse click + MessageName = "mouse left|right|middle down", "mouse left|right|middle up". + """ + + def __init__(self, Window, WindowName, WindowProcName, Position, MessageName): + self.Window = Window + self.WindowName = WindowName + self.WindowProcName = WindowProcName + self.Position = Position + self.MessageName = MessageName + + def __str__(self): + return "Window Handle: " + str(self.Window) + "\nWindow Name: " + str(self.WindowName) + "\nWindow's Process Name: " + str(self.WindowProcName) + "\nPosition: " + str(self.Position) + "\nMessageName: " + str(self.MessageName) + "\n" + +####################################################################### +#########################END CLASS DEF################################# +####################################################################### + +if __name__ == '__main__': + hm = HookManager() + hm.HookKeyboard() + hm.HookMouse() + hm.KeyDown = hm.printevent + hm.KeyUp = hm.printevent + hm.MouseAllButtonsDown = hm.printevent + hm.MouseAllButtonsUp = hm.printevent + hm.start() + time.sleep(10) + hm.cancel() |
