diff --git a/src/xpra/client/ui_client_base.py b/src/xpra/client/ui_client_base.py index d067b22e84..16d2b0a8d8 100644 --- a/src/xpra/client/ui_client_base.py +++ b/src/xpra/client/ui_client_base.py @@ -158,6 +158,7 @@ def __init__(self): self.opengl_props = {} self.toggle_cursors_bell_notify = False self.toggle_keyboard_sync = False + self.force_ungrab = False self.window_unmap = False self.window_refresh_config = False self.server_generic_encodings = False @@ -1059,6 +1060,7 @@ def parse_server_capabilities(self, c): self.session_name = c.strget("session_name", "") set_application_name(self.session_name or "Xpra") self.window_unmap = c.boolget("window_unmap") + self.force_ungrab = c.boolget("force_ungrab") self.window_refresh_config = c.boolget("window_refresh_config") self.suspend_resume = c.boolget("suspend-resume") self.server_supports_notifications = c.boolget("notifications") @@ -1521,6 +1523,9 @@ def send_cursors_enabled(self): assert self.toggle_cursors_bell_notify, "cannot toggle cursors: server lacks the feature" self.send("set-cursors", self.cursors_enabled) + def send_force_ungrab(self, wid): + assert self.force_ungrab + self.send("force-ungrab", wid) def set_deflate_level(self, level): self.compression_level = level diff --git a/src/xpra/platform/win32/gui.py b/src/xpra/platform/win32/gui.py index 7f41b45f18..ba7a70f725 100644 --- a/src/xpra/platform/win32/gui.py +++ b/src/xpra/platform/win32/gui.py @@ -9,6 +9,7 @@ import os from xpra.log import Logger log = Logger("win32") +grablog = Logger("win32", "grab") from xpra.platform.win32.win32_events import get_win32_event_listener from xpra.util import AdHocStruct @@ -80,7 +81,7 @@ def activateapp(self, wParam, lParam): if wParam==0 and self.client: #our app has lost focus wid = self.client.window_with_grab - log("window with grab=%s, UNGRAB_KEY=%s", wid, UNGRAB_KEY) + grablog("window with grab=%s, UNGRAB_KEY=%s", wid, UNGRAB_KEY) if wid is not None and UNGRAB_KEY: self.force_ungrab(wid) @@ -95,22 +96,28 @@ def power_broadcast_event(self, wParam, lParam): self.client.resume() def force_ungrab(self, wid): + grablog("force_ungrab(%s) server supports force ungrab: %s", wid, self.client.force_ungrab) + if self.client.force_ungrab: + #ungrab via dedicated server packet: + self.client.send_force_ungrab(wid) + return + #fallback: try to find a key to press: kh = self.client.keyboard_helper if not kh: if not self._kh_warning: self._kh_warning = True - log.warn("no keyboard support, cannot simulate keypress to lose grab!") + grablog.warn("no keyboard support, cannot simulate keypress to lose grab!") return #xkbmap_keycodes is a list of: (keyval, name, keycode, group, level) ungrab_keys = [x for x in kh.xkbmap_keycodes if x[1]==UNGRAB_KEY] if len(ungrab_keys)==0: if not self._kh_warning: self._kh_warning = True - log.warn("ungrab key %s not found, cannot simulate keypress to lose grab!", UNGRAB_KEY) + grablog.warn("ungrab key %s not found, cannot simulate keypress to lose grab!", UNGRAB_KEY) return #ungrab_keys.append((65307, "Escape", 27, 0, 0)) #ugly hardcoded default value ungrab_key = ungrab_keys[0] - log("lost focus whilst window %s has grab, simulating keypress: %s", wid, ungrab_key) + grablog("lost focus whilst window %s has grab, simulating keypress: %s", wid, ungrab_key) key_event = AdHocStruct() key_event.keyname = ungrab_key[1] key_event.pressed = True diff --git a/src/xpra/x11/bindings/core_bindings.pyx b/src/xpra/x11/bindings/core_bindings.pyx index 15d2dfe56c..52813db7cc 100644 --- a/src/xpra/x11/bindings/core_bindings.pyx +++ b/src/xpra/x11/bindings/core_bindings.pyx @@ -12,6 +12,8 @@ from xpra.log import Logger log = Logger("x11", "bindings", "core") +include "constants.pxi" + ################################### # Headers, python magic ################################### @@ -36,6 +38,7 @@ cdef extern from "X11/Xlib.h": # over and over again, here are some convenience typedefs. (Yes, CARD32 # really is 64 bits on 64-bit systems. Why? I have no idea.) ctypedef CARD32 XID + ctypedef CARD32 Time ctypedef int Bool ctypedef int Status @@ -47,6 +50,9 @@ cdef extern from "X11/Xlib.h": void XGetErrorText(Display * display, int code, char * buffer_return, int length) + int XUngrabKeyboard(Display * display, Time time) + int XUngrabPointer(Display * display, Time time) + from display_source cimport get_display from display_source import get_display_name @@ -77,3 +83,9 @@ cdef class X11CoreBindings: cdef char[128] buffer XGetErrorText(self.display, code, buffer, 128) return str(buffer[:128]) + + def UngrabKeyboard(self, time=CurrentTime): + return XUngrabKeyboard(self.display, time) + + def UngrabPointer(self, time=CurrentTime): + return XUngrabPointer(self.display, time) diff --git a/src/xpra/x11/x11_server_base.py b/src/xpra/x11/x11_server_base.py index 07a44b178b..c6a0ee8e3d 100644 --- a/src/xpra/x11/x11_server_base.py +++ b/src/xpra/x11/x11_server_base.py @@ -116,6 +116,11 @@ def init_keyboard(self): clean_keyboard_state() + def init_packet_handlers(self): + GTKServerBase.init_packet_handlers(self) + self._authenticated_ui_packet_handlers["force-ungrab"] = self._process_force_ungrab + + def get_uuid(self): return get_uuid() @@ -139,6 +144,7 @@ def set_keyboard_repeat(self, key_repeat): def make_hello(self): capabilities = GTKServerBase.make_hello(self) capabilities["resize_screen"] = self.randr + capabilities["force_ungrab"] = True return capabilities def do_get_info(self, proto, server_sources, window_ids): @@ -425,6 +431,14 @@ def update_server_settings(self, settings, reset=False): log("ignoring server settings update in %s", self) + def _process_force_ungrab(self, proto, packet): + #ignore the window id: wid = packet[1] + def X11_ungrab(): + X11Core.UngrabKeyboard() + X11Core.UngrabPointer() + trap.call_synced(X11_ungrab) + + def fake_key(self, keycode, press): keylog("fake_key(%s, %s)", keycode, press) trap.call_synced(X11Keyboard.xtest_fake_key, keycode, press)