From b94486a916d02d05d79b6bb0ccb842b4b806e238 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sat, 30 Aug 2025 21:00:25 +0100 Subject: [PATCH] Added niri WM support --- hints/hints.py | 4 +- hints/window_systems/niri.py | 96 ++++++++++++++++++++++ hints/window_systems/window_system_type.py | 2 +- 3 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 hints/window_systems/niri.py diff --git a/hints/hints.py b/hints/hints.py index 22c92ac..5f11666 100644 --- a/hints/hints.py +++ b/hints/hints.py @@ -273,6 +273,8 @@ def get_window_system_class( from hints.window_systems.hyprland import Hyprland as window_system case "plasmashell": from hints.window_systems.plasmashell import Plasmashell as window_system + case "niri": + from hints.window_systems.niri import Niri as window_system return window_system @@ -294,7 +296,7 @@ def get_window_system(window_system_id: str = "") -> Type[WindowSystem]: if window_system_type == WindowSystemType.WAYLAND: # add new waland wms here, then add a match case below to import the class - supported_wayland_wms = {"sway", "Hyprland", "plasmashell"} + supported_wayland_wms = {"sway", "Hyprland", "plasmashell", "niri"} # Check if there is a process running that matches the supported_wayland_wms window_system_id = ( diff --git a/hints/window_systems/niri.py b/hints/window_systems/niri.py new file mode 100644 index 0000000..4034860 --- /dev/null +++ b/hints/window_systems/niri.py @@ -0,0 +1,96 @@ +"""Niri window system.""" + +from json import loads +from subprocess import run + +from hints.window_systems.window_system import WindowSystem + + +class Niri(WindowSystem): + """Niri Window system class.""" + + def __init__(self): + super().__init__() + self.focused_window = self._get_focused_window_from_niri_msg() + self.focused_output = self._get_focused_output_from_niri_msg() + self.windows = None + + def _get_focused_window_from_niri_msg(self): + focused_window = run( + ["niri", "msg", "--json", "focused-window"], capture_output=True, check=True + ) + return loads(focused_window.stdout.decode("utf-8")) + + def _get_focused_output_from_niri_msg(self): + focused_window = run( + ["niri", "msg", "--json", "focused-output"], capture_output=True, check=True + ) + return loads(focused_window.stdout.decode("utf-8")) + + def _get_windows_from_niri_msg(self): + focused_window = run( + ["niri", "msg", "--json", "windows"], capture_output=True, check=True + ) + return loads(focused_window.stdout.decode("utf-8")) + + @property + def window_system_name(self) -> str: + """Get the name of the window syste. + + This is useful for performing logic specific to a window system. + + :return: The window system name + """ + return "Niri" + + @property + def focused_window_extents(self) -> tuple[int, int, int, int]: + """Get active window extents. + + :return: Active window extents (x, y, width, height). + """ + window_layout = self.focused_window["layout"] + output_logical = self.focused_output["logical"] + + # handling of tiled windows, since niri doesn't provide window position for them + pos = window_layout["tile_pos_in_workspace_view"] + gaps = 16 # TODO: get it properly + strut_left = 25 # TODO: get it properly + strut_top = 0 # TODO: get it properly + x = 0 + y = 0 + left_side = False # TODO: get it properly + if pos is None: + x_gap = gaps + strut_left + if left_side: + x = x_gap + else: + x = output_logical["width"] - x_gap - window_layout["tile_size"][0] + y = gaps + strut_top + else: + x, y = pos + + tile_offset_x, tile_offset_y = window_layout["window_offset_in_tile"] + x += output_logical["x"] + tile_offset_x + y += output_logical["y"] + tile_offset_y + width, height = window_layout["window_size"] + return (x, y, width, height) + + @property + def focused_window_pid(self) -> int: + """Get Process ID corresponding to the focused widnow. + + :return: Process ID of focused window. + """ + return self.focused_window["pid"] + + @property + def focused_applicaiton_name(self) -> str: + """Get focused application name. + + This name is the name used to identify applications for per- + application rules. + + :return: Focused application name. + """ + return self.focused_window["app_id"] diff --git a/hints/window_systems/window_system_type.py b/hints/window_systems/window_system_type.py index 42987dd..c35775f 100644 --- a/hints/window_systems/window_system_type.py +++ b/hints/window_systems/window_system_type.py @@ -14,7 +14,7 @@ class WindowSystemType(Enum): WAYLAND = "wayland" -SupportedWindowSystems = Literal["x11", "sway", "hyprland"] +SupportedWindowSystems = Literal["x11", "sway", "hyprland", "niri"] def get_window_system_type() -> WindowSystemType: