From ec5e539809ec8d2bcdfbdfb70d080fc5edd217ea Mon Sep 17 00:00:00 2001 From: Mike Gabriel Date: Mon, 21 Sep 2015 17:42:30 +0200 Subject: Rebase against unity from Ubuntu 15.04 --- src/idle-monitor.vala | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 src/idle-monitor.vala (limited to 'src/idle-monitor.vala') diff --git a/src/idle-monitor.vala b/src/idle-monitor.vala new file mode 100644 index 0000000..b3a423e --- /dev/null +++ b/src/idle-monitor.vala @@ -0,0 +1,216 @@ +public delegate void IdleMonitorWatchFunc (IdleMonitor monitor, uint id); + +public class IdleMonitor +{ + private unowned X.Display display; + private HashTable watches; + private HashTable alarms; + private int sync_event_base; + private X.ID counter; + private X.ID user_active_alarm; + private int serial = 0; + + public IdleMonitor () + { + watches = new HashTable (null, null); + alarms = new HashTable (null, null); + init_xsync (); + } + + ~IdleMonitor () + { + foreach (var watch in watches.get_values ()) + remove_watch (watch.id); + if (user_active_alarm != X.None) + X.Sync.DestroyAlarm (display, user_active_alarm); + + /* Note this is a bit weird, since we need to pass null as the window by Vala treats this as a method */ + Gdk.Window w = null; + w.remove_filter (xevent_filter); + } + + public uint add_idle_watch (uint64 interval_msec, IdleMonitorWatchFunc callback) + { + var watch = make_watch (xsync_alarm_set (X.Sync.TestType.PositiveTransition, interval_msec, true), callback); + alarms.add ((uint32) watch.xalarm); + return watch.id; + } + + public uint add_user_active_watch (IdleMonitorWatchFunc callback) + { + set_alarm_enabled (display, user_active_alarm, true); + var watch = make_watch (user_active_alarm, callback); + return watch.id; + } + + public void remove_watch (uint id) + { + var watch = watches.lookup (id); + watches.remove (id); + if (watch.xalarm != user_active_alarm) + X.Sync.DestroyAlarm (display, watch.xalarm); + } + + private void init_xsync () + { + var d = Gdk.Display.get_default (); + if (!(d is Gdk.X11.Display)) + { + warning ("Only support idle monitor under X"); + return; + } + display = (d as Gdk.X11.Display).get_xdisplay (); + + int sync_error_base; + var res = X.Sync.QueryExtension (display, out sync_event_base, out sync_error_base); + if (res == 0) + { + warning ("IdleMonitor: Sync extension not present"); + return; + } + + int major, minor; + res = X.Sync.Initialize (display, out major, out minor); + if (res == 0) + { + warning ("IdleMonitor: Unable to initialize Sync extension"); + return; + } + + counter = find_idletime_counter (); + /* IDLETIME counter not found? */ + if (counter == X.None) + return; + + user_active_alarm = xsync_alarm_set (X.Sync.TestType.NegativeTransition, 1, false); + + /* Note this is a bit weird, since we need to pass null as the window by Vala treats this as a method */ + Gdk.Window w = null; + w.add_filter (xevent_filter); + } + + private Gdk.FilterReturn xevent_filter (Gdk.XEvent xevent, Gdk.Event event) + { + var ev = (X.Event*) xevent; + if (ev.xany.type != sync_event_base + X.Sync.AlarmNotify) + return Gdk.FilterReturn.CONTINUE; + + var alarm_event = (X.Sync.AlarmNotifyEvent*) xevent; + handle_alarm_notify_event (alarm_event); + + return Gdk.FilterReturn.CONTINUE; + } + + private IdleMonitorWatch make_watch (X.ID xalarm, IdleMonitorWatchFunc callback) + { + var watch = new IdleMonitorWatch (); + watch.id = get_next_watch_serial (); + watch.callback = callback; + watch.xalarm = xalarm; + + watches.insert (watch.id, watch); + + return watch; + } + + private X.ID xsync_alarm_set (X.Sync.TestType test_type, uint64 interval, bool want_events) + { + var attr = X.Sync.AlarmAttributes (); + X.Sync.Value delta; + X.Sync.IntToValue (out delta, 0); + attr.trigger.counter = counter; + attr.trigger.value_type = X.Sync.ValueType.Absolute; + attr.delta = delta; + attr.events = want_events; + X.Sync.IntsToValue (out attr.trigger.wait_value, (uint) interval, (int) (interval >> 32)); + attr.trigger.test_type = test_type; + + return X.Sync.CreateAlarm (display, X.Sync.CA.Counter | X.Sync.CA.ValueType | X.Sync.CA.TestType | X.Sync.CA.Value | X.Sync.CA.Delta | X.Sync.CA.Events, attr); + } + + private void ensure_alarm_rescheduled (X.Display dpy, X.ID alarm) + { + /* Some versions of Xorg have an issue where alarms aren't + * always rescheduled. Calling X.Sync.ChangeAlarm, even + * without any attributes, will reschedule the alarm. */ + var attr = X.Sync.AlarmAttributes (); + X.Sync.ChangeAlarm (dpy, alarm, 0, attr); + } + + private void set_alarm_enabled (X.Display dpy, X.ID alarm, bool enabled) + { + var attr = X.Sync.AlarmAttributes (); + attr.events = enabled; + X.Sync.ChangeAlarm (dpy, alarm, X.Sync.CA.Events, attr); + } + + private void handle_alarm_notify_event (X.Sync.AlarmNotifyEvent* alarm_event) + { + if (alarm_event.state != X.Sync.AlarmState.Active) + return; + + var alarm = alarm_event.alarm; + var has_alarm = false; + + if (alarm == user_active_alarm) + { + set_alarm_enabled (display, alarm, false); + has_alarm = true; + } + else if (alarms.contains ((uint32) alarm)) + { + ensure_alarm_rescheduled (display, alarm); + has_alarm = true; + } + + if (has_alarm) + { + foreach (var watch in watches.get_values ()) + fire_watch (watch, alarm); + } + } + + private void fire_watch (IdleMonitorWatch watch, X.ID alarm) + { + if (watch.xalarm != alarm) + return; + + if (watch.callback != null) + watch.callback (this, watch.id); + + if (watch.xalarm == user_active_alarm) + remove_watch (watch.id); + } + + private X.ID find_idletime_counter () + { + X.ID counter = X.None; + + int ncounters; + var counters = X.Sync.ListSystemCounters (display, out ncounters); + for (var i = 0; i < ncounters; i++) + { + if (counters[i].name != null && strcmp (counters[i].name, "IDLETIME") == 0) + { + counter = counters[i].counter; + break; + } + } + X.Sync.FreeSystemCounterList (counters); + + return counter; + } + + private uint32 get_next_watch_serial () + { + AtomicInt.inc (ref serial); + return serial; + } +} + +public class IdleMonitorWatch +{ + public uint id; + public IdleMonitorWatchFunc callback; + public X.ID xalarm; +} -- cgit v1.2.3