diff --git a/src/setup.py b/src/setup.py index e73a1c6cef..0a8879dc2e 100755 --- a/src/setup.py +++ b/src/setup.py @@ -1181,6 +1181,30 @@ def add_gui_exe(script, icon, base_name): add_exe(script, icon, base_name, base="Win32GUI") #END OF cx_freeze SECTION else: + #py2exe recipe for win32com: + # ModuleFinder can't handle runtime changes to __path__, but win32com uses them + try: + # py2exe 0.6.4 introduced a replacement modulefinder. + # This means we have to add package paths there, not to the built-in + # one. If this new modulefinder gets integrated into Python, then + # we might be able to revert this some day. + # if this doesn't work, try import modulefinder + try: + import py2exe.mf as modulefinder + except ImportError: + import modulefinder + import win32com, sys + for p in win32com.__path__[1:]: + modulefinder.AddPackagePath("win32com", p) + for extra in ["win32com.propsys"]: #,"win32com.mapi" + __import__(extra) + m = sys.modules[extra] + for p in m.__path__[1:]: + modulefinder.AddPackagePath(extra, p) + except ImportError: + # no build path setup, no worries. + pass + import py2exe #@UnresolvedImport assert py2exe is not None EXCLUDED_DLLS = list(py2exe.build_exe.EXCLUDED_DLLS) + ["nvcuda.dll"] @@ -1193,7 +1217,7 @@ def add_gui_exe(script, icon, base_name): "packages" : packages, "includes" : external_includes, "excludes" : excludes, - "dll_excludes" : ["w9xpopen.exe", "tcl85.dll", "tk85.dll"], + "dll_excludes" : ["w9xpopen.exe", "tcl85.dll", "tk85.dll", "propsys.dll"], } if not zip_ENABLED: #the filename is actually ignored because we specify "skip_archive" diff --git a/src/xpra/client/gtk2/client.py b/src/xpra/client/gtk2/client.py index ecd6897b77..1c57df9f0f 100644 --- a/src/xpra/client/gtk2/client.py +++ b/src/xpra/client/gtk2/client.py @@ -6,7 +6,6 @@ # later version. See the file COPYING for details. import os -import sys import gobject try: #we *have to* do this as early as possible on win32.. @@ -52,9 +51,6 @@ def __init__(self): self.local_clipboard_requests = 0 self.remote_clipboard_requests = 0 - #avoid ugly "not implemented" warning on win32 - self.supports_group_leader = not sys.platform.startswith("win") - self._ref_to_group_leader = {} self._group_leader_wids = {} @@ -353,8 +349,6 @@ def window_ungrab(self): def get_group_leader(self, metadata, override_redirect): - if not self.supports_group_leader: - return None wid = metadata.intget("transient-for", -1) if wid>0: client_window = self._id_to_window.get(wid) diff --git a/src/xpra/platform/win32/gui.py b/src/xpra/platform/win32/gui.py index 746cd6620f..6f7e4ba14c 100644 --- a/src/xpra/platform/win32/gui.py +++ b/src/xpra/platform/win32/gui.py @@ -106,12 +106,39 @@ def get_monitor_workarea_for_window(handle): return None +def noop(*args): + pass + + def add_window_hooks(window): + #win32 cannot use set_group by default: + try: + window.get_window().set_group = noop + except: + pass #gtk2 to window handle: try: handle = window.get_window().handle except: return + #windows 7 onwards can use AppUserModel to emulate the group leader stuff: + try: + import win32com.propsys #@UnresolvedImport + from win32com.propsys import propsys #@UnresolvedImport + def set_group(leader): + log("win32 hooks: set_group(%s)", leader) + try: + ps = propsys.SHGetPropertyStoreForWindow(handle) + key = propsys.PSGetPropertyKeyFromName("System.AppUserModel.ID") + value = propsys.PROPVARIANTType(leader.handle) + log("win32 hooks: calling %s(%s, %s)", ps.SetValue, key, value) + ps.SetValue(key, value) + except Exception as e: + log.error("failed to set group leader: %s", e) + window.get_window().set_group = set_group + log("hooked group leader override using %s", win32com.propsys) + except Exception as e: + log("unable to implement group leader: %s", e, exc_info=True) #OR windows never have any decorations or taskbar menu if not window._override_redirect: #override set_decorated so we can preserve the taskbar menu for undecorated windows