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) 2010 Nathaniel Smith <njs@pobox.com> 

3# Copyright (C) 2011-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. 

6 

7import os 

8import sys 

9import struct 

10 

11from xpra.os_util import bytestostr, hexstr 

12from xpra.util import iround, envbool, envint, csv, ellipsizer 

13from xpra.os_util import is_unity, is_gnome, is_kde, is_Ubuntu, is_Fedora, is_X11, is_Wayland, saved_env 

14from xpra.log import Logger 

15 

16log = Logger("posix") 

17eventlog = Logger("posix", "events") 

18screenlog = Logger("posix", "screen") 

19dbuslog = Logger("posix", "dbus") 

20traylog = Logger("posix", "tray") 

21mouselog = Logger("posix", "mouse") 

22xinputlog = Logger("posix", "xinput") 

23 

24_X11Window = False 

25def X11WindowBindings(): 

26 global _X11Window 

27 if _X11Window is False: 

28 _X11Window = None 

29 if is_X11(): 

30 try: 

31 from xpra.x11.bindings.window_bindings import X11WindowBindings as _X11WindowBindings #@UnresolvedImport 

32 _X11Window = _X11WindowBindings() 

33 except Exception as e: 

34 log("X11WindowBindings()", exc_info=True) 

35 log.error("Error: no X11 bindings") 

36 log.error(" %s", e) 

37 return _X11Window 

38 

39def X11RandRBindings(): 

40 if is_X11(): 

41 try: 

42 from xpra.x11.bindings.randr_bindings import RandRBindings #@UnresolvedImport 

43 return RandRBindings() 

44 except Exception as e: 

45 log("RandRBindings()", exc_info=True) 

46 log.error("Error: no X11 RandR bindings") 

47 log.error(" %s", e) 

48 return None 

49 

50X11XI2 = False 

51def X11XI2Bindings(): 

52 global X11XI2 

53 if X11XI2 is False: 

54 X11XI2 = None 

55 if is_X11(): 

56 try: 

57 from xpra.x11.bindings.xi2_bindings import X11XI2Bindings as _X11XI2Bindings #@UnresolvedImport 

58 X11XI2 = _X11XI2Bindings() 

59 except Exception: 

60 log.error("no XI2 bindings", exc_info=True) 

61 return X11XI2 

62 

63 

64device_bell = None 

65GTK_MENUS = envbool("XPRA_GTK_MENUS", False) 

66RANDR_DPI = envbool("XPRA_RANDR_DPI", True) 

67XSETTINGS_DPI = envbool("XPRA_XSETTINGS_DPI", True) 

68USE_NATIVE_TRAY = envbool("XPRA_USE_NATIVE_TRAY", is_unity() or (is_Ubuntu() and is_gnome()) or (is_gnome() and not is_Fedora()) or is_kde()) 

69XINPUT_WHEEL_DIV = envint("XPRA_XINPUT_WHEEL_DIV", 15) 

70DBUS_SCREENSAVER = envbool("XPRA_DBUS_SCREENSAVER", False) 

71 

72 

73def gl_check(): 

74 if not is_X11() and is_Wayland(): 

75 return "disabled under wayland with GTK3 (buggy)" 

76 return None 

77 

78 

79def get_native_system_tray_classes(): 

80 return [] 

81 

82def get_wm_name(): 

83 wm_name = os.environ.get("XDG_CURRENT_DESKTOP", "") or os.environ.get("XDG_SESSION_DESKTOP") or os.environ.get("DESKTOP_SESSION") 

84 if os.environ.get("XDG_SESSION_TYPE")=="wayland" or os.environ.get("GDK_BACKEND")=="wayland": 

85 if wm_name: 

86 wm_name += " on wayland" 

87 else: 

88 wm_name = "wayland" 

89 elif is_X11(): 

90 try: 

91 wm_check = _get_X11_root_property("_NET_SUPPORTING_WM_CHECK", "WINDOW") 

92 if wm_check: 

93 xid = struct.unpack(b"@L", wm_check)[0] 

94 traylog("_NET_SUPPORTING_WM_CHECK window=%#x", xid) 

95 wm_name = _get_X11_window_property(xid, "_NET_WM_NAME", "UTF8_STRING") 

96 traylog("_NET_WM_NAME=%s", wm_name) 

97 if wm_name: 

98 return wm_name.decode("utf-8") 

99 except Exception as e: 

100 traylog("get_wm_name()", exc_info=True) 

101 traylog.error("Error accessing window manager information:") 

102 traylog.error(" %s", e) 

103 return wm_name 

104 

105def get_clipboard_native_class(): 

106 if is_Wayland(): 

107 return "xpra.gtk_common.gtk_clipboard.GTK_Clipboard" 

108 return "xpra.x11.gtk_x11.clipboard.X11Clipboard" 

109 

110def get_native_tray_classes(): 

111 #could restrict to only DEs that have a broken system tray like "GNOME Shell"? 

112 c = [] 

113 if USE_NATIVE_TRAY: 

114 try: 

115 from xpra.platform.xposix.appindicator_tray import AppindicatorTray 

116 c.append(AppindicatorTray) 

117 except (ImportError, ValueError): 

118 traylog("cannot load appindicator tray", exc_info=True) 

119 traylog.warn("Warning: appindicator library not found") 

120 traylog.warn(" you may want to install libappindicator") 

121 traylog.warn(" to enable the system tray.") 

122 if saved_env.get("XDG_CURRENT_DESKTOP")=="GNOME": 

