#!/usr/bin/env python3
#
# unity.py
"""
GTK wrapper with Unity launcher support.
"""
#
# Copyright © 2026 Dominic Davis-Foster <dominic@davis-foster.co.uk>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
# OR OTHER DEALINGS IN THE SOFTWARE.
#
# stdlib
import sys
from dataclasses import dataclass
# 3rd party
import gi # nodep
# this package
from .base import WrapperGtk, WrapperWindow
gi.require_version("Gtk", "3.0")
gi.require_version("Unity", "7.0")
gi.require_version("Dbusmenu", "0.4")
# 3rd party
from gi.repository import Dbusmenu, Gtk, Unity # nodep # noqa: E402
__all__ = ["WrapperUnity", "WrapperWindowUnity"]
[docs]class WrapperWindowUnity(WrapperWindow):
"""
Standalone terminal wrapper for the app.
Displays the app in a libVTE terminal window, like `gnome-terminal` but without the standard terminal functionality.
Closes when the app exits.
:param wrapper:
"""
# self.terminal.feed_child(b"\x10") # Ctrl+p
# self.terminal.feed_child(b"\x1b[21~") # F10
[docs] def create_launcher_options(self) -> None:
"""
Create the Unity launcher rightclick menu options.
"""
# TODO: gate on desktop file existing and us launching in way to use it (no spaces in install path)
launcher = Unity.LauncherEntry.get_for_desktop_id("radioport.desktop")
ql = Dbusmenu.Menuitem.new()
for option in self.launcher_options:
menuitem = Dbusmenu.Menuitem.new()
menuitem.property_set(Dbusmenu.MENUITEM_PROP_LABEL, option)
menuitem.property_set_bool(Dbusmenu.MENUITEM_PROP_VISIBLE, True)
menuitem.connect(Dbusmenu.MENUITEM_SIGNAL_ITEM_ACTIVATED, self.on_launcher_menuitem_clicked)
ql.child_append(menuitem)
launcher.set_property("quicklist", ql)
[docs] def run(
self,
arguments: list[str],
working_directory: str,
) -> None:
"""
Show the wrapper window and launch the Textual app.
:param arguments: The app executable and any arguments to pass to it.
:param working_directory: Directory to execute the application in.
"""
self.terminal.spawn_app(
arguments=arguments,
working_directory=working_directory,
callback=self.spawn_callback,
)
self.connect("destroy", Gtk.main_quit)
self.show_all()
self.create_launcher_options()
try:
Gtk.main()
except KeyboardInterrupt:
sys.exit()
[docs]@dataclass
class WrapperUnity(WrapperGtk):
"""
A GTK3-based wrapper around a terminal app, with Unity launcher support.
"""
#: The GTK wrapper window itself.
wrapper_window_cls = WrapperWindowUnity