[gnome-shell] pointerWatcher: add a class to track the pointer



commit 446583400e735e5a8acde3bddea731f0ee2f6e7c
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Mon Aug 20 18:19:50 2012 -0400

    pointerWatcher: add a class to track the pointer
    
    Add a class to efficiently poll the pointer position, stopping polling
    when the user is idle.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=682310

 js/Makefile.am          |    1 +
 js/ui/pointerWatcher.js |  126 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 127 insertions(+), 0 deletions(-)
---
diff --git a/js/Makefile.am b/js/Makefile.am
index f574c5b..b5bda79 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -72,6 +72,7 @@ nobase_dist_js_DATA = 	\
 	ui/panel.js		\
 	ui/panelMenu.js		\
 	ui/placeDisplay.js	\
+	ui/pointerWatcher.js    \
 	ui/polkitAuthenticationAgent.js \
 	ui/popupMenu.js		\
 	ui/remoteSearch.js	\
diff --git a/js/ui/pointerWatcher.js b/js/ui/pointerWatcher.js
new file mode 100644
index 0000000..9c5d1fa
--- /dev/null
+++ b/js/ui/pointerWatcher.js
@@ -0,0 +1,126 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+const Shell = imports.gi.Shell;
+
+// We stop polling if the user is idle for more than this amount of time
+const IDLE_TIME = 1000;
+
+// This file implements a reasonably efficient system for tracking the position
+// of the mouse pointer. We simply query the pointer from the X server in a loop,
+// but we turn off the polling when the user is idle.
+
+let _pointerWatcher = null;
+function getPointerWatcher() {
+    if (_pointerWatcher == null)
+        _pointerWatcher = new PointerWatcher();
+
+    return _pointerWatcher;
+}
+
+const PointerWatch = new Lang.Class({
+    Name: 'PointerWatch',
+
+    _init: function(watcher, interval, callback) {
+        this.watcher = watcher;
+        this.interval = interval;
+        this.callback = callback;
+    },
+
+    // remove:
+    // remove this watch. This function may safely be called
+    // while the callback is executing.
+    remove: function() {
+        this.watcher._removeWatch(this);
+    }
+});
+
+const PointerWatcher = new Lang.Class({
+    Name: 'PointerWatcher',
+
+    _init: function() {
+        let idleMonitor = Shell.IdleMonitor.get();
+        idleMonitor.add_watch(IDLE_TIME,
+                              Lang.bind(this, this._onIdleMonitorWatch));
+        this._idle = idleMonitor.get_idletime() > IDLE_TIME;
+        this._watches = [];
+        this.pointerX = null;
+        this.pointerY = null;
+    },
+
+    // addWatch:
+    // @interval: hint as to the time resolution needed. When the user is
+    //   not idle, the position of the pointer will be queried at least
+    //   once every this many milliseconds.
+    // @callback: function to call when the pointer position changes - takes
+    //   two arguments, X and Y.
+    //
+    // Set up a watch on the position of the mouse pointer. Returns a
+    // PointerWatch object which has a remove() method to remove the watch.
+    addWatch: function(interval, callback) {
+        // Avoid unreliably calling the watch for the current position
+        this._updatePointer();
+
+        let watch = new PointerWatch(this, interval, callback);
+        this._watches.push(watch);
+        this._updateTimeout();
+        return watch;
+    },
+
+    _removeWatch: function(watch) {
+        for (let i = 0; i < this._watches.length; i++) {
+            if (this._watches[i] == watch) {
+                this._watches.splice(i, 1);
+                this._updateTimeout();
+                return;
+            }
+        }
+    },
+
+    _onIdleMonitorWatch: function(monitor, id, userBecameIdle) {
+        this._idle = userBecameIdle;
+        if (!userBecameIdle)
+            this._updatePointer();
+
+        this._updateTimeout();
+    },
+
+    _updateTimeout: function() {
+        if (this._timeoutId) {
+            Mainloop.source_remove(this._timeoutId);
+            this._timeoutId = 0;
+        }
+
+        if (this._idle || this._watches.length == 0)
+            return;
+
+        let minInterval = this._watches[0].interval;
+        for (let i = 1; i < this._watches.length; i++)
+            minInterval = Math.min(this._watches[i].interval, minInterval);
+
+        this._timeoutId = Mainloop.timeout_add(minInterval,
+                                               Lang.bind(this, this._onTimeout));
+    },
+
+    _onTimeout: function() {
+        this._updatePointer();
+        return true;
+    },
+
+    _updatePointer: function() {
+        let [x, y, mods] = global.get_pointer();
+        if (this.pointerX == x && this.pointerY == y)
+            return;
+
+        this.pointerX = x;
+        this.pointerY = y;
+
+        for (let i = 0; i < this._watches.length;) {
+            let watch = this._watches[i];
+            watch.callback(x, y);
+            if (watch == this._watches[i]) // guard against self-removal
+                i++;
+        }
+    }
+});



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]