Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ service to run. `mate-hud.py` tries to get the menu of the currently
focused window, lists possible actions and asks the user which one to
run. `mate-hud.py`, binds itself to the `Alt_L` keyboard
shortcut by default (can be changed in the settings GUI).
When no compatible global menu applet is detected, `mate-hud` now asks
the registrar to quit through its DBus `org.freedesktop.Application.Quit`
method and only falls back to terminating the process if that fails.

### Settings

Expand Down
85 changes: 74 additions & 11 deletions usr/lib/mate-hud/mate-hud
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,6 @@ def kill_process(name):
pass

def terminate_appmenu_registrar():
# TODO:
# - Use Dbus Quit method.
appmenu_loaded = False
if process_running('mate-panel'):
applets = get_list( 'org.mate.panel', '/org/mate/panel/general/', 'object-id-list')
Expand Down Expand Up @@ -208,7 +206,13 @@ def terminate_appmenu_registrar():
break

if process_running('appmenu-registrar') and not appmenu_loaded:
kill_process('appmenu-registrar')
try:
bus = dbus.SessionBus()
registrar = bus.get_object('com.canonical.AppMenu.Registrar', '/com/canonical/AppMenu/Registrar')
dbus.Interface(registrar, dbus_interface='org.freedesktop.Application').Quit()
except dbus.exceptions.DBusException as exc:
logging.info('Failed to quit appmenu-registrar over dbus: %s', exc)
kill_process('appmenu-registrar')

def get_running_panels():
panels = []
Expand Down Expand Up @@ -573,10 +577,22 @@ def try_appmenu_interface(window_id):
logging.debug('Unable to access dbusmenu items.')
return False

# Chromium-based browsers expose a buggy dbusmenu implementation that
# blocks AboutToShow; treat them specially to avoid locking the HUD.
current_win_name = (STORE.current_win_name or '').lower()
dbusmenu_bus_name = str(dbusmenu_bus).lower()
chromium_tokens = ('chrom', 'brave', 'vivaldi', 'ungoogled', 'microsoft-edge', 'msedge')
chromium_like = any(token in current_win_name for token in chromium_tokens) or \
any(token in dbusmenu_bus_name for token in chromium_tokens)
dbusmenu_call_timeout = 1.0 if chromium_like else None
if chromium_like:
logging.debug('Detected Chromium-like menu source; applying defensive DBus timeouts.')

# --- Valid menu, so init rofi process to capture keypresses.
init_rofi()

dbusmenu_root_item = dbusmenu_object_iface.GetLayout(0, 0, ["label", "children-display"])
root_layout_kwargs = {'timeout': dbusmenu_call_timeout} if dbusmenu_call_timeout else {}
dbusmenu_root_item = dbusmenu_object_iface.GetLayout(0, 0, ["label", "children-display"], **root_layout_kwargs)
dbusmenu_item_dict = dict()

#For excluding items which have no action
Expand All @@ -587,16 +603,43 @@ def try_appmenu_interface(window_id):
item_id = item[0]
item_props = item[1]

# expand if necessary
# expand if necessary, but tolerate implementations that fail to answer
if 'children-display' in item_props:
dbusmenu_object_iface.AboutToShow(item_id)
dbusmenu_object_iface.Event(item_id, "opened", "not used", dbus.UInt32(time.time())) #fix firefox
opened_timestamp = dbus.UInt32(int(time.time()))
if chromium_like:
try:
dbusmenu_object_iface.Event(item_id, "opened", "not used", opened_timestamp)
except dbus.exceptions.DBusException as exc:
logging.debug('Pre-AboutToShow opened event failed for item %s: %s', item_id, exc)
try:
if dbusmenu_call_timeout:
dbusmenu_object_iface.AboutToShow(item_id, timeout=dbusmenu_call_timeout)
else:
dbusmenu_object_iface.AboutToShow(item_id)
except dbus.exceptions.DBusException as exc:
logging.debug('AboutToShow failed for item %s: %s', item_id, exc)
try:
# Some apps (e.g. Firefox) need an opened event to populate children
dbusmenu_object_iface.Event(item_id, "opened", "not used", opened_timestamp)
except dbus.exceptions.DBusException as exc:
logging.debug('Event("opened") failed for item %s: %s', item_id, exc)

layout_kwargs = {'timeout': dbusmenu_call_timeout} if dbusmenu_call_timeout else {}
try:
item = dbusmenu_object_iface.GetLayout(item_id, 1, ["label", "children-display"])[1]
except:
layout = dbusmenu_object_iface.GetLayout(item_id, 1, ["label", "children-display"], **layout_kwargs)
item = layout[1]
except (dbus.exceptions.DBusException, IndexError) as exc:
logging.debug('GetLayout failed for item %s: %s', item_id, exc)
return
except TypeError as exc:
logging.debug('GetLayout returned unexpected payload for item %s: %s', item_id, exc)
return

item_children = item[2]
try:
item_children = item[2]
except (IndexError, TypeError):
logging.debug('Layout missing children for item %s', item_id)
item_children = []

if 'label' in item_props:
new_path = path + " > " + item_props['label']
Expand All @@ -614,6 +657,26 @@ def try_appmenu_interface(window_id):
expanse_all_menu_with_dbus(child, False, new_path)

expanse_all_menu_with_dbus(dbusmenu_root_item[1], True, "")
if not dbusmenu_item_dict:
logging.debug('AppMenu interface returned no actionable menu items; falling back to alternative interfaces.')
if STORE.rofi_process:
try:
STORE.rofi_process.stdin.close()
except Exception:
pass
try:
STORE.rofi_process.terminate()
except ProcessLookupError:
pass
try:
STORE.rofi_process.wait(timeout=0.2)
except (subprocess.TimeoutExpired, AttributeError):
try:
STORE.rofi_process.kill()
except ProcessLookupError:
pass
STORE.rofi_process = None
return False
menu_result = get_menu()

# --- Use dmenu result
Expand All @@ -629,7 +692,7 @@ def try_appmenu_interface(window_id):
dbusmenu_level1_items = dbusmenu_object_iface.GetLayout(0, 1, ["label"])[1]
for item in dbusmenu_level1_items[2]:
item_id = item[0]
dbusmenu_object_iface.Event(item_id, "closed", "not used", dbus.UInt32(time.time()))
dbusmenu_object_iface.Event(item_id, "closed", "not used", dbus.UInt32(int(time.time())))

return True

Expand Down