Hide keyboard shortcuts

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-2019 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. 

6 

7 

8from xpra.util import envbool 

9from xpra.gtk_common.gobject_util import one_arg_signal 

10from xpra.x11.gtk_x11.gdk_bindings import ( 

11 add_event_receiver, #@UnresolvedImport 

12 remove_event_receiver, #@UnresolvedImport 

13 ) 

14from xpra.gtk_common.error import trap, xsync, xswallow, XError 

15from xpra.x11.common import Unmanageable 

16 

17from xpra.x11.bindings.ximage import XImageBindings #@UnresolvedImport 

18from xpra.x11.bindings.window_bindings import constants, X11WindowBindings #@UnresolvedImport 

19from xpra.log import Logger 

20 

21log = Logger("x11", "window") 

22 

23XImage = XImageBindings() 

24X11Window = X11WindowBindings() 

25X11Window.ensure_XDamage_support() 

26 

27 

28StructureNotifyMask = constants["StructureNotifyMask"] 

29USE_XSHM = envbool("XPRA_XSHM", True) 

30 

31 

32class WindowDamageHandler: 

33 

34 XShmEnabled = USE_XSHM 

35 

36 __common_gsignals__ = { 

37 "xpra-damage-event" : one_arg_signal, 

38 "xpra-unmap-event" : one_arg_signal, 

39 "xpra-configure-event" : one_arg_signal, 

40 "xpra-reparent-event" : one_arg_signal, 

41 } 

42 

43 # This may raise XError. 

44 def __init__(self, client_window, use_xshm=USE_XSHM): 

45 self.client_window = client_window 

46 self.xid = client_window.get_xid() 

47 log("WindowDamageHandler.__init__(%#x, %s)", self.xid, use_xshm) 

48 self._use_xshm = use_xshm 

49 self._damage_handle = None 

50 self._xshm_handle = None 

51 self._contents_handle = None 

52 self._border_width = 0 

53 

54 def __repr__(self): 

55 return "WindowDamageHandler(%#x)" % self.xid 

56 

57 def setup(self): 

58 self.invalidate_pixmap() 

59 geom = X11Window.geometry_with_border(self.xid) 

60 if geom is None: 

61 raise Unmanageable("window %#x disappeared already" % self.xid) 

62 self._border_width = geom[-1] 

63 self.create_damage_handle() 

64 add_event_receiver(self.client_window, self) 

65 

66 def create_damage_handle(self): 

67 self._damage_handle = X11Window.XDamageCreate(self.xid) 

68 log("damage handle(%#x)=%#x", self.xid, self._damage_handle) 

69 

70 def destroy(self): 

71 if self.client_window is None: 

72 log.warn("damage window handler for %s already cleaned up!", self) 

73 return 

74 #clear the reference to the window early: 

75 win = self.client_window 

76 self.client_window = None 

77 self.do_destroy(win) 

78 

79 def do_destroy(self, win): 

80 remove_event_receiver(win, self) 

81 self.destroy_damage_handle() 

82 

83 def destroy_damage_handle(self): 

84 log("close_damage_handle()") 

85 self.invalidate_pixmap() 

86 dh = self._damage_handle 

87 if dh: 

88 self._damage_handle = None 

89 trap.swallow_synced(X11Window.XDamageDestroy, dh) 

90 sh = self._xshm_handle 

91 if sh: 

92 self._xshm_handle = None 

93 sh.cleanup() 

94 #note: this should be redundant since we cleared the 

95 #reference to self.client_window and shortcut out in do_get_property_contents_handle 

96 #but it's cheap anyway 

97 self.invalidate_pixmap() 

98 

99 def acknowledge_changes(self): 

100 sh = self._xshm_handle 

101 dh = self._damage_handle 

102 log("acknowledge_changes() xshm handle=%s, damage handle=%s", sh, dh) 

103 if sh: 

104 sh.discard() 

105 if dh and self.client_window: 

106 #"Synchronously modifies the regions..." so unsynced? 

107 if not trap.swallow_synced(X11Window.XDamageSubtract, dh): 

108 self.invalidate_pixmap() 

109 

110 def invalidate_pixmap(self): 

