Coverage for /home/antoine/projects/xpra-git/dist/python3/lib64/python/xpra/x11/gtk_x11/composite.py : 51%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of Xpra.
2# Copyright (C) 2008, 2009 Nathaniel Smith <njs@pobox.com>
3# Copyright (C) 2012-2020 Antoine Martin <antoine@xpra.org>
4# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
5# later version. See the file COPYING for details.
7from gi.repository import GObject
9from xpra.x11.gtk_x11.window_damage import WindowDamageHandler
10from xpra.gtk_common.gobject_util import one_arg_signal
11from xpra.x11.gtk_x11.gdk_bindings import (
12 add_event_receiver, #@UnresolvedImport
13 remove_event_receiver, #@UnresolvedImport
14 get_parent, #@UnresolvedImport
15 )
16from xpra.gtk_common.error import trap
17from xpra.x11.gtk_x11.world_window import get_world_window
18from xpra.x11.bindings.ximage import XImageBindings #@UnresolvedImport
19from xpra.x11.bindings.window_bindings import constants, X11WindowBindings #@UnresolvedImport
20from xpra.log import Logger
22log = Logger("x11", "window")
24XImage = XImageBindings()
25X11Window = X11WindowBindings()
26X11Window.ensure_XComposite_support()
28StructureNotifyMask = constants["StructureNotifyMask"]
31class CompositeHelper(WindowDamageHandler, GObject.GObject):
33 __gsignals__ = WindowDamageHandler.__common_gsignals__.copy()
34 __gsignals__.update({
35 #emit:
36 "contents-changed" : one_arg_signal,
37 })
39 # This may raise XError.
40 def __init__(self, window):
41 WindowDamageHandler.__init__(self, window)
42 GObject.GObject.__init__(self)
43 self._listening_to = None
45 def __repr__(self):
46 return "CompositeHelper(%#x)" % self.xid
48 def setup(self):
49 X11Window.XCompositeRedirectWindow(self.xid)
50 WindowDamageHandler.setup(self)
52 def do_destroy(self, win):
53 trap.swallow_synced(X11Window.XCompositeUnredirectWindow, self.xid)
54 WindowDamageHandler.do_destroy(self, win)
56 def invalidate_pixmap(self):
57 lt = self._listening_to
58 if lt:
59 self._listening_to = None
60 self._cleanup_listening(lt)
61 WindowDamageHandler.invalidate_pixmap(self)
63 def _cleanup_listening(self, listening):
64 if listening:
65 # Don't want to stop listening to self.client_window!:
66 assert self.client_window is None or self.client_window not in listening
67 for w in listening:
68 remove_event_receiver(w, self)
70 def _set_pixmap(self):
71 # The tricky part here is that the pixmap returned by
72 # NameWindowPixmap gets invalidated every time the window's
73 # viewable state changes. ("viewable" here is the X term that
74 # means "mapped, and all ancestors are also mapped".) But
75 # there is no X event that will tell you when a window's
76 # viewability changes! Instead we have to find all ancestors,
77 # and watch all of them for unmap and reparent events. But
78 # what about races? I hear you cry. By doing things in the
79 # exact order:
80 # 1) select for StructureNotify
81 # 2) QueryTree to get parent
82 # 3) repeat 1 & 2 up to the root
83 # 4) call NameWindowPixmap
84 # we are safe. (I think.)
85 listening = []
86 e = None
87 try:
88 screen = self.client_window.get_screen()
89 if not screen:
90 log("cannot set pixmap on client window - maybe deleted?")
91 return
92 root = screen.get_root_window()
93 gdkworld = None
94 world = get_world_window()
95 if world:
96 gdkworld = world.get_window()
97 win = get_parent(self.client_window)
98 while win not in (None, root, gdkworld) and win.get_parent() is not None:
99 # We have to use a lowlevel function to manipulate the
100 # event selection here, because SubstructureRedirectMask
101 # does not roundtrip through the GDK event mask
102 # functions. So if we used them, here, we would clobber
103 # corral window selection masks, and those don't deserve
104 # clobbering. They are our friends! X is driving me
105 # slowly mad.
106 xid = win.get_xid()
107 X11Window.addXSelectInput(xid, StructureNotifyMask)
108 add_event_receiver(win, self, max_receivers=-1)
109 listening.append(win)
110 win = get_parent(win)
111 handle = XImage.get_xcomposite_pixmap(self.xid)
112 except Exception as e:
113 try:
114 self._cleanup_listening(listening)
115 except Exception:
116 pass
117 raise
118 if handle is None:
119 log("failed to name a window pixmap for %#x: %s", self.xid, e)
120 self._cleanup_listening(listening)
121 else:
122 self._contents_handle = handle
123 # Don't save the listening set until after
124 # NameWindowPixmap has succeeded, to maintain our
125 # invariant:
126 self._listening_to = listening
129 def do_xpra_damage_event(self, event):
130 event.x += self._border_width
131 event.y += self._border_width
132 self.emit("contents-changed", event)
134GObject.type_register(CompositeHelper)