add keyboard layout detection or force

This commit is contained in:
citronneur
2014-11-30 11:22:59 +01:00
parent 3c3d7423a5
commit 99198321a4
5 changed files with 185 additions and 39 deletions

124
README.md
View File

@@ -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. In a nutshell the RDPY can be used as a protocol library with a twisted engine.
### Client library ### Simple RDP Client
The RDP client code looks like this:
```python ```python
from rdpy.protocol.rdp import rdp from rdpy.protocol.rdp import rdp
@@ -135,20 +133,36 @@ class MyRDPFactory(rdp.ClientFactory):
reactor.stop() reactor.stop()
def buildObserver(self, controller, addr): def buildObserver(self, controller, addr):
class MyObserver(rdp.RDPClientObserver) class MyObserver(rdp.RDPClientObserver)
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): def onReady(self):
#here code handle bitmap """
pass @summary: Call when stack is ready
"""
def onReady(self):
#send 'r' key #send 'r' key
self._controller.sendKeyEventUnicode(ord(unicode("r".toUtf8(), encoding="UTF-8")), True) self._controller.sendKeyEventUnicode(ord(unicode("r".toUtf8(), encoding="UTF-8")), True)
#mouse move and click at pixel 200x200 #mouse move and click at pixel 200x200
self._controller.sendPointerEvent(200, 200, 1, true) 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): def onClose(self):
pass """
@summary: Call when stack is close
"""
return MyObserver(controller) return MyObserver(controller)
@@ -157,11 +171,66 @@ reactor.connectTCP("XXX.XXX.XXX.XXX", 3389), MyRDPFactory())
reactor.run() 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 ```python
from rdpy.protocol.rfb import rdp from rdpy.protocol.rfb import rdp
class MyRDPFactory(rfb.ClientFactory): class MyRFBFactory(rfb.ClientFactory):
def clientConnectionLost(self, connector, reason): def clientConnectionLost(self, connector, reason):
reactor.stop() reactor.stop()
@@ -173,18 +242,41 @@ class MyRDPFactory(rfb.ClientFactory):
class MyObserver(rfb.RFBClientObserver) class MyObserver(rfb.RFBClientObserver)
def onReady(self): 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): 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): def onClose(self):
pass """
@summary: Call when stack is close
"""
return MyObserver(controller) return MyObserver(controller)
from twisted.internet import reactor from twisted.internet import reactor
reactor.connectTCP("XXX.XXX.XXX.XXX", 3389), MyRDPFactory()) reactor.connectTCP("XXX.XXX.XXX.XXX", 3389), MyRFBFactory())
reactor.run() reactor.run()
``` ```

View File

@@ -17,14 +17,13 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
""" """
example of use rdpy as rdp client example of use rdpy as rdp client
""" """
import sys, os, getopt import sys, os, getopt
from PyQt4 import QtGui from PyQt4 import QtGui, QtCore
from rdpy.ui.qt4 import RDPClientQt from rdpy.ui.qt4 import RDPClientQt
from rdpy.protocol.rdp import rdp from rdpy.protocol.rdp import rdp
@@ -35,7 +34,7 @@ class RDPClientQtFactory(rdp.ClientFactory):
""" """
@summary: Factory create a RDP GUI client @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 width: width of client
@param heigth: heigth of client @param heigth: heigth of client
@@ -43,6 +42,8 @@ class RDPClientQtFactory(rdp.ClientFactory):
@param password: password present to the server @param password: password present to the server
@param domain: microsoft domain @param domain: microsoft domain
@param fullscreen: show widget in fullscreen mode @param fullscreen: show widget in fullscreen mode
@param keyboardLayout: keyboard layout
@param optimized: enable optimized session orders
""" """
self._width = width self._width = width
self._height = height self._height = height
@@ -50,6 +51,8 @@ class RDPClientQtFactory(rdp.ClientFactory):
self._passwod = password self._passwod = password
self._domain = domain self._domain = domain
self._fullscreen = fullscreen self._fullscreen = fullscreen
self._keyboardLayout = keyboardLayout
self._optimized = optimized
self._w = None self._w = None
def buildObserver(self, controller, addr): def buildObserver(self, controller, addr):
@@ -73,7 +76,9 @@ class RDPClientQtFactory(rdp.ClientFactory):
controller.setUsername(self._username) controller.setUsername(self._username)
controller.setPassword(self._passwod) controller.setPassword(self._passwod)
controller.setDomain(self._domain) controller.setDomain(self._domain)
controller.setPerformanceSession() controller.setKeyboardLayout(self._keyboardLayout)
if self._optimized:
controller.setPerformanceSession()
return client return client
@@ -100,33 +105,54 @@ class RDPClientQtFactory(rdp.ClientFactory):
reactor.stop() reactor.stop()
app.exit() 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(): def help():
print "Usage: rdpy-rdpclient [options] ip[:port]" print "Usage: rdpy-rdpclient [options] ip[:port]"
print "\t-u: user name" print "\t-u: user name"
print "\t-p: password" print "\t-p: password"
print "\t-d: domain" print "\t-d: domain"
print "\t-w: width of screen default value is 1024" print "\t-w: width of screen [default : 1024]"
print "\t-l: height of screen default value is 800" 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__': if __name__ == '__main__':
#create application
app = QtGui.QApplication(sys.argv)
#add qt4 reactor
import qt4reactor
qt4reactor.install()
#default script argument #default script argument
username = "" username = ""
password = "" password = ""
domain = "" domain = ""
width = QtGui.QDesktopWidget().screenGeometry().width() width = 1024
height = QtGui.QDesktopWidget().screenGeometry().height() height = 800
fullscreen = True fullscreen = False
optimized = False
keyboardLayout = autoDetectKeyboardLayout()
try: 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: except getopt.GetoptError:
help() help()
for opt, arg in opts: for opt, arg in opts:
@@ -141,17 +167,37 @@ if __name__ == '__main__':
domain = arg domain = arg
elif opt == "-w": elif opt == "-w":
width = int(arg) width = int(arg)
fullscreen = False
elif opt == "-l": elif opt == "-l":
height = int(arg) height = int(arg)
fullscreen = False elif opt == "-f":
fullscreen = True
elif opt == "-o":
optimized = True
elif opt == "-k":
keyboardLayout = arg
if ':' in args[0]: if ':' in args[0]:
ip, port = args[0].split(':') ip, port = args[0].split(':')
else: else:
ip, port = args[0], "3389" 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 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() reactor.runReturn()
app.exec_() app.exec_()

View File

@@ -230,7 +230,7 @@ class ClientCoreData(CompositeType):
self.desktopHeight = UInt16Le(800) self.desktopHeight = UInt16Le(800)
self.colorDepth = UInt16Le(ColorDepth.RNS_UD_COLOR_8BPP) self.colorDepth = UInt16Le(ColorDepth.RNS_UD_COLOR_8BPP)
self.sasSequence = UInt16Le(Sequence.RNS_UD_SAS_DEL) self.sasSequence = UInt16Le(Sequence.RNS_UD_SAS_DEL)
self.kbdLayout = UInt32Le(KeyboardLayout.FRENCH) self.kbdLayout = UInt32Le(KeyboardLayout.US)
self.clientBuild = UInt32Le(3790) self.clientBuild = UInt32Le(3790)
self.clientName = String("rdpy" + "\x00"*11, readLen = UInt8(32), unicode = True) self.clientName = String("rdpy" + "\x00"*11, readLen = UInt8(32), unicode = True)
self.keyboardType = UInt32Le(KeyboardType.IBM_101_102_KEYS) self.keyboardType = UInt32Le(KeyboardType.IBM_101_102_KEYS)

View File

@@ -111,6 +111,16 @@ class RDPClientController(pdu.layer.PDUClientListener):
""" """
self._pduLayer._info.flag |= pdu.data.InfoFlag.INFO_AUTOLOGON 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): def addClientObserver(self, observer):
""" """
@summary: Add observer to RDP protocol @summary: Add observer to RDP protocol

View File

@@ -126,13 +126,11 @@ class RFBClientQt(RFBClientObserver, QAdaptor):
@summary: event when server send cut text event @summary: event when server send cut text event
@param text: text received @param text: text received
""" """
pass
def onBell(self): def onBell(self):
""" """
@summary: event when server send biiip @summary: event when server send biiip
""" """
pass
def onReady(self): def onReady(self):
""" """