]> git.vomp.tv Git - vompclient.git/blob - boxstack.cc
Some MVP compile fixes and some RBP clear deinitialisation code
[vompclient.git] / boxstack.cc
1 /*\r
2     Copyright 2004-2005 Chris Tallon\r
3 \r
4     This file is part of VOMP.\r
5 \r
6     VOMP is free software; you can redistribute it and/or modify\r
7     it under the terms of the GNU General Public License as published by\r
8     the Free Software Foundation; either version 2 of the License, or\r
9     (at your option) any later version.\r
10 \r
11     VOMP is distributed in the hope that it will be useful,\r
12     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14     GNU General Public License for more details.\r
15 \r
16     You should have received a copy of the GNU General Public License\r
17     along with VOMP; if not, write to the Free Software\r
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\r
19 */\r
20 \r
21 #include "boxstack.h"\r
22 \r
23 #include "command.h"\r
24 #include "remote.h"\r
25 #include "log.h"\r
26 \r
27 BoxStack* BoxStack::instance = NULL;\r
28 \r
29 BoxStack::BoxStack()\r
30 {\r
31   if (instance) return;\r
32   instance = this;\r
33   initted = 0;\r
34   numBoxes = 0;\r
35 }\r
36 \r
37 BoxStack::~BoxStack()\r
38 {\r
39   instance = NULL;\r
40 }\r
41 \r
42 BoxStack* BoxStack::getInstance()\r
43 {\r
44   return instance;\r
45 }\r
46 \r
47 int BoxStack::init()\r
48 {\r
49   if (initted) return 0;\r
50   initted = 1;\r
51   \r
52 #ifndef WIN32\r
53   pthread_mutex_init(&boxLock, NULL);\r
54 #else\r
55   boxLock = CreateMutex(NULL,FALSE,NULL);\r
56 #endif\r
57 \r
58   return 1;\r
59 }\r
60 \r
61 int BoxStack::shutdown()\r
62 {\r
63   if (!initted) return 0;\r
64 \r
65   // FIXME don't think this can work properly, removeAll leaves the wallpaper there!\r
66   removeAll();\r
67 \r
68   initted = 0;\r
69   return 1;\r
70 }\r
71 \r
72 int BoxStack::add(Boxx* v)\r
73 {\r
74   if (!initted) return 0;\r
75   Log::getInstance()->log("BoxStack", Log::DEBUG, "add called"); \r
76 #ifndef WIN32\r
77   pthread_mutex_lock(&boxLock);\r
78 #else\r
79   WaitForSingleObject(boxLock, INFINITE);\r
80 #endif\r
81   Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for add");  \r
82   \r
83   if (numBoxes == 16)\r
84   { //Error\r
85     Log::getInstance()->log("BoxStack", Log::ERR, "More than 16 boxes! Unlocked for add"); \r
86 #ifndef WIN32\r
87     pthread_mutex_unlock(&boxLock);\r
88 #else\r
89     ReleaseMutex(boxLock);\r
90 #endif\r
91     return 0;\r
92   }\r
93   boxes[numBoxes++] = v;\r
94 \r
95 #ifndef WIN32\r
96   pthread_mutex_unlock(&boxLock);\r
97 #else\r
98   ReleaseMutex(boxLock);\r
99 #endif\r
100 \r
101   Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for add");\r
102   \r
103   return 1;\r
104 }\r
105 \r
106 // ---------------------------------------------------- REMOVE CODE\r
107 \r
108 int BoxStack::remove(Boxx* toDelete)\r
109 {\r
110   if (!initted) return 0;\r
111 \r
112   #ifndef WIN32\r
113   pthread_mutex_lock(&boxLock);\r
114   #else\r
115   WaitForSingleObject(boxLock, INFINITE);\r
116   #endif\r
117   Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for remove");  \r
118   \r
119   if (numBoxes == 0)\r
120   {\r
121     #ifndef WIN32\r
122     pthread_mutex_unlock(&boxLock);\r
123     #else\r
124     ReleaseMutex(boxLock);\r
125     #endif\r
126     Log::getInstance()->log("BoxStack", Log::ERR, "Unlocked for remove numBoxes == 0");  \r
127     return 0;\r
128   }\r
129 \r
130 //  Log::getInstance()->log("BoxStack", Log::DEBUG, "entering remove, numBoxes=%i", numBoxes);\r
131 \r
132   int i;\r
133 \r
134   if (toDelete == NULL)\r
135   {\r
136     toDelete = boxes[numBoxes-1];\r
137     i = numBoxes - 1;\r
138   }\r
139   else\r
140   {\r
141     // to be deleted box is more likely to be at the top\r
142     for (i = numBoxes-1; i >= 0; i--)\r
143     {\r
144 //      Log::getInstance()->log("BoxStack", Log::DEBUG, "todel: %p, i=%i, boxes[i]=%p", toDelete, i, boxes[i]);\r
145       if (boxes[i] == toDelete) break;\r
146     }\r
147 \r
148     if (i == -1)\r
149     {\r
150       // not a Box we have!\r
151       // FIXME\r
152       #ifndef WIN32\r
153       pthread_mutex_unlock(&boxLock);\r
154       #else\r
155       ReleaseMutex(boxLock);\r
156       #endif\r
157       Log::getInstance()->log("BoxStack", Log::ERR, "Unlocked for remove - no boxx deleted");  \r
158       return 0;\r
159     }\r
160   }\r
161 \r
162 #ifndef WIN32\r
163   pthread_mutex_unlock(&boxLock);\r
164 #else\r
165   ReleaseMutex(boxLock);\r
166 #endif\r
167 \r
168 toDelete->preDelete();\r
169 \r
170 #ifndef WIN32\r
171   pthread_mutex_lock(&boxLock);\r
172 #else\r
173   WaitForSingleObject(boxLock, INFINITE);\r
174 #endif\r
175 \r
176 //  Log::getInstance()->log("BoxStack", Log::DEBUG, "Starting deleteBox");\r
177   deleteBox(i);\r
178 //  Log::getInstance()->log("BoxStack", Log::DEBUG, "Done deleteBox");\r
179 \r
180   // Shift the boxes on top down one\r
181   --numBoxes;\r
182   for(int j = i; j < numBoxes; j++) boxes[j] = boxes[j+1];\r
183 \r
184   // If there is only the wallpaper left signal command\r
185   if (numBoxes == 1)\r
186   {\r
187     Message* m = new Message();\r
188     m->to = Command::getInstance();\r
189     m->message = Message::LAST_VIEW_CLOSE;\r
190     Command::getInstance()->postMessageNoLock(m);\r
191   }\r
192 \r
193 #ifndef WIN32\r
194   pthread_mutex_unlock(&boxLock);\r
195 #else\r
196   ReleaseMutex(boxLock);\r
197 #endif\r
198   Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for remove");  \r
199  \r
200   // Delete the box\r
201   //AVO: do this delete outside the lock to allow for recursive calls within the destructor\r
202   //     as this box is not in the stack any more, there is no chance for a second delete\r
203   Log::getInstance()->log("BoxStack", Log::DEBUG, "remove: going to delete boxx %p, num %d", toDelete, numBoxes);  \r
204   delete toDelete;\r
205 \r
206   return 1;\r
207 }\r
208 \r
209 /////////////////////////////////////////////////////////////////////////////\r
210 // NEW STUFF\r
211 /////////////////////////////////////////////////////////////////////////////\r
212 \r
213 void BoxStack::deleteBox(int z)\r
214 {\r
215 //  Log::getInstance()->log("BoxStack", Log::DEBUG, "Delete box %i of %i", z, numBoxes);\r
216   RegionList rl;\r
217   boxSplit(boxes[z]->area, z + 1, numBoxes, 1, rl);\r
218   while(!rl.empty())\r
219   {\r
220     repaintRevealed(z, rl.front());\r
221     rl.pop_front();\r
222   }\r
223 }\r
224 \r
225 void BoxStack::update(Boxx* toUpdate, Region* regionToUpdate)\r
226 {\r
227   Log::getInstance()->log("BoxStack", Log::DEBUG, "Update called");\r
228   if (!initted) return; // it is allowed to call this before init\r
229 #ifndef WIN32\r
230   pthread_mutex_lock(&boxLock);\r
231 #else\r
232   WaitForSingleObject(boxLock, INFINITE);\r
233 #endif\r
234   Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for update");\r
235 \r
236   // Get the z index of the box\r
237   int z = 0;\r
238   if (toUpdate)\r
239   {\r
240     for (z = 0; z < numBoxes; z++)\r
241     {\r
242       if (boxes[z] == toUpdate) break;\r
243     }\r
244 \r
245     if (z == numBoxes)\r
246     {\r
247       // not a Box we have!\r
248   #ifndef WIN32\r
249       pthread_mutex_unlock(&boxLock);\r
250   #else\r
251       ReleaseMutex(boxLock);\r
252   #endif \r
253       Log::getInstance()->log("BoxStack", Log::ERR, "Unlocked for update! box not inside boxstack");\r
254       return;\r
255     }\r
256   }\r
257   else\r
258   {\r
259     toUpdate = boxes[0];\r
260   }\r
261 \r
262   // get the region for the whole box, could be less than that\r
263   // for smaller updates\r
264 \r
265   Region r = toUpdate->area;\r
266 \r
267   if (regionToUpdate)\r
268   {\r
269     // Can be null if the whole box should be updated\r
270     // If this is given the numbers are relative to the size of the box, not the screen\r
271 \r
272     r.x += regionToUpdate->x;\r
273     r.y += regionToUpdate->y;\r
274     r.w = regionToUpdate->w;\r
275     r.h = regionToUpdate->h;\r
276   }\r
277 \r
278   RegionList rl;\r
279 \r
280   Region r2;\r
281   boxSplit(r, z+1, numBoxes, 1, rl);\r
282   while(!rl.empty())\r
283   {\r
284     r2 = rl.front();\r
285     r2.z = z;\r
286     boxes[z]->blt(r2);\r
287     rl.pop_front();\r
288   }\r
289 \r
290 #ifndef WIN32\r
291   pthread_mutex_unlock(&boxLock);\r
292 #else\r
293   ReleaseMutex(boxLock);\r
294 #endif  \r
295   Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for update");\r
296 }\r
297 \r
298 void BoxStack::repaintRevealed(int x, Region r)\r
299 {\r
300   RegionList rl;\r
301   boxSplit(r, x - 1, -1, -1, rl);\r
302 \r
303   Region r2;\r
304   while(!rl.empty())\r
305   {\r
306     r2 = rl.front();\r
307     boxes[r2.z]->blt(r2);\r
308     rl.pop_front();\r
309   }\r
310 }\r
311 \r
312 void BoxStack::boxSplit(Region r, int start, int end, int direction, RegionList& rl)\r
313 {\r
314 //  printf("Y=  S=%i E=%i D=%i: Boxsplit: %i %i %i %i\n", start, end, direction, r.x, r.y, r.w, r.h);\r
315 \r
316   for(int z = start; z != end; z += direction)\r
317   {\r
318     if (r.overlappedBy(boxes[z]->area))\r
319     {\r
320 //      printf("Z=%i S=%i E=%i D=%i: %i overlaps\n", z, start, end, direction, z);\r
321 \r
322       int top = r.y;\r
323       int btm = r.y2();\r
324 \r
325       if (boxes[z]->area.y > r.y)\r
326       {\r
327 //        printf("Z=%i S=%i E=%i D=%i: Case 1 for %i %i %i %i split by %i: %i %i %i %i\n", z, start, end, direction, r.x, r.y, r.w, r.h, z, boxes[z]->area.x, boxes[z]->area.y, boxes[z]->area.w, boxes[z]->area.h);\r
328         top = boxes[z]->area.y;\r
329         Region newR;\r
330         newR.x = r.x;\r
331         newR.y = r.y;\r
332         newR.w = r.w;\r
333         newR.h = boxes[z]->area.y - r.y;\r
334         boxSplit(newR, z + direction, end, direction, rl);\r
335 \r
336         if (direction == -1)\r
337         {\r
338           Region newR2;\r
339           newR2.x = r.x;\r
340           newR2.y = boxes[z]->area.y;\r
341           newR2.w = r.w;\r
342           newR2.h = r.h - newR.h;\r
343           boxSplit(newR2, z, end, -1, rl);\r
344           return;\r
345         }\r
346       }\r
347 \r
348       if (boxes[z]->area.y2() < r.y2())\r
349       {\r
350 //        printf("Z=%i S=%i E=%i D=%i: Case 2 for %i %i %i %i split by %i: %i %i %i %i\n", z, start, end, direction, r.x, r.y, r.w, r.h, z, boxes[z]->area.x, boxes[z]->area.y, boxes[z]->area.w, boxes[z]->area.h);\r
351         btm = boxes[z]->area.y2();\r
352         Region newR;\r
353         newR.x = r.x;\r
354         newR.y = boxes[z]->area.y2() + 1;\r
355         newR.w = r.w;\r
356         newR.h = r.y2() - newR.y + 1;\r
357         boxSplit(newR, z + direction, end, direction, rl);\r
358 \r
359         if (direction == -1)\r
360         {\r
361           Region newR2;\r
362           newR2.x = r.x;\r
363           newR2.y = r.y;\r
364           newR2.w = r.w;\r
365           newR2.h = r.h - newR.h;\r
366           boxSplit(newR2, z, end, -1, rl);\r
367           return;\r
368         }\r
369       }\r
370 \r
371       if (boxes[z]->area.x > r.x)\r
372       {\r
373 //        printf("Z=%i S=%i E=%i D=%i: Case 3 for %i %i %i %i split by %i: %i %i %i %i\n", z, start, end, direction, r.x, r.y, r.w, r.h, z, boxes[z]->area.x, boxes[z]->area.y, boxes[z]->area.w, boxes[z]->area.h);\r
374         Region newR;\r
375         newR.x = r.x;\r
376         newR.y = top;\r
377         newR.w = boxes[z]->area.x - r.x;\r
378         newR.h = btm - top + 1;\r
379         boxSplit(newR, z + direction, end, direction, rl);\r
380 \r
381         if (direction == -1)\r
382         {\r
383           Region newR2;\r
384           newR2.x = r.x + newR.w;\r
385           newR2.y = r.y;\r
386           newR2.w = r.w - newR.w;\r
387           newR2.h = r.h;\r
388           boxSplit(newR2, z, end, -1, rl);\r
389           return;\r
390         }\r
391       }\r
392 \r
393       if (boxes[z]->area.x2() < r.x2())\r
394       {\r
395 //        printf("Z=%i S=%i E=%i D=%i: Case 4 for %i %i %i %i split by %i: %i %i %i %i\n", z, start, end, direction, r.x, r.y, r.w, r.h, z, boxes[z]->area.x, boxes[z]->area.y, boxes[z]->area.w, boxes[z]->area.h);\r
396         Region newR;\r
397         newR.x = boxes[z]->area.x2() + 1;\r
398         newR.y = top;\r
399         newR.w = r.x2() - newR.x + 1;\r
400         newR.h = btm - top + 1;\r
401         boxSplit(newR, z + direction, end, direction, rl);\r
402 \r
403         if (direction == -1)\r
404         {\r
405           Region newR2;\r
406           newR2.x = r.x;\r
407           newR2.y = r.y;\r
408           newR2.w = r.w - newR.w;\r
409           newR2.h = r.h;\r
410           boxSplit(newR2, z, end, -1, rl);\r
411           return;\r
412         }\r
413       }\r
414 \r
415       if (direction == -1)\r
416       {\r
417         // we are going down the stack\r
418         // r is underlapped by boxes[z]\r
419         // but we have not split\r
420         // Therefore this region under test is\r
421         // completely covering boxes[z]\r
422 \r
423         // don't go any further down, generate a region and quit\r
424 \r
425 //        printf("Repaint region: %i %i %i %i\n", r.x, r.y, r.w, r.h);\r
426         r.z = z;\r
427         rl.push_front(r);\r
428       }\r
429 \r
430 //      printf("Returning from Z=%i\n", z);\r
431       return;\r
432     }\r
433     else\r
434     {\r
435 //      printf("Z=%i S=%i E=%i D=%i: %i does not overlap\n", z, start, end, direction, z);\r
436     }\r
437   }\r
438 \r
439   // if direction = 1 then we have come to a region that is\r
440   // entirely clear of higher boxes and needs to be redrawn\r
441 \r
442   // if direction = -1 then we have come to a region that is on\r
443   // the very bottom with nothing below it to repaint.\r
444   // do nothing. stale window data will be left on screen?\r
445 \r
446   if (direction == 1)\r
447   {\r
448     rl.push_front(r);\r
449   }\r
450 }\r
451 \r
452 /////////////////////////////////////////////////////////////////////////////\r
453 // END NEW STUFF\r
454 /////////////////////////////////////////////////////////////////////////////\r
455 \r
456 // ---------------------------------------------------- END OF REMOVE CODE\r
457 \r
458 \r
459 void BoxStack::removeAll()\r
460 {\r
461   // 1.. Don't delete wallpaper. No point.\r
462 \r
463   // Need locking on this one??\r
464   \r
465   // This is pretty silly now that preDelete needs mutex unlocked\r
466   \r
467   Boxx* toDel = NULL;\r
468   \r
469   while(numBoxes > 1)\r
470   {\r
471     #ifndef WIN32\r
472       pthread_mutex_lock(&boxLock);\r
473     #else\r
474       WaitForSingleObject(boxLock, INFINITE);\r
475     #endif  \r
476 \r
477     if (numBoxes == 1)\r
478     {\r
479       #ifndef WIN32\r
480         pthread_mutex_unlock(&boxLock);\r
481       #else\r
482         ReleaseMutex(boxLock);\r
483       #endif\r
484       break;\r
485     }    \r
486   \r
487     toDel = boxes[numBoxes - 1];\r
488 \r
489     #ifndef WIN32\r
490       pthread_mutex_unlock(&boxLock);\r
491     #else\r
492       ReleaseMutex(boxLock);\r
493     #endif\r
494 \r
495     toDel->preDelete();\r
496     \r
497     #ifndef WIN32\r
498       pthread_mutex_lock(&boxLock);\r
499     #else\r
500       WaitForSingleObject(boxLock, INFINITE);\r
501     #endif  \r
502 \r
503     // If boxes[numBoxes - 1] isn't toDel then there's a problem\r
504     if (boxes[numBoxes - 1] == toDel)\r
505     {\r
506       --numBoxes;\r
507     }\r
508     else\r
509     {\r
510       Log::getInstance()->log("BoxStack", Log::ERR, "Can this actually happen? Why?");\r
511       toDel = NULL;\r
512     }\r
513 \r
514     #ifndef WIN32\r
515       pthread_mutex_unlock(&boxLock);\r
516     #else\r
517       ReleaseMutex(boxLock);\r
518     #endif\r
519 \r
520     //AVO: do the delete outside the lock to allow for recursive deletes\r
521     Log::getInstance()->log("BoxStack", Log::DEBUG, "going to delete boxx %p, num=%d", toDel, numBoxes);\r
522     if (toDel) delete toDel;\r
523   }\r
524 }\r
525 \r
526 int BoxStack::handleCommand(int command)\r
527 {\r
528   int retVal;\r
529   int retVal2 = 0;\r
530   int i;\r
531 \r
532   if (command != Remote::NA_NONE)\r
533   {\r
534     // handle command return values\r
535     // 0 - drop through to next box\r
536     // 1 - dont drop to next box, but not handled\r
537     // 2 - handled - stop command here\r
538     // 4 - handled - delete this box\r
539 \r
540     for (i=numBoxes-1; i>=0; i--)\r
541     {\r
542 //      Log::getInstance()->log("BoxStack", Log::DEBUG, "Giving command to i=%i", i);\r
543       retVal = boxes[i]->handleCommand(command);\r
544       if (retVal == 1)\r
545       {\r
546         // not handled but don't give to any more boxes\r
547         return 0;\r
548       }\r
549 \r
550       if (retVal == 2)\r
551       {\r
552         // command handled\r
553         retVal2 = 1;\r
554         break;\r
555       }\r
556       else if (retVal == 4)\r
557       {\r
558 //        Log::getInstance()->log("BoxStack", Log::DEBUG, "Return 4: i=%i, boxes[i]=%p", i, boxes[i]);\r
559         remove(boxes[i]);\r
560         retVal2 = 1;\r
561         break;\r
562       }\r
563     }\r
564   }\r
565   else\r
566   {\r
567     // fake the return code\r
568     retVal2 = 2;\r
569   }\r
570 \r
571   return retVal2;\r
572 }\r
573 \r
574 void BoxStack::processMessage(Message* m)\r
575 {\r
576   if (m->to != this)\r
577   {\r
578     for (int i = numBoxes-1; i >= 0; i--)\r
579     {\r
580       if (boxes[i] == m->to)\r
581       {\r
582         Log::getInstance()->log("BoxStack", Log::DEBUG, "sending message from box %p to box %p %lu", m->from, m->to, m->message);\r
583         boxes[i]->processMessage(m);\r
584         return;\r
585       }\r
586     }\r
587     return;\r
588   }\r
589 \r
590   /* Handle mouse events*/\r
591   // They come in with m->to = NULL? and just need to be delivered to top box?\r
592   if ((numBoxes > 1) && ((m->message == Message::MOUSE_MOVE) || (m->message == Message::MOUSE_LBDOWN)\r
593                   || (m->message == Message::MOUSE_ANDROID_SCROLL)))\r
594   {\r
595     boxes[numBoxes-1]->processMessage(m);\r
596     return;\r
597   }\r
598 \r
599   Log::getInstance()->log("BoxStack", Log::DEBUG, "it's for meeee!");\r
600 \r
601   switch(m->message)\r
602   {\r
603     case Message::CLOSE_ME:\r
604     {\r
605       remove((Boxx*)m->from);\r
606       break;\r
607     }\r
608     case Message::ADD_VIEW: // currently not used by anything but it might come in useful again\r
609     {\r
610       Boxx* toAdd = (Boxx*)m->parameter;\r
611       add(toAdd);\r
612       toAdd->draw();\r
613       update(toAdd);\r
614       break;\r
615     }\r
616   }\r
617 }\r