From 23b09f528f0168e0f86a1d7afd76db0eb3b40c6b Mon Sep 17 00:00:00 2001 From: apebl Date: Fri, 16 Aug 2024 09:35:01 +0000 Subject: [PATCH] Add signal handlers to show menus --- qui/clipboard.py | 10 ++++++++++ qui/devices/device_widget.py | 14 ++++++++++++-- qui/tray/disk_space.py | 16 +++++++++++++--- qui/tray/domains.py | 12 +++++++++++- qui/tray/updates.py | 16 +++++++++++++--- 5 files changed, 59 insertions(+), 9 deletions(-) diff --git a/qui/clipboard.py b/qui/clipboard.py index c2a4c2dd..2cb83e4f 100644 --- a/qui/clipboard.py +++ b/qui/clipboard.py @@ -31,8 +31,10 @@ import math import os import fcntl +import signal import qubesadmin import qubesadmin.events +from functools import partial import gi gi.require_version('Gtk', '3.0') # isort:skip @@ -302,6 +304,11 @@ def _convert_to_readable(key: str): return key.upper() return key +def signal_handler(app, _signum, _frame): + event = Gdk.EventButton() + event.button = 1 + app.show_menu(None, event) + def main(): loop = asyncio.get_event_loop() wm = pyinotify.WatchManager() @@ -314,6 +321,9 @@ def main(): handler = EventHandler(loop=loop, gtk_app=gtk_app) pyinotify.AsyncioNotifier(wm, loop, default_proc_fun=handler) + signal_handler_wrapper = partial(signal_handler, gtk_app) + signal.signal(signal.SIGUSR1, signal_handler_wrapper) + return run_asyncio_and_show_errors(loop, [asyncio.ensure_future( dispatcher.listen_for_events())], _("Qubes Clipboard Widget")) diff --git a/qui/devices/device_widget.py b/qui/devices/device_widget.py index 44d0fd9a..784ae3fe 100644 --- a/qui/devices/device_widget.py +++ b/qui/devices/device_widget.py @@ -18,9 +18,11 @@ # You should have received a copy of the GNU Lesser General Public License along # with this program; if not, see . from typing import Set, List, Dict +from functools import partial import asyncio import sys import time +import signal import importlib.resources @@ -294,7 +296,7 @@ def load_css(widget) -> str: return theme - def show_menu(self, _unused, _event): + def show_menu(self, _unused, event): """Show menu at mouse pointer.""" tray_menu = Gtk.Menu() theme = self.load_css(tray_menu) @@ -333,7 +335,7 @@ def show_menu(self, _unused, _event): tray_menu.add(item) tray_menu.show_all() - tray_menu.popup_at_pointer(None) # use current event + tray_menu.popup_at_pointer(event) def emit_notification(self, title, message, priority, error=False, notification_id=None): @@ -347,6 +349,11 @@ def emit_notification(self, title, message, priority, error=False, self.send_notification(notification_id, notification) +def signal_handler(app, _signum, _frame): + event = Gdk.EventButton() + event.window = Gdk.get_default_root_window().get_screen().get_active_window() + app.show_menu(None, event) + def main(): qapp = qubesadmin.Qubes() # qapp = qubesadmin.tests.mock_app.MockQubesComplete() @@ -355,6 +362,9 @@ def main(): app = DevicesTray( 'org.qubes.qui.tray.Devices', qapp, dispatcher) + signal_handler_wrapper = partial(signal_handler, app) + signal.signal(signal.SIGUSR1, signal_handler_wrapper) + loop = asyncio.get_event_loop() return_code = qui.utils.run_asyncio_and_show_errors( loop, [asyncio.ensure_future(dispatcher.listen_for_events())], diff --git a/qui/tray/disk_space.py b/qui/tray/disk_space.py index 8d8c8094..2b63cb18 100644 --- a/qui/tray/disk_space.py +++ b/qui/tray/disk_space.py @@ -1,11 +1,13 @@ # pylint: disable=wrong-import-position,import-error import sys import subprocess +import signal from typing import List +from functools import partial import gi gi.require_version('Gtk', '3.0') # isort:skip -from gi.repository import Gtk, GObject, Gio, GLib # isort:skip +from gi.repository import Gtk, GObject, Gio, GLib, Gdk # isort:skip from qubesadmin import Qubes from qubesadmin.utils import size_to_human from qubesadmin import exc @@ -358,8 +360,16 @@ def __init__(self, **properties): GObject.timeout_add_seconds(120, self.refresh_icon) + signal_handler_wrapper = partial(self.__signal_handler) + signal.signal(signal.SIGUSR1, signal_handler_wrapper) + Gtk.main() + def __signal_handler(self, _signum, _frame): + event = Gdk.EventButton() + event.window = Gdk.get_default_root_window().get_screen().get_active_window() + self.make_menu(None, event) + def refresh_icon(self): pool_data = PoolUsageData(self.qubes_app) vm_data = VMUsageData(self.qubes_app) @@ -418,7 +428,7 @@ def set_icon_state(self, pool_warning=None, vm_warning=None): self.icon.set_tooltip_markup( _('Qubes Disk Space Monitor\nView free disk space.')) - def make_menu(self, _unused, _event): + def make_menu(self, _unused, event): pool_data = PoolUsageData(self.qubes_app) vm_data = VMUsageData(self.qubes_app) @@ -460,7 +470,7 @@ def make_menu(self, _unused, _event): menu.set_reserve_toggle_size(False) menu.show_all() - menu.popup_at_pointer(None) # use current event + menu.popup_at_pointer(event) @staticmethod def make_title_item(text): diff --git a/qui/tray/domains.py b/qui/tray/domains.py index 1eae1310..2698908f 100644 --- a/qui/tray/domains.py +++ b/qui/tray/domains.py @@ -9,6 +9,8 @@ import os import threading import traceback +import signal +from functools import partial import qubesadmin import qubesadmin.events @@ -19,7 +21,7 @@ import gi # isort:skip gi.require_version('Gtk', '3.0') # isort:skip -from gi.repository import Gio, Gtk, GObject, GLib, GdkPixbuf # isort:skip +from gi.repository import Gio, Gtk, GObject, GLib, GdkPixbuf, Gdk # isort:skip import gbulb gbulb.install() @@ -913,6 +915,11 @@ def _disconnect_signals(self, _event): self.stats_dispatcher.remove_handler('vm-stats', self.update_stats) +def signal_handler(app, _signum, _frame): + event = Gdk.EventButton() + event.window = Gdk.get_default_root_window().get_screen().get_active_window() + app.show_menu(None, event) + def main(): ''' main function ''' qapp = qubesadmin.Qubes() @@ -923,6 +930,9 @@ def main(): 'org.qubes.qui.tray.Domains', qapp, dispatcher, stats_dispatcher) app.run() + signal_handler_wrapper = partial(signal_handler, app) + signal.signal(signal.SIGUSR1, signal_handler_wrapper) + loop = asyncio.get_event_loop() tasks = [ asyncio.ensure_future(dispatcher.listen_for_events()), diff --git a/qui/tray/updates.py b/qui/tray/updates.py index 65aa268d..3869f3b3 100644 --- a/qui/tray/updates.py +++ b/qui/tray/updates.py @@ -6,6 +6,8 @@ import asyncio import sys import subprocess +import signal +from functools import partial import qubesadmin import qubesadmin.events @@ -14,7 +16,7 @@ import gi # isort:skip gi.require_version('Gtk', '3.0') # isort:skip -from gi.repository import Gtk, Gio # isort:skip +from gi.repository import Gtk, Gio, Gdk # isort:skip import gbulb gbulb.install() @@ -105,12 +107,12 @@ def setup_menu(self): self.tray_menu.show_all() - def show_menu(self, _unused, _event): + def show_menu(self, _unused, event): self.tray_menu = Gtk.Menu() self.setup_menu() - self.tray_menu.popup_at_pointer(None) # use current event + self.tray_menu.popup_at_pointer(event) @staticmethod def launch_updater(*_args, **_kwargs): @@ -212,6 +214,11 @@ def update_indicator_state(self): self.widget_icon.set_visible(False) +def signal_handler(app, _signum, _frame): + event = Gdk.EventButton() + event.window = Gdk.get_default_root_window().get_screen().get_active_window() + app.show_menu(None, event) + def main(): qapp = qubesadmin.Qubes() dispatcher = qubesadmin.events.EventsDispatcher(qapp) @@ -219,6 +226,9 @@ def main(): 'org.qubes.qui.tray.Updates', qapp, dispatcher) app.run() + signal_handler_wrapper = partial(signal_handler, app) + signal.signal(signal.SIGUSR1, signal_handler_wrapper) + loop = asyncio.get_event_loop() return qui.utils.run_asyncio_and_show_errors(loop, [asyncio.ensure_future(