Skip to content

Commit

Permalink
#1977 minimum changes to allow the client to run
Browse files Browse the repository at this point in the history
it doesn't render anything and everything is broken, do not use
  • Loading branch information
totaam committed Nov 14, 2023
1 parent 4311115 commit 232a118
Show file tree
Hide file tree
Showing 16 changed files with 258 additions and 152 deletions.
6 changes: 3 additions & 3 deletions xpra/client/gtk3/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

import gi
gi.require_version("Gtk", "3.0") # @UndefinedVariable
gi.require_version("Gdk", "3.0") # @UndefinedVariable
#import gi
#gi.require_version("Gtk", "3.0") # @UndefinedVariable
#gi.require_version("Gdk", "3.0") # @UndefinedVariable
3 changes: 3 additions & 0 deletions xpra/client/gtk3/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ def nwarn(notifier_name: str, e: Exception):
return ncs

def get_screen_resolution(self) -> int:
if not hasattr(Gdk, "Screen"):
# Gtk4
return -1
screen = Gdk.Screen.get_default()
if not screen:
#wayland?
Expand Down
50 changes: 35 additions & 15 deletions xpra/client/gtk3/client_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,25 +166,32 @@ def run(self) -> ExitValue:
return self.exit_code

def gtk_main(self) -> None:
log(f"GTKXpraClient.gtk_main() calling {Gtk.main}",)
Gtk.main()
if hasattr(Gtk, "main"):
log(f"GTKXpraClient.gtk_main() calling {Gtk.main}", )
Gtk.main()
else:
log("GTKXpraClient.gtk_main() calling GLib main loop")
self.run_loop()
log("GTKXpraClient.gtk_main() ended")


def quit(self, exit_code:int|ExitCode=0) -> None:
log(f"GTKXpraClient.quit({exit_code}) current exit_code={self.exit_code}")
if self.exit_code is None:
self.exit_code = exit_code
if Gtk.main_level()>0:
#if for some reason cleanup() hangs, maybe this will fire...
self.timeout_add(4*1000, self.exit)
#try harder!:
self.timeout_add(5*1000, self.force_quit)

if self.glib_mainloop:
self.exit_loop()
#if for some reason cleanup() hangs, maybe this will fire...
self.timeout_add(4*1000, self.exit)
#try harder!:
self.timeout_add(5*1000, self.force_quit)
self.cleanup()
log(f"GTKXpraClient.quit({exit_code}) cleanup done, main_level={Gtk.main_level()}")
if Gtk.main_level()>0:
log(f"GTKXpraClient.quit({exit_code}) main loop at level {Gtk.main_level()}, calling gtk quit via timeout")
self.timeout_add(500, self.exit)
if hasattr(Gtk, "main_level"):
log(f"GTKXpraClient.quit({exit_code}) cleanup done, main_level={Gtk.main_level()}")
if Gtk.main_level()>0:
log(f"GTKXpraClient.quit({exit_code}) main loop at level {Gtk.main_level()}, calling gtk quit via timeout")
self.timeout_add(500, self.exit)

def force_quit(self) -> None:
from xpra.os_util import force_quit
Expand All @@ -193,8 +200,9 @@ def force_quit(self) -> None:

def exit(self) -> None:
self.show_progress(100, "terminating")
log(f"GTKXpraClient.exit() calling {Gtk.main_quit}", )
Gtk.main_quit()
if hasattr(Gtk, "main_quit"):
log(f"GTKXpraClient.exit() calling {Gtk.main_quit}", )
Gtk.main_quit()

def cleanup(self) -> None:
log("GTKXpraClient.cleanup()")
Expand Down Expand Up @@ -246,9 +254,12 @@ def get_vrefresh(self) -> int:
rate = envint("XPRA_VREFRESH", 0)
if rate:
return rate
#try via GTK:
# try via GTK:
rates = {}
display = Gdk.Display.get_default()
if not hasattr(display, "get_n_monitors"):
# Gtk4
return -1
for m in range(display.get_n_monitors()):
monitor = display.get_monitor(m)
log(f"monitor {m} ({monitor.get_model()}) refresh-rate={monitor.get_refresh_rate()}")
Expand All @@ -263,7 +274,8 @@ def get_vrefresh(self) -> int:

def _process_startup_complete(self, packet : PacketType) -> None:
super()._process_startup_complete(packet)
Gdk.notify_startup_complete()
if hasattr(Gdk, "notify_startup_complete"):
Gdk.notify_startup_complete()


