xpra icon
Bug tracker and wiki

This bug tracker and wiki are being discontinued
please use https://github.com/Xpra-org/xpra instead.


Ticket #2532: rfb_server.py

File rfb_server.py, 7.6 KB (added by LIU Quanbo, 17 months ago)

server/rfb/rfb_server.py

Line 
1# -*- coding: utf-8 -*-
2# This file is part of Xpra.
3# Copyright (C) 2017-2019 Antoine Martin <antoine@xpra.org>
4# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
5# later version. See the file COPYING for details.
6#pylint: disable-msg=E1101
7
8from xpra.util import nonl, csv
9from xpra.os_util import POSIX, OSX, bytestostr
10from xpra.server.rfb.rfb_const import RFBEncoding, RFB_KEYNAMES
11from xpra.server.rfb.rfb_protocol import RFBProtocol
12from xpra.server.rfb.rfb_source import RFBSource
13from xpra.server import server_features
14from xpra.scripts.config import parse_bool, parse_number
15from xpra.log import Logger
16
17log = Logger("rfb")
18
19
20"""
21    Adds RFB packet handler to a server.
22"""
23class RFBServer(object):
24
25    def __init__(self):
26        self._window_to_id = {}
27        self._rfb_upgrade = 0
28        self.readonly = False
29        self.rfb_buttons = 0
30        self.x11_keycodes_for_keysym = {}
31        if POSIX and not OSX:
32            from xpra.x11.bindings.keyboard_bindings import X11KeyboardBindings #@UnresolvedImport
33            self.X11Keyboard = X11KeyboardBindings()
34
35    def init(self, opts):
36        if not parse_bool("rfb-upgrade", opts.rfb_upgrade):
37            self._rfb_upgrade = 0
38        else:
39            self._rfb_upgrade = parse_number(int, "rfb-upgrade", opts.rfb_upgrade, 0)
40        log("init(..) rfb-upgrade=%i", self._rfb_upgrade)
41
42
43    def _get_rfb_desktop_model(self):
44        models = tuple(self._window_to_id.keys())
45        if not models:
46            log.error("RFB: no window models to export, dropping connection")
47            return None
48        if len(models)!=1:
49            log.error("RFB can only handle a single desktop window, found %i", len(self._window_to_id))
50            return None
51        return models[0]
52
53    def _get_rfb_desktop_wid(self):
54        ids = tuple(self._window_to_id.values())
55        if len(ids)!=1:
56            log.error("RFB can only handle a single desktop window, found %i", len(self._window_to_id))
57            return None
58        return ids[0]
59
60
61    def handle_rfb_connection(self, conn):
62        model = self._get_rfb_desktop_model()
63        if not model:
64            conn.close()
65            return
66        def rfb_protocol_class(conn):
67            auths = self.make_authenticators("rfb", "rfb", conn)
68            assert len(auths)<=1, "rfb does not support multiple authentication modules"
69            auth = None
70            if len(auths)==1:
71                auth = auths[0]
72            return RFBProtocol(self, conn, auth,
73                               self.process_rfb_packet, self.get_rfb_pixelformat, self.session_name or "Xpra Server")
74        p = self.do_make_protocol("rfb", conn, rfb_protocol_class)
75        p.send_protocol_handshake()
76
77    def process_rfb_packet(self, proto, packet):
78        #log("RFB packet: '%s'", nonl(packet))
79        fn_name = "_process_rfb_%s" % bytestostr(packet[0]).replace("-", "_")
80        fn = getattr(self, fn_name, None)
81        if not fn:
82            log.warn("Warning: no RFB handler for %s", fn_name)
83            return
84        self.idle_add(fn, proto, packet)
85
86
87    def get_rfb_pixelformat(self):
88        model = self._get_rfb_desktop_model()
89        w, h = model.get_dimensions()
90        #w, h, bpp, depth, bigendian, truecolor, rmax, gmax, bmax, rshift, bshift, gshift
91        return w, h, 32, 32, False, True, 255, 255, 255, 16, 8, 0
92
93    def _process_rfb_invalid(self, proto, packet):
94        self.disconnect_protocol(proto, "invalid packet: %s" % (packet[1:]))
95
96    def _process_rfb_connection_lost(self, proto, packet):
97        self._process_connection_lost(proto, packet)
98
99    def _process_rfb_authenticated(self, proto, _packet):
100        model = self._get_rfb_desktop_model()
101        if not model:
102            proto.close()
103            return
104        self.accept_protocol(proto)
105        #use blocking sockets from now on:
106        from xpra.net.bytestreams import set_socket_timeout
107        set_socket_timeout(proto._conn, None)
108        accepted, share_count, disconnected = self.handle_sharing(proto, share=proto.share)
109        log("rfb handle sharing: accepted=%s, share count=%s, disconnected=%s", accepted, share_count, disconnected)
110        if not accepted:
111            return
112        source = RFBSource(proto, self._get_rfb_desktop_model(), proto.share)
113        if server_features.input_devices:
114            source.keyboard_config = self.get_keyboard_config()
115            self.set_keymap(source)
116        self._server_sources[proto] = source
117        w, h = model.get_dimensions()
118        source.damage(self._window_to_id[model], model, 0, 0, w, h)
119        #ugly weak dependency,
120        #shadow servers need to be told to start the refresh timer:
121        start_refresh = getattr(self, "start_refresh", None)
122        if start_refresh:
123            for wid in tuple(self._window_to_id.values()):
124                start_refresh(wid)      #pylint: disable=not-callable
125
126    def _process_rfb_PointerEvent(self, _proto, packet):
127        if not server_features.input_devices or self.readonly:
128            return
129        buttons, x, y = packet[1:4]
130        wid = self._get_rfb_desktop_wid()
131        self._move_pointer(wid, (x, y))
132        if buttons!=self.rfb_buttons:
133            #figure out which buttons have changed:
134            for button in range(8):
135                mask = 2**button
136                if buttons & mask != self.rfb_buttons & mask:
137                    pressed = bool(buttons & mask)
138                    self.button_action((x, y), 1+button, pressed, -1)
139            self.rfb_buttons = buttons
140
141    def _process_rfb_KeyEvent(self, proto, packet):
142        if not server_features.input_devices or self.readonly:
143            return
144        source = self.get_server_source(proto)
145        if not source:
146            return
147        pressed, p1, p2, key = packet[1:5]
148        wid = self._get_rfb_desktop_wid()
149        keyname = RFB_KEYNAMES.get(key)
150        if not keyname:
151            if 0<key<255:
152                keyname = chr(key)
153            elif self.X11Keyboard:
154                keyname = self.X11Keyboard.keysym_str(key)
155        if not keyname:
156            log.warn("rfb unknown KeyEvent: %s, %i, %i, %#x", pressed, p1, p2, key)
157            return
158        modifiers = []
159        keyval = 0
160        keycode = source.keyboard_config.get_keycode(0, keyname, pressed, modifiers)
161        log("rfb keycode(%s)=%s", keyname, keycode)
162        if keycode:
163            is_mod = source.keyboard_config.is_modifier(keycode)
164            self._handle_key(wid, bool(pressed), keyname, keyval, keycode, modifiers, is_mod, True)
165
166    def _process_rfb_SetEncodings(self, _proto, packet):
167        n, encodings = packet[2:4]
168        known_encodings = [RFBEncoding.ENCODING_STR.get(x) for x in encodings if x in RFBEncoding.ENCODING_STR]
169        log("%i encodings: %s", n, csv(known_encodings))
170        unknown_encodings = [x for x in encodings if x not in RFBEncoding.ENCODING_STR]
171        if unknown_encodings:
172            log("%i unknown encodings: %s", len(unknown_encodings), csv(unknown_encodings))
173
174    def _process_rfb_SetPixelFormat(self, _proto, packet):
175        log("RFB: SetPixelFormat %s", packet)
176        #w, h, bpp, depth, bigendian, truecolor, rmax, gmax, bmax, rshift, bshift, gshift = packet
177
178    def _process_rfb_FramebufferUpdateRequest(self, _proto, packet):
179        #pressed, _, _, keycode = packet[1:5]
180        inc, x, y, w, h = packet[1:6]
181        log("RFB: FramebufferUpdateRequest inc=%s, geometry=%s", inc, (x, y, w, h))
182        if not inc:
183            model = self._get_rfb_desktop_model()
184            self.refresh_window_area(model, x, y, w, h)
185
186    def _process_rfb_ClientCutText(self, _proto, packet):
187        #l = packet[4]
188        text = packet[5]
189        log("got rfb clipboard text: %s", nonl(text))