diff --git a/src/blankWindow/blankWindow.vala b/src/blankWindow/blankWindow.vala new file mode 100644 index 0000000..abc89e2 --- /dev/null +++ b/src/blankWindow/blankWindow.vala @@ -0,0 +1,55 @@ +namespace SwayNotificationCenter { + public class BlankWindow : Gtk.Window { + unowned Gdk.Display display; + unowned Gdk.Monitor monitor; + unowned SwayncDaemon daemon; + + Gtk.Button button; + + public BlankWindow (Gdk.Display disp, + Gdk.Monitor mon, + SwayncDaemon dae) { + display = disp; + monitor = mon; + daemon = dae; + + // Use button click event instead of Window button_press_event due + // to Gtk layer shell bug. This would grab focus instead of ControlCenter + button = new Gtk.Button () { + expand = true, + opacity = 0, + relief = Gtk.ReliefStyle.NONE, + visible = true, + }; + button.clicked.connect (() => { + try { + daemon.set_visibility (false); + } catch (Error e) { + stderr.printf ("BlankWindow Click Error: %s\n", e.message); + } + }); + add (button); + + if (!GtkLayerShell.is_supported ()) { + stderr.printf ("GTKLAYERSHELL IS NOT SUPPORTED!\n"); + stderr.printf ("Swaync only works on Wayland!\n"); + stderr.printf ("If running waylans session, try running:\n"); + stderr.printf ("\tGDK_BACKEND=wayland swaync\n"); + Process.exit (1); + } + GtkLayerShell.init_for_window (this); + GtkLayerShell.set_monitor (this, monitor); + + GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.TOP, true); + GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.BOTTOM, true); + GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.LEFT, true); + GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.RIGHT, true); + + GtkLayerShell.set_exclusive_zone (this, -1); + + GtkLayerShell.set_layer (this, GtkLayerShell.Layer.TOP); + + get_style_context ().add_class ("blank-window"); + } + } +} diff --git a/src/controlCenter/controlCenter.vala b/src/controlCenter/controlCenter.vala index fb0bc63..b8579a4 100644 --- a/src/controlCenter/controlCenter.vala +++ b/src/controlCenter/controlCenter.vala @@ -275,6 +275,7 @@ namespace SwayNotificationCenter { if (noti != null) noti.set_time (); } } + swaync_daemon.set_blank_window_visibility (this.visible); swaync_daemon.subscribe (notification_count (), noti_daemon.dnd, this.visible); diff --git a/src/functions.vala b/src/functions.vala index e9996ac..344acfb 100644 --- a/src/functions.vala +++ b/src/functions.vala @@ -1,5 +1,15 @@ namespace SwayNotificationCenter { public class Functions { + private static Gtk.CssProvider system_css_provider; + private static Gtk.CssProvider user_css_provider; + + private Functions () {} + + public static void init () { + system_css_provider = new Gtk.CssProvider (); + user_css_provider = new Gtk.CssProvider (); + } + public static void set_image_path (owned string path, Gtk.Image img, int icon_size, @@ -55,29 +65,45 @@ namespace SwayNotificationCenter { img.set_from_surface (surface); } + /** Load the package provided CSS file as a base. + * Without this, an empty user CSS file would result in widgets + * with default GTK style properties + */ public static bool load_css (string ? style_path) { try { - Gtk.CssProvider css_provider = new Gtk.CssProvider (); - css_provider.load_from_path (get_style_path (style_path)); + // Load packaged CSS as backup + string system_css = get_style_path (null, true); + system_css_provider.load_from_path (system_css); Gtk.StyleContext.add_provider_for_screen ( Gdk.Screen.get_default (), - css_provider, + system_css_provider, + Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); + + // Load user CSS + string user_css = get_style_path (style_path); + user_css_provider.load_from_path (user_css); + Gtk.StyleContext.add_provider_for_screen ( + Gdk.Screen.get_default (), + user_css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); return true; } catch (Error e) { - print ("Error: %s\n", e.message); + print ("Load CSS Error: %s\n", e.message); + return false; } - return false; } - public static string get_style_path (string ? custom_path) { + public static string get_style_path (string ? custom_path, + bool only_system = false) { string[] paths = {}; if (custom_path != null && custom_path.length > 0) { paths += custom_path; } - paths += Path.build_path (Path.DIR_SEPARATOR.to_string (), - GLib.Environment.get_user_config_dir (), - "swaync/style.css"); + if (!only_system) { + paths += Path.build_path (Path.DIR_SEPARATOR.to_string (), + GLib.Environment.get_user_config_dir (), + "swaync/style.css"); + } foreach (var path in GLib.Environment.get_system_config_dirs ()) { paths += Path.build_path (Path.DIR_SEPARATOR.to_string (), diff --git a/src/main.vala b/src/main.vala index b3e8cdc..e37eaea 100644 --- a/src/main.vala +++ b/src/main.vala @@ -6,6 +6,7 @@ namespace SwayNotificationCenter { public void main (string[] args) { Gtk.init (ref args); Hdy.init (); + Functions.init (); if (args.length > 0) { for (uint i = 1; i < args.length; i++) { diff --git a/src/meson.build b/src/meson.build index e41cfa1..54f9c2d 100644 --- a/src/meson.build +++ b/src/meson.build @@ -33,6 +33,7 @@ app_sources = [ 'controlCenter/controlCenter.vala', 'controlCenter/topAction/topAction.vala', 'cacher/cacher.vala', + 'blankWindow/blankWindow.vala', 'functions.vala', constants, ] diff --git a/src/style.css b/src/style.css index cfb9122..e57cd46 100644 --- a/src/style.css +++ b/src/style.css @@ -25,8 +25,7 @@ .notification { border-radius: 12px; margin: 6px 12px; - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3), - 0 1px 3px 1px rgba(0, 0, 0, 0.7), + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3), 0 1px 3px 1px rgba(0, 0, 0, 0.7), 0 2px 6px 2px rgba(0, 0, 0, 0.3); padding: 0; } @@ -120,7 +119,8 @@ border-right: 1px solid @noti-border-color; } -.image {} +.image { +} .body-image { margin-top: 6px; @@ -191,3 +191,8 @@ .floating-notifications { background: transparent; } + +/* Window behind control center and on all other monitors */ +.blank-window { + background: alpha(black, 0.25); +} diff --git a/src/swayncDaemon/swayncDaemon.vala b/src/swayncDaemon/swayncDaemon.vala index dbc1ad8..5407975 100644 --- a/src/swayncDaemon/swayncDaemon.vala +++ b/src/swayncDaemon/swayncDaemon.vala @@ -11,6 +11,9 @@ namespace SwayNotificationCenter { public NotiDaemon noti_daemon; + private Array blank_windows = new Array (); + private unowned Gdk.Display ? display = Gdk.Display.get_default (); + public SwayncDaemon () { this.cache_state = Cacher.instance.get_state_cache (); this.cache_state.notify.connect ((x, r) => { @@ -51,6 +54,31 @@ namespace SwayNotificationCenter { } catch (Error e) { stderr.printf (e.message + "\n"); } + + /// Blank windows + + if (display == null) return; + init_blank_windows (false); + + display.closed.connect ((is_error) => { + clear_blank_windows (); + if (is_error) stderr.printf ("Display closed due to error!\n"); + }); + + display.opened.connect ((d) => { + bool visibility = noti_daemon.control_center.get_visibility (); + init_blank_windows (visibility); + }); + + display.monitor_added.connect ((d, m) => { + bool visibility = noti_daemon.control_center.get_visibility (); + add_blank_window (d, m, visibility); + }); + + display.monitor_removed.connect ((monitor) => { + bool visibility = noti_daemon.control_center.get_visibility (); + init_blank_windows (visibility); + }); } private void on_noti_bus_aquired (DBusConnection conn) { @@ -63,6 +91,42 @@ namespace SwayNotificationCenter { } } + private void add_blank_window (Gdk.Display display, + Gdk.Monitor monitor, + bool visible) { + var win = new BlankWindow (display, monitor, this); + win.set_visible (visible); + blank_windows.append_val (win); + } + + private void init_blank_windows (bool visible) { + clear_blank_windows (); + // Add a window to all monitors + for (int i = 0; i < display.get_n_monitors (); i++) { + unowned Gdk.Monitor ? monitor = display.get_monitor (i); + if (monitor == null) continue; + add_blank_window (display, monitor, visible); + } + } + + private void clear_blank_windows () { + while (blank_windows.length > 0) { + uint i = blank_windows.length - 1; + unowned BlankWindow ? win = blank_windows.index (i); + win.close (); + blank_windows.remove_index (i); + } + } + + [DBus (visible = false)] + public void set_blank_window_visibility (bool visibility) { + foreach (unowned BlankWindow win in blank_windows.data) { + win.set_visible (visibility); + } + } + + /// DBus + /** Gets subscribe data but in one call */ [DBus (name = "GetSubscribeData")] public Data get_subscribe_data () throws Error {