namespace SwayNotificationCenter { public enum PositionX { RIGHT, LEFT, CENTER, NONE; } public enum PositionY { TOP, BOTTOM, CENTER, NONE; } public enum ImageVisibility { ALWAYS, WHEN_AVAILABLE, NEVER; public string parse () { switch (this) { default: return "always"; case WHEN_AVAILABLE: return "when_available"; case NEVER: return "never"; } } } public enum Layer { BACKGROUND, BOTTOM, TOP, OVERLAY; public string parse () { switch (this) { case BACKGROUND: return "background"; case BOTTOM: return "bottom"; case TOP: return "top"; default: case OVERLAY: return "overlay"; } } } public enum CssPriority { APPLICATION, USER; public int get_priority () { switch (this) { case APPLICATION: default: return Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION; case USER: return Gtk.STYLE_PROVIDER_PRIORITY_USER; } } } public class Category : Object { public string ? sound { get; set; default = null; } public string ? icon { get; set; default = null; } public string to_string () { string[] fields = {}; if (sound != null) fields += "sound: %s".printf (sound); if (icon != null) fields += "icon: %s".printf (icon); return string.joinv (", ", fields); } } public class NotificationMatching : Object, Json.Serializable { public string ? app_name { get; set; default = null; } public string ? desktop_entry { get; set; default = null; } public string ? summary { get; set; default = null; } public string ? body { get; set; default = null; } public string ? urgency { get; set; default = null; } public string ? category { get; set; default = null; } private const RegexCompileFlags REGEX_COMPILE_OPTIONS = RegexCompileFlags.MULTILINE | RegexCompileFlags.JAVASCRIPT_COMPAT; private const RegexMatchFlags REGEX_MATCH_FLAGS = RegexMatchFlags.NOTEMPTY; public virtual bool matches_notification (NotifyParams param) { if (app_name != null) { if (param.app_name == null) return false; bool result = Regex.match_simple ( app_name, param.app_name, REGEX_COMPILE_OPTIONS, REGEX_MATCH_FLAGS); if (!result) return false; } if (desktop_entry != null) { if (param.desktop_entry == null) return false; bool result = Regex.match_simple ( desktop_entry, param.desktop_entry, REGEX_COMPILE_OPTIONS, REGEX_MATCH_FLAGS); if (!result) return false; } if (summary != null) { if (param.summary == null) return false; bool result = Regex.match_simple ( summary, param.summary, REGEX_COMPILE_OPTIONS, REGEX_MATCH_FLAGS); if (!result) return false; } if (body != null) { if (param.body == null) return false; bool result = Regex.match_simple ( body, param.body, 0, REGEX_MATCH_FLAGS); if (!result) return false; } if (urgency != null) { bool result = Regex.match_simple ( urgency, param.urgency.to_string (), REGEX_COMPILE_OPTIONS, REGEX_MATCH_FLAGS); if (!result) return false; } if (category != null) { if (param.category == null) return false; bool result = Regex.match_simple ( category, param.category, REGEX_COMPILE_OPTIONS, REGEX_MATCH_FLAGS); if (!result) return false; } return true; } public string to_string () { string[] fields = {}; if (app_name != null) fields += "app-name: %s".printf (app_name); if (desktop_entry != null) fields += "desktop-entry: %s".printf (desktop_entry); if (summary != null) fields += "summary: %s".printf (summary); if (body != null) fields += "body: %s".printf (body); if (urgency != null) fields += "urgency: %s".printf (urgency); if (category != null) fields += "category: %s".printf (category); return string.joinv (", ", fields); } public override Json.Node serialize_property (string property_name, Value value, ParamSpec pspec) { // Return enum nickname instead of enum int value if (value.type ().is_a (Type.ENUM)) { var node = new Json.Node (Json.NodeType.VALUE); EnumClass enumc = (EnumClass) value.type ().class_ref (); unowned EnumValue ? eval = enumc.get_value (value.get_enum ()); if (eval == null) { node.set_value (value); return node; } node.set_string (eval.value_nick); return node; } return default_serialize_property (property_name, value, pspec); } } public enum NotificationStatusEnum { ENABLED, MUTED, IGNORED, TRANSIENT; public string to_string () { switch (this) { default : return "enabled"; case MUTED: return "muted"; case IGNORED: return "ignored"; case TRANSIENT: return "transient"; } } public static NotificationStatusEnum from_value (string value) { switch (value) { default: return ENABLED; case "muted": return MUTED; case "ignored": return IGNORED; case "transient": return TRANSIENT; } } } public enum NotificationUrgencyEnum { UNSET = -1, LOW = UrgencyLevels.LOW, NORMAL = UrgencyLevels.NORMAL, CRITICAL = UrgencyLevels.CRITICAL; public uint8 to_byte () { return this; } public string to_string () { switch (this) { case LOW: return "Low"; case NORMAL: return "Normal"; case CRITICAL: return "Critical"; default: return "Unset"; } } } public class NotificationVisibility : NotificationMatching { public NotificationStatusEnum state { get; set; } public NotificationUrgencyEnum override_urgency { get; set; default = NotificationUrgencyEnum.UNSET; } } public enum ScriptRunOnType { ACTION, RECEIVE; } #if WANT_SCRIPTING public class Script : NotificationMatching { public string ? exec { get; set; default = null; } public ScriptRunOnType run_on { get; set; default = ScriptRunOnType.RECEIVE; } public async bool run_script (NotifyParams param, out string msg) { msg = ""; try { string[] spawn_env = Environ.get (); // Export env variables spawn_env += "SWAYNC_APP_NAME=%s".printf (param.app_name); spawn_env += "SWAYNC_SUMMARY=%s".printf (param.summary); spawn_env += "SWAYNC_BODY=%s".printf (param.body); spawn_env += "SWAYNC_URGENCY=%s".printf (param.urgency.to_string ()); spawn_env += "SWAYNC_CATEGORY=%s".printf (param.category); spawn_env += "SWAYNC_ID=%s".printf (param.applied_id.to_string ()); spawn_env += "SWAYNC_REPLACES_ID=%s".printf (param.replaces_id.to_string ()); spawn_env += "SWAYNC_TIME=%s".printf (param.time.to_string ()); spawn_env += "SWAYNC_DESKTOP_ENTRY=%s".printf (param.desktop_entry ?? ""); Pid child_pid; Process.spawn_async ( "/", exec.split (" "), spawn_env, SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD, null, out child_pid); // Close the child when the spawned process is idling int end_status = 0; ChildWatch.add (child_pid, (pid, status) => { Process.close_pid (pid); end_status = status; run_script.callback (); }); // Waits until `run_script.callback()` is called above yield; return end_status == 0; } catch (Error e) { stderr.printf ("Run_Script Error: %s\n", e.message); msg = e.message; return false; } } public override bool matches_notification (NotifyParams param) { if (exec == null) return false; return base.matches_notification (param); } } #endif public class ConfigModel : Object, Json.Serializable { private static ConfigModel _instance; private static string _path = ""; /** Get the static singleton */ public static unowned ConfigModel instance { get { if (_instance == null) _instance = new ConfigModel (); if (_path.length <= 0) _path = Functions.get_config_path (null); return _instance; } } /** Get the static singleton and reload the config */ public static unowned ConfigModel init (string ? path) { _path = Functions.get_config_path (path); reload_config (); return _instance; } public delegate void ModifyNode (Json.Node node); /** Reloads the config and calls `ModifyNode` before deserializing */ public static void reload_config (ModifyNode modify_cb = () => {}) { ConfigModel m = null; try { if (_path.length == 0) return; Json.Parser parser = new Json.Parser (); parser.load_from_file (_path); Json.Node ? node = parser.get_root (); if (node == null) { throw new Json.ParserError.PARSE ("Node is null!"); } modify_cb (node); ConfigModel model = Json.gobject_deserialize ( typeof (ConfigModel), node) as ConfigModel; if (model == null) { throw new Json.ParserError.UNKNOWN ("Json model is null!"); } m = model; } catch (Error e) { stderr.printf (e.message + "\n"); } _instance = m ?? new ConfigModel (); debug (_instance.to_string ()); } /* Properties */ /** The notifications and controlcenters horizontal alignment */ public PositionX positionX { // vala-lint=naming-convention get; set; default = PositionX.RIGHT; } /** The notifications and controlcenters vertical alignment */ public PositionY positionY { // vala-lint=naming-convention get; set; default = PositionY.TOP; } /** Layer of notification window */ public Layer layer { get; set; default = Layer.OVERLAY; } /** * Wether or not the windows should be opened as layer-shell surfaces */ public bool layer_shell { get; set; default = true; } /** The CSS loading priority */ public CssPriority cssPriority { // vala-lint=naming-convention get; set; default = CssPriority.APPLICATION; } /** The timeout for notifications with NORMAL priority */ private const int TIMEOUT_DEFAULT = 10; private int _timeout = TIMEOUT_DEFAULT; public int timeout { get { return _timeout; } set { _timeout = value < 0 ? TIMEOUT_DEFAULT : value; } } /** The timeout for notifications with LOW priority */ private const int TIMEOUT_LOW_DEFAULT = 5; private int _timeout_low = TIMEOUT_LOW_DEFAULT; public int timeout_low { get { return _timeout_low; } set { _timeout_low = value < 0 ? TIMEOUT_LOW_DEFAULT : value; } } /** The timeout for notifications with CRITICAL priority */ private const int TIMEOUT_CRITICAL_DEFAULT = 0; private int _timeout_critical = TIMEOUT_CRITICAL_DEFAULT; public int timeout_critical { get { return _timeout_critical; } set { _timeout_critical = value < 0 ? TIMEOUT_CRITICAL_DEFAULT : value; } } /** The transition time for all animations */ private const int TRANSITION_TIME_DEFAULT = 200; private int _transition_time = TRANSITION_TIME_DEFAULT; public int transition_time { get { return _transition_time; } set { _transition_time = value < 0 ? TRANSITION_TIME_DEFAULT : value; } } /* * Specifies if the control center should use keyboard shortcuts * and block keyboard input for other applications while open */ public bool keyboard_shortcuts { get; set; default = true; } /** Specifies if the notification image should be shown or not */ public ImageVisibility image_visibility { get; set; default = ImageVisibility.WHEN_AVAILABLE; } /** * Notification window's width, in pixels. */ public int notification_window_width { get; set; default = 500; } /** Hides the control center after clearing all notifications */ public bool hide_on_clear { get; set; default = false; } /** Hides the control center when clicking on notification action */ public bool hide_on_action { get; set; default = true; } /** The controlcenters horizontal alignment. Supersedes `positionX` if not `NONE` */ public PositionX control_center_positionX { // vala-lint=naming-convention get; set; default = PositionX.NONE; } /** The controlcenters vertical alignment. Supersedes `positionY` if not `NONE` */ public PositionY control_center_positionY { // vala-lint=naming-convention get; set; default = PositionY.NONE; } /** GtkLayerShell margins around the notification center */ private int control_center_margin_top_ = 0; public int control_center_margin_top { get { return control_center_margin_top_; } set { control_center_margin_top_ = value < 0 ? 0 : value; } } private int control_center_margin_bottom_ = 0; public int control_center_margin_bottom { get { return control_center_margin_bottom_; } set { control_center_margin_bottom_ = value < 0 ? 0 : value; } } private int control_center_margin_left_ = 0; public int control_center_margin_left { get { return control_center_margin_left_; } set { control_center_margin_left_ = value < 0 ? 0 : value; } } private int control_center_margin_right_ = 0; public int control_center_margin_right { get { return control_center_margin_right_; } set { control_center_margin_right_ = value < 0 ? 0 : value; } } /** Layer of Control Center window */ public Layer control_center_layer { get; set; default = Layer.TOP; } /** Categories settings */ public OrderedHashTable categories_settings { get; set; default = new OrderedHashTable (); } /** Notification Status */ public OrderedHashTable notification_visibility { get; set; default = new OrderedHashTable (); } #if WANT_SCRIPTING /** Scripts */ public OrderedHashTable