1 #include "CtrlCore.h"
2 
3 namespace Upp {
4 
5 // #define LOG_QUEUE
6 #define LLOG(x) // LOG(x)
7 
8 int MemoryProbeInt;
9 
10 struct TimeEvent : public Link<TimeEvent> {
11 	dword      time;
12 	int        delay;
13 	Event<>    cb;
14 	void      *id;
15 	bool       rep;
16 };
17 
18 static dword sTClick;
19 
20 static StaticCriticalSection sTimerLock;
21 
22 struct CtrlTimerOwner__ : public LinkOwner<TimeEvent> {
23 	CtrlTimerOwner__();
24 	~CtrlTimerOwner__();
25 };
26 
tevents()27 static TimeEvent *tevents() {
28 	static LinkOwner<TimeEvent> t;
29 	return t.GetPtr();
30 }
31 
sTimeCallback(dword time,int delay,Event<> cb,void * id)32 static void sTimeCallback(dword time, int delay, Event<>  cb, void *id) {
33 	TimeEvent *ne = tevents()->InsertPrev();
34 	ne->time = time;
35 	ne->cb = cb;
36 	ne->delay = delay;
37 	ne->id = id;
38 	ne->rep = false;
39 	LLOG("sTimeCalllback " << ne->time << " " << ne->delay << " " << ne->id);
40 }
41 
SetTimeCallback(int delay_ms,Function<void ()> cb,void * id)42 void SetTimeCallback(int delay_ms, Function<void ()> cb, void *id) {
43 	Mutex::Lock __(sTimerLock);
44 	ASSERT(abs(delay_ms) < 0x40000000);
45 	LLOG("SetTimeCallback " << delay_ms << " " << id);
46 	sTimeCallback(msecs() + abs(delay_ms), delay_ms, Event<> () << cb, id);
47 }
48 
KillTimeCallbacks(void * id,void * idlim)49 void KillTimeCallbacks(void *id, void *idlim) {
50 	Mutex::Lock __(sTimerLock);
51 	TimeEvent *list = tevents();
52 	for(TimeEvent *e = list->GetNext(); e != list;)
53 		if(e->id >= id && e->id < idlim) {
54 			e = e->GetNext();
55 			delete e->GetPrev();
56 		}
57 		else
58 			e = e->GetNext();
59 }
60 
61 EXITBLOCK
62 {
63 	Mutex::Lock __(sTimerLock);
64 	while(tevents()->GetNext() != tevents())
65 		delete tevents()->GetNext();
66 }
67 
68 bool ExistsTimeCallback(void *id) {
69 	Mutex::Lock __(sTimerLock);
70 	TimeEvent *list = tevents();
71 	for(TimeEvent *e = list->GetNext(); e != list; e = e->GetNext())
72 		if(e->id == id)
73 			return true;
74 	return false;
75 }
76 
KillTimeCallback(void * id)77 void KillTimeCallback(void *id) {
78 	KillTimeCallbacks(id, (byte *)id + 1);
79 }
80 
TimerProc(dword time)81 void Ctrl::TimerProc(dword time)
82 {
83 	if(IsPanicMode())
84 		return;
85 	sTimerLock.Enter();
86 	TimeEvent *list = tevents();
87 	if(time == sTClick) {
88 		sTimerLock.Leave();
89 		return;
90 	}
91 	sTClick = time;
92 	sTimerLock.Leave();
93 	Ctrl::CheckMouseCtrl();
94 	Ctrl::SyncCaret();
95 	sTimerLock.Enter();
96 
97 	#ifdef LOG_QUEUE
98 		LLOG("--- Timer queue at " << time);
99 		for(TimeEvent *e = list->GetNext(); e != list; e = e->GetNext())
100 			LLOG("TP " << e->time << " " << e->delay << " " << e->id << " " << e->rep);
101 		LLOG("----");
102 	#endif
103 
104 	for(;;) {
105 		TimeEvent *todo = NULL;
106 		int maxtm = -1;
107 		for(TimeEvent *e = list->GetNext(); e != list; e = e->GetNext()) {
108 			int tm = (int)(time - e->time);
109 			if(!e->rep && tm >= 0 && tm > maxtm) {
110 				maxtm = tm;
111 				todo = e;
112 			}
113 		}
114 		if(!todo)
115 			break;
116 		LLOG("Performing " << todo->time << " " << todo->delay << " " << todo->id);
117 		Event<>  cb = todo->cb;
118 		if(todo->delay < 0)
119 			todo->rep = true;
120 		else
121 			delete todo;
122 		sTimerLock.Leave();
123 		cb();
124 		sTimerLock.Enter();
125 	}
126 	time = msecs();
127 	LLOG("--- Rescheduling at " << time);
128 	TimeEvent *e = list->GetNext();
129 	while(e != list) {
130 		TimeEvent *w = e;
131 		e = e->GetNext();
132 		if(w->rep) {
133 			LLOG("Rescheduling " << e->id);
134 			sTimeCallback(time - w->delay, w->delay, w->cb, w->id);
135 			delete w;
136 		}
137 	}
138 	LLOG("----");
139 	sTimerLock.Leave();
140 }
141 
InitTimer()142 void  Ctrl::InitTimer()
143 {
144 	Mutex::Lock __(sTimerLock);
145 	tevents();
146 }
147 
SetTimeCallback(int delay_ms,Function<void ()> cb,int id)148 void  Ctrl::SetTimeCallback(int delay_ms, Function<void ()> cb, int id) {
149 	ASSERT(id >= 0 && (size_t)id < (int)sizeof(Ctrl));
150 	UPP::SetTimeCallback(delay_ms, cb, (byte *)this + id);
151 }
152 
KillTimeCallback(int id)153 void  Ctrl::KillTimeCallback(int id) {
154 	ASSERT(id >= 0 && (size_t)id < sizeof(Ctrl));
155 	UPP::KillTimeCallback((byte *)this + id);
156 }
157 
KillSetTimeCallback(int delay_ms,Function<void ()> cb,int id)158 void  Ctrl::KillSetTimeCallback(int delay_ms, Function<void ()> cb, int id)
159 {
160 	KillTimeCallback(id);
161 	SetTimeCallback(delay_ms, cb, id);
162 }
163 
PostCallback(Function<void ()> cb,int id)164 void  Ctrl::PostCallback(Function<void ()> cb, int id)
165 {
166 	SetTimeCallback(0, cb, id);
167 }
168 
KillPostCallback(Function<void ()> cb,int id)169 void  Ctrl::KillPostCallback(Function<void ()> cb, int id)
170 {
171 	KillSetTimeCallback(0, cb, id);
172 }
173 
ExistsTimeCallback(int id) const174 bool  Ctrl::ExistsTimeCallback(int id) const {
175 	ASSERT(id >= 0 && (size_t)id < sizeof(Ctrl));
176 	return UPP::ExistsTimeCallback((byte *)this + id);
177 }
178 
GetTimeClick()179 dword GetTimeClick()
180 {
181 	return sTClick;
182 }
183 
184 }
185