mirror of
https://github.imc.re/void-land/hyprland-void-dots
synced 2025-04-27 00:13:43 +02:00
refactor: eww notifications
This commit is contained in:
parent
b73eb585af
commit
caad2ba8ef
10 changed files with 498 additions and 245 deletions
|
@ -73,10 +73,9 @@
|
|||
(deflisten notifications :initial '{
|
||||
"count": 0,
|
||||
"dnd": false,
|
||||
"notifications": [],
|
||||
"popups": []
|
||||
}'
|
||||
"./scripts/notif.py"
|
||||
"./scripts/notification/notifs.py"
|
||||
)
|
||||
|
||||
; Playerctl
|
|
@ -1,4 +1,4 @@
|
|||
(include "./src/_definitions.yuck")
|
||||
(include "./_definitions.yuck")
|
||||
|
||||
(include "./src/-components/_helpers.yuck")
|
||||
(include "./src/-components/_separator.yuck")
|
||||
|
@ -16,7 +16,7 @@
|
|||
(include "./src/windows/_dashboard.yuck")
|
||||
(include "./src/windows/_osd.yuck")
|
||||
|
||||
(include "./setups.yuck")
|
||||
(include "./_setups.yuck")
|
||||
|
||||
(include "./src/wallpapers/main.yuck")
|
||||
(include "./src/dock/main.yuck")
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
#!/usr/bin/bash
|
||||
|
||||
# Taken from Juminai
|
||||
|
||||
dismiss() {
|
||||
dbus-send --session --type=method_call \
|
||||
--dest=org.freedesktop.Notifications \
|
||||
/org/freedesktop/Notifications \
|
||||
org.freedesktop.Notifications.DismissPopup \
|
||||
uint32:$1
|
||||
}
|
||||
|
||||
close() {
|
||||
dbus-send --session --type=method_call \
|
||||
--dest=org.freedesktop.Notifications \
|
||||
/org/freedesktop/Notifications \
|
||||
org.freedesktop.Notifications.CloseNotification \
|
||||
uint32:$1
|
||||
}
|
||||
|
||||
action() {
|
||||
dbus-send --session --type=method_call \
|
||||
--dest=org.freedesktop.Notifications \
|
||||
/org/freedesktop/Notifications \
|
||||
org.freedesktop.Notifications.InvokeAction \
|
||||
uint32:$1 string:$2
|
||||
}
|
||||
|
||||
clear_all() {
|
||||
dbus-send --session --type=method_call \
|
||||
--dest=org.freedesktop.Notifications \
|
||||
/org/freedesktop/Notifications \
|
||||
org.freedesktop.Notifications.ClearAll
|
||||
}
|
||||
|
||||
listen() {
|
||||
dbus-send --session --type=method_call \
|
||||
--dest=org.freedesktop.Notifications \
|
||||
/org/freedesktop/Notifications \
|
||||
org.freedesktop.Notifications.Listen
|
||||
}
|
||||
|
||||
toggle_dnd() {
|
||||
dbus-send --session --type=method_call \
|
||||
--dest=org.freedesktop.Notifications \
|
||||
/org/freedesktop/Notifications \
|
||||
org.freedesktop.Notifications.ToggleDND
|
||||
}
|
||||
|
||||
if [[ $1 == '--dismiss' ]]; then dismiss $2 $3; fi
|
||||
if [[ $1 == '--close' ]]; then close $2; fi
|
||||
if [[ $1 == '--action' ]]; then action $2 $3; fi
|
||||
if [[ $1 == '--clear' ]]; then clear_all; fi
|
||||
if [[ $1 == '--listen' ]]; then listen; fi
|
||||
if [[ $1 == '--toggle' ]]; then toggle_dnd; fi
|
59
hypr-configs/hyprland/eww/scripts/notification/manage
Executable file
59
hypr-configs/hyprland/eww/scripts/notification/manage
Executable file
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/bash
|
||||
|
||||
DBUS_CMD="dbus-send --session --type=method_call --dest=org.freedesktop.Notifications /org/freedesktop/Notifications"
|
||||
|
||||
dismiss() {
|
||||
$DBUS_CMD org.freedesktop.Notifications.DismissPopup uint32:$1
|
||||
}
|
||||
|
||||
close() {
|
||||
$DBUS_CMD org.freedesktop.Notifications.CloseNotification uint32:$1
|
||||
}
|
||||
|
||||
action() {
|
||||
$DBUS_CMD org.freedesktop.Notifications.InvokeAction uint32:$1 string:$2
|
||||
}
|
||||
|
||||
get_current() {
|
||||
$DBUS_CMD org.freedesktop.Notifications.GetCurrent
|
||||
}
|
||||
|
||||
clear_all() {
|
||||
$DBUS_CMD org.freedesktop.Notifications.ClearAll
|
||||
}
|
||||
|
||||
listen() {
|
||||
$DBUS_CMD org.freedesktop.Notifications.Listen
|
||||
}
|
||||
|
||||
toggle_dnd() {
|
||||
$DBUS_CMD org.freedesktop.Notifications.ToggleDND
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
--dismiss)
|
||||
dismiss "$2"
|
||||
;;
|
||||
--close)
|
||||
close "$2"
|
||||
;;
|
||||
--action)
|
||||
action "$2" "$3"
|
||||
;;
|
||||
--current)
|
||||
get_current
|
||||
;;
|
||||
--clear)
|
||||
clear_all
|
||||
;;
|
||||
--listen)
|
||||
listen
|
||||
;;
|
||||
--toggle)
|
||||
toggle_dnd
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {--dismiss|--close|--action|--clear|--listen|--toggle} [args]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
236
hypr-configs/hyprland/eww/scripts/notification/notifications.py
Executable file
236
hypr-configs/hyprland/eww/scripts/notification/notifications.py
Executable file
|
@ -0,0 +1,236 @@
|
|||
#!/usr/bin/python
|
||||
import gi
|
||||
gi.require_version("GdkPixbuf", "2.0")
|
||||
gi.require_version("Gtk", "3.0")
|
||||
|
||||
import dbus
|
||||
import dbus.service
|
||||
from dbus.mainloop.glib import DBusGMainLoop
|
||||
from gi.repository import GLib
|
||||
import datetime
|
||||
import os
|
||||
import typing
|
||||
import sys
|
||||
import json
|
||||
from gi.repository import Gtk, GdkPixbuf
|
||||
import subprocess
|
||||
|
||||
cache_dir = f"{os.getenv('HOME')}/.cache/notify_img_data"
|
||||
log_file = f"{os.getenv('HOME')}/.cache/notifications.json"
|
||||
os.makedirs(cache_dir, exist_ok=True)
|
||||
active_popups = {}
|
||||
|
||||
# RECIEVE NOTIFICATIONS
|
||||
|
||||
class NotificationDaemon(dbus.service.Object):
|
||||
def __init__(self):
|
||||
bus_name = dbus.service.BusName("org.freedesktop.Notifications", dbus.SessionBus())
|
||||
dbus.service.Object.__init__(self, bus_name, "/org/freedesktop/Notifications")
|
||||
self.dnd = False
|
||||
|
||||
@dbus.service.method("org.freedesktop.Notifications", in_signature="susssasa{sv}i", out_signature="u")
|
||||
def Notify(self, app_name, replaces_id, app_icon, summary, body, actions, hints, timeout):
|
||||
replaces_id = int(replaces_id)
|
||||
actions = list(actions)
|
||||
app_icon = str(app_icon)
|
||||
app_name = str(app_name)
|
||||
summary = str(summary)
|
||||
body = str(body)
|
||||
|
||||
if replaces_id != 0:
|
||||
id = replaces_id
|
||||
else:
|
||||
log_file = self.read_log_file()
|
||||
if log_file['notifications'] != []:
|
||||
id = log_file['notifications'][0]['id'] + 1
|
||||
else:
|
||||
id = 1
|
||||
|
||||
acts = []
|
||||
for i in range(0, len(actions), 2):
|
||||
acts.append([str(actions[i]), str(actions[i + 1])])
|
||||
|
||||
details = {
|
||||
"id": id,
|
||||
"app": app_name,
|
||||
"summary": self.format_long_string(summary, 35),
|
||||
"body": self.format_long_string(body, 35),
|
||||
"time": datetime.datetime.now().strftime("%H:%M"),
|
||||
"urgency": hints["urgency"] if "urgency" in hints else 1,
|
||||
"actions": acts
|
||||
}
|
||||
|
||||
|
||||
if app_icon.strip():
|
||||
if os.path.isfile(app_icon) or app_icon.startswith("file://"):
|
||||
details["image"] = app_icon
|
||||
else:
|
||||
details["image"] = self.get_gtk_icon(app_icon)
|
||||
else:
|
||||
details["image"] = None
|
||||
|
||||
if "image-data" in hints:
|
||||
details["image"] = f"{cache_dir}/{details['id']}.png"
|
||||
self.save_img_byte(hints["image-data"], details["image"])
|
||||
|
||||
self.save_notifications(details)
|
||||
if not self.dnd:
|
||||
self.save_popup(details)
|
||||
return id
|
||||
|
||||
|
||||
|
||||
def format_long_string(self, long_string, interval):
|
||||
split_string = []
|
||||
max_length = 256
|
||||
|
||||
for i in range(0, len(long_string), interval):
|
||||
split_string.append(long_string[i:i+interval])
|
||||
|
||||
result = "-\n".join(split_string)
|
||||
|
||||
if len(result) > max_length:
|
||||
result = result[:max_length] + "..."
|
||||
|
||||
return result
|
||||
|
||||
@dbus.service.method("org.freedesktop.Notifications", in_signature="", out_signature="ssss")
|
||||
def GetServerInformation(self):
|
||||
return ("linkfrg's notification daemon", "linkfrg", "1.0", "1.2")
|
||||
|
||||
@dbus.service.method("org.freedesktop.Notifications", in_signature="", out_signature="as")
|
||||
def GetCapabilities(self):
|
||||
return ('actions', 'body', 'icon-static', 'persistence')
|
||||
|
||||
@dbus.service.signal("org.freedesktop.Notifications", signature="us")
|
||||
def ActionInvoked(self, id, action):
|
||||
return (id, action)
|
||||
|
||||
@dbus.service.method("org.freedesktop.Notifications", in_signature="us", out_signature="")
|
||||
def InvokeAction(self, id, action):
|
||||
self.ActionInvoked(id, action)
|
||||
|
||||
@dbus.service.signal("org.freedesktop.Notifications", signature="uu")
|
||||
def NotificationClosed(self, id, reason):
|
||||
return (id, reason)
|
||||
|
||||
@dbus.service.method("org.freedesktop.Notifications", in_signature="u", out_signature="")
|
||||
def CloseNotification(self, id):
|
||||
current = self.read_log_file()
|
||||
current["notifications"] = [n for n in current["notifications"] if n["id"] != id]
|
||||
current["count"] = len(current["notifications"])
|
||||
|
||||
self.write_log_file(current)
|
||||
self.NotificationClosed(id, 2)
|
||||
self.DismissPopup(id)
|
||||
|
||||
@dbus.service.method("org.freedesktop.Notifications", in_signature="", out_signature="")
|
||||
def ToggleDND(self):
|
||||
match self.dnd:
|
||||
case False:
|
||||
self.dnd = True
|
||||
case True:
|
||||
self.dnd = False
|
||||
|
||||
@dbus.service.method("org.freedesktop.Notifications", in_signature="", out_signature="")
|
||||
def GetDNDState(self):
|
||||
subprocess.run(["eww", "update", f"do-not-disturb={json.dumps(self.dnd)}"])
|
||||
|
||||
|
||||
def get_gtk_icon(self, icon_name):
|
||||
theme = Gtk.IconTheme.get_default()
|
||||
icon_info = theme.lookup_icon(icon_name, 128, 0)
|
||||
|
||||
if icon_info is not None:
|
||||
return icon_info.get_filename()
|
||||
|
||||
|
||||
def save_img_byte(self, px_args: typing.Iterable, save_path: str):
|
||||
GdkPixbuf.Pixbuf.new_from_bytes(
|
||||
width=px_args[0],
|
||||
height=px_args[1],
|
||||
has_alpha=px_args[3],
|
||||
data=GLib.Bytes(px_args[6]),
|
||||
colorspace=GdkPixbuf.Colorspace.RGB,
|
||||
rowstride=px_args[2],
|
||||
bits_per_sample=px_args[4],
|
||||
).savev(save_path, "png")
|
||||
|
||||
|
||||
def write_log_file(self, data):
|
||||
output_json = json.dumps(data, indent=2)
|
||||
subprocess.run(["eww", "update", f"notifications={output_json}"])
|
||||
with open(log_file, "w") as log:
|
||||
log.write(output_json)
|
||||
|
||||
def read_log_file(self):
|
||||
empty = {"count": 0, "notifications": [], "popups": []}
|
||||
try:
|
||||
with open(log_file, "r") as log:
|
||||
return json.load(log)
|
||||
except FileNotFoundError:
|
||||
with open(log_file, "w") as log:
|
||||
json.dump(empty, log)
|
||||
return empty
|
||||
|
||||
|
||||
def save_notifications(self, notification):
|
||||
current = self.read_log_file()
|
||||
current["notifications"].insert(0, notification)
|
||||
current["count"] = len(current["notifications"])
|
||||
|
||||
self.write_log_file(current)
|
||||
|
||||
@dbus.service.method("org.freedesktop.Notifications", in_signature="", out_signature="")
|
||||
def ClearAll(self):
|
||||
for notify in self.read_log_file()['notifications']:
|
||||
self.NotificationClosed(notify['id'], 2)
|
||||
data = {"count": 0, "notifications": [], "popups": []}
|
||||
|
||||
self.write_log_file(data)
|
||||
|
||||
|
||||
# OPERATIONS WITH POPUPS
|
||||
|
||||
def save_popup(self, notification):
|
||||
global active_popups
|
||||
|
||||
current = self.read_log_file()
|
||||
if len(current["popups"]) >= 3:
|
||||
oldest_popup = current["popups"].pop()
|
||||
self.DismissPopup(oldest_popup["id"])
|
||||
|
||||
current["popups"].append(notification)
|
||||
self.write_log_file(current)
|
||||
|
||||
popup_id = notification["id"]
|
||||
active_popups[popup_id] = GLib.timeout_add_seconds(5, self.DismissPopup, popup_id)
|
||||
|
||||
@dbus.service.method("org.freedesktop.Notifications", in_signature="u", out_signature="")
|
||||
def DismissPopup(self, id):
|
||||
global active_popups
|
||||
|
||||
current = self.read_log_file()
|
||||
current["popups"] = [n for n in current["popups"] if n["id"] != id]
|
||||
self.write_log_file(current)
|
||||
|
||||
active_popups.pop(id, None)
|
||||
|
||||
@dbus.service.method("org.freedesktop.Notifications", in_signature="", out_signature="")
|
||||
def GetCurrent(self):
|
||||
subprocess.run(["eww", "update", f"notifications={json.dumps(self.read_log_file())}"])
|
||||
|
||||
|
||||
# MAINLOOP
|
||||
|
||||
def main():
|
||||
DBusGMainLoop(set_as_default=True)
|
||||
loop = GLib.MainLoop()
|
||||
NotificationDaemon()
|
||||
try:
|
||||
loop.run()
|
||||
except KeyboardInterrupt:
|
||||
exit(0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
(defwindow notifypopup
|
||||
:geometry (geometry
|
||||
:x 0
|
||||
|
@ -8,6 +7,7 @@
|
|||
:anchor "top right")
|
||||
:stacking "overlay"
|
||||
:monitor 0
|
||||
|
||||
(revealer
|
||||
:reveal { arraylength(notifications.popups) > 0 }
|
||||
:transition "slidedown"
|
||||
|
@ -40,7 +40,7 @@
|
|||
:text {noti.app}))
|
||||
(button
|
||||
:halign "end"
|
||||
:onclick "./scripts/notifManage --close ${noti.id}"
|
||||
:onclick "./scripts/notification/manage --close ${noti.id}"
|
||||
(label
|
||||
:class "notifclose"
|
||||
:text "")))
|
||||
|
@ -54,8 +54,8 @@
|
|||
(image :image-width 80 :image-height 80 :visible {noti.image != "null"} :path {noti.image != "null" ? noti.image : "./assets/image/idk.svg"})
|
||||
|
||||
(button
|
||||
:onclick "./scripts/notifManage --dismiss ${noti.id}"
|
||||
:onrightclick "./scripts/notifManage --close ${noti.id}"
|
||||
:onclick "./scripts/notification/manage --dismiss ${noti.id}"
|
||||
:onrightclick "./scripts/notification/manage --close ${noti.id}"
|
||||
:tooltip "${noti.time}"
|
||||
:hexpand true
|
||||
:vexpand true
|
||||
|
@ -83,7 +83,7 @@
|
|||
:orientation "h"
|
||||
(for action in {noti.actions}
|
||||
(button
|
||||
:onclick "./scripts/notifManage --action ${noti.id} ${action[0]} && ./scripts/notifManage --close ${noti.id}"
|
||||
:onclick "./scripts/notification/manage --action ${noti.id} ${action[0]} && ./scripts/notification/manage --close ${noti.id}"
|
||||
(label
|
||||
:class "notifactions"
|
||||
:text {action[1]}))
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
:valign "fill"
|
||||
:vexpand true
|
||||
|
||||
(user)
|
||||
(User )
|
||||
(chooser)
|
||||
; (weather)
|
||||
|
||||
(MediaPlayer :h 160 :permashow true)
|
||||
; (MediaPlayer :h 160 :permashow true)
|
||||
|
||||
(box
|
||||
:orientation "h"
|
||||
|
@ -197,7 +197,7 @@
|
|||
:width 55
|
||||
(label :text icon))))
|
||||
|
||||
(defwidget user[]
|
||||
(defwidget User []
|
||||
(revealer
|
||||
:reveal {!reveal4 && !reveal5 && !reveal6}
|
||||
:transition "slideup"
|
||||
|
@ -253,16 +253,16 @@
|
|||
:hexpand {!revealWeather}
|
||||
:transition "slideleft"
|
||||
|
||||
(notificationlog))
|
||||
(NotificationsLogs))
|
||||
|
||||
(revealer
|
||||
:reveal revealWeather
|
||||
:hexpand revealWeather
|
||||
:transition "slideleft"
|
||||
|
||||
(weather)))))
|
||||
(Weather)))))
|
||||
|
||||
(defwidget weather[]
|
||||
(defwidget Weather []
|
||||
(overlay
|
||||
(box
|
||||
:orientation "v"
|
||||
|
@ -272,6 +272,7 @@
|
|||
:space-evenly false
|
||||
|
||||
(weathermain)
|
||||
|
||||
(scroll
|
||||
:hscroll false
|
||||
:vscroll true
|
||||
|
@ -290,6 +291,64 @@
|
|||
:class "fadeoutbox")
|
||||
))
|
||||
|
||||
(defwidget NotificationsLogs []
|
||||
(box
|
||||
:halign "fill"
|
||||
:valign "fill"
|
||||
:width 320
|
||||
:vexpand true
|
||||
:space-evenly false
|
||||
:orientation "v"
|
||||
|
||||
(overlay
|
||||
:vexpand true
|
||||
|
||||
(box
|
||||
:halign "fill"
|
||||
:valign "fill"
|
||||
:vexpand true
|
||||
:space-evenly false
|
||||
:orientation "v"
|
||||
|
||||
(scroll
|
||||
:hscroll false
|
||||
:vscroll true
|
||||
:vexpand true
|
||||
:valign "fill"
|
||||
|
||||
(box
|
||||
:orientation "v"
|
||||
:valign "start"
|
||||
:space-evenly false
|
||||
(for notif in {notifications.notifications}
|
||||
(singlenotif :noti notif :initial false))))
|
||||
)
|
||||
|
||||
(box
|
||||
:valign "end"
|
||||
:hexpand true
|
||||
:vexpand false
|
||||
:height 80
|
||||
:class "fadeoutbox")
|
||||
)
|
||||
|
||||
(box
|
||||
:orientation "h"
|
||||
:valign "end"
|
||||
|
||||
(button
|
||||
:onclick "./scripts/notification/manage --clear"
|
||||
(label
|
||||
:class "title"
|
||||
:text "Clear All"))
|
||||
(button
|
||||
:onclick "./scripts/notification/manage --toggle"
|
||||
(label
|
||||
:class { notifications.dnd ? "titlesel" : "title"}
|
||||
:text "Do Not Disturb")))
|
||||
)
|
||||
)
|
||||
|
||||
(defwidget weatherhour[hour]
|
||||
(box
|
||||
:class "smallentry"
|
||||
|
@ -416,52 +475,6 @@
|
|||
:valign "end"
|
||||
:text "")))
|
||||
|
||||
(defwidget notificationlog []
|
||||
(overlay
|
||||
(box
|
||||
:halign "fill"
|
||||
:valign "fill"
|
||||
:vexpand true
|
||||
:space-evenly false
|
||||
:orientation "v"
|
||||
|
||||
(scroll
|
||||
:hscroll false
|
||||
:vscroll true
|
||||
:vexpand true
|
||||
:valign "fill"
|
||||
|
||||
(box
|
||||
:orientation "v"
|
||||
:valign "start"
|
||||
:space-evenly false
|
||||
(for noti in {notifications.notifications}
|
||||
(singlenotif :noti noti :initial false))))
|
||||
|
||||
(box
|
||||
:valign "end"
|
||||
:hexpand true
|
||||
:vexpand false
|
||||
:height 80
|
||||
:class "fadeoutbox")
|
||||
(box
|
||||
:orientation "h"
|
||||
(button
|
||||
:onclick "./scripts/notifManage --clear"
|
||||
(label
|
||||
:class "title"
|
||||
:text "Clear All"))
|
||||
(button
|
||||
:onclick "./scripts/notifManage --toggle"
|
||||
(label
|
||||
:class { notifications.dnd ? "titlesel" : "title"}
|
||||
:text "Do Not Disturb")))
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
(defwidget quote []
|
||||
(box
|
||||
:class "quotewid panel-widget"
|
||||
|
|
Loading…
Add table
Reference in a new issue