""" The 'vnc' implementation. """ from pyromaniac.resource import Resource from pyromaniac.config import Configuration import riscos.graphics as graphics import riscos.graphicsimp.cairo_imp as cairo_imp cairovnc = None # Disable recursive import of the implementation __recurse__ = False class VNCConfig(Configuration): _name = 'VNC' _types = { 'maximum_redraw_rate': int, 'maximum_clients': int, 'port': int, 'password': str, 'password_readonly': str, } _help = { 'maximum_redraw_rate': """ Configures the maximum rate at which the VNC clients will be updated. If this is set too high, the system may spend more time redrawing than executing code. If this is set too low, output may not refresh in a timely manner as it is generated. """, 'maximum_clients': """ Configures the maximum number of clients that the VNC server will allow. """, 'port': """ Configures the port on which the VNC server will listen for connections. """, 'password': """ Configures the password to use for the VNC server (full access, including input). An empty string means that no password will be used. """, 'password_readonly': """ Configures the password to use for the VNC server (read only, no input). An empty string means that no password will be used. """ } maximum_redraw_rate = 20 maximum_clients = 2 port = 5902 password = '' password_readonly = '' class VNCResource(Resource): _name = 'VNC' def __init__(self, ro): super(VNCResource, self).__init__(ro) self.server = None self.options = None self.key_mapping = None self.debug_vnc = False self.debug_vncevent = False self.ro.debug_register_ivar('vnc', self) self.ro.debug_register_ivar('vncevent', self) def boot(self): """ Inform the KeyInput that we want it to run. """ if self.server: # We only inform it of its activation if we're running an application self.ro.resources['keyinput'].activate() self.ro.resources['pointerinput'].activate() self.ro.resources['vducursor'].activate() def finalise(self): """ Finalise the system. """ if self.server: self.server.stop() def start_server(self): if self.server: return self.server global cairovnc from . import cairovnc if not self.key_mapping: from .cairovnc.events import VNCEventKey self.key_mapping = { VNCEventKey.Key_Escape: 0, VNCEventKey.Key_F1: 1, VNCEventKey.Key_F2: 2, VNCEventKey.Key_F3: 3, VNCEventKey.Key_F4: 4, VNCEventKey.Key_F5: 5, VNCEventKey.Key_F6: 6, VNCEventKey.Key_F7: 7, VNCEventKey.Key_F8: 8, VNCEventKey.Key_F9: 9, VNCEventKey.Key_F10: 10, VNCEventKey.Key_F11: 11, VNCEventKey.Key_F12: 12, VNCEventKey.Key_Print: 13, VNCEventKey.Key_ScrollLock: 14, VNCEventKey.Key_Break: 15, # Only Windows appears to report this ord('~'): 16, ord('1'): 17, ord('2'): 18, ord('3'): 19, ord('4'): 20, ord('5'): 21, ord('6'): 22, ord('7'): 23, ord('8'): 24, ord('9'): 25, ord('0'): 26, # The shifted number keys ord('!'): 17, ord('"'): 18, 163: 19, # Pound ord('$'): 20, ord('%'): 21, ord('^'): 22, ord('&'): 23, ord('*'): 24, ord('('): 25, ord(')'): 26, ord('-'): 27, ord('_'): 27, ord('='): 28, ord('+'): 28, # VNCEventKey.Key_yen: 29, VNCEventKey.Key_Backspace: 30, VNCEventKey.Key_Insert: 59, VNCEventKey.Key_Home: 32, VNCEventKey.Key_PageUp: 33, #VNCEventKey.Key_NumLock: 34, #VNCEventKey.Key_Numpad_Divide: 35, #VNCEventKey.Key_Numpad_Multiply: 36, # #VNCEventKey.Numpad_# */ /* jrf: no longer present on keyboards, but mapped: 37, VNCEventKey.Key_Tab: 38, ord('Q'): 39, ord('W'): 40, ord('E'): 41, ord('R'): 42, ord('T'): 43, ord('Y'): 44, ord('U'): 45, ord('I'): 46, ord('O'): 47, ord('P'): 48, ord('['): 49, ord('{'): 49, ord(']'): 50, ord('}'): 50, ord('#'): 51, # Tilde/# VNCEventKey.Key_Delete: 52, VNCEventKey.Key_End: 53, VNCEventKey.Key_PageDown: 54, VNCEventKey.Key_Numpad_7: 55, VNCEventKey.Key_Numpad_8: 56, VNCEventKey.Key_Numpad_9: 57, #VNCEventKey.Key_Numpad_Subtract: 58, VNCEventKey.Key_ControlLeft: 59, ord('A'): 60, ord('S'): 61, ord('D'): 62, ord('F'): 63, ord('G'): 64, ord('H'): 65, ord('J'): 66, ord('K'): 67, ord('L'): 68, ord(';'): 69, ord(':'): 69, ord("'"): 70, ord("@"): 70, VNCEventKey.Key_Return: 71, VNCEventKey.Key_Numpad_4: 72, VNCEventKey.Key_Numpad_5: 73, VNCEventKey.Key_Numpad_6: 74, #VNCEventKey.Key_Numpad_Add: 75, VNCEventKey.Key_ShiftLeft: 76, ord('\\'): 77, ord('Z'): 78, ord('X'): 79, ord('C'): 80, ord('V'): 81, ord('B'): 82, ord('N'): 83, ord('M'): 84, ord(','): 85, ord('<'): 85, ord('.'): 86, ord('>'): 86, ord('/'): 87, ord('?'): 87, VNCEventKey.Key_ShiftRight: 88, VNCEventKey.Key_CursorUp: 89, VNCEventKey.Key_Numpad_1: 90, VNCEventKey.Key_Numpad_2: 91, VNCEventKey.Key_Numpad_3: 92, #VNCEventKey.Key_CapsLock: 93, VNCEventKey.Key_AltLeft: 94, VNCEventKey.Key_AltLeft_2: 94, ord(' '): 95, VNCEventKey.Key_AltRight: 96, VNCEventKey.Key_AltGr: 96, VNCEventKey.Key_ControlRight: 97, VNCEventKey.Key_CursorLeft: 98, VNCEventKey.Key_CursorDown: 99, VNCEventKey.Key_CursorRight: 100, VNCEventKey.Key_Numpad_0: 101, VNCEventKey.Key_Numpad_Delete: 102, VNCEventKey.Key_Numpad_Enter: 103, VNCEventKey.Key_MetaLeft: 104, VNCEventKey.Key_MetaRight: 105, #VNCEventKey.Key_Menu: 106, } # We need to tweak the mappings because the keys are not capitalised. for key, value in list(self.key_mapping.items()): if key > 64 and key < 91: self.key_mapping[key + 32] = value graphics_surface = self.ro.resources['graphicssurface'] config = self.ro.config['vnc'] self.options = cairovnc.CairoVNCOptions(port=config.port, display_name='Pyromaniac') self.options.verbose = self.debug_vnc self.options.password = config.password self.options.password_readonly = config.password_readonly self.options.max_framerate = config.maximum_redraw_rate self.options.max_clients = config.maximum_clients self.options.read_only = False self.server = cairovnc.CairoVNCServer(surface=graphics_surface.display_surface, options=self.options) # We just want to make a small change to the event handling, so that we can directly # insert the mouse and key events from the connection threads, rather than queueing # the events. class PyromaniacVNCConnection(cairovnc.VNCConnection): vncresource = None def queue_event(self, event): self.vncresource.vnc_event(event, self) PyromaniacVNCConnection.vncresource = self self.server.connection_class = PyromaniacVNCConnection self.server.daemonise() return self.server def vnc_event(self, event, connection): """ Insert an event into the queues. Thread: Connection thread. """ if self.debug_vncevent: print("vnc_event: event {!r}".format(event)) if event.name == 'move': y = connection.height - event.y self.ro.resources['pointerinput'].oob_move(event.x, y) elif event.name == 'click': y = connection.height - event.y button = event.button if button in (0, 2): # button 0 => Select; button 2 => Adjust button = 2 - button self.ro.resources['pointerinput'].oob_click(button, event.x, y, event.down) elif event.name == 'key': ro_key = self.key_mapping.get(event.key, None) if ro_key is not None: self.ro.resources['keyinput'].oob_key(ro_key, event.down) else: if self.debug_vnc: print("VNC Key not recognised: %i (&%x), down=%s" % (event.key, event.key, event.down)) @graphics.graphics_implementations.register class GraphicsVNC(cairo_imp.GraphicsCairo): """ VNC Cairo Graphics implementation. """ def __init__(self, ro): self.server = None super(GraphicsVNC, self).__init__(ro) self.server = ro.resources['vnc'].start_server() def change_display_surface(self, surface, mutex): """ Notification that the surface and mutex being displayed have changed. """ super(GraphicsVNC, self).change_display_surface(surface, mutex) self.server.change_surface(surface, mutex) def display_bank_updated(self): """ A region of the display bank has been updated. """ if self._graphics and self.server: self.server.change_frame() super(GraphicsVNC, self).display_bank_updated()