Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Layer shell support #53

Merged
merged 1 commit into from
Dec 17, 2024
Merged
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
4 changes: 2 additions & 2 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ checks:pylint:
stage: checks
before_script:
- sudo dnf install -y python3-gobject gtk3 xorg-x11-server-Xvfb
python3-pip python3-mypy
python3-pip python3-mypy gtk-layer-shell
- pip3 install --quiet -r ci/requirements.txt
- git clone https://github.com/QubesOS/qubes-core-admin-client ~/core-admin-client
script:
Expand All @@ -25,7 +25,7 @@ checks:tests:
- "PATH=$PATH:$HOME/.local/bin"
- sudo dnf install -y python3-gobject gtk3 python3-pytest python3-pytest-asyncio
python3-coverage xorg-x11-server-Xvfb python3-inotify sequoia-sqv
python3-pip
python3-pip gtk-layer-shell
- pip3 install --quiet -r ci/requirements.txt
- git clone https://github.com/QubesOS/qubes-core-admin-client ~/core-admin-client
- git clone https://github.com/QubesOS/qubes-desktop-linux-manager ~/desktop-linux-manager
Expand Down
3 changes: 2 additions & 1 deletion debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ Build-Depends:
qubes-desktop-linux-manager,
python3-gi,
gobject-introspection,
gir1.2-gtk-3.0
gir1.2-gtk-3.0,
gir1.2-gtklayershell-0.1,
Standards-Version: 3.9.5
Homepage: https://www.qubes-os.org/
X-Python3-Version: >= 3.5
Expand Down
115 changes: 90 additions & 25 deletions qubes_menu/appmenu.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GLib, Gio
gi.require_version('GtkLayerShell', '0.1')
from gi.repository import Gtk, Gdk, GLib, Gio, GtkLayerShell

import gbulb
gbulb.install()
Expand Down Expand Up @@ -93,10 +94,12 @@
self.initial_page = "app_page"
self.sort_running = False
self.start_in_background = False
self.kde = "KDE" in os.getenv("XDG_CURRENT_DESKTOP", "").split(":")

self._add_cli_options()

self.builder: Optional[Gtk.Builder] = None
self.layer_shell: bool = False
self.main_window: Optional[Gtk.Window] = None
self.main_notebook: Optional[Gtk.Notebook] = None

Expand Down Expand Up @@ -168,16 +171,13 @@
if "background" in options:
self.start_in_background = True

@staticmethod
def _do_power_button(_widget):
def _do_power_button(self, _widget):
"""
Run xfce4's default logout button. Possible enhancement would be
providing our own tiny program.
"""
# pylint: disable=consider-using-with
current_environs = os.environ.get('XDG_CURRENT_DESKTOP', '').split(':')

if 'KDE' in current_environs:
if self.kde:

Check warning on line 180 in qubes_menu/appmenu.py

View check run for this annotation

Codecov / codecov/patch

qubes_menu/appmenu.py#L180

