diff --git a/README.md b/README.md index 9fa7ad9..902f259 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ You'll need the following dependencies: ``` +libnotify libwingpanel-2.0-dev meson valac diff --git a/assets/screenshot.jpeg b/assets/screenshot.jpeg index b8c4c16..c990a3d 100644 Binary files a/assets/screenshot.jpeg and b/assets/screenshot.jpeg differ diff --git a/meson.build b/meson.build index 3b6bae5..cb73bd3 100644 --- a/meson.build +++ b/meson.build @@ -2,7 +2,7 @@ project( 'wingpanel-caffeine', 'vala', 'c', - version : '0.0.1' + version : '0.1.0' ) i18n = import('i18n') @@ -12,6 +12,7 @@ add_global_arguments('-DGETTEXT_PACKAGE="@0@"'.format(gettext_name), language:'c wingpanel_dep = dependency('wingpanel') granite_dep = dependency('granite') +notify_dep = dependency('libnotify') posix_dep = meson.get_compiler('vala').find_library('posix') install_data( @@ -28,12 +29,15 @@ shared_module( meson.project_name(), 'src/Indicator.vala', 'src/Caffeinate.vala', + 'src/PopOverWidget.vala', + 'src/RevealerSwitch.vala', dependencies: [ dependency('glib-2.0'), dependency('gio-2.0'), dependency('gtk+-3.0'), wingpanel_dep, granite_dep, + notify_dep, posix_dep ], install: true, diff --git a/src/Caffeinate.vala b/src/Caffeinate.vala index c6caa81..ec42675 100644 --- a/src/Caffeinate.vala +++ b/src/Caffeinate.vala @@ -1,10 +1,18 @@ +public delegate void Callback (); + public class Caffeinate { private string xid; + private bool is_active; + private TimeoutSource timer; + private static string get_wingpanel_xid () { string hit = ""; string all_xids; - // this works on bash: read a _ <<< $(xwininfo -root -children | grep -m 1 io.elementary.wingpanel) && echo $a - Process.spawn_command_line_sync ("xwininfo -root -children", out all_xids, null, null); + + try { + // this works on bash: read a _ <<< $(xwininfo -root -children | grep -m 1 io.elementary.wingpanel) && echo $a + Process.spawn_command_line_sync ("xwininfo -root -children", out all_xids, null, null); + } catch {} string[] entries = all_xids.split ("\n"); foreach (unowned string item in entries) { @@ -17,16 +25,40 @@ public class Caffeinate { return hit.split (" ")[0]; } - public Caffeinate() { - this.xid = Caffeinate.get_wingpanel_xid(); + public Caffeinate () { + this.is_active = false; + this.xid = Caffeinate.get_wingpanel_xid (); } public void activate () { + this.is_active = true; Posix.system ("xdg-screensaver suspend " + this.xid); } public void stop () { - Posix.system ("xdg-screensaver resume " + this.xid); + if (this.is_active) { + this.is_active = false; + Posix.system ("xdg-screensaver resume " + this.xid); + } + + if (timer) { + timer.destroy (); + timer = null; + } + } + + public void activateFor (int duration, Callback callback) { + timer = new TimeoutSource.seconds (duration * 60); // duration comes in minutes + + timer.set_callback (() => { + this.stop (); + callback (); + + return false; + }); + + timer.attach (); + this.activate (); } } \ No newline at end of file diff --git a/src/Indicator.vala b/src/Indicator.vala index 8fb12a3..75573d4 100644 --- a/src/Indicator.vala +++ b/src/Indicator.vala @@ -1,7 +1,6 @@ public class Caffeine.Indicator : Wingpanel.Indicator { - private Gtk.Box popover_widget; + private PopOverWidget popover_widget; private Gtk.Image display_widget; - private Caffeinate caffeine; public Indicator () { Object ( @@ -9,7 +8,6 @@ public class Caffeine.Indicator : Wingpanel.Indicator { ); visible = true; - caffeine = new Caffeinate (); } public override Gtk.Widget get_display_widget () { @@ -22,22 +20,11 @@ public class Caffeine.Indicator : Wingpanel.Indicator { public override Gtk.Widget ? get_widget () { if (popover_widget == null) { - popover_widget = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); - - var main_switch = new Granite.SwitchModelButton ("Caffeinate"); - main_switch.get_style_context ().add_class (Granite.STYLE_CLASS_H4_LABEL); - main_switch.toggled.connect ((nextState) => { - if (nextState.active) { - display_widget.icon_name = "caffeine-cup-full-symbolic"; - caffeine.activate (); - - } else { - display_widget.icon_name = "caffeine-cup-empty-symbolic"; - caffeine.stop (); - } - }); + popover_widget = new PopOverWidget (); - popover_widget.add (main_switch); + popover_widget.toggle_caffeine.connect ((is_active) => { + display_widget.icon_name = is_active ? "caffeine-cup-full-symbolic" : "caffeine-cup-empty-symbolic"; + }); } return popover_widget; @@ -54,5 +41,6 @@ public Wingpanel.Indicator ? get_indicator (Module module, Wingpanel.IndicatorMa return null; } + Notify.init("dev.josemunoz.wingpanel-caffeine"); return new Caffeine.Indicator (); } diff --git a/src/PopOverWidget.vala b/src/PopOverWidget.vala new file mode 100644 index 0000000..ce69e9e --- /dev/null +++ b/src/PopOverWidget.vala @@ -0,0 +1,90 @@ +public class PopOverWidget : Gtk.Box { + public signal void toggle_caffeine (bool is_active); + + private Caffeinate caffeine; + private Granite.SwitchModelButton main_switch; + private RevealerSwitch indefinite_switch; + private Notify.Notification disable_alert; + private Notify.Notification invalid_input_alert; + + public PopOverWidget () { + try { + caffeine = new Caffeinate (); + main_switch = new Granite.SwitchModelButton ("Caffeinate"); + indefinite_switch = new RevealerSwitch ("Indefinite", true, true); + invalid_input_alert = new Notify.Notification ("Caffeine", "The timeout can only be in numeric values", "dialog-warning"); + disable_alert = new Notify.Notification ("Decaffeinated", "Caffeine will no longer keep the system awake", "caffeine-cup-empty-symbolic"); + + bool is_indefinite = true; + Regex only_numbers = new Regex ("^[0-9]*$"); + var timeout_label = new Gtk.Label ("timeout in minutes:"); + var container = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0); + var timeout_entry = new Granite.ValidatedEntry.from_regex (only_numbers); + + main_switch.get_style_context ().add_class (Granite.STYLE_CLASS_H3_LABEL); + indefinite_switch.get_style_context ().add_class (Granite.STYLE_CLASS_H3_LABEL); + + indefinite_switch.toggled.connect ((nextState) => { + is_indefinite = nextState; + }); + + main_switch.toggled.connect ((nextState) => { + toggle_caffeine (nextState.active); + timeout_entry.set_sensitive (!nextState.active); + indefinite_switch.set_sensitive (!nextState.active); + + if (nextState.active) { + if (is_indefinite) { + caffeine.activate (); + } else if (timeout_entry.is_valid) { + var timeout = int.parse (timeout_entry.get_text ()); + + caffeine.activateFor (timeout, () => { + main_switch.set_active (false); + + try { + disable_alert.show (); + } catch {} + }); + } else { + TimeoutSource timer = new TimeoutSource (1); + + timer.set_callback (() => { + main_switch.set_active (false); + + return false; + }); + + timer.attach (); + + try { + invalid_input_alert.show (); + } catch {} + } + } else { + caffeine.stop (); + } + }); + + disable_alert.set_timeout(3000); + timeout_entry.set_text ("30"); + timeout_entry.set_alignment (1); + timeout_entry.set_margin_end (10); + timeout_entry.set_width_chars (9); + indefinite_switch.add (container); + container.pack_start (timeout_label, true, true, 0); + container.pack_start (timeout_entry, false, false, 0); + container.get_style_context ().add_class (Granite.STYLE_CLASS_SMALL_LABEL); + + this.pack_start (main_switch); + this.pack_start (new Gtk.Separator (Gtk.Orientation.HORIZONTAL)); + this.pack_start (indefinite_switch); + } catch {} + } + + construct { + orientation = Gtk.Orientation.VERTICAL; + + show_all (); + } +} \ No newline at end of file diff --git a/src/RevealerSwitch.vala b/src/RevealerSwitch.vala new file mode 100644 index 0000000..daa4b9a --- /dev/null +++ b/src/RevealerSwitch.vala @@ -0,0 +1,36 @@ +class RevealerSwitch : Gtk.Box { + public signal void toggled (bool is_active); + + private Gtk.Revealer revealer; + private Granite.SwitchModelButton main_switch; + + public RevealerSwitch (string title, bool defaultOpen = false, bool inverted = false) { + this.main_switch = new Granite.SwitchModelButton (title) { + active = defaultOpen + }; + + this.revealer = new Gtk.Revealer () { + reveal_child = inverted ? !defaultOpen : defaultOpen + }; + + this.main_switch.toggled.connect ((nextState) => { + toggled (nextState.active); + this.revealer.reveal_child = inverted ? !nextState.active : nextState.active; + }); + + + this.pack_start (this.main_switch); + this.pack_start (this.revealer); + } + + construct { + orientation = Gtk.Orientation.VERTICAL; + + show_all (); + } + + public new void add (Gtk.Widget child) { + this.revealer.add (child); + } + +} \ No newline at end of file