aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/notification.vala81
-rw-r--r--src/service.vala154
-rw-r--r--src/volume-warning.vala105
-rw-r--r--src/warn-notification.vala59
4 files changed, 273 insertions, 126 deletions
diff --git a/src/notification.vala b/src/notification.vala
new file mode 100644
index 0000000..e5e1fd2
--- /dev/null
+++ b/src/notification.vala
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2015 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+public abstract class IndicatorSound.Notification: Object
+{
+ public bool visible { get; protected set; default = false; }
+
+ public Notification() {
+ BusWatcher.watch_namespace (GLib.BusType.SESSION,
+ "org.freedesktop.Notifications",
+ () => { debug("Notifications name appeared"); },
+ () => { debug("Notifications name vanshed"); _server_caps = null; });
+
+ _notification = create_notification();
+ _notification.closed.connect((n) => {
+ visible = false;
+ });
+ }
+
+ protected abstract Notify.Notification create_notification();
+
+ ~Notification() {
+ close();
+ }
+
+ protected void show_() {
+ try {
+ _notification.show ();
+ message("after calling show, n.id is %d", (int)_notification.id);
+ visible = true;
+ } catch (GLib.Error e) {
+ warning ("Unable to show notification: %s", e.message);
+ }
+ }
+
+ public void close() {
+ var n = _notification;
+
+ return_if_fail (n != null);
+
+ message("closing id %d", n.id);
+ if (n.id != 0) {
+ try {
+ n.close();
+ } catch (GLib.Error e) {
+ warning("Unable to close notification: %s", e.message);
+ }
+ }
+ }
+
+ protected bool notify_server_supports(string cap) {
+ if (_server_caps == null) {
+ message("getting server caps");
+ _server_caps = Notify.get_server_caps();
+ }
+
+ var ret = _server_caps.find_custom(cap, strcmp) != null;
+ message("%s --> %d", cap, (int)ret);
+ return ret;
+ }
+
+ protected Notify.Notification _notification = null;
+ private List<string> _server_caps = null;
+
+}
diff --git a/src/service.vala b/src/service.vala
index 5f6b00e..acaf639 100644
--- a/src/service.vala
+++ b/src/service.vala
@@ -20,35 +20,24 @@
public class IndicatorSound.Service: Object {
DBusConnection bus;
- /**
- * A copy of volume_control.volume made before just warn_notification
- * is shown. Since the volume is clamped during the warning, we cache
- * the previous volume to use iff the user hits "OK".
- */
- VolumeControl.Volume _pre_warn_volume = null;
-
public Service (MediaPlayerList playerlist, VolumeControl volume, AccountsServiceUser? accounts, Options options, VolumeWarning volume_warning) {
- _options = options;
- _volume_warning = volume_warning;
-
try {
bus = Bus.get_sync(GLib.BusType.SESSION);
} catch (GLib.Error e) {
error("Unable to get DBus session bus: %s", e.message);
}
+ _options = options;
+
info_notification = new Notify.Notification(_("Volume"), "", "audio-volume-muted");
- warn_notification = new Notify.Notification(_("Volume"), _("High volume can damage your hearing."), "audio-volume-high");
- warn_notification.set_hint ("x-canonical-non-shaped-icon", "true");
- warn_notification.set_hint ("x-canonical-snap-decisions", "true");
- warn_notification.set_hint ("x-canonical-private-affirmative-tint", "true");
- warn_notification.closed.connect((n) => {
- n.clear_actions ();
- _volume_warning.active = false;
- increment_volume_sync_action();
+ _volume_warning = volume_warning;
+ _volume_warning.notify["active"].connect(() => {
+ this.increment_volume_sync_action();
+ this.update_notification();
});
+
BusWatcher.watch_namespace (GLib.BusType.SESSION,
"org.freedesktop.Notifications",
() => { debug("Notifications name appeared"); },
@@ -60,8 +49,8 @@ public class IndicatorSound.Service: Object {
this.notify["visible"].connect ( () => this.update_root_icon () );
this.volume_control = volume;
- this.volume_control.active_output_changed.connect (this.update_root_icon);
- this.volume_control.active_output_changed.connect (this.update_notification);
+ this.volume_control.active_output_changed.connect(() => this.update_root_icon());
+ this.volume_control.active_output_changed.connect(() => this.update_notification());
this.accounts_service = accounts;
/* If we're on the greeter, don't export */
@@ -157,8 +146,6 @@ public class IndicatorSound.Service: Object {
clear_acts_player();
- stop_clamp_to_high_timeout();
-
if (this.player_action_update_id > 0) {
Source.remove (this.player_action_update_id);
this.player_action_update_id = 0;
@@ -208,16 +195,24 @@ public class IndicatorSound.Service: Object {
AccountsServiceUser? accounts_service = null;
bool export_to_accounts_service = false;
private Notify.Notification info_notification;
- private Notify.Notification warn_notification;
private Options _options;
private VolumeWarning _volume_warning;
const double volume_step_percentage = 0.06;
private void activate_scroll_action (SimpleAction action, Variant? param) {
- int delta = param.get_int32(); /* positive for up, negative for down */
- double v = volume_control.volume.volume + volume_step_percentage * delta;
- volume_control.set_volume_clamp (v, VolumeControl.VolumeReasons.USER_KEYPRESS);
+ int direction = param.get_int32(); // positive for up, negative for down
+ message("scroll: %d", direction);
+
+ if (_volume_warning.active) {
+ _volume_warning.user_keypress(direction>0
+ ? VolumeWarning.Key.VOLUME_UP
+ : VolumeWarning.Key.VOLUME_DOWN);
+ } else {
+ double delta = volume_step_percentage * direction;
+ double v = volume_control.volume.volume + delta;
+ volume_control.set_volume_clamp (v, VolumeControl.VolumeReasons.USER_KEYPRESS);
+ }
}
void activate_desktop_settings (SimpleAction action, Variant? param) {
@@ -285,7 +280,6 @@ public class IndicatorSound.Service: Object {
}
private bool notify_server_caps_checked = false;
- private bool notify_server_supports_actions = false;
private bool notify_server_supports_sync = false;
private bool block_info_notifications = false;
@@ -610,71 +604,33 @@ public class IndicatorSound.Service: Object {
private void update_notification () {
- if (!notify_server_caps_checked) {
+ if (!notify_server_caps_checked)
+ {
List<string> caps = Notify.get_server_caps ();
- notify_server_supports_actions = caps.find_custom ("actions", strcmp) != null;
notify_server_supports_sync = caps.find_custom ("x-canonical-private-synchronous", strcmp) != null;
notify_server_caps_checked = true;
}
- var loud = _volume_warning.high_volume;
- bool ignore_warning_this_time = _volume_warning.ignore_high_volume;
- var warn = loud
- && this.notify_server_supports_actions
- && !_volume_warning.high_volume_approved
- && !ignore_warning_this_time;
- if (_volume_warning.active && !_options.is_loud(volume_control.volume)) {
- _volume_warning.set_warning_volume();
- close_notification(warn_notification);
- }
- if (warn) {
+ if (_volume_warning.active)
+ {
close_notification(info_notification);
- if (_pre_warn_volume == null) {
- _pre_warn_volume = new VolumeControl.Volume();
- _pre_warn_volume.volume = volume_control.volume.volume;
- _pre_warn_volume.reason = volume_control.volume.reason;
- }
- warn_notification.clear_actions();
- warn_notification.add_action ("ok", _("OK"), (n, a) => {
- stop_clamp_to_high_timeout();
- _volume_warning.approve_high_volume ();
- // restore the volume the user introduced
- VolumeControl.Volume vol = new VolumeControl.Volume();
- vol.volume = volume_control.get_pre_clamped_volume();
- vol.reason = VolumeControl.VolumeReasons.USER_KEYPRESS;
- _pre_warn_volume = null;
- volume_control.volume = vol;
- _volume_warning.active = false;
- });
- warn_notification.add_action ("cancel", _("Cancel"), (n, a) => {
- _pre_warn_volume = null;
- _volume_warning.active = false;
- increment_volume_sync_action();
- });
- _volume_warning.active = true;
- show_notification(warn_notification);
- } else {
- if (!_volume_warning.active) {
- close_notification(warn_notification);
-
- if (notify_server_supports_sync && !block_info_notifications && !ignore_warning_this_time) {
- /* Determine Label */
- string volume_label = get_notification_label ();
-
- /* Choose an icon */
- string icon = get_volume_notification_icon (volume_control.volume.volume, loud, volume_control.active_output);
-
- /* Reset the notification */
- var n = this.info_notification;
- n.update (_("Volume"), volume_label, icon);
- n.clear_hints();
- n.set_hint ("x-canonical-non-shaped-icon", "true");
- n.set_hint ("x-canonical-private-synchronous", "true");
- n.set_hint ("x-canonical-value-bar-tint", loud ? "true" : "false");
- n.set_hint ("value", (int32)Math.round(get_volume_percent() * 100.0));
- show_notification(n);
- }
- }
+ }
+ else if (notify_server_supports_sync && !block_info_notifications)
+ {
+ bool is_loud = _volume_warning.high_volume;
+
+ string volume_label = get_notification_label ();
+ string icon = get_volume_notification_icon (volume_control.volume.volume, is_loud, volume_control.active_output);
+
+ /* Reset the notification */
+ var n = this.info_notification;
+ n.update (_("Volume"), volume_label, icon);
+ n.clear_hints();
+ n.set_hint ("x-canonical-non-shaped-icon", "true");
+ n.set_hint ("x-canonical-private-synchronous", "true");
+ n.set_hint ("x-canonical-value-bar-tint", is_loud ? "true" : "false");
+ n.set_hint ("value", (int32)Math.round(get_volume_percent() * 100.0));
+ show_notification(n);
}
}
@@ -794,9 +750,6 @@ public class IndicatorSound.Service: Object {
if (reason == VolumeControl.VolumeReasons.USER_KEYPRESS ||
reason == VolumeControl.VolumeReasons.DEVICE_OUTPUT_CHANGE)
this.update_notification ();
-
- if ((warn_notification.id != 0) && (_pre_warn_volume != null))
- clamp_to_high_soon();
});
this.volume_control.bind_property ("ready", volume_action, "enabled", BindingFlags.SYNC_CREATE);
@@ -969,27 +922,4 @@ public class IndicatorSound.Service: Object {
this.update_preferred_players ();
}
-
- /** VOLUME CLAMPING **/
-
- private uint _clamp_to_high_timeout = 0;
-
- private void stop_clamp_to_high_timeout() {
- if (_clamp_to_high_timeout != 0) {
- Source.remove(_clamp_to_high_timeout);
- _clamp_to_high_timeout = 0;
- }
- }
-
- private void clamp_to_high_soon() {
- const uint interval_msec = 200;
- if (_clamp_to_high_timeout == 0)
- _clamp_to_high_timeout = Timeout.add(interval_msec, clamp_to_high_idle);
- }
-
- private bool clamp_to_high_idle() {
- _clamp_to_high_timeout = 0;
- _volume_warning.clamp_to_high_volume();
- return false; // Source.REMOVE;
- }
}
diff --git a/src/volume-warning.vala b/src/volume-warning.vala
index 423f129..29a049b 100644
--- a/src/volume-warning.vala
+++ b/src/volume-warning.vala
@@ -40,8 +40,6 @@ public class VolumeWarning : VolumeControl
warning("set_mute not supported for VolumeWarning");
}
- private IndicatorSound.WarnNotification _notification = new IndicatorSound.WarnNotification();
-
/* this is static to ensure it being freed after @context (loop does not have ref counting) */
private static PulseAudio.GLibMainLoop loop;
@@ -103,7 +101,10 @@ public class VolumeWarning : VolumeControl
init_all_properties();
this.reconnect_to_pulse ();
- }
+
+ _notification = new IndicatorSound.WarnNotification();
+ _notification.user_responded.connect((n, response) => on_user_response(response));
+ }
private void init_all_properties()
{
@@ -123,6 +124,7 @@ public class VolumeWarning : VolumeControl
_reconnect_timer = 0;
}
stop_high_volume_approved_timer();
+ stop_clamp_to_loud_timeout();
}
private VolumeControl.ActiveOutput calculate_active_output (SinkInfo? sink) {
@@ -672,16 +674,6 @@ public class VolumeWarning : VolumeControl
&& (stream == "multimedia");
}
- public void clamp_to_high_volume() {
- if (_high_volume && _options.is_loud(_volume)) {
- var vol = new VolumeControl.Volume();
- vol.volume = _volume.volume.clamp(0, volume_to_double(_options.loud_volume()));
- vol.reason = _volume.reason;
- debug("Clamping from %f down to %f", _volume.volume, vol.volume);
- volume = vol;
- }
- }
-
public void set_warning_volume() {
var vol = new VolumeControl.Volume();
vol.volume = volume_to_double(_options.loud_volume());
@@ -694,7 +686,7 @@ public class VolumeWarning : VolumeControl
public bool high_volume_approved { get; private set; default = false; }
- public void approve_high_volume() {
+ private void approve_high_volume() {
_high_volume_approved_at = GLib.get_monotonic_time();
update_high_volume_approved();
update_high_volume_approved_timer();
@@ -843,4 +835,89 @@ public class VolumeWarning : VolumeControl
}
return objp;
}
+
+ private void set_multimedia_volume(VolumeControl.Volume volume)
+ {
+ // FIXME
+ }
+
+ // NOTIFICATION
+
+ private IndicatorSound.WarnNotification _notification = new IndicatorSound.WarnNotification();
+
+ private VolumeControl.Volume _cancel_volume = null;
+ private VolumeControl.Volume _ok_volume = null;
+
+ public void show(VolumeControl.Volume volume) {
+
+ // the volume to use if user hits 'cancel'
+ _cancel_volume = new VolumeControl.Volume();
+ _cancel_volume.volume = VolumeControlPulse.volume_to_double(_options.loud_volume());
+ _cancel_volume.reason = VolumeControl.VolumeReasons.USER_KEYPRESS;
+
+ // the volume to use if user hits 'ok'
+ _ok_volume = new VolumeControl.Volume();
+ _ok_volume.volume = volume.volume;
+ _ok_volume.reason = VolumeControl.VolumeReasons.USER_KEYPRESS;
+
+ _notification.show();
+ this.active = true;
+ }
+
+ public enum Key {
+ VOLUME_UP,
+ VOLUME_DOWN
+ }
+
+ public void user_keypress(Key key) {
+ if (key == Key.VOLUME_DOWN)
+ on_user_response(IndicatorSound.WarnNotification.Response.CANCEL);
+ }
+
+ private void on_user_response(IndicatorSound.WarnNotification.Response response) {
+ _notification.close();
+ stop_clamp_to_loud_timeout();
+
+ if (response == IndicatorSound.WarnNotification.Response.OK) {
+ approve_high_volume();
+ set_multimedia_volume(_ok_volume);
+ } else { // WarnNotification.CANCEL
+ set_multimedia_volume(_cancel_volume);
+ }
+
+ _cancel_volume = null;
+ _ok_volume = null;
+
+ this.active = false;
+ }
+
+ // VOLUME CLAMPING
+
+ private uint _clamp_to_loud_timeout = 0;
+
+ private void stop_clamp_to_loud_timeout() {
+ if (_clamp_to_loud_timeout != 0) {
+ Source.remove(_clamp_to_loud_timeout);
+ _clamp_to_loud_timeout = 0;
+ }
+ }
+
+ private void clamp_to_loud_soon() {
+ const uint interval_msec = 200;
+ if (_clamp_to_loud_timeout == 0)
+ _clamp_to_loud_timeout = Timeout.add(interval_msec, clamp_to_loud_idle);
+ }
+
+ private bool clamp_to_loud_idle() {
+ _clamp_to_loud_timeout = 0;
+ clamp_to_loud_volume();
+ return false; // Source.REMOVE;
+ }
+
+ private void clamp_to_loud_volume() {
+ if ((_cancel_volume != null) && (_volume.volume > _cancel_volume.volume)) {
+ debug("Clamping from %f down to %f", _volume.volume, _cancel_volume.volume);
+ set_multimedia_volume (_cancel_volume);
+ }
+ }
}
diff --git a/src/warn-notification.vala b/src/warn-notification.vala
new file mode 100644
index 0000000..4f45a5c
--- /dev/null
+++ b/src/warn-notification.vala
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2015 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+public class IndicatorSound.WarnNotification: Notification
+{
+ public enum Response {
+ CANCEL,
+ OK
+ }
+
+ public signal void user_responded(WarnNotification.Response response);
+
+ protected override Notify.Notification create_notification() {
+ var n = new Notify.Notification(_("Volume"), _("High volume can damage your hearing."), "audio-volume-high");
+ n.set_hint ("x-canonical-non-shaped-icon", "true");
+ n.set_hint ("x-canonical-snap-decisions", "true");
+ n.set_hint ("x-canonical-private-affirmative-tint", "true");
+ n.closed.connect((n) => {
+ n.clear_actions();
+ });
+ return n;
+ }
+
+ public bool show() {
+ close();
+
+ if (!notify_server_supports("actions"))
+ return false;
+
+ _notification.clear_actions();
+ _notification.add_action ("ok", _("OK"), (n, a) => {
+ user_responded(Response.OK);
+ });
+
+ _notification.add_action ("cancel", _("Cancel"), (n, a) => {
+ user_responded(Response.CANCEL);
+ });
+
+ show_();
+
+ return true;
+ }
+}