Added line #L180 was not covered by tests
dbus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
proxy = Gio.DBusProxy.new_sync(
dbus, # dbus
Expand All @@ -203,21 +203,67 @@
assert self.main_window
match self.appmenu_position:
case 'top-left':
self.main_window.move(0, 0)
if self.layer_shell:
GtkLayerShell.set_anchor(self.main_window,

Check warning on line 207 in qubes_menu/appmenu.py

View check run for this annotation

Codecov / codecov/patch

qubes_menu/appmenu.py#L207

Added line #L207 was not covered by tests
GtkLayerShell.Edge.LEFT, True)
GtkLayerShell.set_anchor(self.main_window,

Check warning on line 209 in qubes_menu/appmenu.py

View check run for this annotation

Codecov / codecov/patch

qubes_menu/appmenu.py#L209

Added line #L209 was not covered by tests
GtkLayerShell.Edge.TOP, True)
else:
self.main_window.move(0, 0)
case 'top-right':
self.main_window.move(
self.main_window.get_screen().get_width() - \
self.main_window.get_size().width, 0)
if self.layer_shell:
GtkLayerShell.set_anchor(self.main_window,

Check warning on line 215 in qubes_menu/appmenu.py

View check run for this annotation

Codecov / codecov/patch

qubes_menu/appmenu.py#L215

Added line #L215 was not covered by tests
GtkLayerShell.Edge.RIGHT, True)
GtkLayerShell.set_anchor(self.main_window,

Check warning on line 217 in qubes_menu/appmenu.py

View check run for this annotation

Codecov / codecov/patch

qubes_menu/appmenu.py#L217

Added line #L217 was not covered by tests
GtkLayerShell.Edge.TOP, True)
else:
self.main_window.move(
self.main_window.get_screen().get_width() -
self.main_window.get_size().width, 0)
case 'bottom-left':
self.main_window.move(0,
self.main_window.get_screen().get_height() - \
self.main_window.get_size().height)
if self.layer_shell:
GtkLayerShell.set_anchor(self.main_window,

Check warning on line 225 in qubes_menu/appmenu.py

View check run for this annotation

Codecov / codecov/patch

qubes_menu/appmenu.py#L225

Added line #L225 was not covered by tests
GtkLayerShell.Edge.LEFT, True)
GtkLayerShell.set_anchor(self.main_window,

Check warning on line 227 in qubes_menu/appmenu.py

View check run for this annotation

Codecov / codecov/patch

qubes_menu/appmenu.py#L227

Added line #L227 was not covered by tests
GtkLayerShell.Edge.BOTTOM, True)
else:
self.main_window.move(0,
self.main_window.get_screen().get_height() -
self.main_window.get_size().height)
case 'bottom-right':
self.main_window.move(
self.main_window.get_screen().get_width() - \
self.main_window.get_size().width,
self.main_window.get_screen().get_height() - \
self.main_window.get_size().height)
if self.layer_shell:
GtkLayerShell.set_anchor(self.main_window,

Check warning on line 235 in qubes_menu/appmenu.py

View check run for this annotation

Codecov / codecov/patch

qubes_menu/appmenu.py#L235

Added line #L235 was not covered by tests
GtkLayerShell.Edge.RIGHT, True)
GtkLayerShell.set_anchor(self.main_window,

Check warning on line 237 in qubes_menu/appmenu.py

View check run for this annotation

Codecov / codecov/patch

qubes_menu/appmenu.py#L237

Added line #L237 was not covered by tests
GtkLayerShell.Edge.BOTTOM, True)
else:
self.main_window.move(
self.main_window.get_screen().get_width() -
self.main_window.get_size().width,
self.main_window.get_screen().get_height() -
self.main_window.get_size().height)

def __present(self) -> None:
assert self.main_window is not None
self.reposition()
self.main_window.present()
if not self.layer_shell:
return

Check warning on line 251 in qubes_menu/appmenu.py

View check run for this annotation

Codecov / codecov/patch

qubes_menu/appmenu.py#L247-L251

Added lines #L247 - L251 were not covered by tests
# Under Wayland, the window size must be re-requested
# every time the window is shown.
current_width = self.main_window.get_allocated_width()
current_height = self.main_window.get_allocated_height()

Check warning on line 255 in qubes_menu/appmenu.py

View check run for this annotation

Codecov / codecov/patch

qubes_menu/appmenu.py#L254-L255

Added lines #L254 - L255 were not covered by tests
# set size if too big
max_height = int(self.main_window.get_screen().get_height() * 0.9)
assert max_height > 0

Check warning on line 258 in qubes_menu/appmenu.py

View check run for this annotation

Codecov / codecov/patch

qubes_menu/appmenu.py#L257-L258

Added lines #L257 - L258 were not covered by tests
# The default for layer shell is no keyboard input.
# Explicitly request exclusive access to the keyboard.
GtkLayerShell.set_keyboard_mode(self.main_window,

Check warning on line 261 in qubes_menu/appmenu.py

View check run for this annotation

Codecov / codecov/patch

qubes_menu/appmenu.py#L261

Added line #L261 was not covered by tests
GtkLayerShell.KeyboardMode.EXCLUSIVE)
# Work around https://github.com/wmww/gtk-layer-shell/issues/167
# by explicitly setting the window size.
self.main_window.set_size_request(current_width,

Check warning on line 265 in qubes_menu/appmenu.py

View check run for this annotation

Codecov / codecov/patch

qubes_menu/appmenu.py#L265

Added line #L265 was not covered by tests
min(current_height, max_height))

def do_activate(self, *args, **kwargs):
"""
Expand All @@ -234,12 +280,24 @@
self.reposition()
self.main_window.show_all()
self.initialize_state()
# set size if too big
current_width = self.main_window.get_allocated_width()

Check warning on line 283 in qubes_menu/appmenu.py

View check run for this annotation

Codecov / codecov/patch

qubes_menu/appmenu.py#L283

Added line #L283 was not covered by tests
current_height = self.main_window.get_allocated_height()
max_height = self.main_window.get_screen().get_height() * 0.9
if current_height > max_height:
self.main_window.resize(self.main_window.get_allocated_width(),
int(max_height))
# set size if too big
max_height = int(self.main_window.get_screen().get_height() * 0.9)
assert max_height > 0
if self.layer_shell:
if not self.start_in_background:

Check warning on line 289 in qubes_menu/appmenu.py

View check run for this annotation

Codecov / codecov/patch

qubes_menu/appmenu.py#L286-L289

Added lines #L286 - L289 were not covered by tests
# The default for layer shell is no keyboard input.
# Explicitly request exclusive access to the keyboard.
GtkLayerShell.set_keyboard_mode(self.main_window,

Check warning on line 292 in qubes_menu/appmenu.py

View check run for this annotation

Codecov / codecov/patch

qubes_menu/appmenu.py#L292

Added line #L292 was not covered by tests
GtkLayerShell.KeyboardMode.EXCLUSIVE)
# Work around https://github.com/wmww/gtk-layer-shell/issues/167
# by explicitly setting the window size.
self.main_window.set_size_request(

Check warning on line 296 in qubes_menu/appmenu.py

View check run for this annotation

Codecov / codecov/patch

qubes_menu/appmenu.py#L296

Added line #L296 was not covered by tests
current_width,
min(current_height, max_height))
elif current_height > max_height:
self.main_window.resize(current_height, max_height)

Check warning on line 300 in qubes_menu/appmenu.py

View check run for this annotation

Codecov / codecov/patch

qubes_menu/appmenu.py#L299-L300

Added lines #L299 - L300 were not covered by tests

# grab a focus on the initially selected page so that keyboard
# navigation works
Expand All @@ -261,8 +319,7 @@
if self.main_window.is_visible() and not self.keep_visible:
self.main_window.hide()
else:
self.reposition()
self.main_window.present()
self.__present()

Check warning on line 322 in qubes_menu/appmenu.py

View check run for this annotation

Codecov / codecov/patch

qubes_menu/appmenu.py#L322

Added line #L322 was not covered by tests

def hide_menu(self):
"""
Expand Down Expand Up @@ -331,6 +388,7 @@
self.builder.add_from_file(str(path))

self.main_window = self.builder.get_object('main_window')
self.layer_shell = GtkLayerShell.is_supported()
self.main_notebook = self.builder.get_object('main_notebook')

self.main_window.set_events(Gdk.EventMask.FOCUS_CHANGE_MASK)
Expand Down Expand Up @@ -375,6 +433,10 @@
'domain-feature-delete:' + feature,
self._update_settings)