123 traylog.warn(" With gnome-shell, you may also need some extensions:") 

124 traylog.warn(" 'top icons plus' and / or 'appindicator'") 

125 return c 

126 

127 

128def get_native_notifier_classes(): 

129 ncs = [] 

130 try: 

131 from xpra.notifications.dbus_notifier import DBUS_Notifier_factory 

132 ncs.append(DBUS_Notifier_factory) 

133 except Exception as e: 

134 dbuslog("cannot load dbus notifier: %s", e) 

135 try: 

136 from xpra.notifications.pynotify_notifier import PyNotify_Notifier 

137 ncs.append(PyNotify_Notifier) 

138 except Exception as e: 

139 log("cannot load pynotify notifier: %s", e) 

140 return ncs 

141 

142 

143def get_session_type(): 

144 return os.environ.get("XDG_SESSION_TYPE", "") 

145 

146 

147#we duplicate some of the code found in gtk_x11.prop ... 

148#which is still better than having dependencies on that GTK2 code 

149def _get_X11_window_property(xid, name, req_type): 

150 try: 

151 from xpra.gtk_common.error import xsync 

152 from xpra.x11.bindings.window_bindings import PropertyError #@UnresolvedImport 

153 try: 

154 with xsync: 

155 prop = X11WindowBindings().XGetWindowProperty(xid, name, req_type) 

156 log("_get_X11_window_property(%#x, %s, %s)=%s, len=%s", xid, name, req_type, type(prop), len(prop or [])) 

157 return prop 

158 except PropertyError as e: 

159 log("_get_X11_window_property(%#x, %s, %s): %s", xid, name, req_type, e) 

160 except Exception as e: 

161 log.warn("Warning: failed to get X11 window property '%s' on window %#x: %s", name, xid, e) 

162 log("get_X11_window_property%s", (xid, name, req_type), exc_info=True) 

163 return None 

164def _get_X11_root_property(name, req_type): 

165 try: 

166 root_xid = X11WindowBindings().getDefaultRootWindow() 

167 return _get_X11_window_property(root_xid, name, req_type) 

168 except Exception as e: 

169 log("_get_X11_root_property(%s, %s)", name, req_type, exc_info=True) 

170 log.warn("Warning: failed to get X11 root property '%s'", name) 

171 log.warn(" %s", e) 

172 return None 

173 

174 

175def _get_xsettings(): 

176 from xpra.gtk_common.error import xlog 

177 X11Window = X11WindowBindings() 

178 if not X11Window: 

179 return None 

180 with xlog: 

181 selection = "_XSETTINGS_S0" 

182 owner = X11Window.XGetSelectionOwner(selection) 

183 if not owner: 

184 return None 

185 XSETTINGS = "_XSETTINGS_SETTINGS" 

186 data = X11Window.XGetWindowProperty(owner, XSETTINGS, XSETTINGS) 

187 if not data: 

188 return None 

189 from xpra.x11.xsettings_prop import get_settings 

190 return get_settings(X11Window.get_display_name(), data) 

191 return None 

192 

193def _get_xsettings_dict(): 

194 d = {} 

195 if is_Wayland(): 

196 return d 

197 v = _get_xsettings() 

198 if v: 

199 _, values = v 

200 for setting_type, prop_name, value, _ in values: 

201 d[bytestostr(prop_name)] = (setting_type, value) 

202 return d 

203 

204 

205def _get_xsettings_dpi(): 

206 if XSETTINGS_DPI and is_X11(): 

207 from xpra.x11.xsettings_prop import XSettingsTypeInteger 

208 d = _get_xsettings_dict() 

209 for k,div in { 

210 "Xft.dpi" : 1, 

211 "Xft/DPI" : 1024, 

212 "gnome.Xft/DPI" : 1024, 

213 #"Gdk/UnscaledDPI" : 1024, ?? 

214 }.items(): 

215 if k in d: 

216 value_type, value = d.get(k) 

217 if value_type==XSettingsTypeInteger: 

