111 lines
2.5 KiB
Python
Executable File
111 lines
2.5 KiB
Python
Executable File
import subprocess
|
|
import sys
|
|
import time
|
|
|
|
import gi
|
|
|
|
gi.require_version('Gtk', '3.0')
|
|
|
|
from gi.repository import Gio # noqa
|
|
from gi.repository import GLib # noqa
|
|
|
|
DMENU_CMD = ['rofi', '-dmenu', '-i', '-no-sort']
|
|
|
|
|
|
class Bus:
|
|
def __init__(self, conn, name, path):
|
|
self.conn = conn
|
|
self.name = name
|
|
self.path = path
|
|
|
|
def call_sync(self, interface, method, params, params_type, return_type):
|
|
return self.conn.call_sync(
|
|
self.name,
|
|
self.path,
|
|
interface,
|
|
method,
|
|
GLib.Variant(params_type, params),
|
|
GLib.VariantType(return_type),
|
|
Gio.DBusCallFlags.NONE,
|
|
-1,
|
|
None,
|
|
)
|
|
|
|
def get_menu_layout(self, *args):
|
|
return self.call_sync(
|
|
'com.canonical.dbusmenu',
|
|
'GetLayout',
|
|
args,
|
|
'(iias)',
|
|
'(u(ia{sv}av))',
|
|
)
|
|
|
|
def menu_event(self, *args):
|
|
self.call_sync('com.canonical.dbusmenu', 'Event', args, '(isvu)', '()')
|
|
|
|
|
|
def dmenu(_input):
|
|
p = subprocess.Popen(
|
|
DMENU_CMD,
|
|
stdin=subprocess.PIPE,
|
|
stdout=subprocess.PIPE,
|
|
encoding='utf-8',
|
|
)
|
|
out, _ = p.communicate(_input)
|
|
return out
|
|
|
|
|
|
def format_toggle_value(props):
|
|
toggle_type = props.get('toggle-type', '')
|
|
toggle_value = props.get('toggle-state', -1)
|
|
|
|
if toggle_value == 0:
|
|
s = ' '
|
|
elif toggle_value == 1:
|
|
s = 'X'
|
|
else:
|
|
s = '~'
|
|
|
|
if toggle_type == 'checkmark':
|
|
return f'[{s}] '
|
|
elif toggle_type == 'radio':
|
|
return f'({s}) '
|
|
else:
|
|
return ''
|
|
|
|
|
|
def format_menu_item(item, level=1):
|
|
id, props, children = item
|
|
|
|
if not props.get('visible', True):
|
|
return ''
|
|
if props.get('type', 'standard') == 'separator':
|
|
label = '---'
|
|
else:
|
|
label = format_toggle_value(props) + props.get('label', '')
|
|
if not props.get('enabled', True):
|
|
label = f'({label})'
|
|
|
|
indentation = ' ' * level
|
|
ret = f'{id}{indentation}{label}\n'
|
|
for child in children:
|
|
ret += format_menu_item(child, level + 1)
|
|
return ret
|
|
|
|
|
|
def show_menu(conn, name, path):
|
|
bus = Bus(conn, name, path)
|
|
item = bus.get_menu_layout(0, -1, [])[1]
|
|
|
|
menu = format_menu_item(item)
|
|
selected = dmenu(menu)
|
|
|
|
if selected:
|
|
id = int(selected.split()[0])
|
|
bus.menu_event(id, 'clicked', GLib.Variant('s', ''), time.time())
|
|
|
|
|
|
if __name__ == '__main__':
|
|
conn = Gio.bus_get_sync(Gio.BusType.SESSION)
|
|
show_menu(conn, sys.argv[1], sys.argv[2])
|