From 39b1638b6a8bd7c0f7df5841a34345c11c5956ba Mon Sep 17 00:00:00 2001 From: speyrefitte Date: Fri, 20 Jun 2014 17:11:39 +0200 Subject: [PATCH] code refactoring --- .gitignore | 1 + README.md | 2 +- README.md~ | 13 ---- rdpy/display/qt.py | 98 +++++++++++------------ rdpy/display/rle.py | 19 +++++ rdpy/examples/client.py | 109 -------------------------- rdpy/examples/rdpclient.py | 65 ++++++++++++++++ rdpy/examples/vncclient.py | 65 ++++++++++++++++ rdpy/network/error.py | 24 +++++- rdpy/protocol/rdp/pdu.py | 17 +++- rdpy/protocol/rdp/rdp.py | 40 ++++++---- rdpy/protocol/rfb/rfb.py | 154 ++++++++++++++++++++++++++----------- 12 files changed, 372 insertions(+), 235 deletions(-) delete mode 100644 README.md~ delete mode 100644 rdpy/examples/client.py create mode 100644 rdpy/examples/rdpclient.py create mode 100644 rdpy/examples/vncclient.py diff --git a/.gitignore b/.gitignore index 3fc678c..54c012d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.pyc .project .pydevproject +README.md~ diff --git a/README.md b/README.md index e5af741..0c5aa5f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # RDPY -## Remote Desktop Protoc in Python +Remote Desktop Protocol in Twisted Python ## Requirements * python2.7 diff --git a/README.md~ b/README.md~ deleted file mode 100644 index 72aa78a..0000000 --- a/README.md~ +++ /dev/null @@ -1,13 +0,0 @@ -# RDPY - -## Remote Desktop Protoc in Python - -## Requirements -* python2.7 -* python-twisted - -## Requirements for examples -* python-qt4 -* python-qt4reactor - -this project is still in progress. diff --git a/rdpy/display/qt.py b/rdpy/display/qt.py index 7ada0b1..216688d 100644 --- a/rdpy/display/qt.py +++ b/rdpy/display/qt.py @@ -2,22 +2,15 @@ @author: sylvain ''' from PyQt4 import QtGui, QtCore -from rdpy.protocol.rfb.rfb import RfbObserver -from rdpy.protocol.rdp.rdp import RDPObserver +from rdpy.protocol.rfb.rfb import RFBClientObserver +from rdpy.protocol.rdp.rdp import RDPClientObserver +from rdpy.network.error import UnRegistredObject class QAdaptor(object): ''' - adaptor model with link beetween protocol + adaptor model with link between protocol and qt widget ''' - def __init__(self, qRemoteDesktop): - ''' - constructor - must set qRemoteDesktop attribute - ''' - #qwidget use for render - self._qRemoteDesktop = qRemoteDesktop - self._qRemoteDesktop._adaptor = self def sendMouseEvent(self, e): ''' @@ -34,38 +27,40 @@ class QAdaptor(object): @param e: qEvent ''' pass + + def getWidget(self): + ''' + @return: widget use for render + ''' + pass -class RfbAdaptor(RfbObserver, QAdaptor): +class RFBClientQt(RFBClientObserver, QAdaptor): ''' QAdaptor for specific RFB protocol stack is to an RFB observer - ''' - def __init__(self, qRemoteDesktop): + ''' + def __init__(self): ''' ctor - @param qRemoteDesktop: widget use for render ''' - QAdaptor.__init__(self, qRemoteDesktop) - self._rfb = None + self._widget = QRemoteDesktop(self) - def setProtocol(self, rfb): + def getWidget(self): ''' - inherit from RfbObserver - init protocol settings + @return: widget use for render ''' - #set RFB observer to - self._rfb = rfb + return self._widget - def notifyFramebufferUpdate(self, width, height, x, y, pixelFormat, encoding, data): + def onUpdate(self, width, height, x, y, pixelFormat, encoding, data): ''' - implement RfbAdaptor interface + 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 pixelformat and encoding + @param data: image data in accordance with pixel format and encoding ''' imageFormat = None if pixelFormat.BitsPerPixel.value == 32 and pixelFormat.RedShift.value == 16: @@ -75,44 +70,47 @@ class RfbAdaptor(RfbObserver, QAdaptor): return image = QtGui.QImage(data, width, height, imageFormat) - self._qRemoteDesktop.notifyImage(x, y, image) + self._widget.notifyImage(x, y, image) def sendMouseEvent(self, e): ''' - convert qt mouse event to rfb mouse event - send mouse event to rfb protocol stack + convert qt mouse event to RFB mouse event @param e: qMouseEvent ''' button = e.button() - mask = 0 + buttonNumber = 0 if button == QtCore.Qt.LeftButton: - mask = 1 + buttonNumber = 1 elif button == QtCore.Qt.MidButton: - mask = 1 << 1 + buttonNumber = 2 elif button == QtCore.Qt.RightButton: - mask = 1 << 2 - self._rfb.sendPointerEvent(mask, e.pos().x(), e.pos().y()) + buttonNumber = 3 + self.mouseEvent(buttonNumber, e.pos().x(), e.pos().y()) def sendKeyEvent(self, e): ''' - convert qt key press event to rfb press event - send key event to protocol stack + convert Qt key press event to RFB press event @param e: qKeyEvent ''' - self._rfb.sendKeyEvent(True, e.nativeVirtualKey()) + self.keyEvent(True, e.nativeVirtualKey()) -class RDPAdaptor(RDPObserver, QAdaptor): +class RDPClientQt(RDPClientObserver, QAdaptor): ''' Adaptor for RDP client ''' - def __init__(self, qRemoteDesktop): + def __init__(self): ''' constructor - @param qRemoteDesktop: widget use for render ''' - QAdaptor.__init__(self, qRemoteDesktop) + self._widget = QRemoteDesktop(self) + + def getWidget(self): + ''' + @return: widget use for render + ''' + return self._widget - def notifyBitmapUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): + def onBitmapUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): ''' notify bitmap update @param destLeft: xmin position @@ -141,19 +139,20 @@ class RDPAdaptor(RDPObserver, QAdaptor): return image = QtGui.QImage(data, width, height, imageFormat) - self._qRemoteDesktop.notifyImage(destLeft, destTop, image) + self._widget.notifyImage(destLeft, destTop, image) + class QRemoteDesktop(QtGui.QWidget): ''' qt display widget ''' - def __init__(self): + def __init__(self, adaptor): ''' constructor ''' super(QRemoteDesktop, self).__init__() - #set by adaptor - self._adaptor = None + #adaptor use to send + self._adaptor = adaptor #refresh stack of image #because we can update image only in paint #event function. When protocol receive image @@ -208,7 +207,8 @@ class QRemoteDesktop(QtGui.QWidget): @param event: qMouseEvent ''' if self._adaptor is None: - print "No adaptor to send mouse press event" + raise UnRegistredObject("No adaptor to send mouse press event") + self._adaptor.sendMouseEvent(event) def keyPressEvent(self, event): @@ -217,5 +217,7 @@ class QRemoteDesktop(QtGui.QWidget): @param event: qKeyEvent ''' if self._adaptor is None: - print "No adaptor to send key press event" - self._adaptor.sendKeyEvent(event) \ No newline at end of file + raise UnRegistredObject("No adaptor to send key press event") + + self._adaptor.sendKeyEvent(event) + \ No newline at end of file diff --git a/rdpy/display/rle.py b/rdpy/display/rle.py index 25bc603..992be6d 100644 --- a/rdpy/display/rle.py +++ b/rdpy/display/rle.py @@ -3,4 +3,23 @@ @file: implement run length encoding algorithm use in RDP protocol to compress bit @see: http://msdn.microsoft.com/en-us/library/dd240593.aspx ''' +from rdpy.network.type import UInt8 + +def extractCodeId(data): + ''' + Read first unsigned char + ''' + res = UInt8() + data.readType(res) + return res + +def decode(dst, src, rowDelta): + + insertFgPel = False + firstLine = True + + while src.dataLen() > 0: + if firstLine: + firstLine = False + insertFgPel = False diff --git a/rdpy/examples/client.py b/rdpy/examples/client.py deleted file mode 100644 index 03febeb..0000000 --- a/rdpy/examples/client.py +++ /dev/null @@ -1,109 +0,0 @@ -''' -@author: citronneur -''' -import sys -import os -# Change path so we find rdpy -sys.path.insert(1, os.path.join(sys.path[0], '../..')) - -from PyQt4 import QtGui -from rdpy.display.qt import RDPAdaptor, RfbAdaptor, QRemoteDesktop -from rdpy.protocol.rdp import rdp -from rdpy.protocol.rfb import rfb - -class RDPClientQtFactory(rdp.ClientFactory): - ''' - Factory create a RDP GUI client - ''' - def __init__(self): - ''' - ctor that init qt context and protocol needed - ''' - #create qt widget - self._w = QRemoteDesktop() - self._w.resize(1024, 800) - self._w.setWindowTitle('rdpyclient-rdp') - self._w.show() - #build protocol - rdp.ClientFactory.__init__(self, RDPAdaptor(self._w)) - - def startedConnecting(self, connector): - pass - - def clientConnectionLost(self, connector, reason): - ''' - connection lost event - @param connector: twisted connector use for rdp connection (use reconnect to restart connection) - @param reason: str use to advertise reason of lost connection - ''' - QtGui.QMessageBox.warning(self._w, "Warning", "Lost connection : %s"%reason) - reactor.stop() - app.exit() - - def clientConnectionFailed(self, connector, reason): - ''' - connection failed event - @param connector: twisted connector use for rdp connection (use reconnect to restart connection) - @param reason: str use to advertise reason of lost connection - ''' - QtGui.QMessageBox.warning(self._w, "Warning", "Connection failed : %s"%reason) - reactor.stop() - app.exit() - -class RFBClientQtFactory(rfb.ClientFactory): - ''' - Factory create a VNC GUI client - ''' - def __init__(self): - ''' - ctor that init qt context and protocol needed - ''' - #create qt widget - self._w = QRemoteDesktop() - self._w.resize(1024, 800) - self._w.setWindowTitle('rdpyclient-vnc') - self._w.show() - - rfb.ClientFactory.__init__(self, RfbAdaptor(self._w)) - - def startedConnecting(self, connector): - pass - - def clientConnectionLost(self, connector, reason): - ''' - connection lost event - @param connector: twisted connector use for vnc connection (use reconnect to restart connection) - @param reason: str use to advertise reason of lost connection - ''' - QtGui.QMessageBox.warning(self._w, "Warning", "Lost connection : %s"%reason) - reactor.stop() - app.exit() - - def clientConnectionFailed(self, connector, reason): - ''' - connection failed event - @param connector: twisted connector use for vnc connection (use reconnect to restart connection) - @param reason: str use to advertise reason of lost connection - ''' - QtGui.QMessageBox.warning(self._w, "Warning", "Connection failed : %s"%reason) - reactor.stop() - app.exit() - -if __name__ == '__main__': - #create application - app = QtGui.QApplication(sys.argv) - - #add qt4 reactor - import qt4reactor - qt4reactor.install() - - if sys.argv[3] == 'rdp': - factory = RDPClientQtFactory() - else: - factory = RFBClientQtFactory() - - from twisted.internet import reactor - reactor.connectTCP(sys.argv[1], int(sys.argv[2]), factory) - reactor.runReturn() - app.exec_() - reactor.stop() \ No newline at end of file diff --git a/rdpy/examples/rdpclient.py b/rdpy/examples/rdpclient.py new file mode 100644 index 0000000..38d85de --- /dev/null +++ b/rdpy/examples/rdpclient.py @@ -0,0 +1,65 @@ +''' +@author: citronneur +''' +import sys +import os +# Change path so we find rdpy +sys.path.insert(1, os.path.join(sys.path[0], '../..')) + +from PyQt4 import QtGui +from rdpy.display.qt import RDPClientQt +from rdpy.protocol.rdp import rdp + +class RDPClientQtFactory(rdp.ClientFactory): + ''' + Factory create a RDP GUI client + ''' + def buildObserver(self): + ''' + build RFB observer + ''' + #create client observer + client = RDPClientQt() + #create qt widget + self._w = client.getWidget() + self._w.resize(1024, 800) + self._w.setWindowTitle('rdpyclient-vnc') + self._w.show() + return client + + def startedConnecting(self, connector): + pass + + def clientConnectionLost(self, connector, reason): + ''' + connection lost event + @param connector: twisted connector use for rdp connection (use reconnect to restart connection) + @param reason: str use to advertise reason of lost connection + ''' + QtGui.QMessageBox.warning(self._w, "Warning", "Lost connection : %s"%reason) + reactor.stop() + app.exit() + + def clientConnectionFailed(self, connector, reason): + ''' + connection failed event + @param connector: twisted connector use for rdp connection (use reconnect to restart connection) + @param reason: str use to advertise reason of lost connection + ''' + QtGui.QMessageBox.warning(self._w, "Warning", "Connection failed : %s"%reason) + reactor.stop() + app.exit() + +if __name__ == '__main__': + #create application + app = QtGui.QApplication(sys.argv) + + #add qt4 reactor + import qt4reactor + qt4reactor.install() + + from twisted.internet import reactor + reactor.connectTCP(sys.argv[1], int(sys.argv[2]), RDPClientQtFactory()) + reactor.runReturn() + app.exec_() + reactor.stop() \ No newline at end of file diff --git a/rdpy/examples/vncclient.py b/rdpy/examples/vncclient.py new file mode 100644 index 0000000..3d53023 --- /dev/null +++ b/rdpy/examples/vncclient.py @@ -0,0 +1,65 @@ +''' +@author: citronneur +''' +import sys +import os +# Change path so we find rdpy +sys.path.insert(1, os.path.join(sys.path[0], '../..')) + +from PyQt4 import QtGui +from rdpy.display.qt import RFBClientQt +from rdpy.protocol.rfb import rfb + +class RFBClientQtFactory(rfb.ClientFactory): + ''' + Factory create a VNC GUI client + ''' + def buildObserver(self): + ''' + build RFB observer + ''' + #create client observer + client = RFBClientQt() + #create qt widget + self._w = client.getWidget() + self._w.resize(1024, 800) + self._w.setWindowTitle('rdpyclient-vnc') + self._w.show() + return client + + def startedConnecting(self, connector): + pass + + def clientConnectionLost(self, connector, reason): + ''' + connection lost event + @param connector: twisted connector use for vnc connection (use reconnect to restart connection) + @param reason: str use to advertise reason of lost connection + ''' + QtGui.QMessageBox.warning(self._w, "Warning", "Lost connection : %s"%reason) + reactor.stop() + app.exit() + + def clientConnectionFailed(self, connector, reason): + ''' + connection failed event + @param connector: twisted connector use for vnc connection (use reconnect to restart connection) + @param reason: str use to advertise reason of lost connection + ''' + QtGui.QMessageBox.warning(self._w, "Warning", "Connection failed : %s"%reason) + reactor.stop() + app.exit() + +if __name__ == '__main__': + #create application + app = QtGui.QApplication(sys.argv) + + #add qt4 reactor + import qt4reactor + qt4reactor.install() + + from twisted.internet import reactor + reactor.connectTCP(sys.argv[1], int(sys.argv[2]), RFBClientQtFactory()) + reactor.runReturn() + app.exec_() + reactor.stop() \ No newline at end of file diff --git a/rdpy/network/error.py b/rdpy/network/error.py index 540aa6f..7f5240f 100644 --- a/rdpy/network/error.py +++ b/rdpy/network/error.py @@ -66,4 +66,26 @@ class ErrorReportedFromPeer(Exception): constructor with message @param message: message show when exception is raised ''' - Exception.__init__(self, message) + Exception.__init__(self, message) + +class DisconnectLayer(Exception): + ''' + raise when try to send on unconnect layer + ''' + def __init__(self, message = ""): + ''' + constructor with message + @param message: message show when exception is raised + ''' + Exception.__init__(self, message) + +class UnRegistredObject(Exception): + ''' + raise when an object is not registred in other objet + ''' + def __init__(self, message = ""): + ''' + constructor with message + @param message: message show when exception is raised + ''' + Exception.__init__(self, message) diff --git a/rdpy/protocol/rdp/pdu.py b/rdpy/protocol/rdp/pdu.py index 08ce76e..7b0010f 100644 --- a/rdpy/protocol/rdp/pdu.py +++ b/rdpy/protocol/rdp/pdu.py @@ -11,6 +11,7 @@ from rdpy.network.error import InvalidExpectedDataException, ErrorReportedFromPe import gcc import lic import caps +import rdp @ConstAttributes @TypeAttributes(UInt16Le) @@ -701,9 +702,10 @@ class PDU(LayerAutomata): Global channel for mcs that handle session identification user, licensing management, and capabilities exchange ''' - def __init__(self, mode, ordersManager): + def __init__(self, mode): ''' Constructor + @param mode: LayerMode ''' LayerAutomata.__init__(self, mode, None) #logon info send from client to server @@ -742,8 +744,15 @@ class PDU(LayerAutomata): #share id between client and server self._shareId = UInt32Le() - #rdp observer - self._ordersManager = ordersManager + #rdp controller + self._controller = rdp.RDPController(self) + + def getController(self): + ''' + Getter for RDP controller + @return: return rdp controller + ''' + return self._controller def connect(self): ''' @@ -871,7 +880,7 @@ class PDU(LayerAutomata): ''' dataPDU = self.readDataPDU(data) if dataPDU.shareDataHeader.pduType2 == PDUType2.PDUTYPE2_UPDATE and dataPDU.pduData._value.updateType == UpdateType.UPDATETYPE_BITMAP: - self._ordersManager.recvBitmapUpdateDataPDU(dataPDU.pduData._value.updateData._value) + self._controller.recvBitmapUpdateDataPDU(dataPDU.pduData._value.updateData._value) def sendConfirmActivePDU(self): diff --git a/rdpy/protocol/rdp/rdp.py b/rdpy/protocol/rdp/rdp.py index 3ec5486..9e12bba 100644 --- a/rdpy/protocol/rdp/rdp.py +++ b/rdpy/protocol/rdp/rdp.py @@ -5,16 +5,18 @@ from twisted.internet import protocol import tpkt, tpdu, mcs, pdu from rdpy.network.layer import LayerMode -class RDPOrdersManager(object): +class RDPController(object): ''' use to decode and dispatch to observer PDU messages and orders ''' - def __init__(self): + def __init__(self, pduLayer): ''' ctor ''' #list of observer self._observers = [] + #transport layer + self._pduLayer = pduLayer def addObserver(self, observer): ''' @@ -22,6 +24,7 @@ class RDPOrdersManager(object): @param observer: new observer to add ''' self._observers.append(observer) + observer._controller = self def recvBitmapUpdateDataPDU(self, bitmapUpdateData): ''' @@ -31,32 +34,39 @@ class RDPOrdersManager(object): for observer in self._observers: #for each rectangle in update PDU for rectangle in bitmapUpdateData.rectangles._array: - observer.notifyBitmapUpdate(rectangle.destLeft.value, rectangle.destTop.value, rectangle.destRight.value, rectangle.destBottom.value, rectangle.width.value, rectangle.height.value, rectangle.bitsPerPixel.value, (rectangle.flags & pdu.BitmapFlag.BITMAP_COMPRESSION).value, rectangle.bitmapDataStream.value) + observer.onBitmapUpdate(rectangle.destLeft.value, rectangle.destTop.value, rectangle.destRight.value, rectangle.destBottom.value, rectangle.width.value, rectangle.height.value, rectangle.bitsPerPixel.value, (rectangle.flags & pdu.BitmapFlag.BITMAP_COMPRESSION).value, rectangle.bitmapDataStream.value) class ClientFactory(protocol.Factory): ''' Factory of Client RDP protocol ''' - def __init__(self, observer): - ''' - ctor - @param observer: observer use by rdp protocol to handle events must implement RDPObserver - ''' - self._observer = observer - def buildProtocol(self, addr): ''' Function call from twisted and build rdp protocol stack + @param addr: destination address ''' - rdpOrdersManager = RDPOrdersManager() - rdpOrdersManager.addObserver(self._observer) - return tpkt.TPKT(tpdu.TPDU(mcs.MCS(pdu.PDU(LayerMode.CLIENT, rdpOrdersManager)))); + pduLayer = pdu.PDU(LayerMode.CLIENT) + pduLayer.getController().addObserver(self.buildObserver()) + return tpkt.TPKT(tpdu.TPDU(mcs.MCS(pduLayer))); + + def buildObserver(self): + ''' + build observer use for connection + ''' + pass -class RDPObserver(object): + +class RDPClientObserver(object): ''' class use to inform all rdp event handle by RDPY ''' - def notifyBitmapUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): + def __init__(self): + ''' + ctor + ''' + self._controller = None + + def onBitmapUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): ''' notify bitmap update @param destLeft: xmin position diff --git a/rdpy/protocol/rfb/rfb.py b/rdpy/protocol/rfb/rfb.py index 2149ddb..2896be4 100644 --- a/rdpy/protocol/rfb/rfb.py +++ b/rdpy/protocol/rfb/rfb.py @@ -5,6 +5,7 @@ from twisted.internet import protocol from rdpy.network.layer import RawLayer, LayerMode from rdpy.network.type import UInt8, UInt16Be, UInt32Be, SInt32Be, String, CompositeType from rdpy.network.const import ConstAttributes, TypeAttributes +from rdpy.network.error import UnRegistredObject, InvalidValue @ConstAttributes @TypeAttributes(String) @@ -79,7 +80,6 @@ class PixelFormat(CompositeType): self.GreenShift = UInt8(8) self.BlueShift = UInt8(0) self.padding = (UInt16Be(), UInt8()) - class ServerInit(CompositeType): ''' @@ -121,21 +121,21 @@ class KeyEvent(CompositeType): ''' key event structure message ''' - def __init__(self, downFlag = False, key = 0): + def __init__(self): CompositeType.__init__(self) - self.downFlag = UInt8(downFlag) + self.downFlag = UInt8(False) self.padding = UInt16Be() - self.key = UInt32Be(key) + self.key = UInt32Be() class PointerEvent(CompositeType): ''' pointer event structure message ''' - def __init__(self, mask = 0, x = 0, y = 0): + def __init__(self): CompositeType.__init__(self) - self.mask = UInt8(mask) - self.x = UInt16Be(x) - self.y = UInt16Be(y) + self.mask = UInt8() + self.x = UInt16Be() + self.y = UInt16Be() class ClientCutText(CompositeType): ''' @@ -152,9 +152,10 @@ class Rfb(RawLayer): implements rfb protocol ''' - def __init__(self, mode, ordersManager): + def __init__(self, mode): ''' constructor + @param mode: LayerMode client or server ''' RawLayer.__init__(self, mode) #usefull for rfb protocol @@ -177,14 +178,14 @@ class Rfb(RawLayer): #current rectangle header self._currentRect = Rectangle() #client or server adaptor - self._ordersManager = ordersManager + self._controller = RFBController(self) - def addObserver(self, observer): + def getController(self): ''' - add observer for input/ouput events - @param observer: RfbObserver interface implementation + Getter for controller + @return: RFBController use by rfb layer ''' - self._observer.append(observer) + return self._controller def expectWithHeader(self, expectedHeaderLen, callbackBody): ''' @@ -355,7 +356,7 @@ class Rfb(RawLayer): ''' read body of rect ''' - self._ordersManager.recvRectangle(self._currentRect, self._pixelFormat, data.getvalue()) + self._controller.recvRectangle(self._currentRect, self._pixelFormat, data.getvalue()) self._nbRect = self._nbRect - 1 #if there is another rect to read @@ -392,17 +393,19 @@ class Rfb(RawLayer): ''' self.send((ClientToServerMessages.FRAME_BUFFER_UPDATE_REQUEST, FrameBufferUpdateRequest(incremental, x, y, width, height))) - def sendKeyEvent(self, downFlag, key): + def sendKeyEvent(self, keyEvent): ''' write key event packet + @param keyEvent: KeyEvent struct to send ''' - self.send((ClientToServerMessages.KEY_EVENT, KeyEvent(downFlag, key))) + self.send((ClientToServerMessages.KEY_EVENT, keyEvent)) - def sendPointerEvent(self, mask, x, y): + def sendPointerEvent(self, pointerEvent): ''' write pointer event packet + @param pointerEvent: PointerEvent struct use ''' - self.send((ClientToServerMessages.POINTER_EVENT, PointerEvent(mask, x, y))) + self.send((ClientToServerMessages.POINTER_EVENT, pointerEvent)) def sendClientCutText(self, text): ''' @@ -410,15 +413,18 @@ class Rfb(RawLayer): ''' self.send((ClientToServerMessages.CUT_TEXT, ClientCutText(text))) -class RFBOrderManager(object): +class RFBController(object): ''' class use to manage rfb order and dispatch throw observers ''' - def __init__(self): + def __init__(self, rfbLayer): ''' ctor + @param rfbLayer: network layer ''' self._observers = [] + #rfb layer to send client orders + self._rfbLayer = rfbLayer def addObserver(self, observer): ''' @@ -426,44 +432,111 @@ class RFBOrderManager(object): @param observer: new observer ''' self._observers.append(observer) + observer._controller = self def recvRectangle(self, rectangle, pixelFormat, data): ''' receive rectangle order + Main update order type @param rectangle: Rectangle type header of packet @param pixelFormat: pixelFormat struct of current session @param data: image data ''' for observer in self._observers: - observer.notifyFramebufferUpdate(rectangle.width.value, rectangle.height.value, rectangle.x.value, rectangle.y.value, self._pixelFormat, rectangle.encoding, data) + observer.onUpdate(rectangle.width.value, rectangle.height.value, rectangle.x.value, rectangle.y.value, pixelFormat, rectangle.encoding, data) + + def sendKeyEvent(self, isDown, key): + ''' + send a key event throw RFB protocol + @param isDown: boolean notify if key is pressed or not (True if key is pressed) + @param key: ascii code of key + ''' + try: + event = KeyEvent() + event.downFlag.value = isDown + event.key.value = key + + self._rfbLayer.sendKeyEvent(event) + except InvalidValue: + print "Try to send an invalid key event" + + def sendPointerEvent(self, mask, x, y): + ''' + Send an pointer event throw RFB protocol + @param mask: mask of button if button 1 and 3 are pressed then mask is 00000101 + @param x: x coordinate of mouse pointer + @param y: y pointer of mouse pointer + ''' + try: + event = PointerEvent() + event.mask.value = mask + event.x.value = x + event.y.value = y + + self._rfbLayer.sendPointerEvent(event) + except InvalidValue: + print "Try to send an invalid pointer event" + class ClientFactory(protocol.Factory): ''' Factory of RFB protocol ''' - def __init__(self, observer): - ''' - save mode and adapter - @param adapter: graphic client adapter - ''' - self._observer = observer - def buildProtocol(self, addr): ''' function call by twisted on connection - @param addr: adress where client try to connect + @param addr: address where client try to connect ''' - orderManager = RFBOrderManager() - orderManager.addObserver(self._observer) - protocol = Rfb(LayerMode.CLIENT, orderManager) - self._observer.setProtocol(protocol) + protocol = Rfb(LayerMode.CLIENT) + protocol.getController().addObserver(self.buildObserver()) return protocol + + def buildObserver(self): + ''' + build an RFB observer object + ''' + pass + -class RfbObserver(object): +class RFBClientObserver(object): ''' - Rfb protocol obserser + RFB client protocol observer ''' - def notifyFramebufferUpdate(self, width, height, x, y, pixelFormat, encoding, data): + def __init__(self): + ''' + ctor + ''' + self._controller = None + + def keyEvent(self, isPressed, key): + ''' + send a key event + @param isPressed: state of key + @param key: ascii code of key + ''' + if self._controller is None: + raise UnRegistredObject("RFBClientObserver need to be registred to a RFBController object") + + self._controller.sendKeyEvent(isPressed, key) + + def mouseEvent(self, button, x, y): + ''' + send a mouse event to RFB Layer + @param button: button number which is pressed (0,1,2,3,4,5,6,7,8) + @param x: x coordinate of mouse pointer + @param y: y coordinate of mouse pointer + ''' + if self._controller is None: + raise UnRegistredObject("RFBClientObserver need to be registred to a RFBController object") + mask = 0 + if button == 1: + mask = 1 + elif button > 1: + mask = 1 << button - 1 + + self._controller.sendPointerEvent(mask, x, y) + + def onUpdate(self, width, height, x, y, pixelFormat, encoding, data): ''' recv framebuffer update @param width : width of image @@ -475,10 +548,3 @@ class RfbObserver(object): @param data : in respect of dataFormat and pixelFormat ''' pass - - def setProtocol(self, rfbLayer): - ''' - call when observer is added to an rfb layer - @param: rfbLayer layer inform the observer - ''' - pass \ No newline at end of file