xpra icon
Bug tracker and wiki

Ticket #476: global-menu-multiwindow.patch

File global-menu-multiwindow.patch, 26.9 KB (added by Antoine Martin, 2 years ago)

trying to make multi window mode more reliable (and failing)

  • tests/xpra/x11/set_gtk_menu.py

     
    5151        vbox.add(btn)
    5252        #no services to begin with:
    5353        self.window_actions_service, self.app_actions_service, self.menus_service = None, None, None
     54        from xpra.dbus.common import init_session_bus
     55        self.session_bus = init_session_bus(private=True)
    5456        self.init_defaults()
    55        
    5657
    5758    def init_defaults(self):
    5859        self.window_actions = {
     
    149150        self.app_path = self.app_path_entry.get_text().decode()
    150151        self.menu_path = u"%s/menus/appmenu" % self.app_path
    151152        self.window_path = u"%s/window/1" % self.app_path
    152         from xpra.dbus.common import init_session_bus
    153         session_bus = init_session_bus()
    154         self.bus_name = session_bus.get_unique_name().decode()
     153        self.bus_name = self.session_bus.get_unique_name().decode()
    155154
    156155    def stop_dbus_services(self):
    157156        for x in (self.window_actions_service, self.app_actions_service, self.menus_service):
     
    160159        self.window_actions_service, self.app_actions_service, self.menus_service = None, None, None
    161160
    162161    def setup_dbus_services(self):
    163         from xpra.dbus.common import init_session_bus
    164         session_bus = init_session_bus()
    165         self.window_actions_service = Actions(self.app_id, self.window_path, session_bus, self.window_actions)
    166         self.app_actions_service = Actions(self.app_id, self.app_path, session_bus, self.app_actions)
    167         self.menus_service = Menus(self.app_id, self.menu_path, session_bus, self.current_menus)
     162        self.window_actions_service = Actions(self.app_id, self.window_path, self.session_bus, self.window_actions)
     163        self.app_actions_service = Actions(self.app_id, self.app_path, self.session_bus, self.app_actions)
     164        self.menus_service = Menus(self.app_id, self.menu_path, self.session_bus, self.current_menus)
    168165
    169166    def set_X11_props(self):
    170167        w = self.get_window()
    171168        def pset(key, value):
    172169            return prop_set(w, key, "utf8", value)
    173         pset("_GTK_APP_MENU_OBJECT_PATH", self.menu_path)
     170        pset("_GTK_APPLICATION_OBJECT_PATH", self.app_path)
    174171        pset("_GTK_WINDOW_OBJECT_PATH", self.window_path)
    175         pset("_GTK_APPLICATION_OBJECT_PATH", self.app_path)
    176172        pset("_GTK_UNIQUE_BUS_NAME", self.bus_name)
    177173        pset("_GTK_APPLICATION_ID", self.app_id)
     174        pset("_GTK_APP_MENU_OBJECT_PATH", self.menu_path)
    178175
    179176    def remove_X11_props(self):
    180177        w = self.get_window()
     
    196193        self.current_menus = m
    197194        if self.menus_service:
    198195            self.menus_service.set_menus(self.current_menus)
     196        self.set_X11_props()
    199197
    200198    def new_window(self, *args):
    201199        w = WindowWithMenu()
     
    207205
    208206
    209207def main(args):
    210     from xpra.dbus.common import init_session_bus
    211208    from xpra.dbus.common import loop_init
    212209    loop_init()
    213     init_session_bus()
    214210    w = WindowWithMenu()
    215211    w.show_all()
    216212    gtk.main()
  • xpra/client/client_window_base.py

     
    518518        pass
    519519
    520520    def show_session_info(self, *args):
    521         self._client.show_session_info(*args)
     521        self._client.show_session_info()
    522522
     523    def show_window_info(self, *args):
     524        self._client.show_window_info(self._id)
     525
    523526    def show_start_new_command(self, *args):
    524         self._client.show_start_new_command(*args)
     527        self._client.show_start_new_command()
    525528
    526529
    527530    def log(self, message=""):
  • xpra/client/gtk_base/gtk_client_base.py

     
    164164        self.session_info.set_args(*args)
    165165        self.session_info.show_all()
    166166
     167    def show_window_info(self, wid):
     168        window = self._id_to_window.get(wid)
     169        if self.window_info and not self.window_info.is_closed:
     170            #exists already: just raise its window:
     171            self.window_info.set_wid(wid, window)
     172            self.window_info.present()
     173            return
     174        pixbuf = self.get_pixbuf("statistics.png")
     175        if not pixbuf:
     176            pixbuf = self.get_pixbuf("xpra.png")
     177        from xpra.client.gtk_base.window_info import WindowInfo
     178        self.window_info = WindowInfo(self, pixbuf, self.get_pixbuf)
     179        self.window_info.set_wid(wid, window)
     180        self.window_info.show_all()
     181
    167182    def show_bug_report(self, *args):
    168183        if self.bug_report:
    169184            self.bug_report.show()
  • xpra/dbus/common.py

     
    1212    return _loop
    1313
    1414_session_bus = None
    15 def init_session_bus():
     15def init_session_bus(private=False):
    1616    global _session_bus
    17     if _session_bus:
     17    if _session_bus and not private:
    1818        return _session_bus
    1919    loop_init()
    2020    import dbus
    21     _session_bus = dbus.SessionBus()
     21    _session_bus = dbus.SessionBus(private=private)
    2222    return _session_bus
    2323
    2424_system_bus = None
  • xpra/dbus/gtk_menuactions.py

     
    4343
    4444class Actions(dbus.service.Object):
    4545    SUPPORTS_MULTIPLE_OBJECT_PATHS = True
    46  
     46    SUPPORTS_MULTIPLE_CONNECTIONS = True
     47
    4748    def __init__(self, name, path, session_bus, actions={}, default_callback=None):
    4849        self.actions = actions
    4950        self.default_callback = default_callback
     
    6263        # - a{sv} a list of actions that had their state changed
    6364        # - a{s(bgav)} a list of new actions added in the same format as the return value of the DescribeAll method"""
    6465        removed = []
    65         enabled_changed = []
    66         state_changed = []
    67         added = []
     66        enabled_changed = {}
     67        state_changed = {}
     68        added = {}
    6869        all_actions = list(set(oldactions.keys() + actions.keys()))
    6970        self.log(".set_actions(..) all actions=%s", csv(all_actions))
    7071        for action in all_actions:
     
    7374                self.log(".set_actions(..) removed %s", action)
    7475            elif action not in oldactions:
    7576                action_def = actions[action]
    76                 added.append(dbus.Struct(self._make_action(*action_def)))
     77                added[action] = dbus.Struct(self._make_action(*action_def))
    7778                self.log(".set_actions(..) added %s=%s", action, action_def)
    7879            else:   #maybe changed state?
    7980                oldaction = oldactions.get(action, [False, None, None]) #default value should be redundant
    8081                newaction = actions.get(action, [False, None, None])    #default value should be redundant
    8182                if oldaction[0]!=newaction[0]:
    82                     enabled_changed.append((ds(action), dbus.Boolean(newaction[0])))
     83                    enabled_changed[action] = bool(newaction[0])
    8384                    self.log(".set_actions(..) enabled changed for %s from %s to %s", action, oldaction[0], newaction[0])
    8485                if oldaction[2]!=newaction[2]:
    85                     state_changed.append((ds(action), newaction[2]))
     86                    state_changed[action] = newaction[2]
    8687                    self.log(".set_actions(..) state changed for %s from %s to %s", action, oldaction[2], newaction[2])
    8788        self.log(".set_actions(..) changes: %s", (removed, enabled_changed, state_changed, added))
    8889        if removed or enabled_changed or state_changed or added:
    89             self.Changed(dbus.Array(removed), dbus.Array(enabled_changed), dbus.Array(state_changed), dbus.Array(added))
     90            self.Changed(dbus.Array(removed),
     91                         dbus.Array(dbus.Dictionary(enabled_changed)),
     92                         dbus.Array(dbus.Dictionary(state_changed)),
     93                         dbus.Array(dbus.Dictionary(added)))
    9094
     95    @dbus.service.signal(ACTIONS, signature='asa{sb}a{sv}a{s(bgav)}')
     96    def Changed(self, removed, enabled_changed, state_changed, added):
     97        pass
    9198
     99
    92100    def log(self, fmt, *args):
    93101        log("%s(%s:%s)"+fmt, ACTIONS,  busnamestr(self), self._object_path, *args)
    94102    def warn(self, fmt, *args):
     
    98106    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='s', out_signature='v')
    99107    def Get(self, property_name):
    100108        raise dbus.exceptions.DBusException("this object does not have any properties")
    101  
     109
    102110    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='', out_signature='a{sv}')
    103111    def GetAll(self, interface_name):
    104112        return []
     
    121129
    122130    def _make_action(self, enabled, ptype, state, callback=None):
    123131        return dbus.Boolean(enabled), dbus.Signature(ptype), dbus.Array(state)
    124  
     132
    125133    @dbus.service.method(ACTIONS, in_signature="s", out_signature="(bgav)")
    126134    def Describe(self, action):
    127135        v = self.actions.get(action)
     
    157165        paction = str(action)
    158166        pstate = n(state)
    159167        ppdata = n(pdata)
    160         self.log(".Activate(%s, %s, %s) calling %s%s", action, state, pdata, cb, (self, paction, pstate, ppdata))
     168        self.log(".Activate%s calling %s%s", (action, state, pdata), cb, (self, paction, pstate, ppdata))
    161169        try:
    162170            cb(self, paction, pstate, ppdata)
    163171        except Exception:
     
    168176    def SetState(self, s, v, a):
    169177        return
    170178
    171     @dbus.service.signal(ACTIONS, signature='asa{sb}a{sv}a{s(bgav)}')
    172     def Changed(self, removed, enabled_changed, state_changed, added):
    173         pass
    174179
    175 
    176180class Menus(dbus.service.Object):
    177181    SUPPORTS_MULTIPLE_OBJECT_PATHS = True
    178  
     182    SUPPORTS_MULTIPLE_CONNECTIONS = True
     183
    179184    def __init__(self, name, path, session_bus, menus={}):
    180185        self.menus = menus
    181186        self.subscribed = {}
     
    204209                self.log(".set_menus(..) found change at group=%i, menu_id=%i : from %s to %s", group_id, menu_id, oldmenu, menu)
    205210                delcount = len(oldmenu)     #remove all
    206211                insert = [self._make_menu_item(menu[i]) for i in range(len(menu))]
    207                 changed.append((di(group_id), di(menu_id), di(0), di(delcount), dbus.Array(insert)))
     212                changed.append(dbus.Struct(di(group_id), di(menu_id), di(0), di(delcount), dbus.Array(dbus.Array(insert))))
    208213        self.log(".set_menus(..) changed: %s", changed)
    209214        if changed:
    210215            self.Changed(dbus.Array(changed))
    211216
     217    @dbus.service.signal(MENUS, signature='a(uuuuaa{sv})')
     218    def Changed(self, changes):
     219        pass
     220
     221
    212222    def log(self, fmt, *args):
    213223        log("%s(%s:%s)"+fmt, MENUS,  busnamestr(self), self._object_path, *args)
    214224    def warn(self, fmt, *args):
     
    218228    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ss', out_signature='v')
    219229    def Get(self, interface_name, property_name):
    220230        raise dbus.exceptions.DBusException("this object does not have any properties")
    221  
     231
    222232    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='s', out_signature='a{sv}')
    223233    def GetAll(self, interface_name):
    224234        return []
     
    281291                    menus.append((group, dbus.UInt32(n), menu_items))
    282292        self.log(".Start(%s)=%s", ids, menus)
    283293        return menus
    284  
     294
    285295    @dbus.service.method(MENUS, in_signature="au")
    286296    def End(self, ids):
    287297        for group in ids:
     
    288298            c = self.subscribed.get(group, 0)
    289299            if c > 0:
    290300                self.subscribed[group] = c - 1
    291  
    292     @dbus.service.signal(MENUS, signature='a(uuuuaa{sv})')
    293     def Changed(self, changes):
    294         pass
    295301
    296302
    297303def get_actions_interface(bus_name, object_path):
     
    375381                        target = d.get("target")
    376382                        if target:
    377383                            menu["target"] = [n(target[x]) for x in range(len(target))]
     384                accel = d.get("accel")
     385                if accel:
     386                    menu["accel"] = accel
    378387                if menu:
    379388                    dmenus.append(menu)
    380389            menus.setdefault(ni(sgroup), {})[ni(menuno)] = dmenus
  • xpra/platform/xposix/gtkmenu_tray.py

     
    5454        log("_info_cb%s", args)
    5555        self.client.show_session_info()
    5656
     57    def _window_info_cb(self, *args):
     58        log("_window_info_cb%s", args)
     59        wid = 0
     60        self.client.show_window_info(wid)
     61
    5762    def _disconnect_cb(self, *args):
    5863        log("_disconnect_cb%s", args)
    5964        self.exit_cb()
     
    7176                                            'xpra-about'         : (True, '', (), self._about_cb),
    7277                                            'xpra-disconnect'    : (True, '', (), self._disconnect_cb),
    7378                                            },
    74                  'window-actions':         {},
     79                 'window-actions':         {
     80                                            'xpra-window-info'   : (True, '', (), self._window_info_cb),
     81                                            },
    7582                 'window-menu':            {0:
    7683                                               {0: ({':section': (0, 1)}, {':section': (0, 2)}),
    77                                                 1: ({'action': 'app.xpra-info',          'label': '_Session Info'},
    78                                                     {'action': 'app.xpra-about',         'label': '_About'},
     84                                                1: (
     85                                                    {'action': 'app.xpra-about',         'label': '_About Xpra'},
     86                                                    {'action': 'app.xpra-info',          'label': '_Session Info'},
     87                                                    {'action': 'app.xpra-window-info',   'label': '_Window Info'},
    7988                                                   ),
    8089                                                2: ({'action': 'app.xpra-disconnect',    'label': '_Disconnect'},),
    8190                                               }
     
    8392               }
    8493        from xpra.x11.gtk_x11 import menu
    8594        menu.fallback_menus = menus
    86         #menu.our_menu = "Xpra", menus
     95        #FIXME: we should trigger a refresh of the dbus menu...
     96        #(rather than wait for the next window event)
  • xpra/platform/xposix/gui.py

     
    1515traylog = Logger("posix", "menu")
    1616menulog = Logger("posix", "menu")
    1717
    18 from xpra.gtk_common.gobject_compat import get_xid, is_gtk3
     18from xpra.gtk_common.gobject_compat import get_xid, is_gtk3, import_gobject
    1919
    2020device_bell = None
    2121
     
    9797
    9898def _set_gtk_x11_window_menu(add, wid, window, menus, application_action_callback=None, window_action_callback=None):
    9999    from xpra.x11.gtk_x11.menu import setup_dbus_window_menu
    100     from xpra.x11.gtk_x11.prop import prop_set, prop_del
     100    from xpra.x11.gtk_x11.prop import prop_get, prop_set, prop_del
    101101    window_props = setup_dbus_window_menu(add, wid, menus, application_action_callback, window_action_callback)
    102102    #window_props may contains X11 window properties we have to clear or set
    103103    if not window_props:
     
    107107        #(but we still want to call setup_dbus_window_menu above to ensure we clear things up!)
    108108        return
    109109    menulog("will set/remove the following window properties for wid=%i: %s", wid, window_props)
    110     try:
    111         from xpra.gtk_common.error import xsync
    112         with xsync:
    113             for k,v in window_props.items():
    114                 if v is None:
    115                     prop_del(window, k)
    116                 else:
    117                     vtype, value = v
    118                     prop_set(window, k, vtype, value)
    119     except Exception as e:
    120         menulog.error("Error setting menu window properties:")
    121         menulog.error(" %s", e)
     110    def export_x11_props():
     111        try:
     112            from xpra.gtk_common.error import xsync
     113            with xsync:
     114                for k,v in window_props.items():
     115                    if v is None:
     116                        prop_del(window, k)
     117                    else:
     118                        vtype, value = v
     119                        try:
     120                            cv = prop_get(window, k, vtype, ignore_errors=True, raise_xerrors=True)
     121                            if cv and cv==value:
     122                                menulog("value for %s unchanged: %s", k, cv)
     123                                continue        #already set
     124                        except:
     125                            pass
     126                        prop_set(window, k, vtype, value)
     127        except Exception as e:
     128            menulog.error("Error setting menu window properties:")
     129            menulog.error(" %s", e)
     130    gobject = import_gobject()
     131    gobject.timeout_add(1000, export_x11_props)
    122132
    123133
    124134_has_gtk_menu_support = None
  • xpra/x11/gtk_x11/menu.py

     
    77from xpra.log import Logger
    88log = Logger("menu")
    99
    10 from xpra.util import typedict, bytestostr
     10from xpra.util import typedict, bytestostr, AtomicInteger
    1111
    1212
    1313def has_gtk_menu_support(root_window):
     
    3232    return show_window_menu
    3333
    3434
     35#for each window id, keep track of the current menu services we have created:
     36#wid -> (app_id, [app_actions_service, window_actions_service, window_menu_service])
    3537window_menus = {}
     38#keep track of all the services we have created, and the usage count for each service
     39#(the same service may be available under different paths):
     40#(service_class, name, path) -> (service, counter)
    3641window_menu_services = weakref.WeakValueDictionary()
    3742
    3843fallback_menus = {}
    3944
     45def unref_service(service):
     46    v = service.reference_count
     47    count = v.decrease()
     48    log("unref service %s, new reference count=%i", service, count)
     49    if count<=0:
     50        try:
     51            service.remove_from_connection()
     52        except Exception:
     53            log.error("Error removing %s", service, exc_info=True)
     54
     55def get_service(current_service, service_class, name, path, session_bus, *args):
     56    """ find the service by name and path, or create one """
     57    global window_menu_services
     58    service = None #window_menu_services.get((service_class, name, path))
     59    log("get_service%s existing service=%s", (current_service, service_class, name, path, args), service)
     60    if current_service:
     61        if current_service==service:
     62            log(" keeping current service")
     63            #no change
     64            return current_service, True
     65        #drop the old service
     66        unref_service(current_service)
     67        current_service = None
     68    if service is None:
     69        service = service_class(name, path, session_bus, *args)
     70        service.reference_count = AtomicInteger(0)
     71        window_menu_services[(service_class, name, path)] = service
     72    else:
     73        log(" reusing matching service")
     74    service.reference_count.increase()
     75    log("get_service service.reference_count=%i", service.reference_count.get())
     76    return service, False
     77
     78
    4079def setup_dbus_window_menu(add, wid, menus, application_action_callback=None, window_action_callback=None):
     80    global window_menus, fallback_menus
     81    cur_app_id, services  = window_menus.get(wid, (None, [None, None, None]))
     82    app_actions_service, window_actions_service, window_menu_service = services
     83
     84    def unref_all_services():
     85        #frees up all the dbus services if their refcount reaches 0:
     86        for x in services:
     87            if x:
     88                unref_service(x)
     89        try:
     90            del window_menus[wid]
     91        except:
     92            pass
    4193    def nomenu():
     94        log("nomenu()")
     95        unref_all_services()
    4296        #tell caller to clear all properties if they exist:
    4397        return {
    4498                "_GTK_APP_MENU_OBJECT_PATH"     : None,
     
    47101                "_GTK_UNIQUE_BUS_NAME"          : None,
    48102                "_GTK_APPLICATION_ID"           : None
    49103                }
    50     if add is False:
     104
     105    enabled = menus.get("enabled", False)
     106    if (len(menus)==0 or (enabled is False)) and fallback_menus:
     107        #use fallback menus if the application menus are empty or disabled
     108        log("using fallback menu")
    51109        return nomenu()
    52     global window_menu_services, window_menus, fallback_menus
    53     if len(menus)==0 and fallback_menus:
    54110        menus = fallback_menus
     111    enabled = menus.get("enabled", False)
     112    if len(menus)==0 or (add is False) or (not enabled):
     113        #remove everything
     114        return nomenu()
    55115    #ie: menu = {
    56116    #         'enabled': True,
    57117    #         'application-id':         'org.xpra.ExampleMenu',
     
    65125    #                }
    66126    #             }
    67127    #           }
    68     enabled = menus.get("enabled", False)
    69     app_actions_service, window_actions_service, window_menu_service = None, None, None
    70     def remove_services(*args):
    71         """ removes all the services if they are not longer used by any windows """
    72         for x in (app_actions_service, window_actions_service, window_menu_service):
    73             if x:
    74                 if x not in window_menu_services.values():
    75                     try:
    76                         x.remove_from_connection()
    77                     except Exception as e:
    78                         log.warn("Error removing %s: %s", x, e)
    79         try:
    80             del window_menus[wid]
    81         except:
    82             pass
    83     if enabled:
    84         m = typedict(menus)
    85         app_id          = bytestostr(m.strget("application-id", b"org.xpra.Window%i" % wid)).decode()
    86         app_actions     = m.dictget("application-actions")
    87         window_actions  = m.dictget("window-actions")
    88         window_menu     = m.dictget("window-menu")
    89     if wid in window_menus:
    90         #update, destroy or re-create the services:
    91         app_actions_service, window_actions_service, window_menu_service, cur_app_id = window_menus[wid]
    92         if not enabled or cur_app_id!=app_id:
    93             remove_services()   #falls through to re-create them if enabled is True
    94             app_actions_service, window_actions_service, window_menu_service = None, None, None
    95         else:
    96             #update them:
    97             app_actions_service.set_actions(app_actions)
    98             window_actions_service.set_actions(window_actions)
    99             window_menu_service.set_menus(window_menu)
    100             return
    101     if not enabled:
    102         #tell caller to clear all properties if they exist:
    103         return nomenu()
     128    m = typedict(menus)
     129    #app_id          = bytestostr(m.strget("application-id", b"Window%i" % wid)).decode()
     130    app_id          = bytestostr(b"Window%i" % wid).decode()
     131    app_actions     = m.dictget("application-actions")
     132    window_actions  = m.dictget("window-actions")
     133    window_menu     = m.dictget("window-menu")
     134
     135    if cur_app_id is not None and cur_app_id!=app_id:
     136        #we can't change the path of a service, so re-create them:
     137        log("app-id has changed from %s to %s, re-creating the services %s", cur_app_id, app_id, services)
     138        unref_all_services()
     139        app_actions_service, window_actions_service, window_menu_service = None, None, None
     140        for i in range(3):
     141            services[i] = None
     142    window_menus[wid] = app_id, services         
    104143    #make or re-use services:
    105144    try:
    106         NAME_PREFIX = "org.xpra."
     145        NAME_PREFIX = u"org.xpra.client."
    107146        from xpra.util import strtobytes
    108147        from xpra.dbus.common import init_session_bus
    109148        from xpra.dbus.gtk_menuactions import Menus, Actions
    110         session_bus = init_session_bus()
     149        session_bus = init_session_bus(private=True)    #(menus!=fallback_menus))
    111150        bus_name = session_bus.get_unique_name().decode()
    112151        name = app_id
    113152        for strip in ("org.", "gtk.", "xpra.", "gnome."):
     
    114153            if name.startswith(strip):
    115154                name = name[len(strip):]
    116155        name = NAME_PREFIX + name
    117         log("normalized named(%s)=%s", app_id, name)
    118156
    119         def get_service(service_class, name, path, *args):
    120             """ find the service by name and path, or create one """
    121             service = window_menu_services.get((service_class, name, path))
    122             if service is None:
    123                 service = service_class(name, path, session_bus, *args)
    124                 window_menu_services[(service_class, name, path)] = service
    125             return service
    126 
    127157        app_path = strtobytes("/"+name.replace(".", "/")).decode()
    128         app_actions_service = get_service(Actions, name, app_path, app_actions, application_action_callback)
     158        app_actions_service, keep = get_service(app_actions_service, Actions, name, app_path, session_bus, app_actions, application_action_callback)
     159        if keep:
     160            app_actions_service.set_actions(app_actions)
     161        else:
     162            services[0] = app_actions_service
    129163
    130164        #this one should be unique and therefore not re-used? (only one "window_action_callback"..)
    131165        window_path = u"%s/window/%s" % (app_path, wid)
    132         window_actions_service = get_service(Actions, name, window_path, window_actions, window_action_callback)
     166        window_actions_service, keep = get_service(window_actions_service, Actions, name, window_path, session_bus, window_actions, window_action_callback)
     167        if keep:
     168            window_actions_service.set_actions(window_actions)
     169        else:
     170            services[1] = window_actions_service
    133171
    134172        menu_path = u"%s/menus/appmenu" % app_path
    135         window_menu_service = get_service(Menus, app_id, menu_path, window_menu)
    136         window_menus[wid] = app_actions_service, window_actions_service, window_menu_service, app_id
     173        window_menu_service, keep = get_service(window_menu_service, Menus, name, menu_path, session_bus, window_menu)
     174        if keep:
     175            window_menu_service.set_menus(window_menu)
     176        else:
     177            services[2] = window_menu_service
    137178
    138179        return {
    139180                "_GTK_APP_MENU_OBJECT_PATH"     : ("utf8", menu_path),
     
    140181                "_GTK_WINDOW_OBJECT_PATH"       : ("utf8", window_path),
    141182                "_GTK_APPLICATION_OBJECT_PATH"  : ("utf8", app_path),
    142183                "_GTK_UNIQUE_BUS_NAME"          : ("utf8", bus_name),
    143                 "_GTK_APPLICATION_ID"           : ("utf8", app_id),
     184                "_GTK_APPLICATION_ID"           : ("utf8", name),
    144185               }
    145186    except Exception:
    146187        log.error("Error: cannot parse or apply menu:", exc_info=True)
    147         remove_services()
    148188        return nomenu()