4
4
5
5
@author Erki Suurjaak
6
6
@created 06.04.2015
7
- @modified 29.04 .2015
7
+ @modified 06.05 .2015
8
8
"""
9
9
from __future__ import print_function
10
10
import datetime
11
- import multiprocessing
12
11
import Queue
13
12
import sys
14
13
import threading
21
20
DEBUG = False
22
21
23
22
24
- class Listener (object ):
23
+ class Listener (threading . Thread ):
25
24
"""Runs mouse and keyboard listeners, and handles incoming commands."""
26
25
27
- def __init__ (self , inqueue , outqueue ):
26
+ def __init__ (self , inqueue , outqueue = None ):
27
+ threading .Thread .__init__ (self )
28
28
self .inqueue = inqueue
29
- self .outqueue = outqueue
30
29
self .running = False
31
30
self .mouse_handler = None
32
31
self .key_handler = None
33
- self .data_handler = DataHandler (self . outqueue . put )
32
+ self .data_handler = DataHandler (getattr ( outqueue , " put" , lambda x : x ) )
34
33
35
34
def run (self ):
36
35
self .running = True
37
36
while self .running :
38
37
command = self .inqueue .get ()
39
- if "exit" == command or command is None :
38
+ if "exit" == command :
40
39
self .stop ()
41
40
elif "mouse_start" == command :
42
41
if not self .mouse_handler :
@@ -110,17 +109,17 @@ class MouseHandler(pymouse.PyMouseEvent):
110
109
111
110
def __init__ (self , output ):
112
111
pymouse .PyMouseEvent .__init__ (self )
113
- self .output = output
112
+ self ._output = output
114
113
self .start ()
115
114
116
115
def click (self , x , y , button , press ):
117
- if press : self .output (type = "clicks" , x = x , y = y , button = button )
116
+ if press : self ._output (type = "clicks" , x = x , y = y , button = button )
118
117
119
118
def move (self , x , y ):
120
- self .output (type = "moves" , x = x , y = y )
119
+ self ._output (type = "moves" , x = x , y = y )
121
120
122
121
def scroll (self , x , y , wheel ):
123
- self .output (type = "scrolls" , x = x , y = y , wheel = wheel )
122
+ self ._output (type = "scrolls" , x = x , y = y , wheel = wheel )
124
123
125
124
126
125
@@ -129,38 +128,48 @@ class KeyHandler(pykeyboard.PyKeyboardEvent):
129
128
CONTROLCODES = {"\x00 " : "Nul" , "\x01 " : "Start-Of-Header" , "\x02 " : "Start-Of-Text" , "\x03 " : "Break" , "\x04 " : "End-Of-Transmission" , "\x05 " : "Enquiry" , "\x06 " : "Ack" , "\x07 " : "Bell" , "\x08 " : "Backspace" , "\x09 " : "Tab" , "\x0a " : "Linefeed" , "\x0b " : "Vertical-Tab" , "\x0c " : "Form-Fe" , "\x0d " : "Enter" , "\x0e " : "Shift-In" , "\x0f " : "Shift-Out" , "\x10 " : "Data-Link-Escape" , "\x11 " : "Devicecontrol1" , "\x12 " : "Devicecontrol2" , "\x13 " : "Devicecontrol3" , "\x14 " : "Devicecontrol4" , "\x15 " : "Nak" , "\x16 " : "Syn" , "\x17 " : "End-Of-Transmission-Block" , "\x18 " : "Break" , "\x19 " : "End-Of-Medium" , "\x1a " : "Substitute" , "\x1b " : "Escape" , "\x1c " : "File-Separator" , "\x1d " : "Group-Separator" , "\x1e " : "Record-Separator" , "\x1f " : "Unit-Separator" , "\x20 " : "Space" , "\x7f " : "Del" , "\xa0 " : "Non-Breaking Space" }
130
129
NUMPAD_SPECIALS = [("Insert" , False ), ("Delete" , False ), ("Home" , False ), ("End" , False ), ("PageUp" , False ), ("PageDown" , False ), ("Up" , False ), ("Down" , False ), ("Left" , False ), ("Right" , False ), ("Clear" , False ), ("Enter" , True )]
131
130
MODIFIERNAMES = {"Lcontrol" : "Ctrl" , "Rcontrol" : "Ctrl" , "Lshift" : "Shift" , "Rshift" : "Shift" , "Alt" : "Alt" , "AltGr" : "Alt" , "Lwin" : "Win" , "Rwin" : "Win" }
132
- RENAMES = {"Prior" : "PageUp" , "Next" : "PageDown" , "Lmenu" : "Alt" , "Rmenu" : "AltGr" , "Apps" : "Menu" , "Return" : "Enter" , "Back" : "Backspace" , "Capital" : "CapsLock" , "Numlock" : "NumLock" , "Snapshot" : "PrintScreen" , "Scroll" : "ScrollLock" , "Decimal" : "Numpad-Decimal" , "Divide" : "Numpad-Divide" , "Subtract" : "Numpad-Subtract" , "Multiply" : "Numpad-Multiply" , "Add" : "Numpad-Add" , "Cancel" : "Break" }
131
+ RENAMES = {"Prior" : "PageUp" , "Next" : "PageDown" , "Lmenu" : "Alt" , "Rmenu" : "AltGr" , "Apps" : "Menu" , "Return" : "Enter" , "Back" : "Backspace" , "Capital" : "CapsLock" , "Numlock" : "NumLock" , "Snapshot" : "PrintScreen" , "Scroll" : "ScrollLock" , "Decimal" : "Numpad-Decimal" , "Divide" : "Numpad-Divide" , "Subtract" : "Numpad-Subtract" , "Multiply" : "Numpad-Multiply" , "Add" : "Numpad-Add" , "Cancel" : "Break" , "Control_L" : "Lcontrol" , "Control_R" : "Rcontrol" , "Alt_L" : "Alt" , "Shift_L" : "Lshift" , "Shift_R" : "Rshift" , "Super_L" : "Lwin" , "Super_R" : "Rwin" , "BackSpace" : "Backspace" , "L1" : "F11" , "L2" : "F12" , "Page_Up" : "PageUp" , "Print" : "PrintScreen" , "Scroll_Lock" : "ScrollLock" , "Caps_Lock" : "CapsLock" , "Num_Lock" : "NumLock" , "Begin" : "Clear" , "Super" : "Win" , "Mode_switch" : "AltGr" }
133
132
KEYS_DOWN = (0x0100 , 0x0104 ) # [WM_KEYDOWN, WM_SYSKEYDOWN]
134
133
KEYS_UP = (0x0101 , 0x0105 ) # [WM_KEYUP, WM_SYSKEYUP]
135
134
ALT_GRS = (36 , 64 , 91 , 92 , 93 , 123 , 124 , 125 , 128 , 163 , 208 , 222 , 240 , 254 ) # $@[\]{|}€£ŠŽšž
135
+ OEM_KEYS = {34 : "Oem_3" , 35 : "Oem_5" , 47 : "Oem_1" , 48 : "Oem_2" , 51 : "Oem_5" , 59 : "Oem_Comma" , 60 : "Oem_Period" , 61 : "Oem_6" , 94 : "Oem_102" }
136
136
137
137
138
138
139
139
def __init__ (self , output ):
140
140
pykeyboard .PyKeyboardEvent .__init__ (self )
141
- self .output = output
141
+ self ._output = output
142
142
NAMES = {"win32" : "handler" , "linux2" : "tap" , "darwin" : "keypress" }
143
- HANDLERS = {"win32" : self .handle_windows , "linux2" : self .handle_linux ,
144
- "darwin" : self .handle_mac }
143
+ HANDLERS = {"win32" : self ._handle_windows , "linux2" : self ._handle_linux ,
144
+ "darwin" : self ._handle_mac }
145
145
setattr (self , NAMES [sys .platform ], HANDLERS [sys .platform ])
146
- self .modifiers = dict ((x , False ) for x in self .MODIFIERNAMES .values ())
147
- self .realmodifiers = dict ((x , False ) for x in self .MODIFIERNAMES )
146
+ self ._modifiers = dict ((x , False ) for x in self .MODIFIERNAMES .values ())
147
+ self ._realmodifiers = dict ((x , False ) for x in self .MODIFIERNAMES )
148
148
self .start ()
149
149
150
150
151
- def keyname (self , key ):
152
- key = self .CONTROLCODES .get (key , key )
153
- key = self .RENAMES .get (key , key )
151
+ def _keyname (self , key , keycode = None ):
152
+ if keycode in self .OEM_KEYS :
153
+ key = self .OEM_KEYS [keycode ]
154
+ elif key .startswith ("KP_" ): # Linux numpad
155
+ if 4 == len (key ):
156
+ key = key .replace ("KP_" , "Numpad" )
157
+ else :
158
+ key = key .replace ("KP_" , "" )
159
+ key = "Numpad-" + self .RENAMES .get (key , key ).replace ("Numpad-" , "" )
160
+ else :
161
+ key = self .CONTROLCODES .get (key , key )
162
+ key = self .RENAMES .get (key , key )
154
163
return key .upper () if 1 == len (key ) else key
155
164
156
165
157
- def handle_windows (self , event ):
166
+ def _handle_windows (self , event ):
158
167
"""Windows key event handler."""
159
- vkey = self .keyname (event .GetKey ())
168
+ vkey = self ._keyname (event .GetKey ())
160
169
if event .Message in self .KEYS_UP + self .KEYS_DOWN :
161
170
if vkey in self .MODIFIERNAMES :
162
- self .realmodifiers [vkey ] = event .Message in self .KEYS_DOWN
163
- self .modifiers [self .MODIFIERNAMES [vkey ]] = self .realmodifiers [vkey ]
171
+ self ._realmodifiers [vkey ] = event .Message in self .KEYS_DOWN
172
+ self ._modifiers [self .MODIFIERNAMES [vkey ]] = self ._realmodifiers [vkey ]
164
173
if event .Message not in self .KEYS_DOWN :
165
174
return True
166
175
@@ -171,22 +180,22 @@ def handle_windows(self, event):
171
180
key = vkey
172
181
else :
173
182
is_altgr = event .Ascii in self .ALT_GRS
174
- key = self .keyname (unichr (event .Ascii ))
183
+ key = self ._keyname (unichr (event .Ascii ))
175
184
176
185
if DEBUG : print ("Adding key %s (real %s)" % (key .encode ("utf-8" ), vkey .encode ("utf-8" )))
177
- self .output (type = "keys" , key = key , realkey = vkey )
186
+ self ._output (type = "keys" , key = key , realkey = vkey )
178
187
179
188
if vkey not in self .MODIFIERNAMES and not is_altgr :
180
189
modifier = "-" .join (k for k in ["Ctrl" , "Alt" , "Shift" , "Win" ]
181
190
if self .modifiers [k ])
182
191
if modifier and modifier != "Shift" : # Shift-X is not a combo
183
- if self .modifiers ["Ctrl" ] and event .Ascii :
184
- key = self .keyname (unichr (event .KeyID ))
185
- realmodifier = "-" .join (k for k , v in self .realmodifiers .items () if v )
192
+ if self ._modifiers ["Ctrl" ] and event .Ascii :
193
+ key = self ._keyname (unichr (event .KeyID ))
194
+ realmodifier = "-" .join (k for k , v in self ._realmodifiers .items () if v )
186
195
realkey = "%s-%s" % (realmodifier , key )
187
196
key = "%s-%s" % (modifier , key )
188
197
if DEBUG : print ("Adding combo %s (real %s)" % (key .encode ("utf-8" ), realkey .encode ("utf-8" )))
189
- self .output (type = "combos" , key = key , realkey = realkey )
198
+ self ._output (type = "combos" , key = key , realkey = realkey )
190
199
191
200
if DEBUG :
192
201
print ("CHARACTER: %r" % key )
@@ -203,29 +212,65 @@ def handle_windows(self, event):
203
212
return True
204
213
205
214
206
- def handle_mac (self , keycode ):
215
+ def _handle_mac (self , keycode ):
207
216
"""Mac key event handler"""
208
- key = self .keyname (unichr (keycode ))
209
- self .output (type = "keys" , key = key , realkey = key )
217
+ key = self ._keyname (unichr (keycode ))
218
+ self ._output (type = "keys" , key = key , realkey = key )
210
219
211
- def handle_linux (self , keycode , character , press ):
220
+ def _handle_linux (self , keycode , character , press ):
212
221
"""Linux key event handler."""
213
- key = self .keyname (character )
214
- if press : self .output (type = "keys" , key = key , realkey = key )
222
+ if character is None : return
223
+ key = self ._keyname (character , keycode )
224
+ if key in self .MODIFIERNAMES :
225
+ self ._modifiers [self .MODIFIERNAMES [key ]] = press
226
+ self ._realmodifiers [key ] = press
227
+ if press :
228
+ self ._output (type = "keys" , key = key , realkey = key )
229
+ if press and key not in self .MODIFIERNAMES :
230
+ modifier = "-" .join (k for k in ["Ctrl" , "Alt" , "Shift" , "Win" ]
231
+ if self ._modifiers [k ])
232
+ if modifier and modifier != "Shift" : # Shift-X is not a combo
233
+ realmodifier = "-" .join (k for k , v in self ._realmodifiers .items () if v )
234
+ realkey = "%s-%s" % (realmodifier , key )
235
+ key = "%s-%s" % (modifier , key )
236
+ if DEBUG : print ("Adding combo %s (real %s)" % (key .encode ("utf-8" ), realkey .encode ("utf-8" )))
237
+ self ._output (type = "combos" , key = key , realkey = realkey )
238
+
215
239
216
240
def escape (self , event ):
217
241
"""Override PyKeyboardEvent.escape to not quit on Escape."""
218
242
return False
219
243
220
244
245
+
246
+ class LineQueue (threading .Thread ):
247
+ """Reads lines from a file-like object and pushes to self.queue."""
248
+ def __init__ (self , input ):
249
+ threading .Thread .__init__ (self )
250
+ self .daemon = True
251
+ self .input , self .queue = input , Queue .Queue ()
252
+ self .start ()
253
+
254
+ def run (self ):
255
+ for line in iter (self .input .readline , "" ):
256
+ self .queue .put (line .strip ())
257
+
258
+
259
+ def start (inqueue , outqueue = None ):
260
+ """Starts the listener with incoming and outgoing queues."""
261
+ conf .init (), db .init (conf .DbPath )
262
+ Listener (inqueue , outqueue ).run ()
263
+
264
+
221
265
def main ():
222
266
"""Entry point for stand-alone execution."""
223
267
conf .init (), db .init (conf .DbPath )
224
- inqueue = multiprocessing .Queue ()
225
- outqueue = type ("PrintQueue" , (), {"put" : lambda self , x : print (x )})()
268
+ inqueue = LineQueue (sys .stdin ).queue
269
+ outqueue = type ("" , (), {"put" : lambda self , x : print ("\r %s" % x , end = " " )})()
270
+ if "--quiet" in sys .argv : outqueue = None
226
271
if conf .MouseEnabled : inqueue .put ("mouse_start" )
227
272
if conf .KeyboardEnabled : inqueue .put ("keyboard_start" )
228
- Listener (inqueue , outqueue ). run ( )
273
+ start (inqueue , outqueue )
229
274
230
275
231
276
if "__main__" == __name__ :
0 commit comments