Compare commits
60 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f3d3ef512c | ||
![]() |
b119542490 | ||
![]() |
142628158e | ||
![]() |
d89a37b267 | ||
![]() |
3394161648 | ||
![]() |
1345106637 | ||
![]() |
4c8fe7dcb0 | ||
![]() |
d4598c8843 | ||
![]() |
4852c8fa5e | ||
![]() |
38c587fa9d | ||
![]() |
1673def218 | ||
![]() |
572b195d5d | ||
![]() |
5f036ce0d8 | ||
![]() |
7de7034dfd | ||
![]() |
ee913ce926 | ||
![]() |
c4cedee4ec | ||
![]() |
b79d259942 | ||
![]() |
bc3b1db1d8 | ||
![]() |
56055db8f3 | ||
![]() |
434d365f0a | ||
![]() |
3a524a278f | ||
![]() |
5ae4d9738b | ||
![]() |
0bb9b9d545 | ||
![]() |
94b4aafe99 | ||
![]() |
ec29c67c5f | ||
![]() |
65ffb8276c | ||
![]() |
fb5e74b52a | ||
![]() |
d53e500aeb | ||
![]() |
0fd5456fd6 | ||
![]() |
3831b46604 | ||
![]() |
fa20711bbe | ||
![]() |
a702829190 | ||
![]() |
35251fe880 | ||
![]() |
6e9c3e12b1 | ||
![]() |
3d070d4f09 | ||
![]() |
2c63f64ccf | ||
![]() |
c007f8e428 | ||
![]() |
caeb1747ef | ||
![]() |
8a5923153f | ||
![]() |
dadebe681f | ||
![]() |
82d6c397e5 | ||
![]() |
503b66b077 | ||
![]() |
076fa222d4 | ||
![]() |
728cd1411b | ||
![]() |
a3ef3ee8fd | ||
![]() |
5156ca2d51 | ||
![]() |
0304c94e8f | ||
![]() |
01b1dcb85d | ||
![]() |
466348aef2 | ||
![]() |
770f0bd393 | ||
![]() |
e096a75dd5 | ||
![]() |
3fd934b0bd | ||
![]() |
fc7787f34a | ||
![]() |
35dbb036c9 | ||
![]() |
f36a80fa5d | ||
![]() |
4aa4b1d51e | ||
![]() |
2cd25c0e14 | ||
![]() |
2c13954dda | ||
![]() |
6bd10471cf | ||
![]() |
277a83f35a |
@@ -215,7 +215,8 @@
|
|||||||
<property name="halign">start</property>
|
<property name="halign">start</property>
|
||||||
<property name="label" translatable="yes"><span size="small"><b>CSS IDs</b>:
|
<property name="label" translatable="yes"><span size="small"><b>CSS IDs</b>:
|
||||||
#hyprland-workspaces, #hyprland-workspaces-item,
|
#hyprland-workspaces, #hyprland-workspaces-item,
|
||||||
#hyprland-workspaces-icon, #hyprland-workspaces-name</span></property>
|
#hyprland-workspaces-icon, #hyprland-workspaces-name
|
||||||
|
#workspace-occupied</span></property>
|
||||||
<property name="use-markup">True</property>
|
<property name="use-markup">True</property>
|
||||||
<property name="wrap">True</property>
|
<property name="wrap">True</property>
|
||||||
</object>
|
</object>
|
||||||
|
@@ -266,7 +266,8 @@
|
|||||||
<property name="halign">start</property>
|
<property name="halign">start</property>
|
||||||
<property name="label" translatable="yes"><span size="small"><b>CSS IDs</b>:
|
<property name="label" translatable="yes"><span size="small"><b>CSS IDs</b>:
|
||||||
#sway-workspaces, #sway-workspaces-item,
|
#sway-workspaces, #sway-workspaces-item,
|
||||||
#sway-workspaces-icon, #sway-workspaces-name</span></property>
|
#sway-workspaces-icon, #sway-workspaces-name
|
||||||
|
#workspace-occupied</span></property>
|
||||||
<property name="use-markup">True</property>
|
<property name="use-markup">True</property>
|
||||||
<property name="wrap">True</property>
|
<property name="wrap">True</property>
|
||||||
</object>
|
</object>
|
||||||
|
@@ -87,8 +87,6 @@ his = os.getenv('HYPRLAND_INSTANCE_SIGNATURE')
|
|||||||
if his:
|
if his:
|
||||||
from nwg_panel.modules.hyprland_taskbar import HyprlandTaskbar
|
from nwg_panel.modules.hyprland_taskbar import HyprlandTaskbar
|
||||||
from nwg_panel.modules.hyprland_workspaces import HyprlandWorkspaces
|
from nwg_panel.modules.hyprland_workspaces import HyprlandWorkspaces
|
||||||
last_client_addr = ""
|
|
||||||
last_client_title = ""
|
|
||||||
|
|
||||||
common_settings = {}
|
common_settings = {}
|
||||||
restart_cmd = ""
|
restart_cmd = ""
|
||||||
@@ -154,7 +152,6 @@ def restart():
|
|||||||
subprocess.Popen(restart_cmd, shell=True)
|
subprocess.Popen(restart_cmd, shell=True)
|
||||||
|
|
||||||
|
|
||||||
# read from Hyprland socket2 on async thread
|
|
||||||
def hypr_watcher():
|
def hypr_watcher():
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
@@ -165,74 +162,48 @@ def hypr_watcher():
|
|||||||
|
|
||||||
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||||
client.connect(f"{hypr_dir}/{his}/.socket2.sock")
|
client.connect(f"{hypr_dir}/{his}/.socket2.sock")
|
||||||
|
just_refreshed = False
|
||||||
global last_client_addr, last_client_title
|
|
||||||
client_addr, client_title = None, None
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
datagram = client.recv(2048)
|
datagram = client.recv(2048)
|
||||||
e_full_string = datagram.decode('utf-8').strip()
|
e_full_string = datagram.decode('utf-8').strip()
|
||||||
# eprint("Event: {}".format(e_full_string))
|
lines = e_full_string.splitlines()
|
||||||
|
|
||||||
# remember client address & title (string) for further event filtering
|
event_names = []
|
||||||
if e_full_string.startswith("activewindow"):
|
for line in lines:
|
||||||
lines = e_full_string.splitlines()
|
event_names.append(line.split(">>")[0])
|
||||||
for line in lines:
|
# print(f"events: {event_names}")
|
||||||
if line.startswith("activewindowv2"):
|
|
||||||
client_addr = e_full_string.split(">>")[1].strip()
|
|
||||||
elif line.startswith("activewindow>>"):
|
|
||||||
client_title = line.split(">>")[1].strip()
|
|
||||||
|
|
||||||
event_name = e_full_string.split(">>")[0]
|
for event_name in event_names:
|
||||||
|
if event_name in ["activespecial",
|
||||||
|
"activewindow",
|
||||||
|
"activewindowv2",
|
||||||
|
"changefloatingmode",
|
||||||
|
"closewindow",
|
||||||
|
"createworkspace",
|
||||||
|
"destroyworkspace",
|
||||||
|
"focusedmon",
|
||||||
|
"monitoradded",
|
||||||
|
"movewindow",
|
||||||
|
"openwindow",
|
||||||
|
"windowtitle",
|
||||||
|
"workspace"]:
|
||||||
|
|
||||||
if event_name in ["monitoradded", "openwindow", "movewindow"]:
|
if "activewindow" in event_name and just_refreshed:
|
||||||
monitors, workspaces, clients, activewindow = h_modules_get_all()
|
just_refreshed = False
|
||||||
for item in common.h_taskbars_list:
|
break
|
||||||
GLib.timeout_add(0, item.refresh, monitors, workspaces, clients, activewindow)
|
|
||||||
last_client_title = client_title
|
|
||||||
last_client_addr = client_addr
|
|
||||||
continue
|
|
||||||
|
|
||||||
if event_name == "focusedmon":
|
# print(f">>> refreshing on {event_name}")
|
||||||
monitors, workspaces, clients, activewindow = h_modules_get_all()
|
monitors, workspaces, clients, activewindow, activeworkspace = h_modules_get_all()
|
||||||
for item in common.h_workspaces_list:
|
for item in common.h_taskbars_list:
|
||||||
GLib.timeout_add(0, item.refresh, monitors, workspaces, clients, activewindow)
|
GLib.timeout_add(0, item.refresh, monitors, workspaces, clients, activewindow)
|
||||||
last_client_title = client_title
|
|
||||||
last_client_addr = client_addr
|
|
||||||
continue
|
|
||||||
|
|
||||||
if event_name == "activewindow" and client_title != last_client_title:
|
for item in common.h_workspaces_list:
|
||||||
monitors, workspaces, clients, activewindow = h_modules_get_all()
|
GLib.timeout_add(0, item.refresh, monitors, workspaces, clients, activewindow, activeworkspace)
|
||||||
for item in common.h_taskbars_list:
|
|
||||||
GLib.timeout_add(0, item.refresh, monitors, workspaces, clients, activewindow)
|
|
||||||
|
|
||||||
for item in common.h_workspaces_list:
|
if event_name in ["createworkspace", "destroyworkspace", "focusedmon", "workspace"]:
|
||||||
GLib.timeout_add(0, item.refresh, monitors, workspaces, clients, activewindow)
|
just_refreshed = True
|
||||||
|
break
|
||||||
last_client_title = client_title
|
|
||||||
continue
|
|
||||||
|
|
||||||
if event_name == "activewindowv2" and client_addr != last_client_addr:
|
|
||||||
monitors, workspaces, clients, activewindow = h_modules_get_all()
|
|
||||||
for item in common.h_taskbars_list:
|
|
||||||
GLib.timeout_add(0, item.refresh, monitors, workspaces, clients, activewindow)
|
|
||||||
|
|
||||||
for item in common.h_workspaces_list:
|
|
||||||
GLib.timeout_add(0, item.refresh, monitors, workspaces, clients, activewindow)
|
|
||||||
|
|
||||||
last_client_addr = client_addr
|
|
||||||
continue
|
|
||||||
|
|
||||||
if event_name in ["changefloatingmode", "closewindow"]:
|
|
||||||
monitors, workspaces, clients, activewindow = h_modules_get_all()
|
|
||||||
for item in common.h_taskbars_list:
|
|
||||||
GLib.timeout_add(0, item.refresh, monitors, workspaces, clients, activewindow)
|
|
||||||
|
|
||||||
for item in common.h_workspaces_list:
|
|
||||||
GLib.timeout_add(0, item.refresh, monitors, workspaces, clients, activewindow)
|
|
||||||
|
|
||||||
last_client_addr = ""
|
|
||||||
last_client_title = ""
|
|
||||||
|
|
||||||
|
|
||||||
def on_i3ipc_event(i3conn, event):
|
def on_i3ipc_event(i3conn, event):
|
||||||
@@ -267,14 +238,14 @@ def instantiate_content(panel, container, content_list, icons_path=""):
|
|||||||
check_key(panel, "position", "top")
|
check_key(panel, "position", "top")
|
||||||
check_key(panel, "items-padding", 0)
|
check_key(panel, "items-padding", 0)
|
||||||
|
|
||||||
# list initial data for Hyprland modules
|
|
||||||
if his:
|
|
||||||
if "hyprland-workspaces" in content_list or "hyprland-taskbar" in content_list:
|
|
||||||
monitors, workspaces, clients, activewindow = h_modules_get_all()
|
|
||||||
else:
|
|
||||||
monitors, workspaces, clients, activewindow = {}, {}, {}, {}
|
|
||||||
|
|
||||||
for item in content_list:
|
for item in content_list:
|
||||||
|
# list initial data for Hyprland modules
|
||||||
|
if his:
|
||||||
|
if "hyprland-workspaces" in content_list or "hyprland-taskbar" in content_list:
|
||||||
|
monitors, workspaces, clients, activewindow, activeworkspace = h_modules_get_all()
|
||||||
|
else:
|
||||||
|
monitors, workspaces, clients, activewindow, activeworkspace = {}, {}, {}, {}, {}
|
||||||
|
|
||||||
if item == "sway-taskbar":
|
if item == "sway-taskbar":
|
||||||
if "sway-taskbar" in panel:
|
if "sway-taskbar" in panel:
|
||||||
if sway:
|
if sway:
|
||||||
@@ -348,7 +319,7 @@ def instantiate_content(panel, container, content_list, icons_path=""):
|
|||||||
if his:
|
if his:
|
||||||
if "hyprland-workspaces" in panel:
|
if "hyprland-workspaces" in panel:
|
||||||
workspaces = HyprlandWorkspaces(panel["hyprland-workspaces"], monitors, workspaces, clients,
|
workspaces = HyprlandWorkspaces(panel["hyprland-workspaces"], monitors, workspaces, clients,
|
||||||
activewindow, icons_path=icons_path)
|
activewindow, activeworkspace, icons_path=icons_path)
|
||||||
container.pack_start(workspaces, False, False, panel["items-padding"])
|
container.pack_start(workspaces, False, False, panel["items-padding"])
|
||||||
common.h_workspaces_list.append(workspaces)
|
common.h_workspaces_list.append(workspaces)
|
||||||
else:
|
else:
|
||||||
@@ -761,12 +732,14 @@ def main():
|
|||||||
left_box.pack_start(ms, False, False, 0)
|
left_box.pack_start(ms, False, False, 0)
|
||||||
|
|
||||||
instantiate_content(panel, left_box, panel["modules-left"], icons_path=icons_path)
|
instantiate_content(panel, left_box, panel["modules-left"], icons_path=icons_path)
|
||||||
|
print("left box created")
|
||||||
|
|
||||||
center_box = Gtk.Box(orientation=o, spacing=panel["spacing"])
|
center_box = Gtk.Box(orientation=o, spacing=panel["spacing"])
|
||||||
center_box.set_property("name", "center-box")
|
center_box.set_property("name", "center-box")
|
||||||
inner_box.pack_start(center_box, True, False, 0)
|
inner_box.pack_start(center_box, True, False, 0)
|
||||||
check_key(panel, "modules-center", [])
|
check_key(panel, "modules-center", [])
|
||||||
instantiate_content(panel, center_box, panel["modules-center"], icons_path=icons_path)
|
instantiate_content(panel, center_box, panel["modules-center"], icons_path=icons_path)
|
||||||
|
print("center box created")
|
||||||
|
|
||||||
right_box = Gtk.Box(orientation=o, spacing=panel["spacing"])
|
right_box = Gtk.Box(orientation=o, spacing=panel["spacing"])
|
||||||
right_box.set_property("name", "right-box")
|
right_box.set_property("name", "right-box")
|
||||||
@@ -776,6 +749,7 @@ def main():
|
|||||||
inner_box.pack_start(helper_box, False, True, 0)
|
inner_box.pack_start(helper_box, False, True, 0)
|
||||||
check_key(panel, "modules-right", [])
|
check_key(panel, "modules-right", [])
|
||||||
instantiate_content(panel, right_box, panel["modules-right"], icons_path=icons_path)
|
instantiate_content(panel, right_box, panel["modules-right"], icons_path=icons_path)
|
||||||
|
print("right box created")
|
||||||
|
|
||||||
if panel["menu-start"] == "right":
|
if panel["menu-start"] == "right":
|
||||||
ms = MenuStart(panel["menu-start-settings"], icons_path=icons_path)
|
ms = MenuStart(panel["menu-start-settings"], icons_path=icons_path)
|
||||||
@@ -803,6 +777,7 @@ def main():
|
|||||||
window.add(vbox)
|
window.add(vbox)
|
||||||
|
|
||||||
GtkLayerShell.init_for_window(window)
|
GtkLayerShell.init_for_window(window)
|
||||||
|
GtkLayerShell.set_namespace(window, f"nwg-panel")
|
||||||
|
|
||||||
monitor = None
|
monitor = None
|
||||||
try:
|
try:
|
||||||
@@ -878,6 +853,7 @@ def main():
|
|||||||
if his:
|
if his:
|
||||||
if len(common.h_taskbars_list) > 0 or len(common.h_workspaces_list) > 0:
|
if len(common.h_taskbars_list) > 0 or len(common.h_workspaces_list) > 0:
|
||||||
print("his: '{}', starting hypr_watcher".format(his))
|
print("his: '{}', starting hypr_watcher".format(his))
|
||||||
|
# read from Hyprland socket2 on another thread
|
||||||
thread = threading.Thread(target=hypr_watcher)
|
thread = threading.Thread(target=hypr_watcher)
|
||||||
thread.daemon = True
|
thread.daemon = True
|
||||||
thread.start()
|
thread.start()
|
||||||
|
@@ -837,6 +837,8 @@ class SinkBox(Gtk.Box):
|
|||||||
desc = sink["desc"]
|
desc = sink["desc"]
|
||||||
if len(desc) > 26:
|
if len(desc) > 26:
|
||||||
desc = "{}\u2026".format(desc[:26])
|
desc = "{}\u2026".format(desc[:26])
|
||||||
|
if sink["running"]:
|
||||||
|
desc = f"✓ {desc}"
|
||||||
label = Gtk.Label(desc)
|
label = Gtk.Label(desc)
|
||||||
hbox.pack_start(label, True, True, 0)
|
hbox.pack_start(label, True, True, 0)
|
||||||
eb.add(vbox)
|
eb.add(vbox)
|
||||||
|
@@ -6,7 +6,7 @@ from nwg_panel.tools import check_key, update_image_fallback_desktop, hyprctl
|
|||||||
|
|
||||||
|
|
||||||
class HyprlandWorkspaces(Gtk.Box):
|
class HyprlandWorkspaces(Gtk.Box):
|
||||||
def __init__(self, settings, monitors, workspaces, clients, activewindow, icons_path):
|
def __init__(self, settings, monitors, workspaces, clients, activewindow, activeworkspace, icons_path):
|
||||||
Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
|
Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
|
||||||
self.settings = settings
|
self.settings = settings
|
||||||
self.num_box = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
|
self.num_box = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
|
||||||
@@ -22,7 +22,7 @@ class HyprlandWorkspaces(Gtk.Box):
|
|||||||
self.ws_nums = []
|
self.ws_nums = []
|
||||||
|
|
||||||
self.build_box()
|
self.build_box()
|
||||||
self.refresh(monitors, workspaces, clients, activewindow)
|
self.refresh(monitors, workspaces, clients, activewindow, activeworkspace)
|
||||||
|
|
||||||
def build_box(self):
|
def build_box(self):
|
||||||
check_key(self.settings, "num-ws", 10)
|
check_key(self.settings, "num-ws", 10)
|
||||||
@@ -79,6 +79,8 @@ class HyprlandWorkspaces(Gtk.Box):
|
|||||||
name = "{} {}".format(num, self.ws_id2name[num])
|
name = "{} {}".format(num, self.ws_id2name[num])
|
||||||
|
|
||||||
lbl = Gtk.Label.new("{}".format(name)) if not add_dot else Gtk.Label.new("{}.".format(name))
|
lbl = Gtk.Label.new("{}".format(name)) if not add_dot else Gtk.Label.new("{}.".format(name))
|
||||||
|
# if add_dot:
|
||||||
|
# lbl.set_property("name", "workspace-occupied")
|
||||||
lbl.set_use_markup(True)
|
lbl.set_use_markup(True)
|
||||||
if self.settings["angle"] != 0.0:
|
if self.settings["angle"] != 0.0:
|
||||||
lbl.set_angle(self.settings["angle"])
|
lbl.set_angle(self.settings["angle"])
|
||||||
@@ -88,7 +90,7 @@ class HyprlandWorkspaces(Gtk.Box):
|
|||||||
|
|
||||||
return eb, lbl
|
return eb, lbl
|
||||||
|
|
||||||
def refresh(self, monitors, workspaces, clients, activewindow):
|
def refresh(self, monitors, workspaces, clients, activewindow, activeworkspace):
|
||||||
occupied_workspaces = []
|
occupied_workspaces = []
|
||||||
self.ws_id2name = {}
|
self.ws_id2name = {}
|
||||||
|
|
||||||
@@ -111,21 +113,22 @@ class HyprlandWorkspaces(Gtk.Box):
|
|||||||
client_title = "X|{}".format(client_title)
|
client_title = "X|{}".format(client_title)
|
||||||
floating = activewindow["floating"]
|
floating = activewindow["floating"]
|
||||||
pinned = activewindow["pinned"]
|
pinned = activewindow["pinned"]
|
||||||
active_ws = activewindow["workspace"]["id"]
|
|
||||||
else:
|
else:
|
||||||
client_class = ""
|
client_class = ""
|
||||||
client_title = ""
|
client_title = ""
|
||||||
floating = False
|
floating = False
|
||||||
pinned = False
|
pinned = False
|
||||||
for m in monitors:
|
|
||||||
if m["focused"]:
|
# fix #310
|
||||||
active_ws = m["activeWorkspace"]["id"]
|
active_ws = activeworkspace["id"]
|
||||||
break
|
|
||||||
|
|
||||||
for num in self.ws_nums:
|
for num in self.ws_nums:
|
||||||
if num in occupied_workspaces or self.settings["show-empty"]:
|
if num in occupied_workspaces or self.settings["show-empty"]:
|
||||||
|
occ = num in occupied_workspaces
|
||||||
dot = num in occupied_workspaces and self.settings["show-empty"] and self.settings["mark-content"]
|
dot = num in occupied_workspaces and self.settings["show-empty"] and self.settings["mark-content"]
|
||||||
eb, lbl = self.build_number(num, add_dot=dot, active_win_ws=active_ws)
|
eb, lbl = self.build_number(num, add_dot=dot, active_win_ws=active_ws)
|
||||||
|
if occ:
|
||||||
|
lbl.set_property("name", "workspace-occupied")
|
||||||
self.num_box.pack_start(eb, False, False, 0)
|
self.num_box.pack_start(eb, False, False, 0)
|
||||||
self.num_box.show_all()
|
self.num_box.show_all()
|
||||||
|
|
||||||
|
@@ -45,54 +45,57 @@ class KeyboardLayout(Gtk.EventBox):
|
|||||||
|
|
||||||
if self.compositor:
|
if self.compositor:
|
||||||
self.keyboards = self.list_keyboards()
|
self.keyboards = self.list_keyboards()
|
||||||
self.keyboard_names = []
|
if self.keyboards:
|
||||||
for k in self.keyboards:
|
self.keyboard_names = []
|
||||||
if self.compositor == "Hyprland":
|
for k in self.keyboards:
|
||||||
self.keyboard_names.append(k["name"])
|
if self.compositor == "Hyprland":
|
||||||
# On sway some devices may be listed twice, let's add them just once
|
self.keyboard_names.append(k["name"])
|
||||||
elif k.identifier not in self.keyboard_names:
|
# On sway some devices may be listed twice, let's add them just once
|
||||||
self.keyboard_names.append(k.identifier)
|
elif k.identifier not in self.keyboard_names:
|
||||||
# print(f"keyboard_names = {self.keyboard_names}")
|
self.keyboard_names.append(k.identifier)
|
||||||
|
|
||||||
self.kb_layouts = self.get_kb_layouts()
|
self.kb_layouts = self.get_kb_layouts()
|
||||||
# print(f"kb_layouts = {self.kb_layouts}")
|
|
||||||
|
|
||||||
check_key(settings, "keyboard-device-sway", "")
|
check_key(settings, "keyboard-device-sway", "")
|
||||||
check_key(settings, "keyboard-device-hyprland", "")
|
check_key(settings, "keyboard-device-hyprland", "")
|
||||||
self.device_name = settings["keyboard-device-sway"] if self.compositor == "sway" else settings[
|
self.device_name = settings["keyboard-device-sway"] if self.compositor == "sway" else settings[
|
||||||
"keyboard-device-hyprland"]
|
"keyboard-device-hyprland"]
|
||||||
|
|
||||||
check_key(settings, "root-css-name", "root-executor")
|
check_key(settings, "root-css-name", "root-executor")
|
||||||
check_key(settings, "css-name", "")
|
check_key(settings, "css-name", "")
|
||||||
check_key(settings, "icon-placement", "left")
|
check_key(settings, "icon-placement", "left")
|
||||||
check_key(settings, "icon-size", 16)
|
check_key(settings, "icon-size", 16)
|
||||||
check_key(settings, "show-icon", True)
|
check_key(settings, "show-icon", True)
|
||||||
check_key(settings, "tooltip-text", "LMB: Next layout, RMB: Menu")
|
check_key(settings, "tooltip-text", "LMB: Next layout, RMB: Menu")
|
||||||
check_key(settings, "angle", 0.0)
|
check_key(settings, "angle", 0.0)
|
||||||
|
|
||||||
self.label.set_angle(settings["angle"])
|
self.label.set_angle(settings["angle"])
|
||||||
|
|
||||||
if settings["angle"] != 0.0:
|
if settings["angle"] != 0.0:
|
||||||
self.box.set_orientation(Gtk.Orientation.VERTICAL)
|
self.box.set_orientation(Gtk.Orientation.VERTICAL)
|
||||||
|
|
||||||
update_image(self.image, "input-keyboard", self.settings["icon-size"], self.icons_path)
|
update_image(self.image, "input-keyboard", self.settings["icon-size"], self.icons_path)
|
||||||
|
|
||||||
self.set_property("name", settings["root-css-name"])
|
self.set_property("name", settings["root-css-name"])
|
||||||
if settings["css-name"]:
|
if settings["css-name"]:
|
||||||
self.label.set_property("name", settings["css-name"])
|
self.label.set_property("name", settings["css-name"])
|
||||||
|
else:
|
||||||
|
self.label.set_property("name", "executor-label")
|
||||||
|
|
||||||
|
if settings["tooltip-text"]:
|
||||||
|
self.set_tooltip_text(settings["tooltip-text"])
|
||||||
|
|
||||||
|
self.connect('button-release-event', self.on_button_release)
|
||||||
|
self.connect('enter-notify-event', on_enter_notify_event)
|
||||||
|
self.connect('leave-notify-event', on_leave_notify_event)
|
||||||
|
|
||||||
|
self.build_box()
|
||||||
|
label = self.get_current_layout()
|
||||||
|
if label:
|
||||||
|
self.label.set_text(label)
|
||||||
|
self.show_all()
|
||||||
else:
|
else:
|
||||||
self.label.set_property("name", "executor-label")
|
print("KeyboardLayout module: failed listing devices, won't create UI, sorry.")
|
||||||
|
|
||||||
if settings["tooltip-text"]:
|
|
||||||
self.set_tooltip_text(settings["tooltip-text"])
|
|
||||||
|
|
||||||
self.connect('button-release-event', self.on_button_release)
|
|
||||||
self.connect('enter-notify-event', on_enter_notify_event)
|
|
||||||
self.connect('leave-notify-event', on_leave_notify_event)
|
|
||||||
|
|
||||||
self.build_box()
|
|
||||||
self.refresh()
|
|
||||||
self.show_all()
|
|
||||||
|
|
||||||
def list_keyboards(self):
|
def list_keyboards(self):
|
||||||
if self.compositor == "Hyprland":
|
if self.compositor == "Hyprland":
|
||||||
|
@@ -211,6 +211,15 @@ class Playerctl(Gtk.EventBox):
|
|||||||
if self.settings["angle"] != 0.0:
|
if self.settings["angle"] != 0.0:
|
||||||
button_box.set_orientation(Gtk.Orientation.VERTICAL)
|
button_box.set_orientation(Gtk.Orientation.VERTICAL)
|
||||||
|
|
||||||
|
img = Gtk.Image()
|
||||||
|
update_image(img, "media-skip-backward-symbolic", self.settings["icon-size"], icons_path=self.icons_path)
|
||||||
|
btn = Gtk.Button()
|
||||||
|
btn.set_image(img)
|
||||||
|
if self.settings["button-css-name"]:
|
||||||
|
btn.set_property("name", self.settings["button-css-name"])
|
||||||
|
btn.connect("clicked", self.launch, self.PlayerOps.PREVIOUS)
|
||||||
|
button_box.pack_start(btn, False, False, 1)
|
||||||
|
|
||||||
self.play_pause_btn = Gtk.Button()
|
self.play_pause_btn = Gtk.Button()
|
||||||
if self.settings["button-css-name"]:
|
if self.settings["button-css-name"]:
|
||||||
self.play_pause_btn.set_property("name", self.settings["button-css-name"])
|
self.play_pause_btn.set_property("name", self.settings["button-css-name"])
|
||||||
@@ -220,6 +229,15 @@ class Playerctl(Gtk.EventBox):
|
|||||||
self.play_pause_btn.connect("clicked", self.launch, self.PlayerOps.PLAY_PAUSE)
|
self.play_pause_btn.connect("clicked", self.launch, self.PlayerOps.PLAY_PAUSE)
|
||||||
button_box.pack_start(self.play_pause_btn, False, False, 1)
|
button_box.pack_start(self.play_pause_btn, False, False, 1)
|
||||||
|
|
||||||
|
img = Gtk.Image()
|
||||||
|
update_image(img, "media-skip-forward-symbolic", self.settings["icon-size"], icons_path=self.icons_path)
|
||||||
|
btn = Gtk.Button()
|
||||||
|
btn.set_image(img)
|
||||||
|
if self.settings["button-css-name"]:
|
||||||
|
btn.set_property("name", self.settings["button-css-name"])
|
||||||
|
btn.connect("clicked", self.launch, self.PlayerOps.NEXT)
|
||||||
|
button_box.pack_start(btn, False, False, 1)
|
||||||
|
|
||||||
self.num_players_lbl = Gtk.Label.new("")
|
self.num_players_lbl = Gtk.Label.new("")
|
||||||
if self.settings["label-css-name"]:
|
if self.settings["label-css-name"]:
|
||||||
self.num_players_lbl.set_property("name", self.settings["label-css-name"])
|
self.num_players_lbl.set_property("name", self.settings["label-css-name"])
|
||||||
@@ -239,9 +257,13 @@ class Playerctl(Gtk.EventBox):
|
|||||||
self.box.pack_start(button_box, False, False, 2)
|
self.box.pack_start(button_box, False, False, 2)
|
||||||
if self.settings["show-cover"]:
|
if self.settings["show-cover"]:
|
||||||
self.box.pack_start(self.cover_img, False, False, 0)
|
self.box.pack_start(self.cover_img, False, False, 0)
|
||||||
|
self.box.pack_start(self.num_players_lbl, False, False, 0)
|
||||||
|
self.box.pack_start(self.label, False, False, 5)
|
||||||
else:
|
else:
|
||||||
if self.settings["show-cover"]:
|
if self.settings["show-cover"]:
|
||||||
self.box.pack_start(self.cover_img, False, False, 2)
|
self.box.pack_start(self.cover_img, False, False, 2)
|
||||||
|
self.box.pack_start(self.num_players_lbl, False, False, 0)
|
||||||
|
self.box.pack_start(self.label, False, False, 2)
|
||||||
self.box.pack_start(button_box, False, False, 10)
|
self.box.pack_start(button_box, False, False, 10)
|
||||||
|
|
||||||
def launch(self, button, op):
|
def launch(self, button, op):
|
||||||
|
@@ -1,10 +1,13 @@
|
|||||||
from gi.repository import Gdk
|
from gi.repository import Gdk
|
||||||
|
|
||||||
from dasbus.connection import SessionMessageBus
|
from dasbus.connection import SessionMessageBus
|
||||||
|
from dasbus.specification import DBusSpecificationParser
|
||||||
from dasbus.client.observer import DBusObserver
|
from dasbus.client.observer import DBusObserver
|
||||||
from dasbus.client.proxy import disconnect_proxy
|
from dasbus.client.proxy import disconnect_proxy
|
||||||
from dasbus.error import DBusError
|
from dasbus.error import DBusError
|
||||||
|
|
||||||
|
from nwg_panel.tools import load_resource
|
||||||
|
|
||||||
PROPERTIES = [
|
PROPERTIES = [
|
||||||
"Id",
|
"Id",
|
||||||
"Category",
|
"Category",
|
||||||
@@ -57,6 +60,13 @@ class StatusNotifierItem(object):
|
|||||||
|
|
||||||
def item_available_handler(self, _observer):
|
def item_available_handler(self, _observer):
|
||||||
self.item_proxy = self.session_bus.get_proxy(self.service_name, self.object_path)
|
self.item_proxy = self.session_bus.get_proxy(self.service_name, self.object_path)
|
||||||
|
try:
|
||||||
|
spec = self.item_proxy._handler.specification
|
||||||
|
if spec is not None:
|
||||||
|
if not any("StatusNotifierItem" in ifname for ifname in spec.interfaces):
|
||||||
|
DBusSpecificationParser._parse_xml(spec, load_resource(__package__,"org.kde.StatusNotifierItem.xml"))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
if hasattr(self.item_proxy, "PropertiesChanged"):
|
if hasattr(self.item_proxy, "PropertiesChanged"):
|
||||||
self.item_proxy.PropertiesChanged.connect(
|
self.item_proxy.PropertiesChanged.connect(
|
||||||
lambda _if, changed, invalid: self.change_handler(list(changed), invalid)
|
lambda _if, changed, invalid: self.change_handler(list(changed), invalid)
|
||||||
@@ -65,6 +75,10 @@ class StatusNotifierItem(object):
|
|||||||
self.item_proxy.NewTitle.connect(
|
self.item_proxy.NewTitle.connect(
|
||||||
lambda: self.change_handler(["Title"])
|
lambda: self.change_handler(["Title"])
|
||||||
)
|
)
|
||||||
|
if hasattr(self.item_proxy, 'NewToolTip'):
|
||||||
|
self.item_proxy.NewToolTip.connect(
|
||||||
|
lambda: self.change_handler(["ToolTip"])
|
||||||
|
)
|
||||||
if hasattr(self.item_proxy, 'NewIcon'):
|
if hasattr(self.item_proxy, 'NewIcon'):
|
||||||
self.item_proxy.NewIcon.connect(
|
self.item_proxy.NewIcon.connect(
|
||||||
lambda: self.change_handler(["IconName", "IconPixmap"])
|
lambda: self.change_handler(["IconName", "IconPixmap"])
|
||||||
|
@@ -0,0 +1,49 @@
|
|||||||
|
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||||||
|
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||||
|
<node>
|
||||||
|
<interface name='org.kde.StatusNotifierItem'>
|
||||||
|
<annotation name="org.gtk.GDBus.C.Name" value="Item" />
|
||||||
|
<method name='ContextMenu'>
|
||||||
|
<arg type='i' direction='in' name='x'/>
|
||||||
|
<arg type='i' direction='in' name='y'/>
|
||||||
|
</method>
|
||||||
|
<method name='Activate'>
|
||||||
|
<arg type='i' direction='in' name='x'/>
|
||||||
|
<arg type='i' direction='in' name='y'/>
|
||||||
|
</method>
|
||||||
|
<method name='SecondaryActivate'>
|
||||||
|
<arg type='i' direction='in' name='x'/>
|
||||||
|
<arg type='i' direction='in' name='y'/>
|
||||||
|
</method>
|
||||||
|
<method name='Scroll'>
|
||||||
|
<arg type='i' direction='in' name='delta'/>
|
||||||
|
<arg type='s' direction='in' name='orientation'/>
|
||||||
|
</method>
|
||||||
|
<signal name='NewTitle'/>
|
||||||
|
<signal name='NewIcon'/>
|
||||||
|
<signal name='NewAttentionIcon'/>
|
||||||
|
<signal name='NewOverlayIcon'/>
|
||||||
|
<signal name='NewToolTip'/>
|
||||||
|
<signal name='NewStatus'>
|
||||||
|
<arg type='s' name='status'/>
|
||||||
|
</signal>
|
||||||
|
<property name='Category' type='s' access='read'/>
|
||||||
|
<property name='Id' type='s' access='read'/>
|
||||||
|
<property name='Title' type='s' access='read'/>
|
||||||
|
<property name='Status' type='s' access='read'/>
|
||||||
|
<!-- See discussion on pull #536
|
||||||
|
<property name='WindowId' type='u' access='read'/>
|
||||||
|
-->
|
||||||
|
<property name='IconThemePath' type='s' access='read'/>
|
||||||
|
<property name='IconName' type='s' access='read'/>
|
||||||
|
<property name='IconPixmap' type='a(iiay)' access='read'/>
|
||||||
|
<property name='OverlayIconName' type='s' access='read'/>
|
||||||
|
<property name='OverlayIconPixmap' type='a(iiay)' access='read'/>
|
||||||
|
<property name='AttentionIconName' type='s' access='read'/>
|
||||||
|
<property name='AttentionIconPixmap' type='a(iiay)' access='read'/>
|
||||||
|
<property name='AttentionMovieName' type='s' access='read'/>
|
||||||
|
<property name='ToolTip' type='(sa(iiay)ss)' access='read'/>
|
||||||
|
<property name='Menu' type='o' access='read'/>
|
||||||
|
<property name='ItemIsMenu' type='b' access='read'/>
|
||||||
|
</interface>
|
||||||
|
</node>
|
@@ -76,7 +76,7 @@ def update_icon_from_pixmap(image, item, icon_size):
|
|||||||
|
|
||||||
|
|
||||||
def update_tooltip(image, item):
|
def update_tooltip(image, item):
|
||||||
icon_name, icon_data, title, description = item.properties["Tooltip"]
|
icon_name, icon_data, title, description = item.properties["ToolTip"] if "ToolTip" in item.properties else item.properties["Tooltip"]
|
||||||
tooltip = title
|
tooltip = title
|
||||||
if description:
|
if description:
|
||||||
tooltip = "<b>{}</b>\n{}".format(title, description)
|
tooltip = "<b>{}</b>\n{}".format(title, description)
|
||||||
@@ -131,7 +131,7 @@ class Tray(Gtk.EventBox):
|
|||||||
elif "IconPixmap" in item.properties and len(item.properties["IconPixmap"]) != 0:
|
elif "IconPixmap" in item.properties and len(item.properties["IconPixmap"]) != 0:
|
||||||
update_icon_from_pixmap(image, item, self.icon_size)
|
update_icon_from_pixmap(image, item, self.icon_size)
|
||||||
|
|
||||||
if "Tooltip" in item.properties:
|
if "Tooltip" in item.properties or "ToolTip" in item.properties:
|
||||||
update_tooltip(image, item)
|
update_tooltip(image, item)
|
||||||
elif "Title" in item.properties:
|
elif "Title" in item.properties:
|
||||||
image.set_tooltip_markup(item.properties["Title"])
|
image.set_tooltip_markup(item.properties["Title"])
|
||||||
@@ -168,7 +168,7 @@ class Tray(Gtk.EventBox):
|
|||||||
update_icon_from_pixmap(image, item, self.icon_size)
|
update_icon_from_pixmap(image, item, self.icon_size)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if "Tooltip" in changed_properties:
|
if "Tooltip" in changed_properties or "ToolTip" in changed_properties:
|
||||||
update_tooltip(image, item)
|
update_tooltip(image, item)
|
||||||
elif "Title" in changed_properties:
|
elif "Title" in changed_properties:
|
||||||
image.set_tooltip_markup(item.properties["Title"])
|
image.set_tooltip_markup(item.properties["Title"])
|
||||||
|
@@ -171,6 +171,10 @@ class SwayWorkspaces(Gtk.Box):
|
|||||||
else:
|
else:
|
||||||
lbl.hide()
|
lbl.hide()
|
||||||
|
|
||||||
|
# mark non-empty WS with CSS ID
|
||||||
|
if int_num in non_empty:
|
||||||
|
lbl.set_property("name", "workspace-occupied")
|
||||||
|
|
||||||
# mark non-empty WS with a dot
|
# mark non-empty WS with a dot
|
||||||
if self.settings["mark-content"]:
|
if self.settings["mark-content"]:
|
||||||
if int_num in non_empty:
|
if int_num in non_empty:
|
||||||
@@ -262,13 +266,13 @@ class SwayWorkspaces(Gtk.Box):
|
|||||||
for item in tree.descendants():
|
for item in tree.descendants():
|
||||||
if item.type == "workspace":
|
if item.type == "workspace":
|
||||||
# find non-empty workspaces
|
# find non-empty workspaces
|
||||||
if self.settings["mark-content"] or self.settings["hide-empty"]:
|
# if self.settings["mark-content"] or self.settings["hide-empty"]:
|
||||||
tasks_num = 0
|
tasks_num = 0
|
||||||
for d in item.descendants():
|
for d in item.descendants():
|
||||||
if d.type == "con" and d.name:
|
if d.type == "con" and d.name:
|
||||||
tasks_num += 1
|
tasks_num += 1
|
||||||
if tasks_num > 0:
|
if tasks_num > 0:
|
||||||
non_empty.append(item.num)
|
non_empty.append(item.num)
|
||||||
|
|
||||||
for node in item.floating_nodes:
|
for node in item.floating_nodes:
|
||||||
if str(node.workspace().num) in self.settings["numbers"]:
|
if str(node.workspace().num) in self.settings["numbers"]:
|
||||||
|
@@ -358,6 +358,10 @@ def list_outputs(sway=False, tree=None, silent=False):
|
|||||||
'transform': transform,
|
'transform': transform,
|
||||||
'scale': scale,
|
'scale': scale,
|
||||||
'monitor': None}
|
'monitor': None}
|
||||||
|
#Each monitor only have a single transform this avoid parsing multiple times the same monitor
|
||||||
|
#Disabled monitors don't have transforms.
|
||||||
|
# Gdk doesn't report disabled monitor, not filtering them would cause crashes
|
||||||
|
transform = None
|
||||||
else:
|
else:
|
||||||
print("'wlr-randr' command not found, terminating")
|
print("'wlr-randr' command not found, terminating")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -371,10 +375,8 @@ def list_outputs(sway=False, tree=None, silent=False):
|
|||||||
monitor = display.get_monitor(i)
|
monitor = display.get_monitor(i)
|
||||||
monitors.append(monitor)
|
monitors.append(monitor)
|
||||||
|
|
||||||
idx = 0
|
for key, monitor in zip(outputs_dict.keys(), monitors):
|
||||||
for key in outputs_dict:
|
outputs_dict[key]["monitor"] = monitor
|
||||||
outputs_dict[key]["monitor"] = monitors[idx]
|
|
||||||
idx += 1
|
|
||||||
|
|
||||||
return outputs_dict
|
return outputs_dict
|
||||||
|
|
||||||
@@ -534,11 +536,14 @@ def list_sinks():
|
|||||||
for line in lines:
|
for line in lines:
|
||||||
details = line.split()
|
details = line.split()
|
||||||
name = details[1][1:-1]
|
name = details[1][1:-1]
|
||||||
desc = " ".join(details[2:])[1:-1]
|
desc = " ".join(details[3:])[1:-1]
|
||||||
sinks.append({"name": name, "desc": desc})
|
sink = {"name": name, "desc": desc, "running": True if "Running" in line else False}
|
||||||
|
sinks.append(sink)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
eprint(e)
|
eprint(e)
|
||||||
if nwg_panel.common.commands["pactl"]:
|
|
||||||
|
elif nwg_panel.common.commands["pactl"]:
|
||||||
try:
|
try:
|
||||||
output = cmd2string("pactl list sinks")
|
output = cmd2string("pactl list sinks")
|
||||||
if output:
|
if output:
|
||||||
@@ -555,6 +560,8 @@ def list_sinks():
|
|||||||
sink.update({"name": line.split(": ")[1]})
|
sink.update({"name": line.split(": ")[1]})
|
||||||
elif line.lower().startswith("description"):
|
elif line.lower().startswith("description"):
|
||||||
sink.update({"desc": line.split(": ")[1]})
|
sink.update({"desc": line.split(": ")[1]})
|
||||||
|
elif line.lower().startswith("state"):
|
||||||
|
sink.update({"running": True if "RUNNING" in line else False})
|
||||||
if sink:
|
if sink:
|
||||||
sinks.append(sink)
|
sinks.append(sink)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -786,7 +793,6 @@ def get_cache_dir():
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def file_age(path):
|
def file_age(path):
|
||||||
return time.time() - os.stat(path)[stat.ST_MTIME]
|
return time.time() - os.stat(path)[stat.ST_MTIME]
|
||||||
|
|
||||||
@@ -885,8 +891,17 @@ def h_get_activewindow():
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def h_get_active_workspace():
|
||||||
|
reply = hyprctl("j/activeworkspace")
|
||||||
|
try:
|
||||||
|
return json.loads(reply)
|
||||||
|
except Exception as e:
|
||||||
|
eprint(e)
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
def h_modules_get_all():
|
def h_modules_get_all():
|
||||||
return h_list_monitors(), h_list_workspaces(), h_list_clients(), h_get_activewindow()
|
return h_list_monitors(), h_list_workspaces(), h_list_clients(), h_get_activewindow(), h_get_active_workspace()
|
||||||
|
|
||||||
|
|
||||||
def cmd_through_compositor(cmd):
|
def cmd_through_compositor(cmd):
|
||||||
@@ -898,3 +913,28 @@ def cmd_through_compositor(cmd):
|
|||||||
elif os.getenv("HYPRLAND_INSTANCE_SIGNATURE"):
|
elif os.getenv("HYPRLAND_INSTANCE_SIGNATURE"):
|
||||||
cmd = f"hyprctl dispatch exec '{cmd}'"
|
cmd = f"hyprctl dispatch exec '{cmd}'"
|
||||||
return cmd
|
return cmd
|
||||||
|
|
||||||
|
def load_resource(package, resource_name):
|
||||||
|
try:
|
||||||
|
import importlib.resources as resources
|
||||||
|
with resources.open_binary(package, resource_name) as resource_file:
|
||||||
|
return resource_file.read()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
import importlib.util
|
||||||
|
spec = importlib.util.find_spec(package)
|
||||||
|
if spec is not None and spec.loader is not None and hasattr(spec.loader, 'get_data'):
|
||||||
|
return spec.loader.get_data(resource_name)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
import pkgutil
|
||||||
|
data = pkgutil.get_data(package, resource_name)
|
||||||
|
if data is None:
|
||||||
|
raise FileNotFoundError(f"Resource {resource_name} not found in package {package}.")
|
||||||
|
return data
|
||||||
|
except ImportError as e:
|
||||||
|
raise ImportError("Failed to load the resource using any available method.") from e
|
||||||
|
5
setup.py
5
setup.py
@@ -8,12 +8,13 @@ def read(f_name):
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='nwg-panel',
|
name='nwg-panel',
|
||||||
version='0.9.34',
|
version='0.9.41',
|
||||||
description='GTK3-based panel for sway and Hyprland Wayland compositors',
|
description='GTK3-based panel for sway and Hyprland Wayland compositors',
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
package_data={
|
package_data={
|
||||||
"": ["config/*", "icons_dark/*", "icons_light/*", "icons_color/*", "langs/*", "executors/*", "local/*"]
|
"": ["config/*", "icons_dark/*", "icons_light/*", "icons_color/*", "langs/*", "executors/*", "local/*",
|
||||||
|
"modules/sni_system_tray/org.kde.StatusNotifierItem.xml"]
|
||||||
},
|
},
|
||||||
url='https://github.com/nwg-piotr/nwg-panel',
|
url='https://github.com/nwg-piotr/nwg-panel',
|
||||||
license='MIT',
|
license='MIT',
|
||||||
|
Reference in New Issue
Block a user