Skip to content

Commit

Permalink
#1995 use xid for damage, composite and event routing
Browse files Browse the repository at this point in the history
  • Loading branch information
totaam committed May 16, 2023
1 parent 1994b30 commit 1e457b6
Show file tree
Hide file tree
Showing 16 changed files with 147 additions and 123 deletions.
3 changes: 2 additions & 1 deletion xpra/client/gtk_base/gtk_client_window_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,8 @@ def do_init_focus(self):
log.warn(" you may experience window focus issues")
else:
grablog("adding event receiver so we can get FocusIn and FocusOut events whilst grabbing the keyboard")
add_event_receiver(self.get_window(), self)
xid = self.get_window().get_xid()
add_event_receiver(xid, self)
#other platforms should bet getting regular focus events instead:
def focus_in(_window, event):
focuslog("focus-in-event for wid=%s", self._id)
Expand Down
2 changes: 1 addition & 1 deletion xpra/x11/desktop/desktop_server_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def x11_init(self):
assert display.get_n_screens()==1
screen = display.get_screen(0)
root = screen.get_root_window()
add_event_receiver(root, self)
add_event_receiver(root.get_xid(), self)
add_catchall_receiver("xpra-motion-event", self)
add_catchall_receiver("xpra-xkb-event", self)
with xlog:
Expand Down
9 changes: 4 additions & 5 deletions xpra/x11/desktop/model_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,12 @@ def __init__(self):

def setup(self):
WindowDamageHandler.setup(self)
xid = self.client_window.get_xid()
self._depth = X11Window.get_depth(xid)
X11Window.addDefaultEvents(xid)
self._depth = X11Window.get_depth(self.xid)
X11Window.addDefaultEvents(self.xid)
self._managed = True
self._setup_done = True
#listen for property changes on the root window:
add_event_receiver(self.client_window, self)
add_event_receiver(self.xid, self)

def do_xpra_property_notify_event(self, event):
eventlog(f"do_xpra_property_notify_event: {event.atom}")
Expand All @@ -101,7 +100,7 @@ def do_xpra_property_notify_event(self, event):
return True

def unmanage(self, exiting=False):
remove_event_receiver(self.client_window, self)
remove_event_receiver(self.xid, self)
WindowDamageHandler.destroy(self)
WindowModelStub.unmanage(self, exiting)
self.cancel_resize_timer()
Expand Down
65 changes: 35 additions & 30 deletions xpra/x11/gtk3/gdk_bindings.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -263,9 +263,6 @@ cdef extern from "gtk-3.0/gdk/gdktypes.h":

# Basic utilities:

cdef int get_xwindow(pywindow):
return GDK_WINDOW_XID(<GdkWindow*>unwrap(pywindow, Gdk.Window))

def get_pywindow(xwindow):
display = Gdk.get_default_root_window().get_display()
return _get_pywindow(display, xwindow)
Expand Down Expand Up @@ -332,34 +329,40 @@ cdef _get_pyatom(display, int xatom):


# Children listing
cdef _query_tree(pywindow):
def get_children(xid : int):
cdef Window root = 0, parent = 0
cdef Window * children = <Window *> 0
cdef unsigned int i, nchildren = 0
if not XQueryTree(get_xdisplay_for(pywindow),
get_xwindow(pywindow),
display = Gdk.get_default_root_window().get_display()
cdef Display *xdisplay = get_xdisplay_for(display)
if not XQueryTree(xdisplay, xid,
&root, &parent, &children, &nchildren):
return (None, [])
cdef object pychildren = []
for i from 0 <= i < nchildren:
for i in range(nchildren):
#we cannot get the gdk window for wid=0
if children[i]>0:
pychildren.append(_get_pywindow(pywindow, children[i]))
pychildren.append(children[i])
# Apparently XQueryTree sometimes returns garbage in the 'children'
# pointer when 'nchildren' is 0, which then leads to a segfault when we
# try to XFree the non-NULL garbage.
if nchildren > 0 and children != NULL:
XFree(children)
cdef object pyparent = None
if parent != XNone:
pyparent = _get_pywindow(pywindow, parent)
return (pyparent, pychildren)

def get_children(pywindow):
return _query_tree(pywindow)[1]
return pychildren

def get_parent(pywindow):
return _query_tree(pywindow)[0]
def get_parent(xid : int) -> int:
cdef Window root = 0, parent = 0
cdef Window * children = <Window *> 0
cdef unsigned int i, nchildren = 0
display = Gdk.get_default_root_window().get_display()
cdef Display *xdisplay = get_xdisplay_for(display)
if not XQueryTree(xdisplay, xid, &root, &parent, &children, &nchildren):
return None
if nchildren > 0 and children != NULL:
XFree(children)
if parent==XNone:
return None
return int(parent)


###################################
Expand Down Expand Up @@ -429,32 +432,33 @@ cdef extern from "gtk-3.0/gdk/gdkevents.h":
# client that owns the window they are sent to, otherwise they go to any
# clients that are selecting for that mask they are sent with.

event_receivers_map = WeakKeyDictionary()
event_receivers_map : dict = dict()

def add_event_receiver(window, receiver, max_receivers=3):
receivers = event_receivers_map.get(window)
def add_event_receiver(xid:int, receiver:callable, max_receivers:int=3):
receivers = event_receivers_map.get(xid)
if receivers is None:
receivers = set()
event_receivers_map[window] = receivers
event_receivers_map[xid] = receivers
if max_receivers>0 and len(receivers)>max_receivers:
log.warn("Warning: already too many event receivers")
log.warn(" for window %s: %s, adding %s to %s", window, len(receivers), receiver, receivers)
log.warn(f" for window {xid:x}: {len(receivers)}")
log.warn(f" adding {receiver!r} to {receivers}")
traceback.print_stack()
receivers.add(receiver)

def remove_event_receiver(window, receiver):
receivers = event_receivers_map.get(window)
def remove_event_receiver(xid:int, receiver:callable):
receivers = event_receivers_map.get(xid)
if receivers is None:
return
receivers.discard(receiver)
if not receivers:
event_receivers_map.pop(window)
event_receivers_map.pop(xid)

#only used for debugging:
def get_event_receivers(window):
return event_receivers_map.get(window)
def get_event_receivers(xid:int):
return event_receivers_map.get(xid)

def cleanup_all_event_receivers():
def cleanup_all_event_receivers() -> None:
event_receivers_map.clear()


Expand Down Expand Up @@ -753,7 +757,8 @@ cdef _route_event(int etype, event, signal, parent_signal):
window = event.window
if DEBUG:
log.info(" delivering event to window itself: %#x (signal=%s)", window.get_xid(), signal)
handlers = get_event_receivers(window)
if window:
handlers = get_event_receivers(window.get_xid())
_maybe_send_event(DEBUG, handlers, signal, event, "window %#x" % window.get_xid())
elif DEBUG:
log.info(" received event on window itself but have no signal for that")
Expand All @@ -766,7 +771,7 @@ cdef _route_event(int etype, event, signal, parent_signal):
else:
if DEBUG:
log.info(" delivering event to parent window: %#x (signal=%s)", window.get_xid(), parent_signal)
handlers = get_event_receivers(window)
handlers = get_event_receivers(window.get_xid())
_maybe_send_event(DEBUG, handlers, parent_signal, event, "parent window %#x" % window.get_xid())
else:
if DEBUG:
Expand Down
4 changes: 2 additions & 2 deletions xpra/x11/gtk_x11/clipboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,13 @@ def init_window(self):
xid = self.window.get_xid()
with xsync:
X11Window.selectSelectionInput(xid)
add_event_receiver(self.window, self)
add_event_receiver(xid, self)

def cleanup_window(self):
w = self.window
if w:
self.window = None
remove_event_receiver(w, self)
remove_event_receiver(w.get_xid(), self)
w.destroy()

def cleanup(self):
Expand Down
38 changes: 18 additions & 20 deletions xpra/x11/gtk_x11/composite.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

from gi.repository import GObject
from gi.repository import GObject, Gdk

from xpra.x11.gtk_x11.window_damage import WindowDamageHandler
from xpra.gtk_common.gobject_util import one_arg_signal
Expand Down Expand Up @@ -37,8 +37,8 @@ class CompositeHelper(WindowDamageHandler, GObject.GObject):
})

# This may raise XError.
def __init__(self, window):
WindowDamageHandler.__init__(self, window)
def __init__(self, xid):
WindowDamageHandler.__init__(self, xid)
GObject.GObject.__init__(self)
self._listening_to = None

Expand All @@ -49,10 +49,10 @@ def setup(self):
X11Window.XCompositeRedirectWindow(self.xid)
WindowDamageHandler.setup(self)

def do_destroy(self, win):
def do_destroy(self):
with xlog:
X11Window.XCompositeUnredirectWindow(self.xid)
WindowDamageHandler.do_destroy(self, win)
WindowDamageHandler.do_destroy(self)

def invalidate_pixmap(self):
lt = self._listening_to
Expand All @@ -63,10 +63,10 @@ def invalidate_pixmap(self):

def _cleanup_listening(self, listening):
if listening:
# Don't want to stop listening to self.client_window!:
assert self.client_window is None or self.client_window not in listening
for w in listening:
remove_event_receiver(w, self)
# Don't want to stop listening to our xid!:
if w!=self.xid:
remove_event_receiver(w, self)

def _set_pixmap(self):
# The tricky part here is that the pixmap returned by
Expand All @@ -86,29 +86,27 @@ def _set_pixmap(self):
listening = []
e = None
try:
screen = self.client_window.get_screen()
if not screen:
log("cannot set pixmap on client window - maybe deleted?")
return
root = screen.get_root_window()
gdkworld = None
world = get_world_window()
gdkworld = None
if world:
gdkworld = world.get_window()
win = get_parent(self.client_window)
while win not in (None, root, gdkworld) and win.get_parent() is not None:
root = Gdk.Screen.get_default().get_root_window()
xid = get_parent(self.xid)
while xid not in (None, root, gdkworld):
# We have to use a lowlevel function to manipulate the
# event selection here, because SubstructureRedirectMask
# does not roundtrip through the GDK event mask
# functions. So if we used them, here, we would clobber
# corral window selection masks, and those don't deserve
# clobbering. They are our friends! X is driving me
# slowly mad.
xid = win.get_xid()
parent = get_parent(xid)
if not parent:
break
X11Window.addXSelectInput(xid, StructureNotifyMask)
add_event_receiver(win, self, max_receivers=-1)
listening.append(win)
win = get_parent(win)
add_event_receiver(xid, self, max_receivers=-1)
listening.append(xid)
xid = parent
handle = XImage.get_xcomposite_pixmap(self.xid)
except Exception:
try:
Expand Down
6 changes: 4 additions & 2 deletions xpra/x11/gtk_x11/selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def acquire(self, when):
log("Previous owner is already gone? not blocking", exc_info=True)
else:
log("Waiting for previous owner to exit...")
add_event_receiver(window, self)
add_event_receiver(window.get_xid(), self)
self.exit_timer = GLib.timeout_add(SELECTION_EXIT_TIMEOUT*1000, self.exit_timeout)
Gtk.main()
if self.exit_timer:
Expand Down Expand Up @@ -171,7 +171,9 @@ def _owner_change(self, clipboard, event):
self.emit("selection-lost")

def do_xpra_destroy_event(self, event):
remove_event_receiver(event.window, self)
w = event.window
if w:
remove_event_receiver(w.get_xid(), self)
Gtk.main_quit()

def window(self):
Expand Down
9 changes: 5 additions & 4 deletions xpra/x11/gtk_x11/tray.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,16 @@ def undock(window):
rxid = root.get_xid()
X11Window.Unmap(wxid)
X11Window.Reparent(wxid, rxid, 0, 0)
tray_xid = self.tray_window.get_xid()
with xlog:
owner = X11Window.XGetSelectionOwner(SELECTION)
if owner==self.tray_window.get_xid():
if owner==tray_xid:
X11Window.XSetSelectionOwner(0, SELECTION)
log("SystemTray.cleanup() reset %s selection owner to %#x",
SELECTION, X11Window.XGetSelectionOwner(SELECTION))
else:
log.warn("Warning: we were no longer the tray selection owner")
remove_event_receiver(self.tray_window, self)
remove_event_receiver(tray_xid, self)
tray_windows = self.tray_windows
self.tray_windows = {}
for window, tray_window in tray_windows.items():
Expand Down Expand Up @@ -171,7 +172,7 @@ def setup_tray_window(self):
CurrentTime, SELECTION, xtray)
owner = X11Window.XGetSelectionOwner(SELECTION)
assert owner==xtray, "we failed to get ownership of the tray selection"
add_event_receiver(self.tray_window, self)
add_event_receiver(xtray, self)
log("setup tray: done")
except Exception:
log("setup_tray failure", exc_info=True)
Expand Down Expand Up @@ -231,7 +232,7 @@ def do_dock_tray(self, xid):
em = Gdk.EventMask
event_mask = em.STRUCTURE_MASK | em.EXPOSURE_MASK | em.PROPERTY_CHANGE_MASK
window.set_events(event_mask=event_mask)
add_event_receiver(window, self)
add_event_receiver(xid, self)
w = max(1, min(MAX_TRAY_SIZE, w))
h = max(1, min(MAX_TRAY_SIZE, h))
title = prop_get(xid, "_NET_WM_NAME", "utf8", ignore_errors=True)
Expand Down
Loading

0 comments on commit 1e457b6

Please sign in to comment.