if self.layer_shell:
GtkLayerShell.init_for_window(self.main_window)
GtkLayerShell.set_exclusive_zone(self.main_window, 0)

Check warning on line 438 in qubes_menu/appmenu.py

View check run for this annotation

Codecov / codecov/patch

qubes_menu/appmenu.py#L437-L438

Added lines #L437 - L438 were not covered by tests

def load_style(self, *_args):
"""Load appropriate CSS stylesheet and associated properties."""
light_ref = (importlib.resources.files('qubes_menu') /
Expand Down Expand Up @@ -415,6 +477,9 @@
position = local_vm.features.get(POSITION_FEATURE, "mouse")
if position not in POSITION_LIST:
position = "mouse"
if position == "mouse" and self.layer_shell:
# "mouse" unsupported under Wayland
position = "bottom-left" if self.kde else "top-left"

Check warning on line 482 in qubes_menu/appmenu.py

View check run for this annotation

Codecov / codecov/patch

qubes_menu/appmenu.py#L482

Added line #L482 was not covered by tests
self.appmenu_position = position

for handler in self.handlers.values():
Expand Down
1 change: 1 addition & 0 deletions rpm_spec/qubes-desktop-linux-menu.spec.in
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ BuildRequires: gettext
Requires: python%{python3_pkgversion}-setuptools
Requires: python%{python3_pkgversion}-gbulb
Requires: gtk3
Requires: gtk-layer-shell
Requires: python%{python3_pkgversion}-qubesadmin >= 4.1.8
Requires: qubes-artwork >= 4.1.5
Requires: qubes-desktop-linux-manager
Expand Down