def do_process_challenge_prompt(self, prompt="password"):
Expand Down Expand Up @@ -900,6 +912,9 @@ def make_hello(self) -> dict[str,Any]:
def has_transparency(self) -> bool:
if not envbool("XPRA_ALPHA", True):
return False
if not hasattr(Gdk, "Screen"):
# Gtk4
return True
screen = Gdk.Screen.get_default()
if screen is None:
return is_Wayland()
Expand Down Expand Up @@ -1073,6 +1088,9 @@ def process_ui_capabilities(self, caps : typedict) -> None:
#this requires the "DisplayClient" mixin:
if not hasattr(self, "screen_size_changed"):
return
if not hasattr(Gdk, "Screen"):
# Gtk4
return
#always one screen per display:
screen = Gdk.Screen.get_default()
screen.connect("size-changed", self.screen_size_changed)
Expand Down Expand Up @@ -1391,6 +1409,8 @@ def find_gdk_window(self, metadata, metadata_key="transient-for"):
return None

def get_group_leader(self, wid:int, metadata, _override_redirect):
if not hasattr(Gdk, "WindowWindowClass"):
return None
def find_gdk_window(metadata_key="transient-for"):
return self.find_gdk_window(metadata, metadata_key)
win = find_gdk_window("group-leader-wid") or find_gdk_window("transient-for") or find_gdk_window("parent")
Expand Down
10 changes: 5 additions & 5 deletions xpra/client/gtk3/keyboard_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ class GTKKeyboardHelper(KeyboardHelper):

def __init__(self, *args):
KeyboardHelper.__init__(self, *args)
#used for delaying the sending of keymap changes
#(as we may be getting dozens of such events at a time)
# used for delaying the sending of keymap changes
# (as we may be getting dozens of such events at a time)
self._keymap_changing = False
self._keymap_change_handler_id = None
self._keymap = None
display = Gdk.Display.get_default()
if display:
if display and hasattr(Gdk, "Keymap"):
self._keymap = Gdk.Keymap.get_for_display(display)
self.update()
if self._keymap:
Expand Down Expand Up @@ -55,11 +55,11 @@ def keymap_changed(self, *args):
display = Gdk.Display.get_default()
self._keymap = Gdk.Keymap.get_for_display(display)
if self._keymap_changing:
#timer due already
# timer is already due
return
self._keymap_changing = True
def do_keys_changed():
#re-register the change handler:
# re-register the change handler:
self._keymap_change_handler_id = self._keymap.connect("keys-changed", self.keymap_changed)
self._keymap_changing = False
if self.locked:
Expand Down
3 changes: 3 additions & 0 deletions xpra/client/gtk3/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ def _icon_size(self) -> int:
return min(128, max(h, 24))

def set_icon(self, pixbuf:GdkPixbuf.Pixbuf) -> None:
if not hasattr(self, "get_window"):
# Gtk4..
return
super().set_icon(pixbuf)
hbi = self.header_bar_image
if hbi and WINDOW_ICON:
Expand Down
137 changes: 81 additions & 56 deletions xpra/client/gtk3/window_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,17 +196,19 @@ def parse_padding_colors(colors_str:str) -> tuple[int,int,int]:
"DND",
}

GDK_MOVERESIZE_MAP = {int(d): we for d, we in {
MoveResize.SIZE_TOPLEFT: Gdk.WindowEdge.NORTH_WEST,
MoveResize.SIZE_TOP: Gdk.WindowEdge.NORTH,
MoveResize.SIZE_TOPRIGHT: Gdk.WindowEdge.NORTH_EAST,
MoveResize.SIZE_RIGHT: Gdk.WindowEdge.EAST,
MoveResize.SIZE_BOTTOMRIGHT: Gdk.WindowEdge.SOUTH_EAST,
MoveResize.SIZE_BOTTOM: Gdk.WindowEdge.SOUTH,
MoveResize.SIZE_BOTTOMLEFT: Gdk.WindowEdge.SOUTH_WEST,
MoveResize.SIZE_LEFT: Gdk.WindowEdge.WEST,
# MOVERESIZE_SIZE_KEYBOARD,
}.items()}
GDK_MOVERESIZE_MAP = {}
if hasattr(Gdk, "WindowEdge"):
GDK_MOVERESIZE_MAP = {int(d): we for d, we in {
MoveResize.SIZE_TOPLEFT: Gdk.WindowEdge.NORTH_WEST,
MoveResize.SIZE_TOP: Gdk.WindowEdge.NORTH,
MoveResize.SIZE_TOPRIGHT: Gdk.WindowEdge.NORTH_EAST,
MoveResize.SIZE_RIGHT: Gdk.WindowEdge.EAST,
MoveResize.SIZE_BOTTOMRIGHT: Gdk.WindowEdge.SOUTH_EAST,
MoveResize.SIZE_BOTTOM: Gdk.WindowEdge.SOUTH,
MoveResize.SIZE_BOTTOMLEFT: Gdk.WindowEdge.SOUTH_WEST,
MoveResize.SIZE_LEFT: Gdk.WindowEdge.WEST,
# MOVERESIZE_SIZE_KEYBOARD,
}.items()}

