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