dotfiles/.i3/i3-hud-menu.py

168 lines
5.7 KiB
Python
Executable file

#!/usr/bin/env python3
import dbus
import subprocess
"""
format_label_list
"""
def format_label_list(label_list):
head, *tail = label_list
result = head
for label in tail:
result = result + " > " + label
return result
"""
try_appmenu_interface
"""
def try_appmenu_interface(window_id):
# --- Get Appmenu Registrar DBus interface
session_bus = dbus.SessionBus()
appmenu_registrar_object = session_bus.get_object('com.canonical.AppMenu.Registrar', '/com/canonical/AppMenu/Registrar')
appmenu_registrar_object_iface = dbus.Interface(appmenu_registrar_object, 'com.canonical.AppMenu.Registrar')
# --- Get dbusmenu object path
try:
dbusmenu_bus, dbusmenu_object_path = appmenu_registrar_object_iface.GetMenuForWindow(window_id)
except dbus.exceptions.DBusException:
return
# --- Access dbusmenu items
dbusmenu_object = session_bus.get_object(dbusmenu_bus, dbusmenu_object_path)
dbusmenu_object_iface = dbus.Interface(dbusmenu_object, 'com.canonical.dbusmenu')
dbusmenu_items = dbusmenu_object_iface.GetLayout(0, -1, ["label"])
dbusmenu_item_dict = dict()
""" explore_dbusmenu_item """
def explore_dbusmenu_item(item, label_list):
item_id = item[0]
item_props = item[1]
item_children = item[2]
if 'label' in item_props:
new_label_list = label_list + [item_props['label']]
else:
new_label_list = label_list
# FIXME: This is not excluding all unactivable menuitems.
if len(item_children) == 0:
dbusmenu_item_dict[format_label_list(new_label_list)] = item_id
else:
for child in item_children:
explore_dbusmenu_item(child, new_label_list)
explore_dbusmenu_item(dbusmenu_items[1], [])
# --- Run dmenu
dmenu_string = ''
head, *tail = dbusmenu_item_dict.keys()
dmenu_string = head
for m in tail:
dmenu_string += '\n'
dmenu_string += m
dmenu_cmd = subprocess.Popen(['dmenu', '-i', '-l', '10', '-fn', 'xos4 Terminus'], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
dmenu_cmd.stdin.write(dmenu_string.encode('utf-8'))
dmenu_result = dmenu_cmd.communicate()[0].decode('utf8')
dmenu_cmd.stdin.close()
# --- Use dmenu result
if dmenu_result in dbusmenu_item_dict:
action = dbusmenu_item_dict[dmenu_result]
print(dbusmenu_object_iface.Event(action, 'clicked', 0, 0))
"""
try_gtk_interface
"""
def try_gtk_interface(gtk_bus_name_cmd, gtk_object_path_cmd):
gtk_bus_name = gtk_bus_name_cmd.split(' ')[2].split('\n')[0].split('"')[1]
print(gtk_object_path_cmd)
gtk_object_path = gtk_object_path_cmd.split(' ')[2].split('\n')[0].split('"')[1]
print("GTK MenuModel Bus name and object path: ", gtk_bus_name, gtk_object_path)
# --- Ask for menus over DBus ---
session_bus = dbus.SessionBus()
gtk_menubar_object = session_bus.get_object(gtk_bus_name, gtk_object_path)
gtk_menubar_object_iface = dbus.Interface(gtk_menubar_object, dbus_interface='org.gtk.Menus')
gtk_action_object_actions_iface = dbus.Interface(gtk_menubar_object, dbus_interface='org.gtk.Actions')
gtk_menubar_results = gtk_menubar_object_iface.Start([x for x in range(1024)])
# --- Construct menu list ---
gtk_menubar_menus = dict()
for gtk_menubar_result in gtk_menubar_results:
gtk_menubar_menus[(gtk_menubar_result[0], gtk_menubar_result[1])] = gtk_menubar_result[2]
gtk_menubar_action_dict = dict()
gtk_menubar_target_dict = dict()
""" explore_menu """
def explore_menu(menu_id, label_list):
for menu in gtk_menubar_menus[menu_id]:
if 'label' in menu:
menu_label = menu['label'].replace('_', '')
else:
menu_label = '?'
new_label_list = label_list + [menu_label]
formatted_label = format_label_list(new_label_list)
if 'action' in menu:
menu_action = menu['action']
gtk_menubar_action_dict[formatted_label] = menu_action
if 'target' in menu:
menu_target = menu['target']
gtk_menubar_target_dict[formatted_label] = menu_target
if ':section' in menu:
menu_section = menu[':section']
section_menu_id = (menu_section[0], menu_section[1])
explore_menu(section_menu_id, label_list)
if ':submenu' in menu:
menu_submenu = menu[':submenu']
submenu_menu_id = (menu_submenu[0], menu_submenu[1])
explore_menu(submenu_menu_id, new_label_list)
explore_menu((0,0), [])
# --- Run dmenu
dmenu_string = ''
head, *tail = gtk_menubar_action_dict.keys()
dmenu_string = head
for m in tail:
dmenu_string += '\n'
dmenu_string += m
dmenu_cmd = subprocess.Popen(['dmenu', '-i', '-l', '10', '-fn', 'xos4 Terminus'], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
dmenu_cmd.stdin.write(dmenu_string.encode('utf-8'))
dmenu_result = dmenu_cmd.communicate()[0].decode('utf8')
dmenu_cmd.stdin.close()
# --- Use dmenu result
if dmenu_result in gtk_menubar_action_dict:
action = gtk_menubar_action_dict[dmenu_result]
print('GTK Action :', action)
gtk_action_object_actions_iface.Activate(action.replace('unity.', ''), [], dict())
"""
main
"""
# --- Get X Window ID ---
window_id_cmd = subprocess.check_output(['xprop', '-root', '-notype', '_NET_ACTIVE_WINDOW']).decode('utf-8')
window_id = window_id_cmd.split(' ')[4].split('\n')[0]
print('Window id is :', window_id)
# --- Get GTK MenuModel Bus name ---
gtk_bus_name_cmd = subprocess.check_output(['xprop', '-id', window_id, '-notype', '_GTK_UNIQUE_BUS_NAME']).decode('utf-8')
gtk_object_path_cmd = subprocess.check_output(['xprop', '-id', window_id, '-notype', '_GTK_MENUBAR_OBJECT_PATH']).decode('utf-8')
if gtk_bus_name_cmd == '_GTK_UNIQUE_BUS_NAME: not found.\n' or gtk_object_path_cmd == '_GTK_MENUBAR_OBJECT_PATH: not found.\n':
try_appmenu_interface(int(window_id, 16))
else:
try_gtk_interface(gtk_bus_name_cmd, gtk_object_path_cmd)