GDK_SCROLL_MAP = {
Gdk.ScrollDirection.UP : 4,
Expand All @@ -223,31 +225,35 @@ def parse_padding_colors(colors_str:str) -> tuple[int,int,int]:
Gdk.ModifierType.BUTTON5_MASK : 5,
}

em = Gdk.EventMask
WINDOW_EVENT_MASK : Gdk.EventMask = em.STRUCTURE_MASK | em.KEY_PRESS_MASK | em.KEY_RELEASE_MASK \
| em.POINTER_MOTION_MASK | em.BUTTON_PRESS_MASK | em.BUTTON_RELEASE_MASK \
| em.PROPERTY_CHANGE_MASK | em.SCROLL_MASK

del em

wth = Gdk.WindowTypeHint
ALL_WINDOW_TYPES : tuple[Gdk.WindowTypeHint, ...] = (
wth.NORMAL,
wth.DIALOG,
wth.MENU,
wth.TOOLBAR,
wth.SPLASHSCREEN,
wth.UTILITY,
wth.DOCK,
wth.DESKTOP,
wth.DROPDOWN_MENU,
wth.POPUP_MENU,
wth.TOOLTIP,
wth.NOTIFICATION,
wth.COMBO,
wth.DND,
)
del wth
WINDOW_EVENT_MASK = 0
if hasattr(Gdk, "EventMask"):
em = Gdk.EventMask
WINDOW_EVENT_MASK : Gdk.EventMask = em.STRUCTURE_MASK | em.KEY_PRESS_MASK | em.KEY_RELEASE_MASK \
| em.POINTER_MOTION_MASK | em.BUTTON_PRESS_MASK | em.BUTTON_RELEASE_MASK \
| em.PROPERTY_CHANGE_MASK | em.SCROLL_MASK

del em

ALL_WINDOW_TYPES = ()
if hasattr(Gdk, "WindowTypeHint"):
wth = Gdk.WindowTypeHint
ALL_WINDOW_TYPES : tuple[Gdk.WindowTypeHint, ...] = (
wth.NORMAL,
wth.DIALOG,
wth.MENU,
wth.TOOLBAR,
wth.SPLASHSCREEN,
wth.UTILITY,
wth.DOCK,
wth.DESKTOP,
wth.DROPDOWN_MENU,
wth.POPUP_MENU,
wth.TOOLTIP,
wth.NOTIFICATION,
wth.COMBO,
wth.DND,
)
del wth
WINDOW_NAME_TO_HINT : dict[str,Gdk.WindowTypeHint] = {
wth.value_name.replace("GDK_WINDOW_TYPE_HINT_", ""): wth for wth in ALL_WINDOW_TYPES
}
Expand Down Expand Up @@ -287,13 +293,17 @@ class GTKClientWindowBase(ClientWindowBase, Gtk.Window):

def init_window(self, metadata):
self.init_max_window_size()
if self._is_popup(metadata):
window_type = Gtk.WindowType.POPUP
else:
window_type = Gtk.WindowType.TOPLEVEL
self.on_realize_cb = {}
Gtk.Window.__init__(self, type = window_type)
self.set_app_paintable(True)
if hasattr(Gtk, "WindowType"):
if self._is_popup(metadata):
window_type = Gtk.WindowType.POPUP
else:
window_type = Gtk.WindowType.TOPLEVEL
Gtk.Window.__init__(self, type=window_type)
self.set_app_paintable(True)
else:
# Gtk4
Gtk.Window.__init__(self)
self.init_drawing_area()
self.set_decorated(self._is_decorated(metadata))
self._window_state = {}
Expand All @@ -318,31 +328,38 @@ def init_window(self, metadata):
#add platform hooks
self.connect_after("realize", self.on_realize)
self.connect("unrealize", self.on_unrealize)
self.connect("enter-notify-event", self.on_enter_notify_event)
self.connect("leave-notify-event", self.on_leave_notify_event)
self.connect("key-press-event", self.handle_key_press_event)
self.connect("key-release-event", self.handle_key_release_event)
self.add_events(self.get_window_event_mask())
if DRAGNDROP and not self._client.readonly:
self.init_dragndrop()
self.init_focus()
if hasattr(Gtk, "WindowType"):
self.connect("enter-notify-event", self.on_enter_notify_event)
self.connect("leave-notify-event", self.on_leave_notify_event)
self.connect("key-press-event", self.handle_key_press_event)
self.connect("key-release-event", self.handle_key_release_event)
self.add_events(self.get_window_event_mask())
if DRAGNDROP and not self._client.readonly:
self.init_dragndrop()
self.init_focus()
ClientWindowBase.init_window(self, metadata)

