def client_startup_complete(self, ss): super().client_startup_complete(ss) log("is_Wayland()=%s", is_Wayland()) if is_Wayland(): ss.may_notify(XPRA_SHADOWWAYLAND_NOTIFICATION_ID, "Wayland Shadow Server", "This shadow session is running under wayland,\n" + "the screen scraping will probably come up empty", icon_name="unticked")
def do_get_keymap_modifiers(self): if not self.keyboard_bindings: log.warn("keyboard bindings are not available") log.warn(" expect keyboard mapping problems") if is_Wayland(): log.warn(" (incomplete wayland support)") return {}, [], [] try: from xpra.gtk_common.error import xsync with xsync: mod_mappings = self.keyboard_bindings.get_modifier_mappings() if mod_mappings: #ie: {"shift" : ["Shift_L", "Shift_R"], "mod1" : "Meta_L", ...]} log("modifier mappings=%s", mod_mappings) meanings = {} for modifier, keys in mod_mappings.items(): for _, keyname in keys: meanings[keyname] = modifier #probably a GTK bug? but easier to put here mod_missing = [] numlock_mod = meanings.get("Num_Lock", []) if numlock_mod: mod_missing.append(numlock_mod) return meanings, [], mod_missing except Exception: log.error("failed to use native get_modifier_mappings", exc_info=True) return {}, [], []
def make_hello(self): caps = XpraClientBase.make_hello(self) caps["session-type"] = get_session_type() #don't try to find the server uuid if this platform cannot run servers.. #(doing so causes lockups on win32 and startup errors on osx) if POSIX and not is_Wayland(): #we may be running inside another server! try: from xpra.server.server_uuid import get_uuid caps["server_uuid"] = get_uuid() or "" except: pass for x in (#generic feature flags: "notify-startup-complete", "wants_events", "setting-change", "xdg-menu-update", ): caps[x] = True #FIXME: the messy bits without proper namespace: caps.update({ #generic server flags: "share" : self.client_supports_sharing, "lock" : self.client_lock, }) caps.update(self.get_keyboard_caps()) for c in CLIENT_BASES: caps.update(c.get_caps(self)) def u(prefix, c): updict(caps, prefix, c, flatten_dicts=False) u("control_commands", self.get_control_commands_caps()) u("platform", get_platform_info()) u("opengl", self.opengl_props) return caps
def _get_xresources(): if not is_Wayland(): try: from xpra.x11.gtk_x11.prop import prop_get from xpra.gtk_common.gtk_util import get_default_root_window root = get_default_root_window() value = prop_get(root, "RESOURCE_MANAGER", "latin1", ignore_errors=True) log("RESOURCE_MANAGER=%s", value) if value is None: return None #parse the resources into a dict: values = {} options = value.split("\n") for option in options: if not option: continue parts = option.split(":\t", 1) if len(parts) != 2: log("skipped invalid option: '%s'", option) continue values[parts[0]] = parts[1] return values except Exception as e: log("_get_xresources error: %s", e) return None
def __init__(self, xwindow): self.xshm = None self.xwindow = xwindow assert USE_XSHM and XImage.has_XShm(), "no XShm support" if is_Wayland(): log.warn("Warning: shadow servers do not support Wayland") log.warn(" please switch to X11 for shadow support")
def get_workarea(): if not is_Wayland(): try: d = get_current_desktop() if d < 0: return None workarea = _get_X11_root_property("_NET_WORKAREA", "CARDINAL") if not workarea: return None screenlog("get_workarea() _NET_WORKAREA=%s (%s), len=%s", ellipsizer(workarea), type(workarea), len(workarea)) #workarea comes as a list of 4 CARDINAL dimensions (x,y,w,h), one for each desktop sizeof_long = struct.calcsize(b"@L") if len(workarea) < (d + 1) * 4 * sizeof_long: screenlog.warn("get_workarea() invalid _NET_WORKAREA value") else: cur_workarea = workarea[d * 4 * sizeof_long:(d + 1) * 4 * sizeof_long] v = struct.unpack(b"@LLLL", cur_workarea) screenlog("get_workarea() %s=%s", hexstr(cur_workarea), v) return v except Exception as e: screenlog("get_workarea()", exc_info=True) screenlog.warn("Warning: failed to query workarea: %s", e) return None
def get_icc_info(): if not is_Wayland(): try: data = _get_X11_root_property("_ICC_PROFILE", "CARDINAL") if data: screenlog("_ICC_PROFILE=%s (%s)", type(data), len(data)) version = _get_X11_root_property("_ICC_PROFILE_IN_X_VERSION", "CARDINAL") screenlog( "get_icc_info() found _ICC_PROFILE_IN_X_VERSION=%s, _ICC_PROFILE=%s", hexstr(version or ""), hexstr(data)) icc = { "source": "_ICC_PROFILE", "data": data, } if version: try: version = ord(version) except TypeError: pass icc["version"] = version screenlog("get_icc_info()=%s", icc) return icc except Exception as e: screenlog.error( "Error: cannot access _ICC_PROFILE X11 window property") screenlog.error(" %s", e) screenlog("get_icc_info()", exc_info=True) from xpra.platform.gui import default_get_icc_info return default_get_icc_info()
def verify_capture(self, ss): #verify capture works: log("verify_capture(%s)", ss) try: capture = GTKImageCapture(self.root) bdata = capture.take_screenshot()[-1] nid = XPRA_DISPLAY_NOTIFICATION_ID title = body = "" if any(b != 0 for b in bdata): log("verify_capture(%s) succeeded", ss) if is_Wayland(): title = "Wayland Session Warning" body = "Wayland sessions are not supported,\n"+\ "the screen capture is likely to be empty" else: log.warn("Warning: shadow screen capture is blank") body = "The shadow display capture is blank" if get_loaded_kernel_modules("vboxguest", "vboxvideo"): body += "\nthis may be caused by the VirtualBox video driver." title = "Shadow Capture Failure" log("verify_capture: title=%r, body=%r", ss, title, body) if title and body: ss.may_notify(nid, title, body, icon_name="server") except Exception as e: ss.may_notify(nid, "Shadow Error", "Error shadowing the display:\n%s" % e, icon_name="bugs")
def do_selection_get(self, selection_data, info, time): # Either call selection_data.set() or don't, and then return. # In practice, send a call across the wire, then block in a recursive # main loop. def nodata(): selectiondata_set(selection_data, "STRING", 8, "") if not self._enabled or not self._can_receive: nodata() return if not self._have_token: try: return gtk.Invisible.do_selection_get(self, selection_data, info, time) except Exception as e: log("gtk.Invisible.do_selection_get", exc_info=True) if first_time("selection-%s-not-implemented" % self._selection): log.warn("Warning: limited clipboard support for %s", self._selection) if is_Wayland(): log.warn( " looks like a Wayland implementation limitation") log.warn(" %s", e) nodata() return selection = selectiondata_get_selection(selection_data) target = selectiondata_get_target(selection_data) log("do_selection_get(%s, %s, %s) selection=%s", selection_data, info, time, selection) self._selection_get_events += 1 assert str(selection) == self._selection, "expected %s but got %s" % ( selection, self._selection) self._request_contents_events += 1 result = self.emit("get-clipboard-from-remote", self._selection, target) if result is None or result["type"] is None: log("remote selection fetch timed out or empty") nodata() return data = result["data"] dformat = result["format"] dtype = result["type"] if dtype: dtype = bytestostr(dtype) log( "do_selection_get(%s,%s,%s) calling selection_data.set(%s, %s, %s:%s)", selection_data, info, time, dtype, dformat, type(data), len(data or "")) boc = self._block_owner_change self._block_owner_change = True if is_gtk3() and dtype in ("UTF8_STRING", "STRING") and dformat == 8: #GTK3 workaround: can only use set_text and only on the clipboard? s = bytestostr(data) self._clipboard.set_text(s, len(s)) else: selectiondata_set(selection_data, dtype, dformat, data) if boc is False: glib.idle_add(self.remove_block)
def populate_form(self): btn = link_btn( "https://github.com/Xpra-org/xpra/blob/master/docs/Features/README.md", label="Open Features Documentation", icon_name=None) self.vbox.pack_start(btn, expand=True, fill=False, padding=20) tb = self.table() self.bool_cb(tb, "Splash Screen", "splash", "Show a splash screen during startup") self.bool_cb(tb, "Read only", "readonly", "Mouse and keyboard events will be ignored") self.radio_cb( tb, "Border", "border", "Show a colored border around xpra windows to differentiate them", None, { "auto": ("auto,5:off", "auto"), "none": FALSE_OPTIONS, "blue": ("blue", ), "red": ("red", ), "green": ("green", ) }) self.radio_cb( tb, "Header Bar", "headerbar", None, None, { "auto": ["auto"] + list(TRUE_OPTIONS), "no": FALSE_OPTIONS, "force": ("force", ), }) self.sep(tb) #"https://github.com/Xpra-org/xpra/blob/master/docs/Features/Notifications.md") self.bool_cb(tb, "Xpra's System Tray", "tray") self.bool_cb(tb, "Forward System Trays", "system-tray") self.bool_cb(tb, "Notifications", "notifications") #"https://github.com/Xpra-org/xpra/blob/master/docs/Features/System-Tray.md") #self.bool_cb(tb, "Cursors", "cursors") #self.bool_cb(tb, "Bell", "bell") self.bool_cb(tb, "Modal Windows", "modal-windows") self.sep(tb) #"https://github.com/Xpra-org/xpra/blob/master/docs/Features/Image-Depth.md") self.combo( tb, "Mouse Wheel", "mousewheel", { "on": "on", "no": "disabled", "invert-x": "invert X axis", "invert-y": "invert Y axis", "invert-z": "invert Z axis", "invert-all": "invert all axes", }) self.combo( tb, "Clipboard", "clipboard-direction", { "both": "enabled", "to-server": "to server only", "to-client": "to client only", "disabled": "disabled", }) if POSIX and not OSX and not is_Wayland(): self.bool_cb(tb, "XSettings", "xsettings") self.vbox.show_all()
def __init__(self): super().__init__() if not is_Wayland(): try: from xpra.x11.bindings.keyboard_bindings import X11KeyboardBindings #@UnresolvedImport self.keyboard_bindings = X11KeyboardBindings() except Exception as e: log.error("Error: failed to load posix keyboard bindings") log.error(" %s", str(e) or type(e))
def client_toolkit(self) -> str: if POSIX and not OSX: backend = os.environ.get("GDK_BACKEND", "") if not backend and is_Wayland(): backend = "Wayland" if backend: #capitalize, ie: "x11" -> "X11" backend = backend[0].upper() + backend[1:] return "GTK3 %s" % backend return "GTK3"
def _get_xsettings_dict(): d = {} if is_Wayland(): return d v = _get_xsettings() if v: _, values = v for setting_type, prop_name, value, _ in values: d[bytestostr(prop_name)] = (setting_type, value) return d
def make_window(): window = Gtk.Window(type=Gtk.WindowType.TOPLEVEL) window.set_title("Window Focus") window.set_size_request(640, 200) window.connect("delete_event", Gtk.main_quit) vbox = Gtk.VBox() N = 8 labels = [] font = Pango.FontDescription("sans 12") for _ in range(N): l = Gtk.Label() l.modify_font(font) labels.append(l) for l in labels: al = Gtk.Alignment(xalign=0, yalign=0.5, xscale=0.0, yscale=0) al.add(l) vbox.add(al) window.add(vbox) window.show_all() text = deque(maxlen=N) def update(s): text.append("%s: %s" % (datetime.now(), s)) for i, t in enumerate(text): labels[i].set_text(t) #self.selectX11FocusChange(self) def focus_in(_window, event): update("focus-in-event") def focus_out(_window, event): update("focus-out-event") def has_toplevel_focus(window, _event): update("has-toplevel-focus: %s" % window.has_toplevel_focus()) window.connect("focus-in-event", focus_in) window.connect("focus-out-event", focus_out) window.connect("notify::has-toplevel-focus", has_toplevel_focus) if POSIX and not OSX: from xpra.gtk_common.error import xlog from xpra.x11.gtk_x11.gdk_display_source import init_gdk_display_source from xpra.x11.gtk_x11.gdk_bindings import init_x11_filter from xpra.x11.bindings.window_bindings import X11WindowBindings #pylint: disable=no-name-in-module from xpra.os_util import is_Wayland if not is_Wayland(): #x11 focus events: gdk_win = window.get_window() xid = gdk_win.get_xid() init_gdk_display_source() os.environ["XPRA_X11_DEBUG_EVENTS"] = "FocusIn,FocusOut" init_x11_filter() with xlog: X11WindowBindings().selectFocusChange(xid) return window
def get_current_desktop(): v = -1 if not is_Wayland(): d = None try: d = _get_X11_root_property("_NET_CURRENT_DESKTOP", "CARDINAL") if d: v = struct.unpack(b"@L", d)[0] except Exception as e: log.warn("failed to get current desktop: %s", e) log("get_current_desktop() %s=%s", hexstr(d or ""), v) return v
def get_number_of_desktops(): v = 0 if not is_Wayland(): d = None try: d = _get_X11_root_property("_NET_NUMBER_OF_DESKTOPS", "CARDINAL") if d: v = struct.unpack(b"@L", d)[0] except Exception as e: screenlog.warn("failed to get number of desktop: %s", e) v = max(1, v) screenlog("get_number_of_desktops() %s=%s", hexstr(d or ""), v) return v
def get_vrefresh(): v = -1 if not is_Wayland(): try: from xpra.x11.bindings.randr_bindings import RandRBindings #@UnresolvedImport randr = RandRBindings() v = randr.get_vrefresh() except Exception as e: log("get_vrefresh()", exc_info=True) log.warn("Warning: failed to query the display vertical refresh rate:") log.warn(" %s", e) screenlog("get_vrefresh()=%s", v) return v
def _get_randr_dpi(): if RANDR_DPI and not is_Wayland(): from xpra.gtk_common.error import xlog from xpra.x11.bindings.randr_bindings import RandRBindings #@UnresolvedImport with xlog: randr_bindings = RandRBindings() wmm, hmm = randr_bindings.get_screen_size_mm() w, h = randr_bindings.get_screen_size() dpix = iround(w * 25.4 / wmm) dpiy = iround(h * 25.4 / hmm) screenlog("xdpi=%s, ydpi=%s - size-mm=%ix%i, size=%ix%i", dpix, dpiy, wmm, hmm, w, h) return dpix, dpiy return -1, -1
def _get_randr_dpi(): if RANDR_DPI and not is_Wayland(): from xpra.gtk_common.error import xlog with xlog: randr_bindings = X11RandRBindings() if randr_bindings and randr_bindings.has_randr(): wmm, hmm = randr_bindings.get_screen_size_mm() if wmm > 0 and hmm > 0: w, h = randr_bindings.get_screen_size() dpix = iround(w * 25.4 / wmm) dpiy = iround(h * 25.4 / hmm) screenlog("xdpi=%s, ydpi=%s - size-mm=%ix%i, size=%ix%i", dpix, dpiy, wmm, hmm, w, h) return dpix, dpiy return -1, -1
def get_desktop_names(): v = [] if not is_Wayland(): v = ["Main"] d = None try: d = _get_X11_root_property("_NET_DESKTOP_NAMES", "UTF8_STRING") if d: v = d.split(b"\0") if len(v) > 1 and v[-1] == "": v = v[:-1] except Exception as e: screenlog.warn("failed to get desktop names: %s", e) screenlog("get_desktop_names() %s=%s", hexstr(d or ""), v) return v
def _get_randr_dpi(): if RANDR_DPI and not is_Wayland(): try: from xpra.gtk_common.error import xsync from xpra.x11.bindings.randr_bindings import RandRBindings #@UnresolvedImport with xsync: randr_bindings = RandRBindings() wmm, hmm = randr_bindings.get_screen_size_mm() w, h = randr_bindings.get_screen_size() dpix = iround(w * 25.4 / wmm) dpiy = iround(h * 25.4 / hmm) screenlog("xdpi=%s, ydpi=%s - size-mm=%ix%i, size=%ix%i", dpix, dpiy, wmm, hmm, w, h) return dpix, dpiy except Exception as e: screenlog.warn("failed to get dpi: %s", e) return -1, -1
def process_ui_capabilities(self, caps : typedict): log("process_ui_capabilities() clipboard_enabled=%s", self.clipboard_enabled) if self.clipboard_enabled: ch = self.make_clipboard_helper() if not ch: log.warn("Warning: no clipboard support") if is_Wayland(): log.warn(" (wayland display)") self.clipboard_helper = ch self.clipboard_enabled = ch is not None log("clipboard helper=%s", ch) if self.clipboard_enabled: #tell the server about which selections we really want to sync with #(could have been translated, or limited if the client only has one, etc) self.send_clipboard_selections(ch.get_remote_selections()) ch.send_all_tokens() #ui may want to know this is now set: self.emit("clipboard-toggled")
def get_keymap_spec(self): log("get_keymap_spec() keyboard_bindings=%s", self.keyboard_bindings) if is_Wayland() or not self.keyboard_bindings: locale = self.get_locale_status() query_struct = {} if locale: layout = locale.get("X11 Layout") if layout: query_struct["layout"] = layout log("query_struct(%s)=%s", locale, query_struct) return None, None, query_struct query_struct = self.keyboard_bindings.getXkbProperties() _query = xkbmap_query_tostring(query_struct) log("get_keymap_spec() Xkb query tostring(%s)=%s", query_struct, _query) #we no longer support servers via xkbmap_print: xkbmap_print = "" log("get_keymap_spec()=(%s, %s, %s)", nonl(xkbmap_print), nonl(_query), nonl(query_struct)) return xkbmap_print, _query, query_struct
def after_draw_refresh(self, success, message=""): plog("after_draw_refresh(%s, %s) pending_refresh=%s", success, message, self.pending_refresh) backing = self._backing if not backing: return if backing.repaint_all or self._xscale != 1 or self._yscale != 1 or is_Wayland( ): #easy: just repaint the whole window: rw, rh = self.get_size() self.idle_add(self.repaint, 0, 0, rw, rh) return pr = self.pending_refresh self.pending_refresh = [] for x, y, w, h in pr: rx, ry, rw, rh = self._client.srect(x, y, w, h) if self.window_offset: rx += self.window_offset[0] ry += self.window_offset[1] self.idle_add(self.repaint, rx, ry, rw, rh)
def get_workarea(): if not is_Wayland(): try: d = get_current_desktop() if d<0: return None workarea = _get_X11_root_property("_NET_WORKAREA", "CARDINAL") if not workarea: return None screenlog("get_workarea()=%s, len=%s", type(workarea), len(workarea)) #workarea comes as a list of 4 CARDINAL dimensions (x,y,w,h), one for each desktop if len(workarea)<(d+1)*4*4: screenlog.warn("get_workarea() invalid _NET_WORKAREA value") else: cur_workarea = workarea[d*4*4:(d+1)*4*4] v = struct.unpack(b"=IIII", cur_workarea) screenlog("get_workarea() %s=%s", hexstr(cur_workarea), v) return v except Exception as e: screenlog.warn("failed to get workarea: %s", e) return None
def make_hello(self): caps = XpraClientBase.make_hello(self) caps["session-type"] = get_session_type() #don't try to find the server uuid if this platform cannot run servers.. #(doing so causes lockups on win32 and startup errors on osx) if POSIX and not is_Wayland(): #we may be running inside another server! try: from xpra.server.server_uuid import get_uuid, get_mode #pylint: disable=import-outside-toplevel if get_mode() != "shadow": caps["server_uuid"] = get_uuid() or "" except ImportError: pass for x in ( #generic feature flags: "wants_events", "setting-change", "xdg-menu-update", ): caps[x] = True caps.update({ #generic server flags: "share": self.client_supports_sharing, "lock": self.client_lock, "xdg-menu": self.start_new_commands, }) caps.update({"mouse": True}) caps.update(self.get_keyboard_caps()) for c in CLIENT_BASES: caps.update(c.get_caps(self)) def u(prefix, c): updict(caps, prefix, c, flatten_dicts=False) u("control_commands", self.get_control_commands_caps()) u("platform", get_platform_info()) u("opengl", self.opengl_props) return caps
def setup_xprops(self): #wait for handshake to complete: if not is_Wayland(): self.client.after_handshake(self.do_setup_xprops)
def gl_check(): if not is_X11() and is_Wayland(): return "disabled under wayland with GTK3 (buggy)" return None
def get_clipboard_native_class(): if is_Wayland(): return None return "xpra.x11.gtk_x11.clipboard.X11Clipboard"
def get_clipboard_native_class(): if is_Wayland(): return "xpra.gtk_common.gtk_clipboard.GTK_Clipboard" return "xpra.x11.gtk_x11.clipboard.X11Clipboard"