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.
### 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()
```

View File

@@ -17,14 +17,13 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""
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_()

View File

@@ -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)

View File

@@ -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

View File

@@ -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):
"""