]> git.vomp.tv Git - vompclient.git/blob - boxstack.cc
Preparations for dynamic mode switching
[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::redrawAllBoxes()\r
226 {\r
227 #ifndef WIN32\r
228   pthread_mutex_lock(&boxLock);\r
229 #else\r
230   WaitForSingleObject(boxLock, INFINITE);\r
231 #endif\r
232 \r
233   for (int z = 0; z < numBoxes; z++)\r
234   {\r
235           boxes[z]->draw();\r
236   }\r
237 \r
238 \r
239 #ifndef WIN32\r
240     pthread_mutex_unlock(&boxLock);\r
241 #else\r
242     ReleaseMutex(boxLock);\r
243 #endif\r
244     update(NULL,NULL); // should blt all\r
245 }\r
246 \r
247 void BoxStack::update(Boxx* toUpdate, Region* regionToUpdate)\r
248 {\r
249   Log::getInstance()->log("BoxStack", Log::DEBUG, "Update called");\r
250   if (!initted) return; // it is allowed to call this before init\r
251 #ifndef WIN32\r
252   pthread_mutex_lock(&boxLock);\r
253 #else\r
254   WaitForSingleObject(boxLock, INFINITE);\r
255 #endif\r
256   Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for update");\r
257 \r
258   // Get the z index of the box\r
259   int z = 0;\r
260   if (toUpdate)\r
261   {\r
262     for (z = 0; z < numBoxes; z++)\r
263     {\r
264       if (boxes[z] == toUpdate) break;\r
265     }\r
266 \r
267     if (z == numBoxes)\r
268     {\r
269       // not a Box we have!\r
270   #ifndef WIN32\r
271       pthread_mutex_unlock(&boxLock);\r
272   #else\r
273       ReleaseMutex(boxLock);\r
274   #endif \r
275       Log::getInstance()->log("BoxStack", Log::ERR, "Unlocked for update! box not inside boxstack");\r
276       return;\r
277     }\r
278   }\r
279   else\r
280   {\r
281     toUpdate = boxes[0];\r
282   }\r
283 \r
284   if (!toUpdate) {\r
285 #ifndef WIN32\r
286           pthread_mutex_unlock(&boxLock);\r
287 #else\r
288           ReleaseMutex(boxLock);\r
289 #endif\r
290           Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for update, no box present");\r
291           return ;\r
292   }\r
293 \r
294   // get the region for the whole box, could be less than that\r
295   // for smaller updates\r
296 \r
297   Region r = toUpdate->area;\r
298 \r
299   if (regionToUpdate)\r
300   {\r
301     // Can be null if the whole box should be updated\r
302     // If this is given the numbers are relative to the size of the box, not the screen\r
303 \r
304     r.x += regionToUpdate->x;\r
305     r.y += regionToUpdate->y;\r
306     r.w = regionToUpdate->w;\r
307     r.h = regionToUpdate->h;\r
308   }\r
309 \r
310   RegionList rl;\r
311 \r
312   Region r2;\r
313   boxSplit(r, z+1, numBoxes, 1, rl);\r
314   while(!rl.empty())\r
315   {\r
316     r2 = rl.front();\r
317     r2.z = z;\r
318     boxes[z]->blt(r2);\r
319     rl.pop_front();\r
320   }\r
321 \r
322 #ifndef WIN32\r
323   pthread_mutex_unlock(&boxLock);\r
324 #else\r
325   ReleaseMutex(boxLock);\r
326 #endif  \r
327   Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for update");\r
328 }\r
329 \r
330 void BoxStack::repaintRevealed(int x, Region r)\r
331 {\r
332   RegionList rl;\r
333   boxSplit(r, x - 1, -1, -1, rl);\r
334 \r
335   Region r2;\r
336   while(!rl.empty())\r
337   {\r
338     r2 = rl.front();\r
339     boxes[r2.z]->blt(r2);\r
340     rl.pop_front();\r
341   }\r
342 }\r
343 \r
344 void BoxStack::boxSplit(Region r, int start, int end, int direction, RegionList& rl)\r
345 {\r
346 //  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
347 \r
348   for(int z = start; z != end; z += direction)\r
349   {\r
350     if (r.overlappedBy(boxes[z]->area))\r
351     {\r
352 //      printf("Z=%i S=%i E=%i D=%i: %i overlaps\n", z, start, end, direction, z);\r
353 \r
354       int top = r.y;\r
355       int btm = r.y2();\r
356 \r
357       if (boxes[z]->area.y > r.y)\r
358       {\r
359 //        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
360         top = boxes[z]->area.y;\r
361         Region newR;\r
362         newR.x = r.x;\r
363         newR.y = r.y;\r
364         newR.w = r.w;\r
365         newR.h = boxes[z]->area.y - r.y;\r
366         boxSplit(newR, z + direction, end, direction, rl);\r
367 \r
368         if (direction == -1)\r
369         {\r
370           Region newR2;\r
371           newR2.x = r.x;\r
372           newR2.y = boxes[z]->area.y;\r
373           newR2.w = r.w;\r
374           newR2.h = r.h - newR.h;\r
375           boxSplit(newR2, z, end, -1, rl);\r
376           return;\r
377         }\r
378       }\r
379 \r
380       if (boxes[z]->area.y2() < r.y2())\r
381       {\r
382 //        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
383         btm = boxes[z]->area.y2();\r
384         Region newR;\r
385         newR.x = r.x;\r
386         newR.y = boxes[z]->area.y2() + 1;\r
387         newR.w = r.w;\r
388         newR.h = r.y2() - newR.y + 1;\r
389         boxSplit(newR, z + direction, end, direction, rl);\r
390 \r
391         if (direction == -1)\r
392         {\r
393           Region newR2;\r
394           newR2.x = r.x;\r
395           newR2.y = r.y;\r
396           newR2.w = r.w;\r
397           newR2.h = r.h - newR.h;\r
398           boxSplit(newR2, z, end, -1, rl);\r
399           return;\r
400         }\r
401       }\r
402 \r
403       if (boxes[z]->area.x > r.x)\r
404       {\r
405 //        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
406         Region newR;\r
407         newR.x = r.x;\r
408         newR.y = top;\r
409         newR.w = boxes[z]->area.x - r.x;\r
410         newR.h = btm - top + 1;\r
411         boxSplit(newR, z + direction, end, direction, rl);\r
412 \r
413         if (direction == -1)\r
414         {\r
415           Region newR2;\r
416           newR2.x = r.x + newR.w;\r
417           newR2.y = r.y;\r
418           newR2.w = r.w - newR.w;\r
419           newR2.h = r.h;\r
420           boxSplit(newR2, z, end, -1, rl);\r
421           return;\r
422         }\r
423       }\r
424 \r
425       if (boxes[z]->area.x2() < r.x2())\r
426       {\r
427 //        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
428         Region newR;\r
429         newR.x = boxes[z]->area.x2() + 1;\r
430         newR.y = top;\r
431         newR.w = r.x2() - newR.x + 1;\r
432         newR.h = btm - top + 1;\r
433         boxSplit(newR, z + direction, end, direction, rl);\r
434 \r
435         if (direction == -1)\r
436         {\r
437           Region newR2;\r
438           newR2.x = r.x;\r
439           newR2.y = r.y;\r
440           newR2.w = r.w - newR.w;\r
441           newR2.h = r.h;\r
442           boxSplit(newR2, z, end, -1, rl);\r
443           return;\r
444         }\r
445       }\r
446 \r
447       if (direction == -1)\r
448       {\r
449         // we are going down the stack\r
450         // r is underlapped by boxes[z]\r
451         // but we have not split\r
452         // Therefore this region under test is\r
453         // completely covering boxes[z]\r
454 \r
455         // don't go any further down, generate a region and quit\r
456 \r
457 //        printf("Repaint region: %i %i %i %i\n", r.x, r.y, r.w, r.h);\r
458         r.z = z;\r
459         rl.push_front(r);\r
460       }\r
461 \r
462 //      printf("Returning from Z=%i\n", z);\r
463       return;\r
464     }\r
465     else\r
466     {\r
467 //      printf("Z=%i S=%i E=%i D=%i: %i does not overlap\n", z, start, end, direction, z);\r
468     }\r
469   }\r
470 \r
471   // if direction = 1 then we have come to a region that is\r
472   // entirely clear of higher boxes and needs to be redrawn\r
473 \r
474   // if direction = -1 then we have come to a region that is on\r
475   // the very bottom with nothing below it to repaint.\r
476   // do nothing. stale window data will be left on screen?\r
477 \r
478   if (direction == 1)\r
479   {\r
480     rl.push_front(r);\r
481   }\r
482 }\r
483 \r
484 /////////////////////////////////////////////////////////////////////////////\r
485 // END NEW STUFF\r
486 /////////////////////////////////////////////////////////////////////////////\r
487 \r
488 // ---------------------------------------------------- END OF REMOVE CODE\r
489 \r
490 \r
491 void BoxStack::removeAll()\r
492 {\r
493   // 1.. Don't delete wallpaper. No point.\r
494 \r
495   // Need locking on this one??\r
496   \r
497   // This is pretty silly now that preDelete needs mutex unlocked\r
498   \r
499   Boxx* toDel = NULL;\r
500   \r
501   while(numBoxes > 1)\r
502   {\r
503     #ifndef WIN32\r
504       pthread_mutex_lock(&boxLock);\r
505     #else\r
506       WaitForSingleObject(boxLock, INFINITE);\r
507     #endif  \r
508 \r
509     if (numBoxes == 1)\r
510     {\r
511       #ifndef WIN32\r
512         pthread_mutex_unlock(&boxLock);\r
513       #else\r
514         ReleaseMutex(boxLock);\r
515       #endif\r
516       break;\r
517     }    \r
518   \r
519     toDel = boxes[numBoxes - 1];\r
520 \r
521     #ifndef WIN32\r
522       pthread_mutex_unlock(&boxLock);\r
523     #else\r
524       ReleaseMutex(boxLock);\r
525     #endif\r
526 \r
527     toDel->preDelete();\r
528     \r
529     #ifndef WIN32\r
530       pthread_mutex_lock(&boxLock);\r
531     #else\r
532       WaitForSingleObject(boxLock, INFINITE);\r
533     #endif  \r
534 \r
535     // If boxes[numBoxes - 1] isn't toDel then there's a problem\r
536     if (boxes[numBoxes - 1] == toDel)\r
537     {\r
538       --numBoxes;\r
539     }\r
540     else\r
541     {\r
542       Log::getInstance()->log("BoxStack", Log::ERR, "Can this actually happen? Why?");\r
543       toDel = NULL;\r
544     }\r
545 \r
546     #ifndef WIN32\r
547       pthread_mutex_unlock(&boxLock);\r
548     #else\r
549       ReleaseMutex(boxLock);\r
550     #endif\r
551 \r
552     //AVO: do the delete outside the lock to allow for recursive deletes\r
553     Log::getInstance()->log("BoxStack", Log::DEBUG, "going to delete boxx %p, num=%d", toDel, numBoxes);\r
554     if (toDel) delete toDel;\r
555   }\r
556 }\r
557 \r
558 int BoxStack::handleCommand(int command)\r
559 {\r
560   int retVal;\r
561   int retVal2 = 0;\r
562   int i;\r
563 \r
564   if (command != Remote::NA_NONE)\r
565   {\r
566     // handle command return values\r
567     // 0 - drop through to next box\r
568     // 1 - dont drop to next box, but not handled\r
569     // 2 - handled - stop command here\r
570     // 4 - handled - delete this box\r
571 \r
572     for (i=numBoxes-1; i>=0; i--)\r
573     {\r
574 //      Log::getInstance()->log("BoxStack", Log::DEBUG, "Giving command to i=%i", i);\r
575       retVal = boxes[i]->handleCommand(command);\r
576       if (retVal == 1)\r
577       {\r
578         // not handled but don't give to any more boxes\r
579         return 0;\r
580       }\r
581 \r
582       if (retVal == 2)\r
583       {\r
584         // command handled\r
585         retVal2 = 1;\r
586         break;\r
587       }\r
588       else if (retVal == 4)\r
589       {\r
590 //        Log::getInstance()->log("BoxStack", Log::DEBUG, "Return 4: i=%i, boxes[i]=%p", i, boxes[i]);\r
591         remove(boxes[i]);\r
592         retVal2 = 1;\r
593         break;\r
594       }\r
595     }\r
596   }\r
597   else\r
598   {\r
599     // fake the return code\r
600     retVal2 = 2;\r
601   }\r
602 \r
603   return retVal2;\r
604 }\r
605 \r
606 void BoxStack::processMessage(Message* m)\r
607 {\r
608   if (m->to != this)\r
609   {\r
610     for (int i = numBoxes-1; i >= 0; i--)\r
611     {\r
612       if (boxes[i] == m->to)\r
613       {\r
614         Log::getInstance()->log("BoxStack", Log::DEBUG, "sending message from box %p to box %p %lu", m->from, m->to, m->message);\r
615         boxes[i]->processMessage(m);\r
616         return;\r
617       }\r
618     }\r
619     return;\r
620   }\r
621 \r
622   /* Handle mouse events*/\r
623   // They come in with m->to = NULL? and just need to be delivered to top box?\r
624   if ((numBoxes > 1) && ((m->message == Message::MOUSE_MOVE) || (m->message == Message::MOUSE_LBDOWN)\r
625                   || (m->message == Message::MOUSE_ANDROID_SCROLL)))\r
626   {\r
627     boxes[numBoxes-1]->processMessage(m);\r
628     return;\r
629   }\r
630 \r
631   Log::getInstance()->log("BoxStack", Log::DEBUG, "it's for meeee!");\r
632 \r
633   switch(m->message)\r
634   {\r
635     case Message::CLOSE_ME:\r
636     {\r
637       remove((Boxx*)m->from);\r
638       break;\r
639     }\r
640     case Message::ADD_VIEW: // currently not used by anything but it might come in useful again\r
641     {\r
642       Boxx* toAdd = (Boxx*)m->parameter;\r
643       add(toAdd);\r
644       toAdd->draw();\r
645       update(toAdd);\r
646       break;\r
647     }\r
648   }\r
649 }\r