From 99198321a43a7f8b9e72146cb20dfbc4fc10154e Mon Sep 17 00:00:00 2001 From: citronneur Date: Sun, 30 Nov 2014 11:22:59 +0100 Subject: [PATCH] add keyboard layout detection or force --- README.md | 124 ++++++++++++++++++++++++++++++++++----- bin/rdpy-rdpclient.py | 86 ++++++++++++++++++++------- rdpy/protocol/rdp/gcc.py | 2 +- rdpy/protocol/rdp/rdp.py | 10 ++++ rdpy/ui/qt4.py | 2 - 5 files changed, 185 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index d65a924..6717182 100644 --- a/README.md +++ b/README.md @@ -119,9 +119,7 @@ RDPY can also be used as Qt widget throw rdpy.ui.qt4.QRemoteDesktop class. It ca In a nutshell the RDPY can be used as a protocol library with a twisted engine. -### Client library - -The RDP client code looks like this: +### Simple RDP Client ```python from rdpy.protocol.rdp import rdp @@ -135,20 +133,36 @@ class MyRDPFactory(rdp.ClientFactory): reactor.stop() def buildObserver(self, controller, addr): + class MyObserver(rdp.RDPClientObserver) - def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): - #here code handle bitmap - pass - - def onReady(self): + def onReady(self): + """ + @summary: Call when stack is ready + """ #send 'r' key self._controller.sendKeyEventUnicode(ord(unicode("r".toUtf8(), encoding="UTF-8")), True) #mouse move and click at pixel 200x200 self._controller.sendPointerEvent(200, 200, 1, true) + def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): + """ + @summary: Notify bitmap update + @param destLeft: xmin position + @param destTop: ymin position + @param destRight: xmax position because RDP can send bitmap with padding + @param destBottom: ymax position because RDP can send bitmap with padding + @param width: width of bitmap + @param height: height of bitmap + @param bitsPerPixel: number of bit per pixel + @param isCompress: use RLE compression + @param data: bitmap data + """ + def onClose(self): - pass + """ + @summary: Call when stack is close + """ return MyObserver(controller) @@ -157,11 +171,66 @@ reactor.connectTCP("XXX.XXX.XXX.XXX", 3389), MyRDPFactory()) reactor.run() ``` -The VNC client code looks like this: +### Simple RDP Server +```python +from rdpy.protocol.rdp import rdp + +class MyRDPFactory(rdp.ServerFactory): + + def buildObserver(self, controller, addr): + + class MyObserver(rdp.RDPServerObserver) + + def onReady(self): + """ + @summary: Call when server is ready + to send and receive messages + """ + + def onKeyEventScancode(self, code, isPressed): + """ + @summary: Event call when a keyboard event is catch in scan code format + @param code: scan code of key + @param isPressed: True if key is down + @see: rdp.RDPServerObserver.onKeyEventScancode + """ + + def onKeyEventUnicode(self, code, isPressed): + """ + @summary: Event call when a keyboard event is catch in unicode format + @param code: unicode of key + @param isPressed: True if key is down + @see: rdp.RDPServerObserver.onKeyEventUnicode + """ + + def onPointerEvent(self, x, y, button, isPressed): + """ + @summary: Event call on mouse event + @param x: x position + @param y: y position + @param button: 1, 2 or 3 button + @param isPressed: True if mouse button is pressed + @see: rdp.RDPServerObserver.onPointerEvent + """ + + def onClose(self): + """ + @summary: Call when human client close connection + @see: rdp.RDPServerObserver.onClose + """ + + return MyObserver(controller) + +from twisted.internet import reactor +reactor.listenTCP(3389, MyRDPFactory()) +reactor.run() +``` + +### Simple VNC Client ```python from rdpy.protocol.rfb import rdp -class MyRDPFactory(rfb.ClientFactory): +class MyRFBFactory(rfb.ClientFactory): def clientConnectionLost(self, connector, reason): reactor.stop() @@ -173,18 +242,41 @@ class MyRDPFactory(rfb.ClientFactory): class MyObserver(rfb.RFBClientObserver) def onReady(self): - pass + """ + @summary: Event when network stack is ready to receive or send event + """ def onUpdate(self, width, height, x, y, pixelFormat, encoding, data): - #here code handle bitmap - pass + """ + @summary: Implement RFBClientObserver interface + @param width: width of new image + @param height: height of new image + @param x: x position of new image + @param y: y position of new image + @param pixelFormat: pixefFormat structure in rfb.message.PixelFormat + @param encoding: encoding type rfb.message.Encoding + @param data: image data in accordance with pixel format and encoding + """ + + def onCutText(self, text): + """ + @summary: event when server send cut text event + @param text: text received + """ + + def onBell(self): + """ + @summary: event when server send biiip + """ def onClose(self): - pass + """ + @summary: Call when stack is close + """ return MyObserver(controller) from twisted.internet import reactor -reactor.connectTCP("XXX.XXX.XXX.XXX", 3389), MyRDPFactory()) +reactor.connectTCP("XXX.XXX.XXX.XXX", 3389), MyRFBFactory()) reactor.run() ``` diff --git a/bin/rdpy-rdpclient.py b/bin/rdpy-rdpclient.py index f40209c..f0a6127 100755 --- a/bin/rdpy-rdpclient.py +++ b/bin/rdpy-rdpclient.py @@ -17,14 +17,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # - """ example of use rdpy as rdp client """ import sys, os, getopt -from PyQt4 import QtGui +from PyQt4 import QtGui, QtCore from rdpy.ui.qt4 import RDPClientQt from rdpy.protocol.rdp import rdp @@ -35,7 +34,7 @@ class RDPClientQtFactory(rdp.ClientFactory): """ @summary: Factory create a RDP GUI client """ - def __init__(self, width, height, username, password, domain, fullscreen): + def __init__(self, width, height, username, password, domain, fullscreen, keyboardLayout, optimized): """ @param width: width of client @param heigth: heigth of client @@ -43,6 +42,8 @@ class RDPClientQtFactory(rdp.ClientFactory): @param password: password present to the server @param domain: microsoft domain @param fullscreen: show widget in fullscreen mode + @param keyboardLayout: keyboard layout + @param optimized: enable optimized session orders """ self._width = width self._height = height @@ -50,6 +51,8 @@ class RDPClientQtFactory(rdp.ClientFactory): self._passwod = password self._domain = domain self._fullscreen = fullscreen + self._keyboardLayout = keyboardLayout + self._optimized = optimized self._w = None def buildObserver(self, controller, addr): @@ -73,7 +76,9 @@ class RDPClientQtFactory(rdp.ClientFactory): controller.setUsername(self._username) controller.setPassword(self._passwod) controller.setDomain(self._domain) - controller.setPerformanceSession() + controller.setKeyboardLayout(self._keyboardLayout) + if self._optimized: + controller.setPerformanceSession() return client @@ -100,33 +105,54 @@ class RDPClientQtFactory(rdp.ClientFactory): reactor.stop() app.exit() +def autoDetectKeyboardLayout(): + """ + @summary: try to auto detect keyboard layout + """ + try: + if os.name == 'posix': + from subprocess import check_output + result = check_output(["setxkbmap", "-print"]) + if "azerty" in result: + return "fr" + elif os.name == 'nt': + import win32api, win32con, win32process + import ctypes.windll.user32 as user32 + w = user32.GetForegroundWindow() + tid = user32.GetWindowThreadProcessId(w, 0) + result = user32.GetKeyboardLayout(tid) + if result == 0x409409: + return "fr" + except: + log.info("failed to auto detect keyboard layout") + pass + return "en" + def help(): print "Usage: rdpy-rdpclient [options] ip[:port]" print "\t-u: user name" print "\t-p: password" print "\t-d: domain" - print "\t-w: width of screen default value is 1024" - print "\t-l: height of screen default value is 800" + print "\t-w: width of screen [default : 1024]" + print "\t-l: height of screen [default : 800]" + print "\t-f: enable full screen mode [default : False]" + print "\t-k: keyboard layout [en|fr] [default : en]" + print "\t-o: optimized session (disable costly effect) [default : False]" if __name__ == '__main__': - #create application - app = QtGui.QApplication(sys.argv) - - #add qt4 reactor - import qt4reactor - qt4reactor.install() - #default script argument username = "" password = "" domain = "" - width = QtGui.QDesktopWidget().screenGeometry().width() - height = QtGui.QDesktopWidget().screenGeometry().height() - fullscreen = True + width = 1024 + height = 800 + fullscreen = False + optimized = False + keyboardLayout = autoDetectKeyboardLayout() try: - opts, args = getopt.getopt(sys.argv[1:], "hu:p:d:w:l:") + opts, args = getopt.getopt(sys.argv[1:], "hfou:p:d:w:l:k:") except getopt.GetoptError: help() for opt, arg in opts: @@ -141,17 +167,37 @@ if __name__ == '__main__': domain = arg elif opt == "-w": width = int(arg) - fullscreen = False elif opt == "-l": height = int(arg) - fullscreen = False + elif opt == "-f": + fullscreen = True + elif opt == "-o": + optimized = True + elif opt == "-k": + keyboardLayout = arg if ':' in args[0]: ip, port = args[0].split(':') else: ip, port = args[0], "3389" + #create application + app = QtGui.QApplication(sys.argv) + + #add qt4 reactor + import qt4reactor + qt4reactor.install() + + if fullscreen: + width = QtGui.QDesktopWidget().screenGeometry().width() + height = QtGui.QDesktopWidget().screenGeometry().height() + + + + + log.info("keyboard layout set to %s"%keyboardLayout) + from twisted.internet import reactor - reactor.connectTCP(ip, int(port), RDPClientQtFactory(width, height, username, password, domain, fullscreen)) + reactor.connectTCP(ip, int(port), RDPClientQtFactory(width, height, username, password, domain, fullscreen, keyboardLayout, optimized)) reactor.runReturn() app.exec_() \ No newline at end of file diff --git a/rdpy/protocol/rdp/gcc.py b/rdpy/protocol/rdp/gcc.py index a934e71..c419cab 100644 --- a/rdpy/protocol/rdp/gcc.py +++ b/rdpy/protocol/rdp/gcc.py @@ -230,7 +230,7 @@ class ClientCoreData(CompositeType): self.desktopHeight = UInt16Le(800) self.colorDepth = UInt16Le(ColorDepth.RNS_UD_COLOR_8BPP) self.sasSequence = UInt16Le(Sequence.RNS_UD_SAS_DEL) - self.kbdLayout = UInt32Le(KeyboardLayout.FRENCH) + self.kbdLayout = UInt32Le(KeyboardLayout.US) self.clientBuild = UInt32Le(3790) self.clientName = String("rdpy" + "\x00"*11, readLen = UInt8(32), unicode = True) self.keyboardType = UInt32Le(KeyboardType.IBM_101_102_KEYS) diff --git a/rdpy/protocol/rdp/rdp.py b/rdpy/protocol/rdp/rdp.py index 2576011..1b72263 100644 --- a/rdpy/protocol/rdp/rdp.py +++ b/rdpy/protocol/rdp/rdp.py @@ -111,6 +111,16 @@ class RDPClientController(pdu.layer.PDUClientListener): """ self._pduLayer._info.flag |= pdu.data.InfoFlag.INFO_AUTOLOGON + def setKeyboardLayout(self, layout): + """ + @summary: keyboard layout + @param layout: us | fr + """ + if layout == "fr": + self._mcsLayer._clientSettings.getBlock(gcc.MessageType.CS_CORE).kbdLayout.value = gcc.KeyboardLayout.FRENCH + elif layout == "us": + self._mcsLayer._clientSettings.getBlock(gcc.MessageType.CS_CORE).kbdLayout.value = gcc.KeyboardLayout.US + def addClientObserver(self, observer): """ @summary: Add observer to RDP protocol diff --git a/rdpy/ui/qt4.py b/rdpy/ui/qt4.py index 9f357fb..735fc34 100644 --- a/rdpy/ui/qt4.py +++ b/rdpy/ui/qt4.py @@ -126,13 +126,11 @@ class RFBClientQt(RFBClientObserver, QAdaptor): @summary: event when server send cut text event @param text: text received """ - pass def onBell(self): """ @summary: event when server send biiip """ - pass def onReady(self): """