[Feature] Add ability to inhibit notifications through DBus (#223)

This commit is contained in:
Erik Reider
2023-02-23 21:41:08 +01:00
committed by GitHub
parent c7644dbf91
commit 4f25e2e609
19 changed files with 409 additions and 76 deletions

View File

@@ -30,6 +30,7 @@ Post your setup here: [Config flex 💪](https://github.com/ErikReider/SwayNotif
- A panel to view previous notifications - A panel to view previous notifications
- Show album art for notifications like Spotify - Show album art for notifications like Spotify
- Do not disturb - Do not disturb
- Inhibiting notifications through DBUS or client
- Restores previous Do not disturb value after restart - Restores previous Do not disturb value after restart
- Click notification to execute default action - Click notification to execute default action
- Show alternative notification actions - Show alternative notification actions
@@ -278,7 +279,11 @@ Waybar config
"notification": "<span foreground='red'><sup></sup></span>", "notification": "<span foreground='red'><sup></sup></span>",
"none": "", "none": "",
"dnd-notification": "<span foreground='red'><sup></sup></span>", "dnd-notification": "<span foreground='red'><sup></sup></span>",
"dnd-none": "" "dnd-none": "",
"inhibited-notification": "<span foreground='red'><sup></sup></span>",
"inhibited-none": "",
"dnd-inhibited-notification": "<span foreground='red'><sup></sup></span>",
"dnd-inhibited-none": ""
}, },
"return-type": "json", "return-type": "json",
"exec-if": "which swaync-client", "exec-if": "which swaync-client",

View File

@@ -14,6 +14,11 @@ _swaync-client() {
-D -D
-dn -dn
-df -df
-I
-In
-Ia
-Ir
-Ic
-c -c
-C -C
-sw -sw
@@ -33,6 +38,11 @@ _swaync-client() {
--get-dnd --get-dnd
--dnd-on --dnd-on
--dnd-off --dnd-off
--get-inhibited
--get-num-inhibitors
--inhibitor-add
--inhibitor-remove
--inhibitors-clear
--count --count
--hide-latest --hide-latest
--close-latest --close-latest

View File

@@ -10,6 +10,11 @@ complete -c swaync-client -s d -l toggle-dnd --description "Toggle and print the
complete -c swaync-client -s D -l get-dnd --description "Print the current dnd state" -r complete -c swaync-client -s D -l get-dnd --description "Print the current dnd state" -r
complete -c swaync-client -s dn -l dnd-on --description "Turn dnd on and print the new dnd state" -r complete -c swaync-client -s dn -l dnd-on --description "Turn dnd on and print the new dnd state" -r
complete -c swaync-client -s df -l dnd-off --description "Turn dnd off and print the new dnd state" -r complete -c swaync-client -s df -l dnd-off --description "Turn dnd off and print the new dnd state" -r
complete -c swaync-client -s I -l get-inhibited --description "Print if currently inhibited or not" -r
complete -c swaync-client -s In -l get-num-inhibitors --description "Print number of inhibitors" -r
complete -c swaync-client -s Ia -l inhibitor-add --description "Add an inhibitor" -r
complete -c swaync-client -s Ir -l inhibitor-remove --description "Remove an inhibitor" -r
complete -c swaync-client -s Ic -l inhibitors-clear --description "Clears all inhibitors" -r
complete -c swaync-client -s c -l count --description "Print the current notificaion count" -r complete -c swaync-client -s c -l count --description "Print the current notificaion count" -r
complete -c swaync-client -l hide-latest --description "Hides latest notification. Still shown in Control Center" -r complete -c swaync-client -l hide-latest --description "Hides latest notification. Still shown in Control Center" -r
complete -c swaync-client -l close-latest --description "Closes latest notification" -r complete -c swaync-client -l close-latest --description "Closes latest notification" -r

View File

@@ -12,6 +12,11 @@ _arguments -s \
'(-D --get-dnd)'{-D,--get-dnd}'[Print the current dnd state]' \ '(-D --get-dnd)'{-D,--get-dnd}'[Print the current dnd state]' \
'(-dn --dnd-on)'{-dn,--dnd-on}'[Turn dnd on and print the new dnd state]' \ '(-dn --dnd-on)'{-dn,--dnd-on}'[Turn dnd on and print the new dnd state]' \
'(-df --dnd-off)'{-df,--dnd-off}'[Turn dnd off and print the new dnd state]' \ '(-df --dnd-off)'{-df,--dnd-off}'[Turn dnd off and print the new dnd state]' \
'(-I --get-inhibited)'{-I,--get-inhibited}'[Print if currently inhibited or not]' \
'(-In --get-num-inhibitors)'{-In,--get-num-inhibitors}'[Print number of inhibitors]' \
'(-Ia --inhibitor-add)'{-Ia,--inhibitor-add}'[Add an inhibitor]' \
'(-Ir --inhibitor-remove)'{-Ir,--inhibitor-remove}'[Remove an inhibitor]' \
'(-Ic --inhibitors-clear)'{-Ic,--inhibitors-clear}'[Clears all inhibitors]' \
'(-c --count)'{-c,--count}'[Print the current notificaion count]' \ '(-c --count)'{-c,--count}'[Print the current notificaion count]' \
'(--hide-latest)'--hide-latest'[Closes all notifications]' \ '(--hide-latest)'--hide-latest'[Closes all notifications]' \
'(--close-latest)'--close-latest'[Hides latest notification. Still shown in Control Center]' \ '(--close-latest)'--close-latest'[Hides latest notification. Still shown in Control Center]' \

View File

@@ -43,6 +43,21 @@ swaync-client - Client executable
*-df, --dnd-off* *-df, --dnd-off*
Turn dnd off and print the new dnd state Turn dnd off and print the new dnd state
*-I, --get-inhibited*
Print if currently inhibited or not
*-In, --get-num-inhibitors*
Print number of inhibitors
*-Ia, --inhibitor-add [APP_ID]*
Add an inhibitor
*-Ir, --inhibitor-remove [APP_ID]*
Remove an inhibitor
*-Ic, --inhibitors-clear*
Clears all inhibitors
*-c, --count* *-c, --count*
Print the current notificaion count Print the current notificaion count

View File

@@ -129,7 +129,11 @@ Waybar config
"notification": "<span foreground='red'><sup></sup></span>", "notification": "<span foreground='red'><sup></sup></span>",
"none": "", "none": "",
"dnd-notification": "<span foreground='red'><sup></sup></span>", "dnd-notification": "<span foreground='red'><sup></sup></span>",
"dnd-none": "" "dnd-none": "",
"inhibited-notification": "<span foreground='red'><sup></sup></span>",
"inhibited-none": "",
"dnd-inhibited-notification": "<span foreground='red'><sup></sup></span>",
"dnd-inhibited-none": ""
}, },
"return-type": "json", "return-type": "json",
"exec-if": "which swaync-client", "exec-if": "which swaync-client",

