def do_xpra_client_message_event(self, event): if event.message_type == "_NET_SYSTEM_TRAY_OPCODE" and event.window == self.tray_window and event.format == 32: opcode = event.data[1] SYSTEM_TRAY_REQUEST_DOCK = 0 SYSTEM_TRAY_BEGIN_MESSAGE = 1 SYSTEM_TRAY_CANCEL_MESSAGE = 2 if opcode == SYSTEM_TRAY_REQUEST_DOCK: xid = event.data[2] trap.call_synced(self.dock_tray, xid) elif opcode == SYSTEM_TRAY_BEGIN_MESSAGE: timeout = event.data[2] mlen = event.data[3] mid = event.data[4] log.info( "tray begin message timeout=%s, mlen=%s, mid=%s - not handled yet!", timeout, mlen, mid) elif opcode == SYSTEM_TRAY_CANCEL_MESSAGE: mid = event.data[2] log.info("tray cancel message for mid=%s - not handled yet!", mid) elif event.message_type == "_NET_SYSTEM_TRAY_MESSAGE_DATA": assert event.format == 8 log.info("tray message data - not handled yet!") else: log.info("do_xpra_client_message_event(%s)", event)
def system_bell(window, device, percent, pitch, duration, bell_class, bell_id, bell_name): global device_bell if device_bell is None: return False try: xwindow = get_xwindow(window) trap.call_synced(device_bell, xwindow, device, bell_class, bell_id, percent, bell_name) return True except XError, e: log.error("error using device_bell: %s, switching native X11 bell support off", e) device_bell = None return False
def cleanup(self): #this must be called from the UI thread! remove_event_receiver(self._root, self) self._root.set_events(self._saved_event_mask) if self._own_x11_filter: #only remove the x11 filter if we initialized it (ie: when running in client) try: trap.call_synced(cleanup_x11_filter) except Exception, e: log.error("failed to remove x11 event filter: %s", e) try: trap.call_synced(cleanup_all_event_receivers) except Exception, e: log.error("failed to remove event receivers: %s", e)
def _process_button_action(self, proto, packet): ss = self._server_sources.get(proto) if ss is None: return wid, button, pressed, pointer, modifiers = packet[1:6] self._process_mouse_common(proto, wid, pointer, modifiers) ss.user_event() try: trap.call_synced(X11Keyboard.xtest_fake_button, button, pressed) except XError: err = "Failed to pass on (un)press of mouse button %s" % button if button >= 4: err += " (perhaps your Xvfb does not support mousewheels?)" log.warn(err)
def _process_button_action(self, proto, packet): ss = self._server_sources.get(proto) if ss is None: return wid, button, pressed, pointer, modifiers = packet[1:6] self._process_mouse_common(proto, wid, pointer, modifiers) ss.user_event() try: trap.call_synced(X11Keyboard.xtest_fake_button, button, pressed) except XError: err = "Failed to pass on (un)press of mouse button %s" % button if button>=4: err += " (perhaps your Xvfb does not support mousewheels?)" log.warn(err)
def system_bell(window, device, percent, pitch, duration, bell_class, bell_id, bell_name): global device_bell if device_bell is None: return False try: xwindow = get_xwindow(window) trap.call_synced(device_bell, xwindow, device, bell_class, bell_id, percent, bell_name) return True except XError, e: log.error( "error using device_bell: %s, switching native X11 bell support off", e) device_bell = None return False
def get_settings_blob(self): log("Fetching current XSettings data") try: return trap.call_synced(self._get_settings_blob) except XError: log("X error while fetching XSettings data; ignored") return None
def prop_get(target, key, etype, ignore_errors=False, raise_xerrors=False): if isinstance(etype, list): scalar_type = etype[0] else: scalar_type = etype (_, atom, _, _, _, _) = _prop_types[scalar_type] try: data = trap.call_synced(X11Window.XGetWindowProperty, get_xwindow(target), key, atom) except NoSuchProperty: log.debug("Missing property %s (%s)", key, etype) return None except XError: if raise_xerrors: raise log.info("Missing window %s or wrong property type %s (%s)", target, key, etype, exc_info=True) return None except PropertyError: if not ignore_errors: log.info("Missing property or wrong property type %s (%s)", key, etype, exc_info=True) return None try: return _prop_decode(target, etype, data) except: if not ignore_errors: log.warn("Error parsing property %s (type %s); this may be a" + " misbehaving application, or bug in Xpra\n" + " Data: %r[...?]", key, etype, data[:160], exc_info=True) raise
def do_set_workspace(self, workspace): assert HAS_X11_BINDINGS root = self.gdk_window().get_screen().get_root_window() ndesktops = self.xget_u32_property(root, "_NET_NUMBER_OF_DESKTOPS") self.debug("set_workspace() ndesktops=%s", ndesktops) if ndesktops is None or ndesktops<=1: return -1 workspace = max(0, min(ndesktops-1, workspace)) event_mask = const["SubstructureNotifyMask"] | const["SubstructureRedirectMask"] def send(): root_window = get_xwindow(root) window = get_xwindow(self.gdk_window()) X11WindowBindings.sendClientMessage(root_window, window, False, event_mask, "_NET_WM_DESKTOP", workspace, const["CurrentTime"], 0, 0, 0) trap.call_synced(send) return workspace
def _owner(self): owner_x = X11Window.XGetSelectionOwner(self._selection) if owner_x == constants["XNone"]: return None try: return trap.call_synced(get_pywindow, self._clipboard, owner_x) except XError: log("X error while fetching owner of XSettings data; ignored") return None
def set_workspace(self): if not HAS_X11_BINDINGS or not self._been_mapped: return -1 root = self.gdk_window().get_screen().get_root_window() ndesktops = self.xget_u32_property(root, "_NET_NUMBER_OF_DESKTOPS") workspacelog("%s.set_workspace() workspace=%s ndesktops=%s", self, self._window_workspace, ndesktops) if ndesktops is None or ndesktops<=1: return -1 workspace = max(0, min(ndesktops-1, self._window_workspace)) event_mask = SubstructureNotifyMask | SubstructureRedirectMask def send(): root_xid = root.xid xwin = self.gdk_window().xid X11Window.sendClientMessage(root_xid, xwin, False, event_mask, "_NET_WM_DESKTOP", workspace, CurrentTime, 0, 0, 0) trap.call_synced(send) return workspace
def get_settings(self): owner = self.xsettings_owner() log("Fetching current XSettings data, owner=%s", owner) if owner is None: return None try: return trap.call_synced(prop_get, owner, XSETTINGS, XSETTINGS_TYPE) except XError: log("X error while fetching XSettings data; ignored") return None
def xsettings_owner(self): owner_x = X11Window.XGetSelectionOwner(self._selection) log("XGetSelectionOwner(%s)=%s", self._selection, owner_x) if owner_x == XNone: return None try: return trap.call_synced(get_pywindow, self._clipboard, owner_x) except XError: log("X error while fetching owner of XSettings data; ignored") return None
def apply_xmodmap(instructions): try: unset = trap.call_synced(X11Keyboard.set_xmodmap, instructions) except: log.error("apply_xmodmap", exc_info=True) unset = instructions if unset is None: #None means an X11 error occurred, re-do all: unset = instructions return unset
def do_set_workspace(self, workspace): assert HAS_X11_BINDINGS root = self.gdk_window().get_screen().get_root_window() ndesktops = self.xget_u32_property(root, "_NET_NUMBER_OF_DESKTOPS") self.debug("set_workspace() ndesktops=%s", ndesktops) if ndesktops is None or ndesktops <= 1: return -1 workspace = max(0, min(ndesktops - 1, workspace)) event_mask = const["SubstructureNotifyMask"] | const[ "SubstructureRedirectMask"] def send(): root_window = get_xwindow(root) window = get_xwindow(self.gdk_window()) X11WindowBindings.sendClientMessage(root_window, window, False, event_mask, "_NET_WM_DESKTOP", workspace, const["CurrentTime"], 0, 0, 0) trap.call_synced(send) return workspace
def set_workspace(self): if not HAS_X11_BINDINGS or not self._been_mapped: return -1 root = self.gdk_window().get_screen().get_root_window() ndesktops = self.xget_u32_property(root, "_NET_NUMBER_OF_DESKTOPS") workspacelog("%s.set_workspace() workspace=%s ndesktops=%s", self, self._window_workspace, ndesktops) if ndesktops is None or ndesktops <= 1: return -1 workspace = max(0, min(ndesktops - 1, self._window_workspace)) event_mask = SubstructureNotifyMask | SubstructureRedirectMask def send(): root_xid = root.xid xwin = self.gdk_window().xid X11Window.sendClientMessage(root_xid, xwin, False, event_mask, "_NET_WM_DESKTOP", workspace, CurrentTime, 0, 0, 0) trap.call_synced(send) return workspace
def do_xpra_client_message_event(self, event): if event.message_type=="_NET_SYSTEM_TRAY_OPCODE" and event.window==self.tray_window and event.format==32: opcode = event.data[1] SYSTEM_TRAY_REQUEST_DOCK = 0 SYSTEM_TRAY_BEGIN_MESSAGE = 1 SYSTEM_TRAY_CANCEL_MESSAGE = 2 if opcode==SYSTEM_TRAY_REQUEST_DOCK: xid = event.data[2] trap.call_synced(self.dock_tray, xid) elif opcode==SYSTEM_TRAY_BEGIN_MESSAGE: timeout = event.data[2] mlen = event.data[3] mid = event.data[4] log.info("tray begin message timeout=%s, mlen=%s, mid=%s - not handled yet!", timeout, mlen, mid) elif opcode==SYSTEM_TRAY_CANCEL_MESSAGE: mid = event.data[2] log.info("tray cancel message for mid=%s - not handled yet!", mid) elif event.message_type=="_NET_SYSTEM_TRAY_MESSAGE_DATA": assert event.format==8 log.info("tray message data - not handled yet!") else: log.info("do_xpra_client_message_event(%s)", event)
def set_windows_cursor(self, gtkwindows, new_cursor): cursor = None if len(new_cursor)>0: cursor = None if len(new_cursor)>=9 and cursor_names: cursor_name = new_cursor[8] if cursor_name: gdk_cursor = cursor_names.get(cursor_name.upper()) if gdk_cursor is not None: try: from xpra.x11.gtk_x11.error import trap log("setting new cursor: %s=%s", cursor_name, gdk_cursor) cursor = trap.call_synced(gdk.Cursor, gdk_cursor) except: pass if cursor is None: w, h, xhot, yhot, serial, pixels = new_cursor[2:8] log("new cursor at %s,%s with serial=%s, dimensions: %sx%s, len(pixels)=%s" % (xhot,yhot, serial, w,h, len(pixels))) pixbuf = gdk.pixbuf_new_from_data(pixels, gdk.COLORSPACE_RGB, True, 8, w, h, w * 4) x = max(0, min(xhot, w-1)) y = max(0, min(yhot, h-1)) size = gdk.display_get_default().get_default_cursor_size() if size>0 and (size<w or size<h): ratio = float(max(w,h))/size pixbuf = pixbuf.scale_simple(int(w/ratio), int(h/ratio), gdk.INTERP_BILINEAR) x = int(x/ratio) y = int(y/ratio) cursor = gdk.Cursor(gdk.display_get_default(), pixbuf, x, y) for gtkwindow in gtkwindows: if gtk.gtk_version>=(2,14): gdkwin = gtkwindow.get_window() else: gdkwin = gtkwindow.window #trays don't have a gdk window if gdkwin: gdkwin.set_cursor(cursor)
def acquire(self, when): old_owner = self._owner() if when is self.IF_UNOWNED and old_owner != XNone: raise AlreadyOwned self.clipboard.set_with_data([("VERSION", 0, 0)], self._get, self._clear, None) # Having acquired the selection, we have to announce our existence # (ICCCM 2.8, still). The details here probably don't matter too # much; I've never heard of an app that cares about these messages, # and metacity actually gets the format wrong in several ways (no # MANAGER or owner_window atoms). But might as well get it as right # as possible. # To announce our existence, we need: # -- the timestamp we arrived at # -- the manager selection atom # -- the window that registered the selection # Of course, because Gtk is doing so much magic for us, we have to do # some weird tricks to get at these. # Ask ourselves when we acquired the selection: ts_data = self.clipboard.wait_for_contents("TIMESTAMP").data #data is a timestamp, X11 datatype is Time which is CARD32, #(which is 64 bits on 64-bit systems!) Lsize = calcsize("@L") if len(ts_data)==Lsize: ts_num = unpack("@L", ts_data[:Lsize])[0] else: ts_num = 0 #CurrentTime log.warn("invalid data for 'TIMESTAMP': %s", ([hex(ord(x)) for x in ts_data])) # Calculate the X atom for this selection: selection_xatom = get_xatom(self.atom) # Ask X what window we used: self._xwindow = X11Window.XGetSelectionOwner(self.atom) root = self.clipboard.get_display().get_default_screen().get_root_window() X11Window.sendClientMessage(root.xid, root.xid, False, StructureNotifyMask, "MANAGER", ts_num, selection_xatom, self._xwindow, 0, 0) if old_owner != XNone and when is self.FORCE: # Block in a recursive mainloop until the previous owner has # cleared out. def getwin(): window = get_pywindow(self.clipboard, old_owner) window.set_events(window.get_events() | gtk.gdk.STRUCTURE_MASK) return window try: window = trap.call_synced(getwin) log("got window") except XError: log("Previous owner is already gone, not blocking") else: log("Waiting for previous owner to exit...") add_event_receiver(window, self) gtk.main() log("...they did.") window = get_pywindow(self.clipboard, self._xwindow) window.set_title("Xpra-ManagerSelection")
def do_make_screenshot_packet(self): debug = log.debug debug("grabbing screenshot") regions = [] OR_regions = [] for wid in reversed(sorted(self._id_to_window.keys())): window = self._id_to_window.get(wid) debug("screenshot: window(%s)=%s", wid, window) if window is None: continue if window.is_tray(): debug("screenshot: skipping tray window %s", wid) continue if not window.is_managed(): debug("screenshot: window %s is not/no longer managed", wid) continue if window.is_OR(): x, y = window.get_property("geometry")[:2] else: x, y = self._desktop_manager.window_geometry(window)[:2] debug("screenshot: position(%s)=%s,%s", window, x, y) w, h = window.get_dimensions() debug("screenshot: size(%s)=%sx%s", window, w, h) try: img = trap.call_synced(window.get_image, 0, 0, w, h) except: log.warn("screenshot: window %s could not be captured", wid) continue if img is None: log.warn("screenshot: no pixels for window %s", wid) continue debug("screenshot: image=%s, size=%s", (img, img.get_size())) if img.get_pixel_format() not in ("RGB", "RGBA", "XRGB", "BGRX", "ARGB", "BGRA"): log.warn("window pixels for window %s using an unexpected rgb format: %s", wid, img.get_pixel_format()) continue item = (wid, x, y, img) if window.is_OR(): OR_regions.append(item) elif self._has_focus==wid: #window with focus first (drawn last) regions.insert(0, item) else: regions.append(item) all_regions = OR_regions+regions if len(all_regions)==0: debug("screenshot: no regions found, returning empty 0x0 image!") return ["screenshot", 0, 0, "png", -1, ""] debug("screenshot: found regions=%s, OR_regions=%s", len(regions), len(OR_regions)) #in theory, we could run the rest in a non-UI thread since we're done with GTK.. minx = min([x for (_,x,_,_) in all_regions]) miny = min([y for (_,_,y,_) in all_regions]) maxx = max([(x+img.get_width()) for (_,x,_,img) in all_regions]) maxy = max([(y+img.get_height()) for (_,_,y,img) in all_regions]) width = maxx-minx height = maxy-miny debug("screenshot: %sx%s, min x=%s y=%s", width, height, minx, miny) from PIL import Image #@UnresolvedImport screenshot = Image.new("RGBA", (width, height)) for wid, x, y, img in reversed(all_regions): pixel_format = img.get_pixel_format() target_format = { "XRGB" : "RGB", "BGRX" : "RGB", "BGRA" : "RGBA"}.get(pixel_format, pixel_format) try: window_image = Image.frombuffer(target_format, (w, h), img.get_pixels(), "raw", pixel_format, img.get_rowstride()) except: log.warn("failed to parse window pixels in %s format", pixel_format) continue tx = x-minx ty = y-miny screenshot.paste(window_image, (tx, ty)) buf = StringIOClass() screenshot.save(buf, "png") data = buf.getvalue() buf.close() packet = ["screenshot", width, height, "png", width*4, Compressed("png", data)] debug("screenshot: %sx%s %s", packet[1], packet[2], packet[-1]) return packet
def _setup_listening(self): try: trap.call_synced(self.do_setup_listening) except Exception, e: log("PointerGrabHelper._setup_listening() failed: %s", e)
def _manage_client(self, gdkwindow): try: if gdkwindow not in self._windows: trap.call_synced(self.do_manage_client, gdkwindow) except Exception, e: log("failed to manage client %s: %s", gdkwindow, e)
def prop_set(target, key, etype, value): trap.call_synced(X11Window.XChangeProperty, get_xwindow(target), key, _prop_encode(target, etype, value))
def fake_key(self, keycode, press): keylog("fake_key(%s, %s)", keycode, press) trap.call_synced(X11Keyboard.xtest_fake_key, keycode, press)