From c7fb528f0d80f9d96a2d84444352386d5d4288dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Tue, 24 Nov 2009 19:09:37 +0100
Subject: Added SwifTools/Idle module.


diff --git a/SConstruct b/SConstruct
index 0fa9a58..29be052 100644
--- a/SConstruct
+++ b/SConstruct
@@ -12,17 +12,17 @@ vars.Add(EnumVariable("test", "Compile and run tests", "none", ["none", "all", "
 vars.Add(BoolVariable("optimize", "Compile with optimizations turned on", "no"))
 vars.Add(BoolVariable("debug", "Compile with debug information", "yes" if os.name != "nt" else "no"))
 vars.Add(BoolVariable("warnings", "Compile with warnings turned on", 
-    "yes" if os.name != "nt" else "no"))
+		"yes" if os.name != "nt" else "no"))
 if os.name != "nt" :
-  vars.Add(BoolVariable("coverage", "Compile with coverage information", "no"))
+	vars.Add(BoolVariable("coverage", "Compile with coverage information", "no"))
 if os.name == "posix" :
-  vars.Add(BoolVariable("valgrind", "Run tests with valgrind", "no"))
+	vars.Add(BoolVariable("valgrind", "Run tests with valgrind", "no"))
 if os.name == "mac" :
-  vars.Add(BoolVariable("universal", "Create universal binaries", "no"))
+	vars.Add(BoolVariable("universal", "Create universal binaries", "no"))
 if os.name == "nt" :
-  vars.Add(PathVariable("vcredist", "MSVC redistributable dir", "", PathVariable.PathAccept))
+	vars.Add(PathVariable("vcredist", "MSVC redistributable dir", "", PathVariable.PathAccept))
 if os.name == "nt" :
-  vars.Add(PackageVariable("bonjour", "Bonjour SDK location", "yes"))
+	vars.Add(PackageVariable("bonjour", "Bonjour SDK location", "yes"))
 vars.Add(PackageVariable("openssl", "OpenSSL location", "yes"))
 vars.Add(PathVariable("qt", "Qt location", "", PathVariable.PathAccept))
 
@@ -104,7 +104,7 @@ if "check" in ARGUMENTS or "check" in COMMAND_LINE_TARGETS :
 	env["TEST_TYPE"] = "unit"
 env["TEST"] = (env["TEST_TYPE"] != "none") or env.GetOption("clean")
 if env.get("valgrind", 0) :
-  env["TEST_RUNNER"] = "valgrind --suppressions=QA/valgrind.supp -q --leak-check=full --track-origins=yes "
+	env["TEST_RUNNER"] = "valgrind --suppressions=QA/valgrind.supp -q --leak-check=full --track-origins=yes "
 
 # Packaging
 if ARGUMENTS.get("SWIFT_INSTALLDIR", "") :
@@ -183,6 +183,21 @@ if conf.CheckCHeader("expat.h") and conf.CheckLib("expat") :
 
 conf.Finish()
 
+# Xss
+env["HAVE_XSS"] = 0
+if env["PLATFORM"] != "win32" and env["PLATFORM"] != "darwin" :
+	xss_flags = {
+			"LIBPATH": ["/usr/X11R6/lib"],
+			"LIBS": ["X11", "Xss"]
+		}
+	xss_env = conf_env.Clone()
+	xss_env.MergeFlags(xss_flags)
+	conf = Configure(xss_env)
+	if conf.CheckFunc("XScreenSaverQueryExtension") :
+		env["HAVE_XSS"] = 1
+		env["XSS_FLAGS"] = xss_flags
+	conf.Finish()
+
 # LibXML
 conf = Configure(conf_env)
 if conf.CheckCHeader("libxml/parser.h") and conf.CheckLib("xml2") :
diff --git a/SwifTools/Idle/ActualIdleDetector.cpp b/SwifTools/Idle/ActualIdleDetector.cpp
new file mode 100644
index 0000000..2e3f307
--- /dev/null
+++ b/SwifTools/Idle/ActualIdleDetector.cpp
@@ -0,0 +1,29 @@
+#include "SwifTools/Idle/ActualIdleDetector.h"
+
+#include <boost/bind.hpp>
+
+#include "SwifTools/Idle/IdleQuerier.h"
+#include "Swiften/Network/Timer.h"
+#include "Swiften/Network/TimerFactory.h"
+
+namespace Swift {
+
+ActualIdleDetector::ActualIdleDetector(IdleQuerier* querier, TimerFactory* timerFactory, int refreshRateSeconds) : querier(querier), isIdle(false) {
+	timer = timerFactory->createTimer(refreshRateSeconds*1000);
+	timer->onTick.connect(boost::bind(&ActualIdleDetector::handleTimerTick, this));
+	timer->start();
+}
+
+ActualIdleDetector::~ActualIdleDetector() {
+	timer->stop();
+}
+
+void ActualIdleDetector::handleTimerTick() {
+	bool idle = (querier->getIdleTimeSeconds() >= getIdleTimeSeconds());
+	if (idle != isIdle) {
+		isIdle = idle;
+		onIdleChanged(isIdle);
+	}
+}
+
+}
diff --git a/SwifTools/Idle/ActualIdleDetector.h b/SwifTools/Idle/ActualIdleDetector.h
new file mode 100644
index 0000000..48428bb
--- /dev/null
+++ b/SwifTools/Idle/ActualIdleDetector.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include "SwifTools/Idle/IdleDetector.h"
+
+namespace Swift {
+	class IdleQuerier;
+	class TimerFactory;
+	class Timer;
+
+	class ActualIdleDetector : public IdleDetector, public boost::bsignals::trackable {
+		public:
+			ActualIdleDetector(IdleQuerier*, TimerFactory*, int refreshRateSeconds);
+			~ActualIdleDetector();
+
+		private:
+			void handleTimerTick();
+
+		private:
+			IdleQuerier* querier;
+			bool isIdle;
+			boost::shared_ptr<Timer> timer;
+	};
+}
diff --git a/SwifTools/Idle/DummyIdleQuerier.h b/SwifTools/Idle/DummyIdleQuerier.h
new file mode 100644
index 0000000..b8ba776
--- /dev/null
+++ b/SwifTools/Idle/DummyIdleQuerier.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "SwifTools/Idle/IdleQuerier.h"
+
+namespace Swift {
+	class DummyIdleQuerier : public IdleQuerier {
+		public:
+			DummyIdleQuerier() {}
+
+			virtual int getIdleTimeSeconds() {
+				return 0;
+			}
+	};
+}
diff --git a/SwifTools/Idle/IdleDetector.cpp b/SwifTools/Idle/IdleDetector.cpp
new file mode 100644
index 0000000..42559cd
--- /dev/null
+++ b/SwifTools/Idle/IdleDetector.cpp
@@ -0,0 +1,8 @@
+#include "SwifTools/Idle/IdleDetector.h"
+
+namespace Swift {
+
+IdleDetector::~IdleDetector() {
+}
+
+}
diff --git a/SwifTools/Idle/IdleDetector.h b/SwifTools/Idle/IdleDetector.h
new file mode 100644
index 0000000..1fb3d69
--- /dev/null
+++ b/SwifTools/Idle/IdleDetector.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+	class IdleDetector {
+		public:
+			virtual ~IdleDetector();
+
+			void setIdleTimeSeconds(int time) {
+				idleTimeSeconds = time;
+			}
+
+			int getIdleTimeSeconds() const {
+				return idleTimeSeconds;
+			}
+
+			boost::signal<void (bool /* isIdle */)> onIdleChanged;
+
+		private:
+			int idleTimeSeconds;
+	};
+}
diff --git a/SwifTools/Idle/IdleQuerier.cpp b/SwifTools/Idle/IdleQuerier.cpp
new file mode 100644
index 0000000..0974aba
--- /dev/null
+++ b/SwifTools/Idle/IdleQuerier.cpp
@@ -0,0 +1,8 @@
+#include "SwifTools/Idle/IdleQuerier.h"
+
+namespace Swift {
+
+IdleQuerier::~IdleQuerier() {
+}
+
+}
diff --git a/SwifTools/Idle/IdleQuerier.h b/SwifTools/Idle/IdleQuerier.h
new file mode 100644
index 0000000..71f201e
--- /dev/null
+++ b/SwifTools/Idle/IdleQuerier.h
@@ -0,0 +1,10 @@
+#pragma once
+
+namespace Swift {
+	class IdleQuerier {
+		public:
+			virtual ~IdleQuerier();
+
+			virtual int getIdleTimeSeconds() = 0;
+	};
+}
diff --git a/SwifTools/Idle/IdleQuerierTest/.gitignore b/SwifTools/Idle/IdleQuerierTest/.gitignore
new file mode 100644
index 0000000..6c63ee9
--- /dev/null
+++ b/SwifTools/Idle/IdleQuerierTest/.gitignore
@@ -0,0 +1 @@
+IdleQuerierTest
diff --git a/SwifTools/Idle/IdleQuerierTest/IdleQuerierTest.cpp b/SwifTools/Idle/IdleQuerierTest/IdleQuerierTest.cpp
new file mode 100644
index 0000000..94f7e2b
--- /dev/null
+++ b/SwifTools/Idle/IdleQuerierTest/IdleQuerierTest.cpp
@@ -0,0 +1,16 @@
+#include <iostream>
+
+#include "SwifTools/Idle/PlatformIdleQuerier.h"
+#include "Swiften/Base/sleep.h"
+
+using namespace Swift;
+
+int main() {
+	PlatformIdleQuerier querier;
+	while (true) {
+		std::cout << "Idle time: " << querier.getIdleTimeSeconds() << std::endl;
+		Swift::sleep(1000);
+	}
+
+	return 0;
+}
diff --git a/SwifTools/Idle/IdleQuerierTest/SConscript b/SwifTools/Idle/IdleQuerierTest/SConscript
new file mode 100644
index 0000000..d45aeea
--- /dev/null
+++ b/SwifTools/Idle/IdleQuerierTest/SConscript
@@ -0,0 +1,11 @@
+Import("env")
+
+if env["TEST"] :
+	myenv = env.Clone()
+	myenv.MergeFlags(myenv["SWIFTOOLS_FLAGS"])
+	myenv.MergeFlags(myenv["SWIFTEN_FLAGS"])
+	myenv.MergeFlags(myenv["BOOST_FLAGS"])
+	myenv.MergeFlags(myenv.get("XSS_FLAGS", {}))
+	myenv.Append(LIBPATH = ["/usr/X11R6/lib"])
+	myenv.Append(LIBS = ["X11", "Xss"])
+	tester = myenv.Program("IdleQuerierTest", ["IdleQuerierTest.cpp"])
diff --git a/SwifTools/Idle/PlatformIdleQuerier.cpp b/SwifTools/Idle/PlatformIdleQuerier.cpp
new file mode 100644
index 0000000..76a3f7d
--- /dev/null
+++ b/SwifTools/Idle/PlatformIdleQuerier.cpp
@@ -0,0 +1,26 @@
+#include "SwifTools/Idle/PlatformIdleQuerier.h"
+
+#if defined(HAVE_XSS)
+#include "SwifTools/Idle/XSSIdleQuerier.h"
+#else
+#include "SwifTools/Idle/DummyIdleQuerier.h"
+#endif
+
+#include <cassert>
+#include <iostream>
+
+namespace Swift {
+
+PlatformIdleQuerier::PlatformIdleQuerier() : querier(NULL) {
+#if defined(HAVE_XSS)
+	querier = new XSSIdleQuerier();
+#else
+	querier = new DummyIdleQuerier();
+#endif
+}
+
+PlatformIdleQuerier::~PlatformIdleQuerier() {
+	delete querier;
+}
+
+}
diff --git a/SwifTools/Idle/PlatformIdleQuerier.h b/SwifTools/Idle/PlatformIdleQuerier.h
new file mode 100644
index 0000000..e567b0e
--- /dev/null
+++ b/SwifTools/Idle/PlatformIdleQuerier.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "SwifTools/Idle/IdleQuerier.h"
+
+namespace Swift {
+	class PlatformIdleQuerier : public IdleQuerier {
+		public:
+			PlatformIdleQuerier();
+			~PlatformIdleQuerier();
+
+			virtual int getIdleTimeSeconds() {
+				return querier->getIdleTimeSeconds();
+			}
+
+		private:
+			IdleQuerier* querier;
+	};
+}
diff --git a/SwifTools/Idle/UnitTest/ActualIdleDetectorTest.cpp b/SwifTools/Idle/UnitTest/ActualIdleDetectorTest.cpp
new file mode 100644
index 0000000..99a7099
--- /dev/null
+++ b/SwifTools/Idle/UnitTest/ActualIdleDetectorTest.cpp
@@ -0,0 +1,164 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/bind.hpp>
+
+#include "SwifTools/Idle/ActualIdleDetector.h"
+#include "SwifTools/Idle/IdleQuerier.h"
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Network/TimerFactory.h"
+#include "Swiften/Network/Timer.h"
+
+using namespace Swift;
+
+class ActualIdleDetectorTest : public CppUnit::TestFixture {
+		CPPUNIT_TEST_SUITE(ActualIdleDetectorTest);
+		CPPUNIT_TEST(testDestructor);
+		CPPUNIT_TEST(testHandleTick_Idle);
+		CPPUNIT_TEST(testHandleTick_Idle_AlreadyIdle);
+		CPPUNIT_TEST(testHandleTick_NotIdle);
+		CPPUNIT_TEST(testHandleTick_NotIdle_AlreadyNotIdle);
+		CPPUNIT_TEST_SUITE_END();
+
+	public:
+		void setUp() {
+			querier = new MockIdleQuerier();
+			timerFactory = new MockTimerFactory();
+			idleEvents.clear();
+		}
+
+		void tearDown() {
+			delete timerFactory;
+			delete querier;
+		}
+
+		void testDestructor()  {
+			ActualIdleDetector* testling = createDetector();
+			testling->setIdleTimeSeconds(15);
+			delete testling;
+
+			querier->idleTime = 15;
+			timerFactory->updateTime(15000);
+			
+			CPPUNIT_ASSERT_EQUAL(0U, idleEvents.size());
+		}
+
+		void testHandleTick_Idle() {
+			std::auto_ptr<ActualIdleDetector> testling(createDetector());
+			testling->setIdleTimeSeconds(15);
+			querier->idleTime = 15;
+
+			timerFactory->updateTime(15000);
+			
+			CPPUNIT_ASSERT_EQUAL(1U, idleEvents.size());
+			CPPUNIT_ASSERT(idleEvents[0]);
+		}
+
+		void testHandleTick_Idle_AlreadyIdle() {
+			std::auto_ptr<ActualIdleDetector> testling(createDetector());
+			testling->setIdleTimeSeconds(15);
+			querier->idleTime = 15;
+			timerFactory->updateTime(15000);
+
+			querier->idleTime = 30;
+			timerFactory->updateTime(30000);
+			
+			CPPUNIT_ASSERT_EQUAL(1U, idleEvents.size());
+			CPPUNIT_ASSERT(idleEvents[0]);
+		}
+
+		void testHandleTick_NotIdle() {
+			std::auto_ptr<ActualIdleDetector> testling(createDetector());
+			testling->setIdleTimeSeconds(15);
+			querier->idleTime = 15;
+			timerFactory->updateTime(15000);
+
+			querier->idleTime = 5;
+			timerFactory->updateTime(30000);
+			
+			CPPUNIT_ASSERT_EQUAL(2U, idleEvents.size());
+			CPPUNIT_ASSERT(idleEvents[0]);
+			CPPUNIT_ASSERT(!idleEvents[1]);
+		}
+
+		void testHandleTick_NotIdle_AlreadyNotIdle() {
+			std::auto_ptr<ActualIdleDetector> testling(createDetector());
+			testling->setIdleTimeSeconds(15);
+			querier->idleTime = 5;
+
+			timerFactory->updateTime(15000);
+			
+			CPPUNIT_ASSERT_EQUAL(0U, idleEvents.size());
+		}
+	
+	private:
+		ActualIdleDetector* createDetector() {
+			ActualIdleDetector* detector = new ActualIdleDetector(querier, timerFactory, 10);
+			detector->onIdleChanged.connect(boost::bind(&ActualIdleDetectorTest::handleIdle, this, _1));
+			return detector;
+		}
+
+		void handleIdle(bool b) {
+			idleEvents.push_back(b);
+		}
+
+	private:
+		struct MockIdleQuerier : public IdleQuerier {
+			MockIdleQuerier() : idleTime(0) {}
+			virtual int getIdleTimeSeconds() { return idleTime; }
+			int idleTime;
+		};
+
+		struct MockTimer : public Timer {
+			MockTimer(int interval) : interval(interval), running(false), lastTime(0) {}
+
+			virtual void start() {
+				running = true;
+			}
+
+			virtual void stop() {
+				running = false;
+			}
+
+			virtual void updateTime(int currentTime) {
+				if (lastTime == currentTime) {
+					return;
+				}
+				if (running) {
+					int time = lastTime;
+					while (time <= currentTime) {
+						onTick();
+						time += interval;
+					}
+				}
+				lastTime = currentTime;
+			}
+
+			int interval;
+			bool running;
+			int lastTime;
+		};
+
+		struct MockTimerFactory : public TimerFactory {
+			MockTimerFactory() {}
+
+			void updateTime(int milliseconds) {
+				foreach(boost::shared_ptr<MockTimer> timer, timers) {
+					timer->updateTime(milliseconds);
+				}
+			}
+
+			boost::shared_ptr<Timer> createTimer(int milliseconds) {
+				boost::shared_ptr<MockTimer> timer(new MockTimer(milliseconds));
+				timers.push_back(timer);
+				return timer;
+			}
+
+			std::vector<boost::shared_ptr<MockTimer> > timers;
+		};
+
+		MockIdleQuerier* querier;
+		MockTimerFactory* timerFactory;
+		std::vector<bool> idleEvents;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ActualIdleDetectorTest);
diff --git a/SwifTools/Idle/UnitTest/SConscript b/SwifTools/Idle/UnitTest/SConscript
new file mode 100644
index 0000000..f193349
--- /dev/null
+++ b/SwifTools/Idle/UnitTest/SConscript
@@ -0,0 +1,5 @@
+Import("env")
+
+env.Append(UNITTEST_SOURCES = [
+		File("ActualIdleDetectorTest.cpp")
+	])
diff --git a/SwifTools/Idle/XSSIdleQuerier.cpp b/SwifTools/Idle/XSSIdleQuerier.cpp
new file mode 100644
index 0000000..2259620
--- /dev/null
+++ b/SwifTools/Idle/XSSIdleQuerier.cpp
@@ -0,0 +1,36 @@
+#include "SwifTools/Idle/XSSIdleQuerier.h"
+
+#include <cassert>
+#include <iostream>
+
+namespace Swift {
+
+XSSIdleQuerier::XSSIdleQuerier() : display(NULL), info(NULL) {
+	display = XOpenDisplay(NULL);
+	assert(display);
+	rootWindow = DefaultRootWindow(display);
+	int event, error;
+	available = XScreenSaverQueryExtension(display, &event, &error);
+	if (available) {
+		info = XScreenSaverAllocInfo();
+	}
+	else {
+		std::cerr << "Warning: XScreenSaver extension not found. Idle time detection will not work." << std::endl;
+	}
+}
+
+XSSIdleQuerier::~XSSIdleQuerier() {
+	XFree(info);
+}
+
+int XSSIdleQuerier::getIdleTimeSeconds() {
+	if (available) {
+		XScreenSaverQueryInfo(display, rootWindow, info);
+		return info->idle / 1000;
+	}
+	else {
+		return 0;
+	}
+}
+
+}
diff --git a/SwifTools/Idle/XSSIdleQuerier.h b/SwifTools/Idle/XSSIdleQuerier.h
new file mode 100644
index 0000000..476784d
--- /dev/null
+++ b/SwifTools/Idle/XSSIdleQuerier.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <X11/Xlib.h>
+#include <X11/extensions/scrnsaver.h>
+
+#include "SwifTools/Idle/IdleQuerier.h"
+
+namespace Swift {
+	class XSSIdleQuerier : public IdleQuerier {
+		public:
+			XSSIdleQuerier();
+			~XSSIdleQuerier();
+
+			virtual int getIdleTimeSeconds();
+
+		private:
+			Display* display;
+			Window rootWindow;
+			bool available;
+			XScreenSaverInfo* info;
+	};
+}
diff --git a/SwifTools/SConscript b/SwifTools/SConscript
index 2caff5f..cff2c6b 100644
--- a/SwifTools/SConscript
+++ b/SwifTools/SConscript
@@ -6,10 +6,30 @@ env["SWIFTOOLS_FLAGS"] = {
 	}
 
 myenv = env.Clone()
-
 myenv.MergeFlags(myenv["BOOST_FLAGS"])
-myenv.StaticLibrary("SwifTools", [
-		"Linkify.cpp"
-	])
 
-SConscript(dirs = ["UnitTest"])
+sources = [
+		"Idle/IdleDetector.cpp",
+		"Idle/ActualIdleDetector.cpp",
+		"Idle/IdleQuerier.cpp",
+		"Idle/PlatformIdleQuerier.cpp",
+		"Linkify.cpp",
+	]
+
+if myenv["PLATFORM"] == "win32" :
+	pass
+elif myenv["PLATFORM"] == "darwin" :
+	pass
+elif myenv["HAVE_XSS"] :
+	myenv.Append(CPPDEFINES = ["HAVE_XSS"])
+	sources += ["Idle/XSSIdleQuerier.cpp"]
+else :
+  sources += ["Idle/DummyIdleQuerier.cpp"]
+
+myenv.StaticLibrary("SwifTools", sources)
+
+SConscript(dirs = [
+		"Idle/IdleQuerierTest",
+		"Idle/UnitTest",
+		"UnitTest"
+	])
-- 
cgit v0.10.2-6-g49f6