def init_drawing_area(self) -> None:
widget = Gtk.DrawingArea()
widget.set_app_paintable(True)
if hasattr(widget, "set_app_paintable"):
# not available with Gtk4
widget.set_app_paintable(True)
widget.set_size_request(*self._size)
widget.show()
self.drawing_area = widget
self.init_widget_events(widget)
self.add(widget)
if hasattr(self, "add"):
self.add(widget)
else:
# Gtk4:
self.set_child(widget)

def repaint(self, x:int, y:int, w:int, h:int) -> None:
def repaint(self, x: int, y: int, w: int, h: int) -> None:
if OSX:
self.queue_draw_area(x, y, w, h)
return
widget = self.drawing_area
#log("repaint%s widget=%s", (x, y, w, h), widget)
# log("repaint%s widget=%s", (x, y, w, h), widget)
if widget:
widget.queue_draw_area(x, y, w, h)

Expand All @@ -354,6 +371,9 @@ def get_window_event_mask(self) -> Gdk.EventMask:
return mask

def init_widget_events(self, widget) -> None:
if not hasattr(widget, "add_events"):
# Nothing is available with Gtk4!?
return
widget.add_events(self.get_window_event_mask())
def motion(_w, event):
self._do_motion_notify_event(event)
Expand Down Expand Up @@ -723,8 +743,9 @@ def setup_window(self, *args):
log("setup_window%s OR=%s", args, self._override_redirect)
self.set_alpha()

self.connect("property-notify-event", self.property_changed)
self.connect("window-state-event", self.window_state_updated)
if hasattr(self, "get_window"):
self.connect("property-notify-event", self.property_changed)
self.connect("window-state-event", self.window_state_updated)

#this will create the backing:
ClientWindowBase.setup_window(self, *args)
Expand Down Expand Up @@ -1146,6 +1167,8 @@ def do_set_x11_property(self, prop_name:str, dtype:str=None, value=None) -> None
prop_set(xid, prop_name, dtype, value)

def set_class_instance(self, wmclass_name, wmclass_class) -> None:
if not hasattr(self, "set_wmclass"):
return
if not self.get_realized():
#Warning: window managers may ignore the icons we try to set
#if the wm_class value is set and matches something somewhere undocumented
Expand Down Expand Up @@ -1524,6 +1547,8 @@ def set_workspace(self, workspace) -> None:
send_wm_workspace(root.get_xid(), gdkwin.get_xid(), workspace)

def get_desktop_workspace(self) -> int:
if not hasattr(self, "get_window"):
return -1
window = self.get_window()
if window:
root = window.get_screen().get_root_window()
Expand Down
2 changes: 1 addition & 1 deletion xpra/client/mixins/clipboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ def make_clipboard_helper(self):
for helperclass in clipboard_options:
try:
return self.setup_clipboard_helper(helperclass)
except ImportError as e:
except (ImportError, AttributeError) as e:
log.error("Error: cannot instantiate %s:", helperclass)
log.estr(e)
del e
Expand Down
2 changes: 1 addition & 1 deletion xpra/client/mixins/windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,7 @@ def make_new_window(self, wid: int, wx: int, wy: int, ww: int, wh: int, bw: int,
return window

def show_window(self, wid: int, window, metadata, override_redirect: bool) -> None:
window.show_all()
window.show()
if override_redirect and self.should_force_grab(metadata):
grablog.warn("forcing grab for OR window %i, matches %s", wid, OR_FORCE_GRAB)
self.window_grab(wid, window)
Expand Down
Loading

0 comments on commit 232a118

Please sign in to comment.