View File

@@ -199,6 +199,8 @@ config file to be able to detect config errors
optional: true ++ optional: true ++
*buttons-grid*++ *buttons-grid*++
optional: true ++ optional: true ++
*inhibitors*++
optional: true ++
description: ++ description: ++
Which order and which widgets to display. ++ Which order and which widgets to display. ++
If the \"notifications\" widget isn't specified, it ++ If the \"notifications\" widget isn't specified, it ++
@@ -211,6 +213,7 @@ config file to be able to detect config errors
``` ```
{ {
"widgets": [ "widgets": [
"inhibitors",
"title", "title",
"dnd", "dnd",
"notifications" "notifications"
@@ -407,7 +410,27 @@ config file to be able to detect config errors
optional: true ++ optional: true ++
default: 0 ++ default: 0 ++
description: Lowest possible value for brightness ++ description: Lowest possible value for brightness ++
description: Slider to control screen brightness ++ description: Slider to control screen brightness ++
*inhibitors*++
type: object ++
css class: widget-inhibitors ++
properties: ++
text: ++
type: string ++
optional: true ++
default: "Inhibitors" ++
description: The title of the widget ++
clear-all-button: ++
type: bool ++
optional: true ++
default: true ++
description: Whether to display a "Clear All" button ++
button-text: ++
type: string ++
optional: true ++
default: "Clear All" ++
description: "Clear All" button text ++
description: Displayed if notifications are inhibited.
example: example:
``` ```

View File

@@ -2,6 +2,7 @@ public struct SwayncDaemonData {
public bool dnd; public bool dnd;
public bool cc_open; public bool cc_open;
public uint count; public uint count;
public bool inhibited;
} }
[DBus (name = "org.erikreider.swaync.cc")] [DBus (name = "org.erikreider.swaync.cc")]
@@ -32,54 +33,67 @@ interface CcDaemon : Object {
[DBus (name = "GetSubscribeData")] [DBus (name = "GetSubscribeData")]
public abstract SwayncDaemonData get_subscribe_data () throws Error; public abstract SwayncDaemonData get_subscribe_data () throws Error;
public signal void subscribe (uint count, bool dnd, bool cc_open); public signal void subscribe_v2 (uint count, bool dnd, bool cc_open, bool inhibited);
public abstract bool add_inhibitor (string application_id) throws DBusError, IOError;
public abstract bool remove_inhibitor (string application_id) throws DBusError, IOError;
public abstract uint number_of_inhibitors () throws DBusError, IOError;
public abstract bool is_inhibited () throws DBusError, IOError;
public abstract bool clear_inhibitors () throws DBusError, IOError;
} }
private CcDaemon cc_daemon = null; private CcDaemon cc_daemon = null;
private void print_help (string[] args) { private void print_help (string[] args) {
print ("Usage:\n"); print ("Usage:\n");
print ("\t %s <OPTION>\n".printf (args[0])); print (" %s <OPTION>\n".printf (args[0]));
print ("Help:\n"); print ("Help:\n");
print ("\t -h, \t --help \t\t Show help options\n"); print (" -h, \t --help \t\t\t Show help options\n");
print ("\t -v, \t --version \t\t Prints version\n"); print (" -v, \t --version \t\t\t Prints version\n");
print ("Options:\n"); print ("Options:\n");
print ("\t -R, \t --reload-config \t Reload the config file\n"); print (" -R, \t --reload-config \t\t Reload the config file\n");
print ("\t -rs, \t --reload-css \t\t Reload the css file. Location change requires restart\n"); print (" -rs, \t --reload-css \t\t\t Reload the css file. Location change requires restart\n");
print ("\t -t, \t --toggle-panel \t Toggle the notificaion panel\n"); print (" -t, \t --toggle-panel \t\t Toggle the notificaion panel\n");
print ("\t -op, \t --open-panel \t\t Opens the notificaion panel\n"); print (" -op, \t --open-panel \t\t\t Opens the notificaion panel\n");
print ("\t -cp, \t --close-panel \t\t Closes the notificaion panel\n"); print (" -cp, \t --close-panel \t\t\t Closes the notificaion panel\n");
print ("\t -d, \t --toggle-dnd \t\t Toggle and print the current dnd state\n"); print (" -d, \t --toggle-dnd \t\t\t Toggle and print the current dnd state\n");
print ("\t -D, \t --get-dnd \t\t Print the current dnd state\n"); print (" -D, \t --get-dnd \t\t\t Print the current dnd state\n");
print ("\t -dn, \t --dnd-on \t\t Turn dnd on and print the new dnd state\n"); print (" -dn, \t --dnd-on \t\t\t Turn dnd on and print the new dnd state\n");
print ("\t -df, \t --dnd-off \t\t Turn dnd off and print the new dnd state\n"); print (" -df, \t --dnd-off \t\t\t Turn dnd off and print the new dnd state\n");
print ("\t -c, \t --count \t\t Print the current notificaion count\n"); print (" -I, \t --get-inhibited \t\t Print if currently inhibited or not\n");
print ("\t \t --hide-latest \t\t Hides latest notification. Still shown in Control Center\n"); print (" -In, \t --get-num-inhibitors \t\t Print number of inhibitors\n");
print ("\t \t --close-latest \t Closes latest notification\n"); print (" -Ia, \t --inhibitor-add [APP_ID] \t Add an inhibitor\n");
print ("\t -C, \t --close-all \t\t Closes all notifications\n"); print (" -Ir, \t --inhibitor-remove [APP_ID] \t Remove an inhibitor\n");
print ("\t -sw, \t --skip-wait \t\t Doesn't wait when swaync hasn't been started\n"); print (" -Ic, \t --inhibitors-clear \t\t Clears all inhibitors\n");
print ("\t -s, \t --subscribe \t\t Subscribe to notificaion add and close events\n"); print (" -c, \t --count \t\t\t Print the current notificaion count\n");
print ("\t -swb, \t --subscribe-waybar \t Subscribe to notificaion add and close events " print (" \t --hide-latest \t\t\t Hides latest notification. Still shown in Control Center\n");
print (" \t --close-latest \t\t Closes latest notification\n");
print (" -C, \t --close-all \t\t\t Closes all notifications\n");
print (" -sw, \t --skip-wait \t\t\t Doesn't wait when swaync hasn't been started\n");
print (" -s, \t --subscribe \t\t\t Subscribe to notificaion add and close events\n");
print (" -swb, --subscribe-waybar \t\t Subscribe to notificaion add and close events "
+ "with waybar support. Read README for example\n"); + "with waybar support. Read README for example\n");
} }
private void on_subscribe (uint count, bool dnd, bool cc_open) { private void on_subscribe (uint count, bool dnd, bool cc_open, bool inhibited) {
stdout.printf ( stdout.printf (
"{ \"count\": %u, \"dnd\": %s, \"visible\": %s }\n" "{ \"count\": %u, \"dnd\": %s, \"visible\": %s, \"inhibited\": %s }\n"
.printf (count, dnd.to_string (), cc_open.to_string ())); .printf (count, dnd.to_string (), cc_open.to_string (), inhibited.to_string ()));
} }
private void print_subscribe () { private void print_subscribe () {
try { try {
SwayncDaemonData data = cc_daemon.get_subscribe_data (); SwayncDaemonData data = cc_daemon.get_subscribe_data ();
on_subscribe (data.count, data.dnd, data.cc_open); on_subscribe (data.count, data.dnd, data.cc_open, data.inhibited);
} catch (Error e) { } catch (Error e) {
on_subscribe (0, false, false); on_subscribe (0, false, false, false);
} }
} }
private void on_subscribe_waybar (uint count, bool dnd, bool cc_open) { private void on_subscribe_waybar (uint count, bool dnd, bool cc_open, bool inhibited) {
string state = (dnd ? "dnd-" : "") + (count > 0 ? "notification" : "none"); string state = (dnd ? "dnd-" : "")
+ (inhibited ? "inhibited-" : "")
+ (count > 0 ? "notification" : "none");
string tooltip = ""; string tooltip = "";
if (count > 0) { if (count > 0) {
@@ -99,9 +113,9 @@ private void on_subscribe_waybar (uint count, bool dnd, bool cc_open) {
private void print_subscribe_waybar () { private void print_subscribe_waybar () {
try { try {
SwayncDaemonData data = cc_daemon.get_subscribe_data (); SwayncDaemonData data = cc_daemon.get_subscribe_data ();
on_subscribe_waybar (data.count, data.dnd, data.cc_open); on_subscribe_waybar (data.count, data.dnd, data.cc_open, data.inhibited);
} catch (Error e) { } catch (Error e) {
on_subscribe_waybar (0, false, false); on_subscribe_waybar (0, false, false, false);
} }
} }
@@ -175,12 +189,54 @@ public int command_line (string[] args) {
cc_daemon.set_dnd (false); cc_daemon.set_dnd (false);
print (cc_daemon.get_dnd ().to_string ()); print (cc_daemon.get_dnd ().to_string ());
break; break;
case "--get-inhibited":
case "-I":
print (cc_daemon.is_inhibited ().to_string ());
break;
case "--get-num-inhibitors":
case "-In":
print (cc_daemon.number_of_inhibitors ().to_string ());
break;
case "--inhibitor-add":
case "-Ia":
if (args.length < 3) {
stderr.printf ("Application ID needed!");
Process.exit (1);
}
if (cc_daemon.add_inhibitor (args[2])) {
print ("Added inhibitor: \"%s\"", args[2]);
break;
}
stderr.printf ("Inhibitor: \"%s\" already added!...", args[2]);
break;
case "--inhibitor-remove":
case "-Ir":
if (args.length < 3) {
stderr.printf ("Application ID needed!");
Process.exit (1);
}
if (cc_daemon.remove_inhibitor (args[2])) {
print ("Removed inhibitor: \"%s\"", args[2]);
break;
}
stderr.printf ("Inhibitor: \"%s\" does not exist!...", args[2]);
break;
case "inhibitors-clear":
case "-Ic":
if (cc_daemon.clear_inhibitors ()) {
print ("Cleared all inhibitors");
break;
}
print ("No inhibitors to clear...");
break;
case "--subscribe": case "--subscribe":
case "-s": case "-s":
cc_daemon.subscribe.connect (on_subscribe); cc_daemon.subscribe_v2.connect (on_subscribe);
on_subscribe (cc_daemon.notification_count (), var data = cc_daemon.get_subscribe_data ();
cc_daemon.get_dnd (), on_subscribe (data.count,
cc_daemon.get_visibility ()); data.dnd,
data.cc_open,
data.inhibited);
var loop = new MainLoop (); var loop = new MainLoop ();
Bus.watch_name ( Bus.watch_name (
BusType.SESSION, BusType.SESSION,
@@ -192,7 +248,7 @@ public int command_line (string[] args) {
break; break;
case "--subscribe-waybar": case "--subscribe-waybar":
case "-swb": case "-swb":
cc_daemon.subscribe.connect (on_subscribe_waybar); cc_daemon.subscribe_v2.connect (on_subscribe_waybar);
var loop = new MainLoop (); var loop = new MainLoop ();
Bus.watch_name ( Bus.watch_name (
BusType.SESSION, BusType.SESSION,

View File

@@ -43,11 +43,17 @@
} }
}, },
"widgets": [ "widgets": [
"inhibitors",
"title", "title",
"dnd", "dnd",
"notifications" "notifications"
], ],
"widget-config": { "widget-config": {
"inhibitors": {
"text": "Inhibitors",
"button-text": "Clear All",
"clear-all-button": true
},
"title": { "title": {
"text": "Notifications", "text": "Notifications",
"clear-all-button": true, "clear-all-button": true,

View File

@@ -261,7 +261,7 @@
"widgets": { "widgets": {
"type": "array", "type": "array",
"description": "Which order and which widgets to display. If the \"notifications\" widget isn't specified, it will be placed at the bottom.", "description": "Which order and which widgets to display. If the \"notifications\" widget isn't specified, it will be placed at the bottom.",
"default": ["title", "dnd", "notifications"], "default": ["inhibitors", "title", "dnd", "notifications"],
"items": { "items": {
"type": "string", "type": "string",
// Sadly can't use regex and enums at the same time. Fix in the future? // Sadly can't use regex and enums at the same time. Fix in the future?
@@ -298,6 +298,10 @@
}, },
"^backlight(#[a-zA-Z0-9_-]{1,}){0,1}?$": { "^backlight(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
"$ref": "#/widgets/backlight" "$ref": "#/widgets/backlight"
},
"^inhibitors(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
// References the widget structure from "widgets" below
"$ref": "#/widgets/inhibitors"
} }
} }
} }
@@ -452,7 +456,7 @@
"description": "Text displayed in front of the volume slider", "description": "Text displayed in front of the volume slider",
"default": "Volume" "default": "Volume"
} }
} }
}, },
"backlight": { "backlight": {
"type": "object", "type": "object",
@@ -480,7 +484,29 @@
"default": 0, "default": 0,
"description": "Lowest possible value for brightness" "description": "Lowest possible value for brightness"
} }
} }
},
"inhibitors": {
"type": "object",
"description": "Control Center Inhibitors Widget",
"additionalProperties": false,
"properties": {
"text": {
"type": "string",
"description": "The title of the widget",
"default": "Inhibitors"
},
"clear-all-button": {
"type": "boolean",
"description": "Wether to display a \"Clear All\" button",
"default": true
},
"button-text": {
"type": "string",
"description": "\"Clear All\" button text",
"default": "Clear All"
}
}
} }
} }
} }