111 ch = self._contents_handle 

112 log("invalidating named pixmap, contents handle=%s", ch) 

113 if ch: 

114 self._contents_handle = None 

115 with xswallow: 

116 ch.cleanup() 

117 

118 def has_xshm(self): 

119 return self._use_xshm and WindowDamageHandler.XShmEnabled and XImage.has_XShm() 

120 

121 def get_xshm_handle(self): 

122 if not self.has_xshm(): 

123 return None 

124 if self._xshm_handle: 

125 sw, sh = self._xshm_handle.get_size() 

126 ww, wh = self.client_window.get_geometry()[2:4] 

127 if sw!=ww or sh!=wh: 

128 #size has changed! 

129 #make sure the current wrapper gets garbage collected: 

130 self._xshm_handle.cleanup() 

131 self._xshm_handle = None 

132 if self._xshm_handle is None: 

133 #make a new one: 

134 self._xshm_handle = XImage.get_XShmWrapper(self.xid) 

135 if self._xshm_handle is None: 

136 #failed (may retry) 

137 return None 

138 init_ok, retry_window, xshm_failed = self._xshm_handle.setup() 

139 if not init_ok: 

140 #this handle is not valid, clear it: 

141 self._xshm_handle = None 

142 if not retry_window: 

143 #and it looks like it is not worth re-trying this window: 

144 self._use_xshm = False 

145 if xshm_failed: 

146 log.warn("Warning: disabling XShm support following irrecoverable error") 

147 WindowDamageHandler.XShmEnabled = False 

148 return self._xshm_handle 

149 

150 def _set_pixmap(self): 

151 self._contents_handle = XImage.get_xwindow_pixmap_wrapper(self.xid) 

152 

153 def get_contents_handle(self): 

154 if not self.client_window: 

155 #shortcut out 

156 return None 

157 if self._contents_handle is None: 

158 log("refreshing named pixmap") 

159 trap.swallow_synced(self._set_pixmap) 

160 return self._contents_handle 

161 

162 

163 def get_image(self, x, y, width, height): 

164 handle = self.get_contents_handle() 

165 if handle is None: 

166 log("get_image(..) pixmap is None for window %#x", self.xid) 

167 return None 

168 

169 #try XShm: 

170 try: 

171 with xsync: 

172 shm = self.get_xshm_handle() 

173 #log("get_image(..) XShm handle: %s, handle=%s, pixmap=%s", shm, handle, handle.get_pixmap()) 

174 if shm is not None: 

175 shm_image = shm.get_image(handle.get_pixmap(), x, y, width, height) 

176 #log("get_image(..) XShm image: %s", shm_image) 

177 if shm_image: 

178 return shm_image 

179 except XError as e: 

180 if e.msg.startswith("BadMatch"): 

181 log("get_image(%s, %s, %s, %s) get_image BadMatch ignored (window already gone?)", x, y, width, height) 

182 else: 

183 log.warn("get_image(%s, %s, %s, %s) '%s'", x, y, width, height, e.msg, exc_info=True) 

184 

185 try: 

186 w = min(handle.get_width(), width) 

187 h = min(handle.get_height(), height) 

188 if w!=width or h!=height: 

189 log("get_image(%s, %s, %s, %s) clamped to pixmap dimensions: %sx%s", x, y, width, height, w, h) 

190 with xsync: 

191 return handle.get_image(x, y, w, h) 

192 except XError as e: 

193 if e.msg.startswith("BadMatch"): 

194 log("get_image(%s, %s, %s, %s) get_image BadMatch ignored (window already gone?)", x, y, width, height) 

195 else: 

196 log.warn("get_image(%s, %s, %s, %s) get_image %s", x, y, width, height, e, exc_info=True) 

197 return None 

198 

199 

200 def do_xpra_damage_event(self, _event): 

201 raise NotImplementedError() 

202 

203 def do_xpra_reparent_event(self, _event): 

204 self.invalidate_pixmap() 

205 

206 def xpra_unmap_event(self, _event): 

207 self.invalidate_pixmap() 

208 

209 def do_xpra_configure_event(self, event): 

210 self._border_width = event.border_width 

211 self.invalidate_pixmap()