aboutsummaryrefslogtreecommitdiff
path: root/Src/Wasabi/api/timer
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Wasabi/api/timer')
-rw-r--r--Src/Wasabi/api/timer/api_timer.h47
-rw-r--r--Src/Wasabi/api/timer/osx_timer.cpp38
-rw-r--r--Src/Wasabi/api/timer/osx_timer.h17
-rw-r--r--Src/Wasabi/api/timer/timerclient.cpp108
-rw-r--r--Src/Wasabi/api/timer/timerclient.h183
-rw-r--r--Src/Wasabi/api/timer/timeslicer.cpp62
-rw-r--r--Src/Wasabi/api/timer/timeslicer.h64
7 files changed, 519 insertions, 0 deletions
diff --git a/Src/Wasabi/api/timer/api_timer.h b/Src/Wasabi/api/timer/api_timer.h
new file mode 100644
index 00000000..4bc8cb81
--- /dev/null
+++ b/Src/Wasabi/api/timer/api_timer.h
@@ -0,0 +1,47 @@
+#ifndef __TIMER_API_H
+#define __TIMER_API_H
+
+// Under linux, the Timer API requires the Linux API
+
+#include <bfc/dispatch.h>
+#include <bfc/platform/platform.h>
+
+class TimerClient;
+
+#ifdef _WIN32
+typedef UINT_PTR TimerToken ;
+#elif defined(__APPLE__)
+typedef EventLoopTimerRef TimerToken;
+#else
+#error port me!
+#endif
+
+class timer_api : public Dispatchable
+{
+ public:
+ TimerToken timer_add(TimerClient *client, intptr_t id, int ms);
+ void timer_remove(TimerClient *client, TimerToken token);
+
+ enum {
+ TIMER_API_ADD = 1,
+ TIMER_API_REMOVE = 11,
+ };
+};
+
+inline TimerToken timer_api::timer_add(TimerClient *client, intptr_t id, int ms)
+{
+ return _call(TIMER_API_ADD, (TimerToken)0, client, id, ms);
+}
+
+inline void timer_api::timer_remove(TimerClient *client, TimerToken token)
+{
+ _voidcall(TIMER_API_REMOVE, client, token);
+}
+
+// {3130D81C-AE1F-4954-9765-698473B627B0}
+static const GUID timerApiServiceGuid =
+{ 0x3130d81c, 0xae1f, 0x4954, { 0x97, 0x65, 0x69, 0x84, 0x73, 0xb6, 0x27, 0xb0 } };
+
+extern timer_api *timerApi;
+
+#endif \ No newline at end of file
diff --git a/Src/Wasabi/api/timer/osx_timer.cpp b/Src/Wasabi/api/timer/osx_timer.cpp
new file mode 100644
index 00000000..450c58e1
--- /dev/null
+++ b/Src/Wasabi/api/timer/osx_timer.cpp
@@ -0,0 +1,38 @@
+#include "osx_timer.h"
+#include <api/timer/timerclient.h>
+
+timer_api *timerApi = NULL;
+
+
+TimerApi::TimerApi()
+{
+ mainEventLoop = GetMainEventLoop();
+}
+
+static void WasabiTimerProc(EventLoopTimerRef inTimer, void * inUserData)
+{
+ TimerClient *client = (TimerClient *)inUserData;
+ if (client)
+ client->timerclient_timerCallback(inTimer);
+}
+
+
+TimerToken TimerApi::timer_add(TimerClient *client, int id, int ms)
+{
+ EventLoopTimerRef token;
+ OSStatus err = InstallEventLoopTimer(mainEventLoop,
+ (float)ms/1000.0f,
+ (float)ms/1000.0f,
+ WasabiTimerProc,
+ client,
+ &token);
+ if (err == noErr)
+ return token;
+ else
+ return 0;
+}
+
+void TimerApi::timer_remove(TimerClient *client, TimerToken token)
+{
+ RemoveEventLoopTimer(token);
+}
diff --git a/Src/Wasabi/api/timer/osx_timer.h b/Src/Wasabi/api/timer/osx_timer.h
new file mode 100644
index 00000000..9084ef3a
--- /dev/null
+++ b/Src/Wasabi/api/timer/osx_timer.h
@@ -0,0 +1,17 @@
+#ifndef NULLSOFT_WASABI_OSX_TIMER_H
+#define NULLSOFT_WASABI_OSX_TIMER_H
+
+#include <api/timer/api_timer.h>
+
+class TimerApi : public timer_apiI
+{
+public:
+ TimerApi();
+ TimerToken timer_add(TimerClient *client, int id, int ms);
+ void timer_remove(TimerClient *client, TimerToken);
+private:
+ EventLoopRef mainEventLoop;
+};
+
+
+#endif \ No newline at end of file
diff --git a/Src/Wasabi/api/timer/timerclient.cpp b/Src/Wasabi/api/timer/timerclient.cpp
new file mode 100644
index 00000000..e44e03ce
--- /dev/null
+++ b/Src/Wasabi/api/timer/timerclient.cpp
@@ -0,0 +1,108 @@
+#include "api.h"
+#include <api/timer/api_timer.h>
+#include <api/timer/timerclient.h>
+
+#define CBCLASS TimerClientI
+START_DISPATCH;
+// this one doesn't map directly so that we can catch deferredcb timers
+VCB(TIMERCLIENT_TIMERCALLBACK, timerclient_handleDeferredCallback);
+CB(TIMERCLIENT_GETMASTERCLIENT, timerclient_getMasterClient);
+VCB(TIMERCLIENT_ONMASTERMUX, timerclient_onMasterClientMultiplex);
+CB(TIMERCLIENT_GETDEPPTR, timerclient_getDependencyPtr);
+CB(TIMERCLIENT_GETSKIPPED, timerclient_getSkipped);
+VCB(TIMERCLIENT_SETSKIPPED, timerclient_setSkipped);
+VCB(TIMERCLIENT_POSTDEFERREDCB , timerclient_postDeferredCallback);
+CB(TIMERCLIENT_ONDEFERREDCB , timerclient_onDeferredCallback);
+CB(TIMERCLIENT_GETNAME, timerclient_getName);
+END_DISPATCH;
+
+TimerClientI::TimerClientI()
+ : skipped(0), timerdelay(0), disallowset(0)
+{ }
+
+TimerClientI::~TimerClientI()
+{
+ disallowset = 1;
+ if (cbs.getNumItems() > 0)
+ {
+ TimerToken token;
+ if (tokens.reverseGetItem(DEFERREDCB_TIMERID, &token)) // TODO: it would be nice to have a combo get/del, so we don't have to search twice
+ {
+ WASABI_API_TIMER->timer_remove(this, token);
+ tokens.delItem(token);
+ }
+ }
+ cbs.deleteAll();
+}
+
+int TimerClientI::timerclient_setTimer(intptr_t id, int ms)
+{
+ if (disallowset) return 0;
+ ASSERTPR(id > 0, "A timer id cannot be <= 0");
+ ASSERTPR(id != DEFERREDCB_TIMERID, "please chose another timer id");
+ TimerToken token = WASABI_API_TIMER->timer_add(this, id, ms);
+ tokens.addItem(token, id);
+ return 1;
+}
+
+int TimerClientI::timerclient_killTimer(intptr_t id)
+{
+ TimerToken token;
+ if (tokens.reverseGetItem(id, &token)) // TODO: it would be nice to have a combo get/del, so we don't have to search twice
+ {
+ WASABI_API_TIMER->timer_remove(this, token);
+ tokens.delItem(token);
+ }
+ return 1;
+}
+
+void TimerClientI::timerclient_postDeferredCallback(intptr_t param1, intptr_t param2, int mindelay)
+{
+ if (disallowset) return ;
+ for (int i = 0;i < cbs.getNumItems();i++)
+ {
+ deferred_callback *cb = cbs.enumItem(i);
+ if (cb->param1 == param1 && cb->param2 == param2)
+ {
+ cbs.removeByPos(i);
+ break;
+ }
+ }
+ deferred_callback *c = new deferred_callback;
+ c->origin = this;
+ c->param1 = param1;
+ c->param2 = param2;
+ cbs.addItem(c);
+ TimerToken token = WASABI_API_TIMER->timer_add(this, DEFERREDCB_TIMERID, MAX(1, mindelay));
+ tokens.addItem(token, DEFERREDCB_TIMERID);
+}
+
+void TimerClientI::timerclient_handleDeferredCallback(TimerToken token)
+{
+ // let deriving class handle it. note we call into here even if
+ // it's the deferred cb id, since older versions did that too,
+ // expecting the user to call down on the method.
+
+#ifdef WIN64
+ LPARAM id;
+#else
+ intptr_t id;
+#endif
+ if (tokens.getItem(token, &id))
+ {
+ timerclient_timerCallback((int)id);
+
+ // process our deferred cb id
+ if (id == DEFERREDCB_TIMERID)
+ {
+ WASABI_API_TIMER->timer_remove(this, token);
+ PtrList<deferred_callback> temp(cbs);
+ cbs.removeAll();
+ foreach(temp)
+ deferred_callback *c = temp.getfor();
+ c->origin->timerclient_onDeferredCallback(c->param1, c->param2);
+ delete c;
+ endfor
+ }
+ }
+}
diff --git a/Src/Wasabi/api/timer/timerclient.h b/Src/Wasabi/api/timer/timerclient.h
new file mode 100644
index 00000000..52860a01
--- /dev/null
+++ b/Src/Wasabi/api/timer/timerclient.h
@@ -0,0 +1,183 @@
+#ifndef __TIMER_CLIENT_H
+#define __TIMER_CLIENT_H
+
+#include <bfc/dispatch.h>
+#include <bfc/common.h>
+#include <bfc/depend.h>
+#include <map>
+
+#define DEFERREDCB_TIMERID -2
+
+class TimerClient;
+
+#ifdef _WIN32
+typedef UINT_PTR TimerToken ;
+#elif defined(__APPLE__)
+typedef EventLoopTimerRef TimerToken;
+#else
+#error port me!
+#endif
+
+typedef struct {
+ TimerClient *origin;
+ intptr_t param1;
+ intptr_t param2;
+} deferred_callback;
+
+class NOVTABLE TimerClient : public Dispatchable
+{
+protected:
+ TimerClient() { }
+
+public:
+ int timerclient_setTimer(int id, int ms);
+ int timerclient_killTimer(int id);
+ void timerclient_postDeferredCallback(intptr_t param1, intptr_t param2=0, int mindelay=0);
+ int timerclient_onDeferredCallback(intptr_t param1, intptr_t param2);
+ void timerclient_timerCallback(TimerToken token);
+
+ TimerClient *timerclient_getMasterClient();
+ void timerclient_onMasterClientMultiplex();
+ api_dependent *timerclient_getDependencyPtr();
+ void timerclient_setSkipped(int s);
+ int timerclient_getSkipped();
+ void timerclient_setTimerDelay(int td);
+ int timerclient_getTimerDelay();
+ const wchar_t *timerclient_getName();
+
+ enum {
+ TIMERCLIENT_TIMERCALLBACK = 101,
+ TIMERCLIENT_SETTIMER = 110,
+ TIMERCLIENT_KILLTIMER = 120,
+ TIMERCLIENT_GETMASTERCLIENT = 130,
+ TIMERCLIENT_ONMASTERMUX = 140,
+ TIMERCLIENT_GETDEPPTR = 150,
+ TIMERCLIENT_SETSKIPPED = 160,
+ TIMERCLIENT_GETSKIPPED = 170,
+ TIMERCLIENT_SETTIMERDELAY = 180,
+ TIMERCLIENT_GETTIMERDELAY = 190,
+ TIMERCLIENT_POSTDEFERREDCB = 200,
+ TIMERCLIENT_ONDEFERREDCB = 210,
+ TIMERCLIENT_GETNAME = 220,
+ };
+};
+
+inline void TimerClient::timerclient_timerCallback(TimerToken token) {
+ _voidcall(TIMERCLIENT_TIMERCALLBACK, token);
+}
+
+inline int TimerClient::timerclient_setTimer(int id, int ms) {
+ return _call(TIMERCLIENT_SETTIMER, 0, id, ms);
+}
+
+inline int TimerClient::timerclient_killTimer(int id) {
+ return _call(TIMERCLIENT_KILLTIMER, 0, id);
+}
+
+inline TimerClient *TimerClient::timerclient_getMasterClient() {
+ return _call(TIMERCLIENT_GETMASTERCLIENT, (TimerClient *)NULL);
+}
+
+inline void TimerClient::timerclient_onMasterClientMultiplex() {
+ _voidcall(TIMERCLIENT_ONMASTERMUX);
+}
+
+inline api_dependent *TimerClient::timerclient_getDependencyPtr() {
+ return _call(TIMERCLIENT_GETDEPPTR, (api_dependent *)NULL);
+}
+
+inline void TimerClient::timerclient_setSkipped(int s) {
+ _voidcall(TIMERCLIENT_SETSKIPPED, s);
+}
+
+inline int TimerClient::timerclient_getSkipped() {
+ return _call(TIMERCLIENT_GETSKIPPED, 0);
+}
+
+inline void TimerClient::timerclient_setTimerDelay(int td) {
+ _voidcall(TIMERCLIENT_SETTIMERDELAY, td);
+}
+
+inline int TimerClient::timerclient_getTimerDelay() {
+ return _call(TIMERCLIENT_GETTIMERDELAY, 0);
+}
+
+inline void TimerClient::timerclient_postDeferredCallback(intptr_t param1, intptr_t param2, int mindelay) {
+ _voidcall(TIMERCLIENT_POSTDEFERREDCB, param1, param2, mindelay);
+}
+
+inline int TimerClient::timerclient_onDeferredCallback(intptr_t param1, intptr_t param2) {
+ return _call(TIMERCLIENT_ONDEFERREDCB, 0, param1, param2);
+}
+
+inline const wchar_t *TimerClient::timerclient_getName() {
+ return _call(TIMERCLIENT_GETNAME, (const wchar_t *)NULL);
+}
+
+class NOVTABLE TimerClientI : public TimerClient {
+protected:
+ TimerClientI();
+
+public:
+ virtual ~TimerClientI();
+
+ virtual int timerclient_setTimer(intptr_t id, int ms);
+ virtual int timerclient_killTimer(intptr_t id);
+
+ // override this to catch your timer events
+ virtual void timerclient_timerCallback(int id) { }
+
+ virtual TimerClient *timerclient_getMasterClient() { return NULL; }
+ virtual void timerclient_onMasterClientMultiplex() { };
+ virtual api_dependent *timerclient_getDependencyPtr()=0;
+ virtual void timerclient_setSkipped(int s) { skipped = s; }
+ virtual int timerclient_getSkipped() { return skipped; }
+ virtual void timerclient_setTimerDelay(int td) { timerdelay = td; }
+ virtual int timerclient_getTimerDelay() { return timerdelay; }
+ virtual void timerclient_postDeferredCallback(intptr_t param1, intptr_t param2=0, int mindelay=0);
+ virtual int timerclient_onDeferredCallback(intptr_t param1, intptr_t param2) { return 1; };
+ virtual const wchar_t *timerclient_getName() { return NULL; }
+
+protected:
+ RECVS_DISPATCH;
+
+private:
+ virtual void timerclient_handleDeferredCallback(TimerToken token);
+
+ int skipped;
+ int timerdelay;
+ int disallowset;
+ PtrList<deferred_callback> cbs;
+#ifdef _WIN32
+ class TokenMap
+ {
+ public:
+ void delItem(TimerToken) {}
+ bool reverseGetItem(intptr_t id, TimerToken *token)
+ {
+ *token = id;
+ return true;
+ }
+ bool getItem(TimerToken token, intptr_t *id)
+ {
+ *id = token;
+ return true;
+ }
+ void addItem(TimerToken, intptr_t) {}
+ };
+#else
+ typedef std::map<TimerToken, intptr_t> TokenMap;
+#endif
+ TokenMap tokens;
+};
+
+class NOVTABLE TimerClientDI : public TimerClientI, public DependentI {
+protected:
+ TimerClientDI() { }
+
+public:
+ api_dependent *timerclient_getDependencyPtr() { return this; }
+};
+
+
+#endif
diff --git a/Src/Wasabi/api/timer/timeslicer.cpp b/Src/Wasabi/api/timer/timeslicer.cpp
new file mode 100644
index 00000000..04d37421
--- /dev/null
+++ b/Src/Wasabi/api/timer/timeslicer.cpp
@@ -0,0 +1,62 @@
+#include "precomp.h"
+#include "timeslicer.h"
+
+#define TIMER_SLICE 0x7816
+
+TimeSlicer::TimeSlicer(int percent_cpu_usage/* =50 */, int slice_duration/* =-1 */) {
+ max_cpu_usage = MIN((float)percent_cpu_usage, 99.0f) / 100.0f;
+ duration = slice_duration;
+ started = 0;
+ slicecount = 0;
+ firstslicetime = -1;
+}
+
+TimeSlicer::~TimeSlicer() {
+}
+
+void TimeSlicer::startSlicer() {
+ if (started) return;
+ started = 1;
+ timerclient_setTimer(TIMER_SLICE, duration);
+ onSlicerStart();
+ timerclient_timerCallback(TIMER_SLICE);
+}
+
+void TimeSlicer::stopSlicer() {
+ if (!started) return;
+ started = 0;
+ timerclient_killTimer(TIMER_SLICE);
+ onSlicerStop();
+ firstslicetime = -1;
+}
+
+void TimeSlicer::onSlicerStop() {
+}
+
+void TimeSlicer::onSlicerStart() {
+}
+
+int TimeSlicer::isSlicerStarted() {
+ return started;
+}
+
+void TimeSlicer::timerclient_timerCallback(int id) {
+ if (id == TIMER_SLICE) {
+ DWORD now = Std::getTickCount();
+ runSlice(now, now + (int)((float)duration * max_cpu_usage));
+ } else
+ TimerClient::timerclient_timerCallback(id);
+}
+
+void TimeSlicer::runSlice(DWORD start, DWORD stopwhen) {
+ DWORD now = Std::getTickCount();
+ slicecount = 0;
+ onBeginSliceBatch();
+ if (slicecount == 0 && firstslicetime != -1) stopwhen = now + firstslicetime;
+ while (Std::getTickCount() < stopwhen) {
+ onSlice();
+ slicecount++;
+ if (!started) break; // got aborted
+ }
+ onEndSliceBatch();
+}
diff --git a/Src/Wasabi/api/timer/timeslicer.h b/Src/Wasabi/api/timer/timeslicer.h
new file mode 100644
index 00000000..06ae67f4
--- /dev/null
+++ b/Src/Wasabi/api/timer/timeslicer.h
@@ -0,0 +1,64 @@
+#ifndef __TIMESLICER_H
+#define __TIMESLICER_H
+
+// TimeSlicer allows you to create a background job to perform while not blocking
+// the main GUI thread. You give it the max percentage of CPU it should use, call star()
+// and it'll start calling onSlice as many times as it can without using more cpu than requested
+//
+// To use this class, you need to break down your job into multiple tiny chunks that
+// you perform in onSlice. Typical uses include adding files to or filtering entries from
+// the database, driving state machines, etc.
+//
+// onSlice will be called multiple times per timer.
+
+#include "timerclient.h"
+
+enum {
+ GRANULARITY_EXTRALOW = 20,
+ GRANULARITY_LOW = 50,
+ GRANULARITY_MEDIUM = 100,
+ GRANULARITY_HIGH = 250,
+ GRANULARITY_EXTRAHIGH = 1000,
+};
+
+class TimeSlicer : public TimerClientI {
+ public:
+
+ TimeSlicer(int percent_cpu_usage=25, int slice_duration=GRANULARITY_LOW);
+ virtual ~TimeSlicer();
+
+ virtual void timerclient_timerCallback(int id);
+
+ void startSlicer();
+ void stopSlicer();
+ int isSlicerStarted();
+
+ virtual void onSlicerStart();
+ virtual void onSlicerStop();
+ virtual void onBeginSliceBatch() {}
+ virtual void onEndSliceBatch() {}
+ api_dependent *timerclient_getDependencyPtr() { return timeslicer_getDependencyPtr(); }
+ virtual api_dependent *timeslicer_getDependencyPtr()=0;
+ virtual void setFirstSliceMinTime(int ms) { firstslicetime = ms; }
+ virtual int getSliceCount() { return slicecount; }
+
+
+ // override this to do your work
+ virtual void onSlice() { }
+
+ private:
+
+ virtual void runSlice(DWORD start, DWORD stopwhen);
+ float max_cpu_usage;
+ int duration;
+ int started;
+ int firstslicetime;
+ int slicecount;
+};
+
+class TimeSlicerD : public TimeSlicer, public DependentI {
+ public:
+ virtual api_dependent *timeslicer_getDependencyPtr() { return this; }
+};
+
+#endif \ No newline at end of file