View File

@@ -206,7 +206,7 @@ namespace SwayNotificationCenter {
return true; return true;
}); });
// Switches the stack page depending on the // Switches the stack page depending on the
list_box.add.connect (() => { list_box.add.connect (() => {
stack.set_visible_child_name (STACK_NOTIFICATIONS_PAGE); stack.set_visible_child_name (STACK_NOTIFICATIONS_PAGE);
}); });
@@ -374,9 +374,10 @@ namespace SwayNotificationCenter {
} }
try { try {
swaync_daemon.subscribe (notification_count (), swaync_daemon.subscribe_v2 (notification_count (),
swaync_daemon.get_dnd (), swaync_daemon.get_dnd (),
get_visibility ()); get_visibility (),
swaync_daemon.inhibited);
} catch (Error e) { } catch (Error e) {
stderr.printf (e.message + "\n"); stderr.printf (e.message + "\n");
} }
@@ -413,9 +414,10 @@ namespace SwayNotificationCenter {
if (noti != null) noti.set_time (); if (noti != null) noti.set_time ();
} }
} }
swaync_daemon.subscribe (notification_count (), swaync_daemon.subscribe_v2 (notification_count (),
noti_daemon.dnd, noti_daemon.dnd,
this.visible); this.visible,
swaync_daemon.inhibited);
} }
public bool toggle_visibility () { public bool toggle_visibility () {
@@ -462,9 +464,10 @@ namespace SwayNotificationCenter {
list_box.add (noti); list_box.add (noti);
scroll_to_start (list_reverse); scroll_to_start (list_reverse);
try { try {
swaync_daemon.subscribe (notification_count (), swaync_daemon.subscribe_v2 (notification_count (),
swaync_daemon.get_dnd (), swaync_daemon.get_dnd (),
get_visibility ()); get_visibility (),
swaync_daemon.inhibited);
} catch (Error e) { } catch (Error e) {
stderr.printf (e.message + "\n"); stderr.printf (e.message + "\n");
} }

View File

@@ -41,7 +41,8 @@ namespace SwayNotificationCenter.Widgets {
public virtual void on_cc_visibility_change (bool value) {} public virtual void on_cc_visibility_change (bool value) {}
protected T ? get_prop<T> (Json.Object config, string value_key) { protected T ? get_prop<T> (Json.Object config, string value_key, out bool found = null) {
found = false;
if (!config.has_member (value_key)) { if (!config.has_member (value_key)) {
debug ("%s: Config doesn't have key: %s!\n", key, value_key); debug ("%s: Config doesn't have key: %s!\n", key, value_key);
return null; return null;
@@ -61,6 +62,7 @@ namespace SwayNotificationCenter.Widgets {
member.get_value_type ().name ()); member.get_value_type ().name ());
return null; return null;
} }
found = true;
switch (generic_base_type) { switch (generic_base_type) {
case Type.STRING: case Type.STRING:
return member.get_string (); return member.get_string ();
@@ -69,6 +71,7 @@ namespace SwayNotificationCenter.Widgets {
case Type.BOOLEAN: case Type.BOOLEAN:
return member.get_boolean (); return member.get_boolean ();
default: default:
found = false;
return null; return null;
} }
} }

View File

@@ -32,6 +32,9 @@ namespace SwayNotificationCenter.Widgets {
case "backlight": case "backlight":
widget = new Backlight (suffix, swaync_daemon, noti_daemon); widget = new Backlight (suffix, swaync_daemon, noti_daemon);
break; break;
case "inhibitors":
widget = new Inhibitors (suffix, swaync_daemon, noti_daemon);
break;
default: default:
warning ("Could not find widget: \"%s\"!", key); warning ("Could not find widget: \"%s\"!", key);
return null; return null;

View File

@@ -0,0 +1,64 @@
namespace SwayNotificationCenter.Widgets {
public class Inhibitors : BaseWidget {
public override string widget_name {
get {
return "inhibitors";
}
}
Gtk.Label title_widget;
Gtk.Button clear_all_button;
// Default config values
string title = "Inhibitors";
bool has_clear_all_button = true;
string button_text = "Clear All";
public Inhibitors (string suffix, SwayncDaemon swaync_daemon, NotiDaemon noti_daemon) {
base (suffix, swaync_daemon, noti_daemon);
swaync_daemon.inhibited_changed.connect ((length) => {
if (!swaync_daemon.inhibited) {
hide ();
return;
}
show ();
title_widget.set_text ("%s %u".printf (title, length));
});
Json.Object ? config = get_config (this);
if (config != null) {
// Get title
string ? title = get_prop<string> (config, "text");
if (title != null) this.title = title;
// Get has clear-all-button
bool found_clear_all;
bool ? has_clear_all_button = get_prop<bool> (
config, "clear-all-button", out found_clear_all);
if (found_clear_all) this.has_clear_all_button = has_clear_all_button;
// Get button text
string ? button_text = get_prop<string> (config, "button-text");
if (button_text != null) this.button_text = button_text;
}
title_widget = new Gtk.Label (title);
add (title_widget);
if (has_clear_all_button) {
clear_all_button = new Gtk.Button.with_label (button_text);
clear_all_button.clicked.connect (() => {
try {
swaync_daemon.clear_inhibitors ();
} catch (Error e) {
error ("Error: %s\n", e.message);
}
});
clear_all_button.set_can_focus (false);
clear_all_button.valign = Gtk.Align.CENTER;
pack_end (clear_all_button, false);
}
show_all ();
}
}
}

View File

@@ -20,11 +20,13 @@ namespace SwayNotificationCenter.Widgets {
Json.Object ? config = get_config (this); Json.Object ? config = get_config (this);
if (config != null) { if (config != null) {
// Get title // Get title
string? title = get_prop<string> (config, "text"); string ? title = get_prop<string> (config, "text");
if (title != null) this.title = title; if (title != null) this.title = title;
// Get has clear-all-button // Get has clear-all-button
bool? has_clear_all_button = get_prop<bool> (config, "clear-all-button"); bool found_clear_all;
if (has_clear_all_button != null) this.has_clear_all_button = has_clear_all_button; bool? has_clear_all_button = get_prop<bool> (
config, "clear-all-button", out found_clear_all);
if (found_clear_all) this.has_clear_all_button = has_clear_all_button;
// Get button text // Get button text
string? button_text = get_prop<string> (config, "button-text"); string? button_text = get_prop<string> (config, "button-text");
if (button_text != null) this.button_text = button_text; if (button_text != null) this.button_text = button_text;

View File

@@ -47,6 +47,8 @@ widget_sources = [
# Widget: Backlight Slider # Widget: Backlight Slider
'controlCenter/widgets/backlight/backlight.vala', 'controlCenter/widgets/backlight/backlight.vala',
'controlCenter/widgets/backlight/backlightUtil.vala', 'controlCenter/widgets/backlight/backlightUtil.vala',
# Widget: Inhibitors
'controlCenter/widgets/inhibitors/inhibitors.vala',
] ]
app_sources = [ app_sources = [

View File

@@ -2,13 +2,19 @@ namespace SwayNotificationCenter {
[DBus (name = "org.freedesktop.Notifications")] [DBus (name = "org.freedesktop.Notifications")]
public class NotiDaemon : Object { public class NotiDaemon : Object {
private uint32 noti_id = 0; private uint32 noti_id = 0;
public bool dnd { get; set; default = false; } public bool dnd { get; set; default = false; }
private HashTable<string, uint32> synchronous_ids = private HashTable<string, uint32> synchronous_ids =
new HashTable<string, uint32> (str_hash, str_equal); new HashTable<string, uint32> (str_hash, str_equal);
public ControlCenter control_center; public ControlCenter control_center;
public unowned SwayncDaemon swaync_daemon;
public NotiDaemon (SwayncDaemon swaync_daemon) { public NotiDaemon (SwayncDaemon swaync_daemon) {
this.swaync_daemon = swaync_daemon;
this.notify["dnd"].connect (() => on_dnd_toggle (dnd)); this.notify["dnd"].connect (() => on_dnd_toggle (dnd));
// Init dnd from gsettings // Init dnd from gsettings
@@ -56,9 +62,10 @@ namespace SwayNotificationCenter {
control_center.close_notification (id); control_center.close_notification (id);
NotificationClosed (id, ClosedReasons.DISMISSED); NotificationClosed (id, ClosedReasons.DISMISSED);
swaync_daemon.subscribe (control_center.notification_count (), swaync_daemon.subscribe_v2 (control_center.notification_count (),
dnd, dnd,
control_center.get_visibility ()); control_center.get_visibility (),
swaync_daemon.inhibited);
} }
} }
@@ -119,13 +126,13 @@ namespace SwayNotificationCenter {
*/ */
[DBus (name = "Notify")] [DBus (name = "Notify")]
public uint32 new_notification (string app_name, public uint32 new_notification (string app_name,
uint32 replaces_id, uint32 replaces_id,
string app_icon, string app_icon,
string summary, string summary,
string body, string body,
string[] actions, string[] actions,
HashTable<string, Variant> hints, HashTable<string, Variant> hints,
int expire_timeout) throws DBusError, IOError { int expire_timeout) throws DBusError, IOError {
uint32 id = replaces_id; uint32 id = replaces_id;
if (replaces_id == 0 || replaces_id > noti_id) id = ++noti_id; if (replaces_id == 0 || replaces_id > noti_id) id = ++noti_id;
@@ -179,15 +186,17 @@ namespace SwayNotificationCenter {
// Only show popup notification if it is ENABLED or TRANSIENT // Only show popup notification if it is ENABLED or TRANSIENT
if ((state == NotificationStatusEnum.ENABLED || state == NotificationStatusEnum.TRANSIENT) if ((state == NotificationStatusEnum.ENABLED || state == NotificationStatusEnum.TRANSIENT)
&& !control_center.get_visibility ()) { && !control_center.get_visibility ()) {
// Also check if urgency is Critical and not inhibited and dnd
if (param.urgency == UrgencyLevels.CRITICAL || if (param.urgency == UrgencyLevels.CRITICAL ||
(!dnd && param.urgency != UrgencyLevels.CRITICAL)) { (!dnd && !swaync_daemon.inhibited
&& param.urgency != UrgencyLevels.CRITICAL)) {
NotificationWindow.instance.add_notification (param, this); NotificationWindow.instance.add_notification (param, this);
} }
} }
// Only add notification to CC if it isn't IGNORED and not transient/TRANSIENT // Only add notification to CC if it isn't IGNORED and not transient/TRANSIENT
if (state != NotificationStatusEnum.IGNORED if (state != NotificationStatusEnum.IGNORED
&& state != NotificationStatusEnum.TRANSIENT && state != NotificationStatusEnum.TRANSIENT
&& !param.transient) { && !param.transient) {
control_center.add_notification (param, this); control_center.add_notification (param, this);
} }
@@ -245,13 +254,13 @@ namespace SwayNotificationCenter {
string _summary = "Failed to run script: %s".printf (key); string _summary = "Failed to run script: %s".printf (key);
string _body = "<b>Output:</b> " + error_msg; string _body = "<b>Output:</b> " + error_msg;
this.new_notification ("SwayNotificationCenter", this.new_notification ("SwayNotificationCenter",
0, 0,
"dialog-error", "dialog-error",
_summary, _summary,
_body, _body,
{}, {},
_hints, _hints,
-1); -1);
} catch (Error e) { } catch (Error e) {
stderr.printf ("NOTIFING SCRIPT-FAIL ERROR: %s\n", stderr.printf ("NOTIFING SCRIPT-FAIL ERROR: %s\n",
e.message); e.message);

View File

@@ -298,3 +298,21 @@
margin: 8px; margin: 8px;
border-radius: 12px; border-radius: 12px;
} }
/* Title widget */
.widget-inhibitors {
margin: 8px;
font-size: 1.5rem;
}
.widget-inhibitors > button {
font-size: initial;
color: white;
text-shadow: none;
background: @noti-bg;
border: 1px solid @noti-border-color;
box-shadow: none;
border-radius: 12px;
}
.widget-inhibitors > button:hover {
background: @noti-bg-hover;
}

View File

@@ -3,12 +3,18 @@ namespace SwayNotificationCenter {
public bool dnd; public bool dnd;
public bool cc_open; public bool cc_open;
public uint count; public uint count;
public bool inhibited;
} }
[DBus (name = "org.erikreider.swaync.cc")] [DBus (name = "org.erikreider.swaync.cc")]
public class SwayncDaemon : Object { public class SwayncDaemon : Object {
public NotiDaemon noti_daemon; public NotiDaemon noti_daemon;
private GenericSet<string> inhibitors = new GenericSet<string> (str_hash, str_equal);
public bool inhibited { get; set; default = false; }
[DBus (visible = false)]
public signal void inhibited_changed (uint length);
private Array<BlankWindow> blank_windows = new Array<BlankWindow> (); private Array<BlankWindow> blank_windows = new Array<BlankWindow> ();
private unowned Gdk.Display ? display = Gdk.Display.get_default (); private unowned Gdk.Display ? display = Gdk.Display.get_default ();
@@ -32,9 +38,10 @@ namespace SwayNotificationCenter {
noti_daemon.on_dnd_toggle.connect ((dnd) => { noti_daemon.on_dnd_toggle.connect ((dnd) => {
try { try {
subscribe (noti_daemon.control_center.notification_count (), subscribe_v2 (noti_daemon.control_center.notification_count (),
dnd, dnd,
get_visibility ()); get_visibility (),
inhibited);
} catch (Error e) { } catch (Error e) {
stderr.printf (e.message + "\n"); stderr.printf (e.message + "\n");
} }
@@ -42,9 +49,10 @@ namespace SwayNotificationCenter {
// Update on start // Update on start
try { try {
subscribe (notification_count (), subscribe_v2 (notification_count (),
get_dnd (), get_dnd (),
get_visibility ()); get_visibility (),
inhibited);
} catch (Error e) { } catch (Error e) {
stderr.printf (e.message + "\n"); stderr.printf (e.message + "\n");
} }
@@ -135,13 +143,21 @@ namespace SwayNotificationCenter {
dnd = get_dnd (), dnd = get_dnd (),
cc_open = get_visibility (), cc_open = get_visibility (),
count = notification_count (), count = notification_count (),
inhibited = is_inhibited (),
}; };
} }
/** /**
* Called when Dot Not Disturb state changes and when * Called when Dot Not Disturb state changes, notification gets
* notification gets added/removed * added/removed, and when Control Center opens
*/ */
public signal void subscribe_v2 (uint count, bool dnd, bool cc_open, bool inhibited);
/**
* Called when Dot Not Disturb state changes, notification gets
* added/removed, Control Center opens, and when inhibitor state changes
*/
[Version (deprecated = true, replacement = "SwayncDaemon.subscribe_v2")]
public signal void subscribe (uint count, bool dnd, bool cc_open); public signal void subscribe (uint count, bool dnd, bool cc_open);
/** Reloads the CSS file */ /** Reloads the CSS file */
@@ -230,5 +246,63 @@ namespace SwayNotificationCenter {
public void close_notification (uint32 id) throws DBusError, IOError { public void close_notification (uint32 id) throws DBusError, IOError {
noti_daemon.control_center.close_notification (id); noti_daemon.control_center.close_notification (id);
} }
/**
* Adds an inhibitor with the Application ID
* (ex: "org.erikreider.swaysettings", "swayidle", etc...).
*
* @return false if the `application_id` already exists, otherwise true.
*/
public bool add_inhibitor (string application_id) throws DBusError, IOError {
if (inhibitors.contains (application_id)) return false;
inhibitors.add (application_id);
inhibited = inhibitors.length > 0;
inhibited_changed (inhibitors.length);
subscribe_v2 (noti_daemon.control_center.notification_count (),
noti_daemon.dnd,
get_visibility (),
inhibited);
return true;
}
/**
* Removes an inhibitor with the Application ID
* (ex: "org.erikreider.swaysettings", "swayidle", etc...).
*
* @return false if the `application_id` doesn't exist, otherwise true
*/
public bool remove_inhibitor (string application_id) throws DBusError, IOError {
if (!inhibitors.remove (application_id)) return false;
inhibited = inhibitors.length > 0;
inhibited_changed (inhibitors.length);
subscribe_v2 (noti_daemon.control_center.notification_count (),
noti_daemon.dnd,
get_visibility (),
inhibited);
return true;
}
/** Get the number of inhibitors */
public uint number_of_inhibitors () throws DBusError, IOError {
return inhibitors.length;
}
/** Get if is inhibited */
public bool is_inhibited () throws DBusError, IOError {
return inhibited;
}
/** Clear all inhibitors */
public bool clear_inhibitors () throws DBusError, IOError {
if (inhibitors.length == 0) return false;
inhibitors.remove_all ();
inhibited = false;
inhibited_changed (0);
subscribe_v2 (noti_daemon.control_center.notification_count (),
noti_daemon.dnd,
get_visibility (),
inhibited);
return true;
}
} }
} }