| 1 |
# WinDictator: |
|---|
| 2 |
# Dictate in Windows, have the text typed in Linux via X faked keystroke events |
|---|
| 3 |
# |
|---|
| 4 |
# Copyright (C) 2005-2006 by Edwin A. Suominen, http://www.eepatents.com |
|---|
| 5 |
# |
|---|
| 6 |
# This program is free software; you can redistribute it and/or modify it under |
|---|
| 7 |
# the terms of the GNU General Public License as published by the Free Software |
|---|
| 8 |
# Foundation; either version 2 of the License, or (at your option) any later |
|---|
| 9 |
# version. |
|---|
| 10 |
# |
|---|
| 11 |
# This program is distributed in the hope that it will be useful, but WITHOUT |
|---|
| 12 |
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
|---|
| 13 |
# FOR A PARTICULAR PURPOSE. See the file COPYING for more details. |
|---|
| 14 |
# |
|---|
| 15 |
# You should have received a copy of the GNU General Public License along with |
|---|
| 16 |
# this program; if not, write to the Free Software Foundation, Inc., 51 |
|---|
| 17 |
# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|---|
| 18 |
|
|---|
| 19 |
|
|---|
| 20 |
""" |
|---|
| 21 |
Keyer for the virtual typist |
|---|
| 22 |
""" |
|---|
| 23 |
|
|---|
| 24 |
import os, warnings |
|---|
| 25 |
from twisted.internet import defer, protocol, reactor |
|---|
| 26 |
|
|---|
| 27 |
from protocol import ProcessProtocol |
|---|
| 28 |
|
|---|
| 29 |
BINARY = "/usr/local/bin/typist" |
|---|
| 30 |
|
|---|
| 31 |
|
|---|
| 32 |
class Keyer: |
|---|
| 33 |
""" |
|---|
| 34 |
I send fake keystrokes to the currently active window using the I{typist} |
|---|
| 35 |
process. |
|---|
| 36 |
""" |
|---|
| 37 |
def __init__(self): |
|---|
| 38 |
self.dStop = defer.Deferred() |
|---|
| 39 |
|
|---|
| 40 |
def __del__(self): |
|---|
| 41 |
""" |
|---|
| 42 |
Closes the pipe to the typist process |
|---|
| 43 |
""" |
|---|
| 44 |
self.shutdown() |
|---|
| 45 |
|
|---|
| 46 |
def startup(self): |
|---|
| 47 |
""" |
|---|
| 48 |
Opens a pipe to the typist process, which executes a keystroke with |
|---|
| 49 |
each new line sent and returns the keycode used on stdout. |
|---|
| 50 |
""" |
|---|
| 51 |
def ready(transport): |
|---|
| 52 |
self.dStack = [] |
|---|
| 53 |
self.process = transport |
|---|
| 54 |
|
|---|
| 55 |
def error(self, failure): |
|---|
| 56 |
print "Error running typist process:", failure.printTraceback() |
|---|
| 57 |
return failure |
|---|
| 58 |
|
|---|
| 59 |
d = defer.Deferred() |
|---|
| 60 |
d.addCallbacks(ready, error) |
|---|
| 61 |
p = ProcessProtocol(d, self.dStop, self.whatTyped) |
|---|
| 62 |
reactor.spawnProcess(p, BINARY, ('typist',), env=os.environ) |
|---|
| 63 |
return d |
|---|
| 64 |
|
|---|
| 65 |
def shutdown(self): |
|---|
| 66 |
if hasattr(self, 'process'): |
|---|
| 67 |
self.process.loseConnection() |
|---|
| 68 |
del self.process |
|---|
| 69 |
return self.dStop |
|---|
| 70 |
else: |
|---|
| 71 |
return defer.succeed(None) |
|---|
| 72 |
|
|---|
| 73 |
def pressKey(self, keySym): |
|---|
| 74 |
""" |
|---|
| 75 |
Instructs the typist process to depress the specified I{keySym}. |
|---|
| 76 |
""" |
|---|
| 77 |
return self.key('d', keySym) |
|---|
| 78 |
|
|---|
| 79 |
def releaseKey(self, keySym): |
|---|
| 80 |
""" |
|---|
| 81 |
Instructs the typist process to release the specified I{keySym}. |
|---|
| 82 |
""" |
|---|
| 83 |
return self.key('u', keySym) |
|---|
| 84 |
|
|---|
| 85 |
def key(self, du, keySym): |
|---|
| 86 |
""" |
|---|
| 87 |
Sends a key press (I{d}) or release (I{u}) command to the typist |
|---|
| 88 |
process via STDIN. Returns a C{Deferred} that fires with the keycode |
|---|
| 89 |
for the pressed or released key. |
|---|
| 90 |
""" |
|---|
| 91 |
def send(null): |
|---|
| 92 |
self.process.write("%s %s\n" % (du, keySym)) |
|---|
| 93 |
d = defer.Deferred() |
|---|
| 94 |
if self.dStack: |
|---|
| 95 |
warnings.warn( |
|---|
| 96 |
"New keystroke being sent before previous one confirmed", |
|---|
| 97 |
stacklevel=2) |
|---|
| 98 |
self.dStack.append(d) |
|---|
| 99 |
return d |
|---|
| 100 |
|
|---|
| 101 |
if hasattr(self, 'process'): |
|---|
| 102 |
return send(None) |
|---|
| 103 |
else: |
|---|
| 104 |
d = self.startup() |
|---|
| 105 |
d.addCallback(send) |
|---|
| 106 |
return d |
|---|
| 107 |
|
|---|
| 108 |
def whatTyped(self, line): |
|---|
| 109 |
""" |
|---|
| 110 |
Callback for STDOUT feedback from the I{typist} process, with a line |
|---|
| 111 |
containing the keycode of the keysym that it has accepted. |
|---|
| 112 |
|
|---|
| 113 |
Fires the C{Deferred} for the last-sent keystroke with the integer |
|---|
| 114 |
keycode if returned from the I{typist} process, or with C{None} if the |
|---|
| 115 |
keystroke wasn't accepted. |
|---|
| 116 |
""" |
|---|
| 117 |
d = self.dStack.pop() |
|---|
| 118 |
try: |
|---|
| 119 |
result = int(line.strip()) |
|---|
| 120 |
except: |
|---|
| 121 |
result = None |
|---|
| 122 |
d.callback(result) |
|---|
| 123 |
|
|---|
| 124 |
|
|---|
| 125 |
|
|---|
| 126 |
|
|---|