diff --git a/bin/rdpy-rdpproxy b/bin/rdpy-rdpproxy index 945e3a8..e49e55c 100755 --- a/bin/rdpy-rdpproxy +++ b/bin/rdpy-rdpproxy @@ -17,6 +17,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +from twisted.mail.pop3client import ERR """ RDP proxy recorder and spy function @@ -34,16 +35,56 @@ from twisted.internet import reactor from PyQt4 import QtCore, QtGui class IProxyClient(object): + """ + Interface use by Proxy server to interact with client + """ + def close(self): + """ + Close proxy client + """ + raise error.CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "getColorDepth", "IProxyClient")) + def getColorDepth(self): - pass + """ + Color depth client, Use to re-negociate color depth with server + """ + raise error.CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "getColorDepth", "IProxyClient")) + def sendKeyEventScancode(self, code, isPressed): - pass + """ + Key event as scan code + @param code: scan code of key + @param isPressed: True if key is down + """ + raise error.CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "sendKeyEventScancode", "IProxyClient")) + def sendKeyEventUnicode(self, code, isPressed): - pass + """ + Key event as unicode + @param code: unicode of key + @param isPressed: True if key is down + """ + raise error.CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "sendKeyEventUnicode", "IProxyClient")) + def sendPointerEvent(self, x, y, button, isPressed): - pass + """ + Mouse event + @param x: x position + @param y: y position + @param isPressed: True if button is down + """ + raise error.CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "sendPointerEvent", "IProxyClient")) + def sendRefreshOrder(self, left, top, right, bottom): - pass + """ + Refresh zone + @param left: left postion + @param top: top position + @param right: right position + @param bottom: bottom position + """ + raise error.CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "sendRefreshOrder", "IProxyClient")) + class ProxyServer(rdp.RDPServerObserver): """ @@ -92,6 +133,13 @@ class ProxyServer(rdp.RDPServerObserver): #refresh client width, height = self._controller.getScreen() self._client.sendRefreshOrder(0, 0, width, height) + + def onClose(self): + """ + Call when client close connection + """ + if not self._client is None: + self._client.close() def onKeyEventScancode(self, code, isPressed): """ @@ -111,7 +159,7 @@ class ProxyServer(rdp.RDPServerObserver): @param code: unicode of key @param isPressed: True if key is down """ - #no client connectedomaind + #no client connected domain if self._client is None: return @@ -135,7 +183,8 @@ class ProxyClient(rdp.RDPClientObserver, IProxyClient): """ Client side of proxy """ - def __init__(self, controller, server): + _CONNECTED_ = {} + def __init__(self, controller, server, name): """ @param controller: RDPClientObserver @param server: ProxyServer @@ -143,6 +192,7 @@ class ProxyClient(rdp.RDPClientObserver, IProxyClient): """ rdp.RDPClientObserver.__init__(self, controller) self._server = server + self._name = name def onReady(self): """ @@ -150,8 +200,23 @@ class ProxyClient(rdp.RDPClientObserver, IProxyClient): Inform proxy server that i'm connected implement RDPClientObserver """ + ProxyClient._CONNECTED_[self._name] = self self._server.clientConnected(self) + def onClose(self): + """ + Stack is closes + """ + if ProxyClient._CONNECTED_.has_key(self._name): + del ProxyClient._CONNECTED_[self._name] + self._server._controller.close() + + def close(self): + """ + Close proxy client + """ + self._controller.close() + def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): """ Event use to inform bitmap update @@ -169,18 +234,44 @@ class ProxyClient(rdp.RDPClientObserver, IProxyClient): self._server._controller.sendUpdate(destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data) def getColorDepth(self): + """ + Color depth client, Use to re-negociate color depth with server + """ return self._controller.getColorDepth() def sendKeyEventScancode(self, code, isPressed): + """ + Key event as scan code + @param code: scan code of key + @param isPressed: True if key is down + """ self._controller.sendKeyEventScancode(code, isPressed) def sendKeyEventUnicode(self, code, isPressed): + """ + Key event as uni code + @param code: uni code of key + @param isPressed: True if key is down + """ self._controller.sendKeyEventUnicode(code, isPressed) def sendPointerEvent(self, x, y, button, isPressed): + """ + Mouse event + @param x: x position + @param y: y position + @param isPressed: True if button is down + """ self._controller.sendPointerEvent(x, y, button, isPressed) def sendRefreshOrder(self, left, top, right, bottom): + """ + Refresh zone + @param left: left postion + @param top: top position + @param right: right position + @param bottom: bottom position + """ self._controller.sendRefreshOrder(left, top, right, bottom) class ProxyServerFactory(rdp.ServerFactory): @@ -192,7 +283,7 @@ class ProxyServerFactory(rdp.ServerFactory): @param config: rdp-proxy configuration @param credentialProvider: CredentialProvider """ - rdp.ServerFactory.__init__(self, "/home/sylvain/dev/certificate/rdpy.key", "/home/sylvain/dev/certificate/rdpy.crt", 16) + rdp.ServerFactory.__init__(self, "/home/speyrefitte/dev/certificate/rdpy.key", "/home/speyrefitte/dev/certificate/rdpy.crt", 16) self._credentialProvider = credentialProvider def buildObserver(self, controller): @@ -201,21 +292,11 @@ class ProxyServerFactory(rdp.ServerFactory): @param controller: rdp.RDPServerController """ return ProxyServer(controller, self._credentialProvider) - - def startedConnecting(self, connector): - pass - - def clientConnectionLost(self, connector, reason): - pass - - def clientConnectionFailed(self, connector, reason): - pass class ProxyClientFactory(rdp.ClientFactory): """ Factory for proxy client """ - _CLIENT_PROXY_ = {} def __init__(self, server, width, height, domain, username, password, name): """ @param server: ProxyServer @@ -224,6 +305,7 @@ class ProxyClientFactory(rdp.ClientFactory): @param domain: domain session @param username: username session @param password: password session + @param name: name of session """ self._server = server self._width = width @@ -245,21 +327,128 @@ class ProxyClientFactory(rdp.ClientFactory): controller.setDomain(self._domain) controller.setUsername(self._username) controller.setPassword(self._password) - proxy = ProxyClient(controller, self._server) - ProxyClientFactory._CLIENT_PROXY_[self._name] = proxy + proxy = ProxyClient(controller, self._server, self._name) return proxy def startedConnecting(self, connector): pass def clientConnectionLost(self, connector, reason): - if ProxyClientFactory._CLIENT_PROXY_.has_key(self._name): - del ProxyClientFactory._CLIENT_PROXY_[self._name] + pass def clientConnectionFailed(self, connector, reason): - if ProxyClientFactory._CLIENT_PROXY_.has_key(self._name): - del ProxyClientFactory._CLIENT_PROXY_[self._name] + pass + +class ProxyAdmin(IProxyClient): + """ + Use to manage client side of admin session + Add GUI to select which session to see + And manage see session + Just escape key is authorized during see session + """ + class State(object): + """ + GUI state -> list of active session + SPY state -> watch active session + """ + GUI = 0 + SPY = 1 + + def __init__(self, server): + """ + @param server: rdp.RDPServerController + """ + self._server = server + self._spyProxy = None + self.initView() + self._state = ProxyAdmin.State.GUI + + def initView(self): + """ + Init GUI view + """ + width, height = self._server._controller.getScreen() + self._window = view.WindowView(width, height, QtGui.QColor(24, 93, 123)) + self._window.addView(view.AnchorView(width / 2 - 250, height / 2 - 250, view.ListView(ProxyClient._CONNECTED_.keys(), 500, 500, self.onSelect, QtGui.QColor(24, 93, 123)))) + self._render = view.RDPRenderer(self._server._controller) + + def close(self): + """ + Close proxy client + """ + pass + + def getColorDepth(self): + """ + Use same Color depth as server init + @return color depth of GUI + """ + if self._state == ProxyAdmin.State.GUI: + return self._server._controller.getColorDepth() + elif self._state == ProxyAdmin.State.SPY: + return self._spyProxy.getColorDepth() + + def sendKeyEventScancode(self, code, isPressed): + """ + IProxyClient implement is unauthorized during admin session + Only for GUI + @param code: scan code of key + @param isPressed: True if key is down + """ + if self._state == ProxyAdmin.State.GUI: + if not isPressed: + return + self._window.keyEvent(code) + self._window.update(self._render) + elif code == 1: + #escape button refresh GUI + self._spyProxy._controller.removeClientObserver(self._spyProxy) + self._state = ProxyAdmin.State.GUI + self._server.clientConnected(self) + + def sendKeyEventUnicode(self, code, isPressed): + """ + Key event as uni code is unauthorized during admin session + @param code: uni code of key + @param isPressed: True if key is down + """ + pass + + def sendPointerEvent(self, x, y, button, isPressed): + """ + Mouse event is unauthorized during admin session + @param x: x position + @param y: y position + @param isPressed: True if button is down + """ + pass + + def sendRefreshOrder(self, left, top, right, bottom): + """ + Refresh zone + @param left: left postion + @param top: top position + @param right: right position + @param bottom: bottom position + """ + if self._state == ProxyAdmin.State.GUI: + self._window.update(self._render) + elif self._state == ProxyAdmin.State.SPY: + self._spyProxy.sendRefreshOrder(left, top, right, bottom) + + def onSelect(self, name): + """ + Call back of list view + @param name: name selected by user + """ + if not ProxyClient._CONNECTED_.has_key(name): + return + self._spyProxy = ProxyClient(ProxyClient._CONNECTED_[name]._controller, self._server, "Admin") + self._state = ProxyAdmin.State.SPY + #reconnect me + self._server.clientConnected(self) + class CredentialProvider(object): """ Credential provider for proxy @@ -293,33 +482,7 @@ class CredentialProvider(object): """ account = self.getAccount(domain, username) return account.has_key("admin") and account["admin"] and account.has_key("password") and str(account["password"]) == password - -class ProxyAdmin(IProxyClient): - """ - Use to manage client side of admin session - Add GUI to select wich session to see - """ - def __init__(self, server): - self._server = server - self._list = view.ListView(ProxyClientFactory._CLIENT_PROXY_.keys(), 300, 300, self.onSelect) - self._render = view.RDPRenderer(self._server._controller) - self._list.update(self._render) - def getColorDepth(self): - return 16 - def sendKeyEventScancode(self, code, isPressed): - if isPressed: - self._list.keyEvent(code) - self._list.update(self._render) - def sendKeyEventUnicode(self, code, isPressed): - pass - def sendPointerEvent(self, x, y, button, isPressed): - pass - def sendRefreshOrder(self, left, top, right, bottom): - self._list.update(self._render) - def onSelect(self, name): - if ProxyClientFactory._CLIENT_PROXY_.has_key(name): - self._server.clientConnected(ProxyClient(ProxyClientFactory._CLIENT_PROXY_[name]._controller, self._server)) - + def help(): """ Print help in console diff --git a/rdpy/network/layer.py b/rdpy/network/layer.py index 80b60ab..7425e0f 100644 --- a/rdpy/network/layer.py +++ b/rdpy/network/layer.py @@ -108,9 +108,63 @@ class LayerAutomata(Layer, IStreamListener): #twisted layer concept from twisted.internet import protocol +from twisted.internet.abstract import FileDescriptor #first that handle stream from type import Stream +class RawLayerClientFactory(protocol.ClientFactory): + """ + Abstract class for Raw layer client factory + """ + def buildProtocol(self, addr): + """ + Function call from twisted and build rdp protocol stack + @param addr: destination address + """ + rawLayer = self.buildRawLayer(addr) + rawLayer.setFactory(self) + return rawLayer + + def buildRawLayer(self, addr): + """ + Override this function to build raw layer + @param addr: destination address + """ + raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "IStreamListener")) + + def connectionLost(self, rawlayer): + """ + Overirde this method to handle connection lost + """ + raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "IStreamListener")) + +class RawLayerServerFactory(protocol.ClientFactory): + """ + Abstract class for Raw layer server factory + """ + def buildProtocol(self, addr): + """ + Function call from twisted and build rdp protocol stack + @param addr: destination address + """ + rawLayer = self.buildRawLayer(addr) + rawLayer.setFactory(self) + return rawLayer + + def buildRawLayer(self, addr): + """ + Override this function to build raw layer + @param addr: destination address + """ + raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "IStreamListener")) + + def connectionLost(self, rawlayer): + """ + Overirde this method to handle connection lost + """ + raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "IStreamListener")) + + class RawLayer(protocol.Protocol, LayerAutomata, IStreamSender): """ Inherit from protocol twisted class @@ -127,6 +181,14 @@ class RawLayer(protocol.Protocol, LayerAutomata, IStreamSender): self._buffer = "" #len of next packet pass to next state function self._expectedLen = 0 + self._factory = None + + def setFactory(self, factory): + """ + Call by RawLayer Factory + @param param: RawLayerClientFactory or RawLayerFactory + """ + self._factory = factory def dataReceived(self, data): """ @@ -152,11 +214,14 @@ class RawLayer(protocol.Protocol, LayerAutomata, IStreamSender): #join two scheme self.connect() + def connectionLost(self, reason): + self._factory.connectionLost(self) + def close(self): """ Close raw layer """ - self.transport.loseConnection() + FileDescriptor.loseConnection(self.transport) def expect(self, expectedLen, callback = None): """ diff --git a/rdpy/protocol/rdp/pdu/data.py b/rdpy/protocol/rdp/pdu/data.py index 0d6ae22..cf53585 100644 --- a/rdpy/protocol/rdp/pdu/data.py +++ b/rdpy/protocol/rdp/pdu/data.py @@ -25,7 +25,7 @@ In this layer are managed all mains bitmap update orders end user inputs from rdpy.network.type import CompositeType, String, UInt8, UInt16Le, UInt32Le, sizeof, ArrayType, FactoryType from rdpy.base.error import InvalidExpectedDataException import rdpy.base.log as log -import caps +import caps, order class SecurityFlag(object): """ @@ -930,17 +930,7 @@ class OrderUpdateDataPDU(CompositeType): self.pad2OctetsA = UInt16Le() self.numberOrders = UInt16Le(lambda:len(self.orderData._array)) self.pad2OctetsB = UInt16Le() - self.orderData = ArrayType(DrawingOrder, readLen = self.numberOrders) - -class DrawingOrder(CompositeType): - """ - GDI drawing orders - @see: http://msdn.microsoft.com/en-us/library/cc241574.aspx - @todo: not implemented yet but need it - """ - def __init__(self): - CompositeType.__init__(self) - self.controlFlags = UInt8() + self.orderData = ArrayType(order.DrawingOrder, readLen = self.numberOrders) class BitmapCompressedDataHeader(CompositeType): """ diff --git a/rdpy/protocol/rdp/pdu/layer.py b/rdpy/protocol/rdp/pdu/layer.py index 295350c..9281da5 100644 --- a/rdpy/protocol/rdp/pdu/layer.py +++ b/rdpy/protocol/rdp/pdu/layer.py @@ -573,10 +573,11 @@ class Server(PDULayer, tpkt.IFastPathListener): elif dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_INPUT: self._listener.onSlowPathInput(dataPDU.pduData.slowPathInputEvents._array) elif dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SHUTDOWN_REQUEST: + log.debug("Receive Shutdown Request") self._transport.close() def recvFastPath(self, fastPathS): - """r + """ Implement IFastPathListener interface Fast path is needed by RDP 8.0 @param fastPathS: Stream that contain fast path data diff --git a/rdpy/protocol/rdp/pdu/order.py b/rdpy/protocol/rdp/pdu/order.py new file mode 100644 index 0000000..1a3ca64 --- /dev/null +++ b/rdpy/protocol/rdp/pdu/order.py @@ -0,0 +1,45 @@ +# +# Copyright (c) 2014 Sylvain Peyrefitte +# +# This file is part of rdpy. +# +# rdpy is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +""" +GDI order structure +""" + +from rdpy.network.type import CompositeType, UInt8 + +class ControlFlag(object): + TS_STANDARD = 0x01 + TS_SECONDARY = 0x02 + +class DrawingOrder(CompositeType): + """ + GDI drawing orders + @see: http://msdn.microsoft.com/en-us/library/cc241574.aspx + """ + def __init__(self): + CompositeType.__init__(self) + self.controlFlags = UInt8() + +class PrimaryDrawingOrder(CompositeType): + """ + GDI Primary drawing order + @see: http://msdn.microsoft.com/en-us/library/cc241586.aspx + """ + def __init__(self): + CompositeType.__init__(self) \ No newline at end of file diff --git a/rdpy/protocol/rdp/rdp.py b/rdpy/protocol/rdp/rdp.py index 2fff3e8..a5edb5a 100644 --- a/rdpy/protocol/rdp/rdp.py +++ b/rdpy/protocol/rdp/rdp.py @@ -21,7 +21,7 @@ Use to manage RDP stack in twisted """ -from twisted.internet import protocol +from rdpy.network import layer from rdpy.base.error import CallPureVirtualFuntion, InvalidValue import pdu.layer import pdu.data @@ -46,7 +46,6 @@ class RDPClientController(pdu.layer.PDUClientListener): self._tpktLayer = tpkt.TPKT(self._tpduLayer, self._pduLayer) #is pdu layer is ready to send self._isReady = False - self._sendReady = False def getProtocol(self): """ @@ -112,6 +111,16 @@ class RDPClientController(pdu.layer.PDUClientListener): """ self._clientObserver.append(observer) + def removeClientObserver(self, observer): + """ + Remove observer to RDP protocol stack + @param observer: observer to remove + """ + for i in range(0, len(self._clientObserver)): + if self._clientObserver[i] == observer: + del self._clientObserver[i] + return + def onUpdate(self, rectangles): """ Call when a bitmap data is received from update PDU @@ -127,12 +136,17 @@ class RDPClientController(pdu.layer.PDUClientListener): Call when PDU layer is connected """ self._isReady = True - if self._sendReady: - return - self._sendReady = False #signal all listener for observer in self._clientObserver: observer.onReady() + + def onClose(self): + """ + Event call when RDP stack is closed + """ + self._isReady = False + for observer in self._clientObserver: + observer.onClose() def sendPointerEvent(self, x, y, button, isPressed): """ @@ -260,6 +274,12 @@ class RDPServerController(pdu.layer.PDUServerListener): #set color depth of session self.setColorDepth(colorDepth) + def close(self): + """ + Close protocol stack + """ + self._pduLayer.close() + def getProtocol(self): """ @return: the twisted protocol layer @@ -342,6 +362,14 @@ class RDPServerController(pdu.layer.PDUServerListener): for observer in self._serverObserver: observer.onReady() + def onClose(self): + """ + Event call when RDP stack is closed + """ + self._isReady = False + for observer in self._serverObserver: + observer.onClose() + def onSlowPathInput(self, slowPathInputEvents): """ Event call when slow path input are available @@ -388,27 +416,36 @@ class RDPServerController(pdu.layer.PDUServerListener): self._pduLayer.sendBitmapUpdatePDU([bitmapData]) -class ClientFactory(protocol.Factory): +class ClientFactory(layer.RawLayerClientFactory): """ Factory of Client RDP protocol """ - def buildProtocol(self, addr): + def connectionLost(self, tpktLayer): + #retrieve controller + tpduLayer = tpktLayer._presentation + mcsLayer = tpduLayer._presentation + pduLayer = mcsLayer._channels[mcs.Channel.MCS_GLOBAL_CHANNEL] + controller = pduLayer._listener + controller.onClose() + + def buildRawLayer(self, addr): """ Function call from twisted and build rdp protocol stack @param addr: destination address """ controller = RDPClientController() self.buildObserver(controller) - - return controller.getProtocol(); + controller.getProtocol()._factory = self + return controller.getProtocol() def buildObserver(self, controller): - ''' - build observer use for connection - ''' + """ + Build observer use for connection + @param controller: RDPClientController + """ raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "buildObserver", "ClientFactory")) -class ServerFactory(protocol.Factory): +class ServerFactory(layer.RawLayerServerFactory): """ Factory of Server RDP protocol """ @@ -421,14 +458,23 @@ class ServerFactory(protocol.Factory): self._privateKeyFileName = privateKeyFileName self._certificateFileName = certificateFileName self._colorDepth = colorDepth - - def buildProtocol(self, addr): + + def connectionLost(self, tpktLayer): + #retrieve controller + tpduLayer = tpktLayer._presentation + mcsLayer = tpduLayer._presentation + pduLayer = mcsLayer._channels[mcs.Channel.MCS_GLOBAL_CHANNEL] + controller = pduLayer._listener + controller.onClose() + + def buildRawLayer(self, addr): """ Function call from twisted and build rdp protocol stack @param addr: destination address """ controller = RDPServerController(self._privateKeyFileName, self._certificateFileName, self._colorDepth) self.buildObserver(controller) + controller.getProtocol()._factory = self return controller.getProtocol() def buildObserver(self, controller): @@ -455,6 +501,12 @@ class RDPClientObserver(object): """ raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "RDPClientObserver")) + def onClose(self): + """ + Stack is closes + """ + raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onClose", "RDPClientObserver")) + def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): """ Notify bitmap update @@ -488,6 +540,12 @@ class RDPServerObserver(object): """ raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "RDPServerObserver")) + def onClose(self): + """ + Stack is closes + """ + raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onClose", "RDPClientObserver")) + def onKeyEventScancode(self, code, isPressed): """ Event call when a keyboard event is catch in scan code format diff --git a/rdpy/ui/view.py b/rdpy/ui/view.py index a92b02a..8654d79 100644 --- a/rdpy/ui/view.py +++ b/rdpy/ui/view.py @@ -20,7 +20,7 @@ """ Fake widget """ -from rdpy.base.error import CallPureVirtualFuntion +from rdpy.base.error import CallPureVirtualFuntion, InvalidExpectedDataException from PyQt4 import QtGui, QtCore @@ -34,6 +34,8 @@ class IRender(object): pass def drawImage(self, image): pass + def getImageFormat(self): + pass class IView(object): def keyEvent(self, code): @@ -54,20 +56,19 @@ class AnchorView(IView): self._view.pointerEvent(x - self._x, y - self._y) def update(self, render): render.translate(self._x, self._y) - self._view.update(self._view, render) + self._view.update(render) render.translate(- self._x, - self._y) - class ListView(IView): """ List widget simulate by QT painter """ - def __init__(self, labels, width, height, callback): + def __init__(self, labels, width, height, callback, backgroudColor = QtCore.Qt.black): self._labels = labels self._width = width self._height = height self._cellHeight = 25 - self._backGroudColor = QtGui.QColor(24, 93, 123) + self._backgroudColor = backgroudColor self._fontSize = 14 self._current = 0 self._callback = callback @@ -91,9 +92,9 @@ class ListView(IView): Draw GUI that list active session """ i = 0 - drawArea = QtGui.QImage(self._width, self._height, QtGui.QImage.Format_RGB16) + drawArea = QtGui.QImage(self._width, self._height, render.getImageFormat()) #fill with background Color - drawArea.fill(self._backGroudColor) + drawArea.fill(self._backgroudColor) with QtGui.QPainter(drawArea) as qp: for label in self._labels: rect = QtCore.QRect(0, i * self._cellHeight, self._width, self._cellHeight) @@ -105,6 +106,29 @@ class ListView(IView): qp.drawText(rect, QtCore.Qt.AlignCenter, label) i += 1 render.drawImage(drawArea) + +class WindowView(IView): + def __init__(self, width, height, backgroundColor = QtCore.Qt.black): + self._views = [] + self._focusIndex = 0 + self._width = width + self._height = height + self._backgroundColor = backgroundColor + def addView(self, view): + self._views.append(view) + def keyEvent(self, code): + if self._focusIndex < len(self._views): + self._views[self._focusIndex].keyEvent(code) + def pointerEvent(self, x, y, button): + if self._focusIndex < len(self._views): + self._views[self._focusIndex].pointerEvent(x, y, button) + def update(self, render): + drawArea = QtGui.QImage(self._width, self._height, render.getImageFormat()) + #fill with background Color + drawArea.fill(self._backgroundColor) + render.drawImage(drawArea) + for view in self._views: + view.update(render) class RDPRenderer(object): def __init__(self, server): @@ -112,10 +136,21 @@ class RDPRenderer(object): @param server: RDPServerController """ self._server = server + self._colorDepth = self._server.getColorDepth() self._dx = 0 self._dy = 0 self._renderSize = 64 + def getImageFormat(self): + if self._colorDepth == 15: + return QtGui.QImage.Format_RGB15 + elif self._colorDepth == 16: + return QtGui.QImage.Format_RGB16 + elif self._colorDepth == 24: + return QtGui.QImage.Format_RGB24 + elif self._colorDepth == 32: + return QtGui.QImage.Format_RGB32 + def translate(self, dx, dy): self._dx += dx self._dy += dy @@ -133,5 +168,5 @@ class RDPRenderer(object): tmp = tmp.transformed(QtGui.QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0)) ptr = tmp.bits() ptr.setsize(tmp.byteCount()) - self._server.sendUpdate(i * self._renderSize + self._dx, j * self._renderSize + self._dy, min((i + 1) * self._renderSize, image.width()) + self._dx - 1, min((j + 1) * self._renderSize, image.height()) + self._dy - 1, tmp.width(), tmp.height(), 16, False, ptr.asstring()) + self._server.sendUpdate(i * self._renderSize + self._dx, j * self._renderSize + self._dy, min((i + 1) * self._renderSize, image.width()) + self._dx - 1, min((j + 1) * self._renderSize, image.height()) + self._dy - 1, tmp.width(), tmp.height(), self._colorDepth, False, ptr.asstring()) \ No newline at end of file