218 actual_value = max(10, min(1000, value//div)) 

219 screenlog("_get_xsettings_dpi() found %s=%s, div=%i, actual value=%i", k, value, div, actual_value) 

220 return actual_value 

221 return -1 

222 

223def _get_randr_dpi(): 

224 if RANDR_DPI and not is_Wayland(): 

225 from xpra.gtk_common.error import xlog 

226 with xlog: 

227 randr_bindings = X11RandRBindings() 

228 if randr_bindings and randr_bindings.has_randr(): 

229 wmm, hmm = randr_bindings.get_screen_size_mm() 

230 if wmm>0 and hmm>0: 

231 w, h = randr_bindings.get_screen_size() 

232 dpix = iround(w * 25.4 / wmm) 

233 dpiy = iround(h * 25.4 / hmm) 

234 screenlog("xdpi=%s, ydpi=%s - size-mm=%ix%i, size=%ix%i", dpix, dpiy, wmm, hmm, w, h) 

235 return dpix, dpiy 

236 return -1, -1 

237 

238def get_xdpi(): 

239 dpi = _get_xsettings_dpi() 

240 if dpi>0: 

241 return dpi 

242 return _get_randr_dpi()[0] 

243 

244def get_ydpi(): 

245 dpi = _get_xsettings_dpi() 

246 if dpi>0: 

247 return dpi 

248 return _get_randr_dpi()[1] 

249 

250 

251def get_icc_info(): 

252 if not is_Wayland(): 

253 try: 

254 data = _get_X11_root_property("_ICC_PROFILE", "CARDINAL") 

255 if data: 

256 screenlog("_ICC_PROFILE=%s (%s)", type(data), len(data)) 

257 version = _get_X11_root_property("_ICC_PROFILE_IN_X_VERSION", "CARDINAL") 

258 screenlog("get_icc_info() found _ICC_PROFILE_IN_X_VERSION=%s, _ICC_PROFILE=%s", 

259 hexstr(version or ""), hexstr(data)) 

260 icc = { 

261 "source" : "_ICC_PROFILE", 

262 "data" : data, 

263 } 

264 if version: 

265 try: 

266 version = ord(version) 

267 except TypeError: 

268 pass 

269 icc["version"] = version 

270 screenlog("get_icc_info()=%s", icc) 

271 return icc 

272 except Exception as e: 

273 screenlog.error("Error: cannot access _ICC_PROFILE X11 window property") 

274 screenlog.error(" %s", e) 

275 screenlog("get_icc_info()", exc_info=True) 

276 from xpra.platform.gui import default_get_icc_info 

277 return default_get_icc_info() 

278 

279 

280def get_antialias_info(): 

281 info = {} 

282 try: 

283 from xpra.x11.xsettings_prop import XSettingsTypeInteger, XSettingsTypeString 

284 d = _get_xsettings_dict() 

285 for prop_name, name in {"Xft/Antialias" : "enabled", 

286 "Xft/Hinting" : "hinting"}.items(): 

287 if prop_name in d: 

288 value_type, value = d.get(prop_name) 

289 if value_type==XSettingsTypeInteger and value>0: 

290 info[name] = bool(value) 

291 def get_contrast(value): 

292 #win32 API uses numerical values: 

293 #(this is my best guess at translating the X11 names) 

294 return {"hintnone" : 0, 

295 "hintslight" : 1000, 

296 "hintmedium" : 1600, 

297 "hintfull" : 2200}.get(bytestostr(value)) 

298 for prop_name, name, convert in ( 

299 ("Xft/HintStyle", "hintstyle", bytestostr), 

300 ("Xft/HintStyle", "contrast", get_contrast), 

301 ("Xft/RGBA", "orientation", lambda x : bytestostr(x).upper()) 

302 ): 

303 if prop_name in d: 

304 value_type, value = d.get(prop_name) 

305 if value_type==XSettingsTypeString: 

306 cval = convert(value) 

307 if cval is not None: 

308 info[name] = cval 

309 except Exception as e: 

310 screenlog.warn("failed to get antialias info from xsettings: %s", e) 

311 screenlog("get_antialias_info()=%s", info) 

312 return info 

313 

314 

315def get_current_desktop(): 

316 v = -1 

317 if not is_Wayland(): 

318 d = None 

319 try: 

320 d = _get_X11_root_property("_NET_CURRENT_DESKTOP", "CARDINAL") 

321 if d: 

322 v = struct.unpack(b"@L", d)[0] 

323 except Exception as e: 

324 log.warn("failed to get current desktop: %s", e) 

325 log("get_current_desktop() %s=%s", hexstr(d or ""), v) 

326 return v 

327 

328def get_workarea(): 

329 if not is_Wayland(): 

330 try: 

331 d = get_current_desktop() 

332 if d<0: 

333 return None 

334 workarea = _get_X11_root_property("_NET_WORKAREA", "CARDINAL") 

335 if not workarea: 

336 return None 

337 screenlog("get_workarea() _NET_WORKAREA=%s (%s), len=%s", 

338 ellipsizer(workarea), type(workarea), len(workarea)) 

339 #workarea comes as a list of 4 CARDINAL dimensions (x,y,w,h), one for each desktop 

340 sizeof_long = struct.calcsize(b"@L") 

341 if len(workarea)<(d+1)*4*sizeof_long: 

342 screenlog.warn("get_workarea() invalid _NET_WORKAREA value") 

343 else: 

344 cur_workarea = workarea[d*4*sizeof_long:(d+1)*4*sizeof_long] 

345 v = struct.unpack(b"@LLLL", cur_workarea) 

346 screenlog("get_workarea() %s=%s", hexstr(cur_workarea), v) 

347 return v 

348 except Exception as e: 

349 screenlog("get_workarea()", exc_info=True) 

350 screenlog.warn("Warning: failed to query workarea: %s", e) 

351 return None 

352 

353 

354def get_number_of_desktops(): 

355 v = 0 

356 if not is_Wayland(): 

357 d = None 

358 try: 

359 d = _get_X11_root_property("_NET_NUMBER_OF_DESKTOPS", "CARDINAL") 

360 if d: 

361 v = struct.unpack(b"@L", d)[0] 

362 except Exception as e: 

363 screenlog.warn("failed to get number of desktop: %s", e) 

364 v = max(1, v) 

365 screenlog("get_number_of_desktops() %s=%s", hexstr(d or ""), v) 

366 return v 

367 

368def get_desktop_names(): 

369 v = [] 

370 if not is_Wayland(): 

371 v = ["Main"] 

372 d = None 

373 try: 

374 d = _get_X11_root_property("_NET_DESKTOP_NAMES", "UTF8_STRING") 

375 if d: 

376 v = d.split(b"\0") 

377 if len(v)>1 and v[-1]==b"": 

378 v = v[:-1] 

379 return [x.decode("utf8") for x in v] 

380 except Exception as e: 

381 screenlog.warn("failed to get desktop names: %s", e) 

382 screenlog("get_desktop_names() %s=%s", hexstr(d or ""), v) 

383 return v 

384 

385 

386def get_vrefresh(): 

387 v = -1 

388 if not is_Wayland(): 

389 try: 

390 from xpra.x11.bindings.randr_bindings import RandRBindings #@UnresolvedImport 

391 randr = RandRBindings() 

392 if randr.has_randr(): 

393 v = randr.get_vrefresh() 

394 except Exception as e: 

395 log("get_vrefresh()", exc_info=True) 

396 log.warn("Warning: failed to query the display vertical refresh rate:") 

397 log.warn(" %s", e) 

398 screenlog("get_vrefresh()=%s", v) 

399 return v 

400 

401 

402def _get_xresources(): 

403 if not is_Wayland(): 

404 try: 

405 from xpra.x11.gtk_x11.prop import prop_get 

406 from xpra.gtk_common.gtk_util import get_default_root_window 

407 root = get_default_root_window() 

408 value = prop_get(root, "RESOURCE_MANAGER", "latin1", ignore_errors=True) 

409 log("RESOURCE_MANAGER=%s", value) 

410 if value is None: 

411 return None 

412 #parse the resources into a dict: 

413 values={} 

414 options = value.split("\n") 

415 for option in options: 

416 if not option: 

417 continue 

418 parts = option.split(":\t", 1) 

419 if len(parts)!=2: 

420 log("skipped invalid option: '%s'", option) 

421 continue 

422 values[parts[0]] = parts[1] 

423 return values 

424 except Exception as e: 

425 log("_get_xresources error: %s", e) 

426 return None 

427 

428def get_cursor_size(): 

429 d = _get_xresources() or {} 

430 try: 

431 return int(d.get("Xcursor.size", 0)) 

432 except ValueError: 

433 return -1 

434 

435 

436def _get_xsettings_int(name, default_value): 

437 d = _get_xsettings_dict() 

438 if name not in d: 

439 return default_value 

440 value_type, value = d.get(name) 

441 from xpra.x11.xsettings_prop import XSettingsTypeInteger 

442 if value_type!=XSettingsTypeInteger: 

443 return default_value 

444 return value 

445 

446def get_double_click_time(): 

447 return _get_xsettings_int("Net/DoubleClickTime", -1) 

448 

449def get_double_click_distance(): 

450 v = _get_xsettings_int("Net/DoubleClickDistance", -1) 

451 return v, v 

452 

453def get_window_frame_sizes(): 

454 #for X11, have to create a window and then check the 

455 #_NET_FRAME_EXTENTS value after sending a _NET_REQUEST_FRAME_EXTENTS message, 

456 #so this is done in the gtk client instead of here... 

457 return {} 

458 

459 

460def system_bell(window, device, percent, _pitch, _duration, bell_class, bell_id, bell_name): 

461 if not is_X11(): 

462 return False 

463 global device_bell 

464 if device_bell is False: 

465 #failed already 

466 return False 

467 from xpra.gtk_common.error import XError 

468 def x11_bell(): 

469 global device_bell 

470 if device_bell is None: 

471 #try to load it: 

472 from xpra.x11.bindings.keyboard_bindings import X11KeyboardBindings #@UnresolvedImport 

473 device_bell = X11KeyboardBindings().device_bell 

474 device_bell(window.get_xid(), device, bell_class, bell_id, percent, bell_name) 

475 try: 

476 from xpra.gtk_common.error import xlog 

477 with xlog: 

478 x11_bell() 

479 return True 

480 except XError as e: 

481 log("x11_bell()", exc_info=True) 

482 log.error("Error using device_bell: %s", e) 

483 log.error(" switching native X11 bell support off") 

484 device_bell = False 

485 return False 

486 

487 

488def _send_client_message(window, message_type, *values): 

489 try: 

490 from xpra.x11.gtk_x11.gdk_display_source import init_gdk_display_source 

491 init_gdk_display_source() 

492 from xpra.x11.bindings.window_bindings import constants #@UnresolvedImport 

493 X11Window = X11WindowBindings() 

494 root_xid = X11Window.getDefaultRootWindow() 

495 if window: 

496 xid = window.get_xid() 

497 else: 

498 xid = root_xid 

499 SubstructureNotifyMask = constants["SubstructureNotifyMask"] 

500 SubstructureRedirectMask = constants["SubstructureRedirectMask"] 

501 event_mask = SubstructureNotifyMask | SubstructureRedirectMask 

502 from xpra.gtk_common.error import xsync 

503 with xsync: 

504 X11Window.sendClientMessage(root_xid, xid, False, event_mask, message_type, *values) 

505 except Exception as e: 

506 log.warn("failed to send client message '%s' with values=%s: %s", message_type, values, e) 

507 

508def show_desktop(b): 

509 _send_client_message(None, "_NET_SHOWING_DESKTOP", int(bool(b))) 

510 

511def set_fullscreen_monitors(window, fsm, source_indication=0): 

512 if not isinstance(fsm, (tuple, list)): 

513 log.warn("invalid type for fullscreen-monitors: %s", type(fsm)) 

514 return 

515 if len(fsm)!=4: 

516 log.warn("invalid number of fullscreen-monitors: %s", len(fsm)) 

517 return 

518 values = list(fsm)+[source_indication] 

519 _send_client_message(window, "_NET_WM_FULLSCREEN_MONITORS", *values) 

520 

521def _toggle_wm_state(window, state, enabled): 

522 if enabled: 

523 action = 1 #"_NET_WM_STATE_ADD" 

524 else: 

525 action = 0 #"_NET_WM_STATE_REMOVE" 

526 _send_client_message(window, "_NET_WM_STATE", action, state) 

527 

528def set_shaded(window, shaded): 

529 _toggle_wm_state(window, "_NET_WM_STATE_SHADED", shaded) 

530 

531 

532 

533WINDOW_ADD_HOOKS = [] 

534def add_window_hooks(window): 

535 global WINDOW_ADD_HOOKS 

536 for x in WINDOW_ADD_HOOKS: 

537 x(window) 

538 log("add_window_hooks(%s) added %s", window, WINDOW_ADD_HOOKS) 

539 

540WINDOW_REMOVE_HOOKS = [] 

541def remove_window_hooks(window): 

542 global WINDOW_REMOVE_HOOKS 

543 for x in WINDOW_REMOVE_HOOKS: 

544 x(window) 

545 log("remove_window_hooks(%s) added %s", window, WINDOW_REMOVE_HOOKS) 

546 

547 

548def get_info(): 

549 from xpra.platform.gui import get_info_base 

550 i = get_info_base() 

551 s = _get_xsettings() 

552 if s: 

553 serial, values = s 

554 xi = {"serial" : serial} 

555 for _,name,value,_ in values: 

556 xi[bytestostr(name)] = value 

557 i["xsettings"] = xi 

558 i.setdefault("dpi", { 

559 "xsettings" : _get_xsettings_dpi(), 

560 "randr" : _get_randr_dpi() 

561 }) 

562 return i 

563 

564 

565class XI2_Window: 

566 def __init__(self, window): 

567 log("XI2_Window(%s)", window) 

568 self.XI2 = X11XI2Bindings() 

569 self.X11Window = X11WindowBindings() 

570 self.window = window 

571 self.xid = window.get_window().get_xid() 

572 self.windows = () 

573 self.motion_valuators = {} 

574 window.connect("configure-event", self.configured) 

575 self.configured() 

576 #replace event handlers with XI2 version: 

577 self._do_motion_notify_event = window._do_motion_notify_event 

578 window._do_motion_notify_event = self.noop 

579 window._do_button_press_event = self.noop 

580 window._do_button_release_event = self.noop 

581 window._do_scroll_event = self.noop 

582 window.connect("destroy", self.cleanup) 

583 

584 def noop(self, *args): 

585 pass 

586 

587 def cleanup(self, *_args): 

588 for window in self.windows: 

589 self.XI2.disconnect(window) 

590 self.windows = [] 

591 self.window = None 

592 

593 def configured(self, *_args): 

594 from xpra.gtk_common.error import xlog 

595 with xlog: 

596 self.windows = self.get_parent_windows(self.xid) 

597 for window in (self.windows or ()): 

598 self.XI2.connect(window, "XI_Motion", self.do_xi_motion) 

599 self.XI2.connect(window, "XI_ButtonPress", self.do_xi_button) 

600 self.XI2.connect(window, "XI_ButtonRelease", self.do_xi_button) 

601 self.XI2.connect(window, "XI_DeviceChanged", self.do_xi_device_changed) 

602 self.XI2.connect(window, "XI_HierarchyChanged", self.do_xi_hierarchy_changed) 

603 

604 def do_xi_device_changed(self, *_args): 

605 self.motion_valuators = {} 

606 

607 def do_xi_hierarchy_changed(self, *_args): 

608 self.motion_valuators = {} 

609 

610 

611 def get_parent_windows(self, oxid): 

612 windows = [oxid] 

613 root = self.X11Window.getDefaultRootWindow() 

614 xid = oxid 

615 while True: 

616 xid = self.X11Window.getParent(xid) 

617 if xid==0 or xid==root: 

618 break 

619 windows.append(xid) 

620 xinputlog("get_parent_windows(%#x)=%s", oxid, csv(hex(x) for x in windows)) 

621 return windows 

622 

623 

624 def do_xi_button(self, event, device): 

625 window = self.window 

626 client = window._client 

627 if client.readonly: 

628 return 

629 xinputlog("do_xi_button(%s, %s) server_input_devices=%s", event, device, client.server_input_devices) 

630 if client.server_input_devices=="xi" or (client.server_input_devices=="uinput" and client.server_precise_wheel): 

631 #skip synthetic scroll events, 

632 #as the server should synthesize them from the motion events 

633 #those have the same serial: 

634 matching_motion = self.XI2.find_event("XI_Motion", event.serial) 

635 #maybe we need more to distinguish? 

636 if matching_motion: 

637 return 

638 button = event.detail 

639 depressed = (event.name == "XI_ButtonPress") 

640 args = self.get_pointer_extra_args(event) 

641 window._button_action(button, event, depressed, *args) 

642 

643 def do_xi_motion(self, event, device): 

644 window = self.window 

645 if window.moveresize_event: 

646 xinputlog("do_xi_motion(%s, %s) handling as a moveresize event on window %s", event, device, window) 

647 window.motion_moveresize(event) 

648 self._do_motion_notify_event(event) 

649 return 

650 client = window._client 

651 if client.readonly: 

652 return 

653 pointer, relative_pointer, modifiers, buttons = window._pointer_modifiers(event) 

654 wid = self.window.get_mouse_event_wid(*pointer) 

655 #log("server_input_devices=%s, server_precise_wheel=%s", 

656 # client.server_input_devices, client.server_precise_wheel) 

657 valuators = event.valuators 

658 unused_valuators = valuators.copy() 

659 dx, dy = 0, 0 

660 if (valuators and device and device.get("enabled") and 

661 client.server_input_devices=="uinput" and client.server_precise_wheel): 

662 XIModeRelative = 0 

663 classes = device.get("classes") 

664 val_classes = {} 

665 for c in classes.values(): 

666 number = c.get("number") 

667 if number is not None and c.get("type")=="valuator" and c.get("mode")==XIModeRelative: 

668 val_classes[number] = c 

669 #previous values: 

670 mv = self.motion_valuators.setdefault(event.device, {}) 

671 last_x, last_y = 0, 0 

672 wheel_x, wheel_y = 0, 0 

673 unused_valuators = {} 

674 for number, value in valuators.items(): 

675 valuator = val_classes.get(number) 

676 if valuator: 

677 label = valuator.get("label") 

678 if label: 

679 mouselog("%s: %s", label, value) 

680 if label.lower().find("horiz")>=0: 

681 wheel_x = value 

682 last_x = mv.get(number) 

683 continue 

684 elif label.lower().find("vert")>=0: 

685 wheel_y = value 

686 last_y = mv.get(number) 

687 continue 

688 unused_valuators[number] = value 

689 #new absolute motion values: 

690 #calculate delta if we have both old and new values: 

691 if last_x is not None and wheel_x is not None: 

692 dx = last_x-wheel_x 

693 if last_y is not None and wheel_y is not None: 

694 dy = last_y-wheel_y 

695 #whatever happens, update our motion cached values: 

696 mv.update(event.valuators) 

697 #send plain motion first, if any: 

698 if unused_valuators: 

699 xinputlog("do_xi_motion(%s, %s) wid=%s / focus=%s / window wid=%i, device=%s, pointer=%s, modifiers=%s, buttons=%s", 

700 event, device, wid, window._client._focused, window._id, event.device, pointer, modifiers, buttons) 

701 pdata = pointer 

702 if client.server_pointer_relative: 

703 pdata = list(pointer)+list(relative_pointer) 

704 packet = ["pointer-position", wid, pdata, modifiers, buttons] + self.get_pointer_extra_args(event) 

705 client.send_mouse_position(packet) 

706 #now see if we have anything to send as a wheel event: 

707 if dx!=0 or dy!=0: 

708 xinputlog("do_xi_motion(%s, %s) wheel deltas: dx=%i, dy=%i", event, device, dx, dy) 

709 #normalize (xinput is always using 15 degrees?) 

710 client.wheel_event(wid, dx/XINPUT_WHEEL_DIV, dy/XINPUT_WHEEL_DIV, event.device) 

711 

712 def get_pointer_extra_args(self, event): 

713 def intscaled(f): 

714 return int(f*1000000), 1000000 

715 def dictscaled(d): 

716 return dict((k,intscaled(v)) for k,v in d.items()) 

717 raw_valuators = {} 

718 raw_event_name = event.name.replace("XI_", "XI_Raw") #ie: XI_Motion -> XI_RawMotion 

719 raw = self.XI2.find_event(raw_event_name, event.serial) 

720 #mouselog("raw(%s)=%s", raw_event_name, raw) 

721 if raw: 

722 raw_valuators = raw.raw_valuators 

723 args = [event.device] 

724 for x in ("x", "y", "x_root", "y_root"): 

725 args.append(intscaled(getattr(event, x))) 

726 for v in (event.valuators, raw_valuators): 

727 args.append(dictscaled(v)) 

728 return args 

729 

730 

731class ClientExtras: 

732 def __init__(self, client, _opts): 

733 self.client = client 

734 self._xsettings_watcher = None 

735 self._root_props_watcher = None 

736 self.system_bus = None 

737 self.session_bus = None 

738 self.upower_resuming_match = None 

739 self.upower_sleeping_match = None 

740 self.login1_match = None 

741 self.screensaver_match = None 

742 self.x11_filter = None 

743 if client.xsettings_enabled: 

744 self.setup_xprops() 

745 self.xi_setup_failures = 0 

746 input_devices = getattr(client, "input_devices", None) 

747 if input_devices in ("xi", "auto"): 

748 #this would trigger warnings with our temporary opengl windows: 

749 #only enable it after we have connected: 

750 self.client.after_handshake(self.setup_xi) 

751 self.setup_dbus_signals() 

752 

753 def ready(self): 

754 pass 

755 

756 def init_x11_filter(self): 

757 if self.x11_filter: 

758 return 

759 try: 

760 from xpra.x11.gtk_x11.gdk_bindings import init_x11_filter #@UnresolvedImport, @UnusedImport 

761 self.x11_filter = init_x11_filter() 

762 log("x11_filter=%s", self.x11_filter) 

763 except Exception as e: 

764 log("init_x11_filter()", exc_info=True) 

765 log.error("Error: failed to initialize X11 GDK filter:") 

766 log.error(" %s", e) 

767 self.x11_filter = None 

768 

769 def cleanup(self): 

770 log("cleanup() xsettings_watcher=%s, root_props_watcher=%s", self._xsettings_watcher, self._root_props_watcher) 

771 if self.x11_filter: 

772 self.x11_filter = None 

773 from xpra.x11.gtk_x11.gdk_bindings import cleanup_x11_filter #@UnresolvedImport, @UnusedImport 

774 cleanup_x11_filter() 

775 if self._xsettings_watcher: 

776 self._xsettings_watcher.cleanup() 

777 self._xsettings_watcher = None 

778 if self._root_props_watcher: 

779 self._root_props_watcher.cleanup() 

780 self._root_props_watcher = None 

781 if self.system_bus: 

782 bus = self.system_bus 

783 log("cleanup() system bus=%s, matches: %s", 

784 bus, (self.upower_resuming_match, self.upower_sleeping_match, self.login1_match)) 

785 self.system_bus = None 

786 if self.upower_resuming_match: 

787 bus._clean_up_signal_match(self.upower_resuming_match) 

788 self.upower_resuming_match = None 

789 if self.upower_sleeping_match: 

790 bus._clean_up_signal_match(self.upower_sleeping_match) 

791 self.upower_sleeping_match = None 

792 if self.login1_match: 

793 bus._clean_up_signal_match(self.login1_match) 

794 self.login1_match = None 

795 if self.session_bus: 

796 if self.screensaver_match: 

797 self.session_bus._clean_up_signal_match(self.screensaver_match) 

798 self.screensaver_match = None 

799 global WINDOW_METHOD_OVERRIDES 

800 WINDOW_METHOD_OVERRIDES = {} 

801 

802 def resuming_callback(self, *args): 

803 eventlog("resuming_callback%s", args) 

804 self.client.resume() 

805 

806 def sleeping_callback(self, *args): 

807 eventlog("sleeping_callback%s", args) 

808 self.client.suspend() 

809 

810 

811 def setup_dbus_signals(self): 

812 try: 

813 import xpra.dbus 

814 assert xpra.dbus 

815 except ImportError as e: 

816 dbuslog("setup_dbus_signals()", exc_info=True) 

817 dbuslog.info("dbus support is not installed") 

818 dbuslog.info(" no support for power events") 

819 return 

820 try: 

821 from xpra.dbus.common import init_system_bus, init_session_bus 

822 except ImportError as e: 

823 dbuslog("setup_dbus_signals()", exc_info=True) 

824 dbuslog.error("Error: dbus bindings are missing,") 

825 dbuslog.error(" cannot setup event listeners:") 

826 dbuslog.error(" %s", e) 

827 return 

828 

829 try: 

830 bus = init_system_bus() 

831 self.system_bus = bus 

832 dbuslog("setup_dbus_signals() system bus=%s", bus) 

833 except Exception as e: 

834 dbuslog("setup_dbus_signals()", exc_info=True) 

835 dbuslog.error("Error setting up dbus signals:") 

836 dbuslog.error(" %s", e) 

837 else: 

838 #the UPower signals: 

839 try: 

840 bus_name = 'org.freedesktop.UPower' 

841 dbuslog("bus has owner(%s)=%s", bus_name, bus.name_has_owner(bus_name)) 

842 iface_name = 'org.freedesktop.UPower' 

843 self.upower_resuming_match = bus.add_signal_receiver(self.resuming_callback, 'Resuming', iface_name, bus_name) 

844 self.upower_sleeping_match = bus.add_signal_receiver(self.sleeping_callback, 'Sleeping', iface_name, bus_name) 

845 dbuslog("listening for 'Resuming' and 'Sleeping' signals on %s", iface_name) 

846 except Exception as e: 

847 dbuslog("failed to setup UPower event listener: %s", e) 

848 

849 #the "logind" signals: 

850 try: 

851 bus_name = 'org.freedesktop.login1' 

852 dbuslog("bus has owner(%s)=%s", bus_name, bus.name_has_owner(bus_name)) 

853 def sleep_event_handler(suspend): 

854 if suspend: 

855 self.sleeping_callback() 

856 else: 

857 self.resuming_callback() 

858 iface_name = 'org.freedesktop.login1.Manager' 

859 self.login1_match = bus.add_signal_receiver(sleep_event_handler, 'PrepareForSleep', iface_name, bus_name) 

860 dbuslog("listening for 'PrepareForSleep' signal on %s", iface_name) 

861 except Exception as e: 

862 dbuslog("failed to setup login1 event listener: %s", e) 

863 

864 if DBUS_SCREENSAVER: 

865 try: 

866 session_bus = init_session_bus() 

867 self.session_bus = session_bus 

868 dbuslog("setup_dbus_signals() session bus=%s", session_bus) 

869 except Exception as e: 

870 dbuslog("setup_dbus_signals()", exc_info=True) 

871 dbuslog.error("Error setting up dbus signals:") 

872 dbuslog.error(" %s", e) 

873 else: 

874 #screensaver signals: 

875 try: 

876 bus_name = "org.gnome.ScreenSaver" 

877 iface_name = bus_name 

878 self.screensaver_match = bus.add_signal_receiver(self.ActiveChanged, "ActiveChanged", iface_name, bus_name) 

879 dbuslog("listening for 'ActiveChanged' signal on %s", iface_name) 

880 except Exception as e: 

881 dbuslog.warn("Warning: failed to setup screensaver event listener: %s", e) 

882 

883 def ActiveChanged(self, active): 

884 log("ActiveChanged(%s)", active) 

885 if active: 

886 self.client.suspend() 

887 else: 

888 self.client.resume() 

889 

890 

891 def setup_xprops(self): 

892 #wait for handshake to complete: 

893 if not is_Wayland(): 

894 self.client.after_handshake(self.do_setup_xprops) 

895 

896 def do_setup_xprops(self, *args): 

897 log("do_setup_xprops(%s)", args) 

898 ROOT_PROPS = ["RESOURCE_MANAGER", "_NET_WORKAREA", "_NET_CURRENT_DESKTOP"] 

899 try: 

900 self.init_x11_filter() 

901 from xpra.gtk_common.gtk_util import get_default_root_window 

902 from xpra.x11.xsettings import XSettingsWatcher 

903 from xpra.x11.xroot_props import XRootPropWatcher 

904 root = get_default_root_window() 

905 if self._xsettings_watcher is None: 

906 self._xsettings_watcher = XSettingsWatcher() 

907 self._xsettings_watcher.connect("xsettings-changed", self._handle_xsettings_changed) 

908 self._handle_xsettings_changed() 

909 if self._root_props_watcher is None: 

910 self._root_props_watcher = XRootPropWatcher(ROOT_PROPS, root) 

911 self._root_props_watcher.connect("root-prop-changed", self._handle_root_prop_changed) 

912 #ensure we get the initial value: 

913 self._root_props_watcher.do_notify("RESOURCE_MANAGER") 

914 except ImportError as e: 

915 log("do_setup_xprops%s", args, exc_info=True) 

916 log.error("Error: failed to load X11 properties/settings bindings:") 

917 log.error(" %s", e) 

918 log.error(" root window properties will not be propagated") 

919 

920 

921 def do_xi_devices_changed(self, event): 

922 log("do_xi_devices_changed(%s)", event) 

923 XI2 = X11XI2Bindings() 

924 devices = XI2.get_devices() 

925 if devices: 

926 self.client.send_input_devices("xi", devices) 

927 

928 def setup_xi(self): 

929 self.client.timeout_add(100, self.do_setup_xi) 

930 

931 def do_setup_xi(self): 

932 if self.client.server_input_devices not in ("xi", "uinput"): 

933 xinputlog("server does not support xi input devices") 

934 if self.client.server_input_devices: 

935 log(" server uses: %s", self.client.server_input_devices) 

936 return False 

937 try: 

938 from xpra.gtk_common.error import xsync, XError 

939 assert X11WindowBindings, "no X11 window bindings" 

940 assert X11XI2Bindings, "no XI2 window bindings" 

941 XI2 = X11XI2Bindings() 

942 #this may fail when windows are being destroyed, 

943 #ie: when another client disconnects because we are stealing the session 

944 try: 

945 with xsync: 

946 XI2.select_xi2_events() 

947 except XError: 

948 self.xi_setup_failures += 1 

949 xinputlog("select_xi2_events() failed, attempt %i", 

950 self.xi_setup_failures, exc_info=True) 

951 return self.xi_setup_failures<10 #try again 

952 with xsync: 

953 XI2.gdk_inject() 

954 self.init_x11_filter() 

955 if self.client.server_input_devices: 

956 XI2.connect(0, "XI_HierarchyChanged", self.do_xi_devices_changed) 

957 devices = XI2.get_devices() 

958 if devices: 

959 self.client.send_input_devices("xi", devices) 

960 except Exception as e: 

961 xinputlog("enable_xi2()", exc_info=True) 

962 xinputlog.error("Error: cannot enable XI2 events") 

963 xinputlog.error(" %s", e) 

964 else: 

965 #register our enhanced event handlers: 

966 self.add_xi2_method_overrides() 

967 return False 

968 

969 def add_xi2_method_overrides(self): 

970 global WINDOW_ADD_HOOKS 

971 WINDOW_ADD_HOOKS = [XI2_Window] 

972 

973 

974 def _get_xsettings(self): 

975 try: 

976 return self._xsettings_watcher.get_settings() 

977 except: 

978 log.error("failed to get XSETTINGS", exc_info=True) 

979 return None 

980 

981 def _handle_xsettings_changed(self, *_args): 

982 settings = self._get_xsettings() 

983 log("xsettings_changed new value=%s", settings) 

984 if settings is not None: 

985 self.client.send("server-settings", {"xsettings-blob": settings}) 

986 

987 def get_resource_manager(self): 

988 try: 

989 from xpra.gtk_common.gtk_util import get_default_root_window 

990 from xpra.x11.gtk_x11.prop import prop_get 

991 root = get_default_root_window() 

992 value = prop_get(root, "RESOURCE_MANAGER", "latin1", ignore_errors=True) 

993 if value is not None: 

994 return value.encode("utf-8") 

995 except: 

996 log.error("failed to get RESOURCE_MANAGER", exc_info=True) 

997 return None 

998 

999 def _handle_root_prop_changed(self, obj, prop): 

1000 log("root_prop_changed(%s, %s)", obj, prop) 

1001 if prop=="RESOURCE_MANAGER": 

1002 rm = self.get_resource_manager() 

1003 if rm is not None: 

1004 self.client.send("server-settings", {"resource-manager" : rm}) 

1005 elif prop=="_NET_WORKAREA": 

1006 self.client.screen_size_changed("from %s event" % self._root_props_watcher) 

1007 elif prop=="_NET_CURRENT_DESKTOP": 

1008 self.client.workspace_changed("from %s event" % self._root_props_watcher) 

1009 elif prop in ("_NET_DESKTOP_NAMES", "_NET_NUMBER_OF_DESKTOPS"): 

1010 self.client.desktops_changed("from %s event" % self._root_props_watcher) 

1011 else: 

1012 log.error("unknown property %s", prop) 

1013 

1014 

1015def main(): 

1016 try: 

1017 from xpra.x11.gtk_x11.gdk_display_source import init_gdk_display_source 

1018 init_gdk_display_source() 

1019 except ImportError: 

1020 pass 

1021 from xpra.platform.gui import main as gui_main 

1022 gui_main() 

1023 

1024 

1025if __name__ == "__main__": 

1026 sys.exit(main())