]> git.vomp.tv Git - vompclient-marten.git/blob - boxstack.cc
PES Packet object: dynamic sizing
[vompclient-marten.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   
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) return 0;
84   boxes[numBoxes++] = v;
85
86 #ifndef WIN32
87   pthread_mutex_unlock(&boxLock);
88 #else
89   ReleaseMutex(boxLock);
90 #endif
91
92   Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for add");
93   
94   return 1;
95 }
96
97 // ---------------------------------------------------- REMOVE CODE
98
99 int BoxStack::remove(Boxx* toDelete)
100 {
101   if (!initted) return 0;
102
103   toDelete->preDelete();
104
105 #ifndef WIN32
106   pthread_mutex_lock(&boxLock);
107 #else
108   WaitForSingleObject(boxLock, INFINITE);
109 #endif
110   Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for remove");  
111   
112   if (numBoxes == 0)
113   {           // FIXME
114             #ifndef WIN32
115             pthread_mutex_unlock(&boxLock);
116           #else
117             ReleaseMutex(boxLock);
118           #endif
119             Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for remove");  
120               return 0;
121   }
122
123 //  Log::getInstance()->log("BoxStack", Log::DEBUG, "entering remove, numBoxes=%i", numBoxes);
124
125   int i;
126
127   if (toDelete == NULL)
128   {
129     toDelete = boxes[numBoxes-1];
130     i = numBoxes - 1;
131   }
132   else
133   {
134     // to be deleted box is more likely to be at the top
135     for (i = numBoxes-1; i >= 0; i--)
136     {
137 //      Log::getInstance()->log("BoxStack", Log::DEBUG, "todel: %p, i=%i, boxes[i]=%p", toDelete, i, boxes[i]);
138       if (boxes[i] == toDelete) break;
139     }
140
141     if (i == -1)
142     {
143       // not a Box we have!
144         {           // FIXME
145             #ifndef WIN32
146             pthread_mutex_unlock(&boxLock);
147           #else
148             ReleaseMutex(boxLock);
149           #endif
150             Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for remove");  
151               return 0;
152         }
153     }
154   }
155
156 //  Log::getInstance()->log("BoxStack", Log::DEBUG, "Starting deleteBox");
157   deleteBox(i);
158 //  Log::getInstance()->log("BoxStack", Log::DEBUG, "Done deleteBox");
159
160   // Shift the boxes on top down one
161   --numBoxes;
162   for(int j = i; j < numBoxes; j++) boxes[j] = boxes[j+1];
163
164   // Delete the box
165   delete toDelete;
166
167   // If there is only the wallpaper left signal command
168   if (numBoxes == 1)
169   {
170     Message* m = new Message();
171     m->to = Command::getInstance();
172     m->message = Message::LAST_VIEW_CLOSE;
173     Command::getInstance()->postMessageNoLock(m);
174   }
175
176 #ifndef WIN32
177   pthread_mutex_unlock(&boxLock);
178 #else
179   ReleaseMutex(boxLock);
180 #endif
181   Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for remove");  
182
183   return 1;
184 }
185
186 /////////////////////////////////////////////////////////////////////////////
187 // NEW STUFF
188 /////////////////////////////////////////////////////////////////////////////
189
190 void BoxStack::deleteBox(int z)
191 {
192 //  Log::getInstance()->log("BoxStack", Log::DEBUG, "Delete box %i of %i", z, numBoxes);
193   RegionList rl;
194   boxSplit(boxes[z]->area, z + 1, numBoxes, 1, rl);
195   while(!rl.empty())
196   {
197     repaintRevealed(z, rl.front());
198     rl.pop_front();
199   }
200 }
201
202 void BoxStack::update(Boxx* toUpdate, Region* regionToUpdate)
203 {
204 //  Log::getInstance()->log("BoxStack", Log::DEBUG, "Update called");
205   // Get the z index of the box
206
207 #ifndef WIN32
208   pthread_mutex_lock(&boxLock);
209 #else
210   WaitForSingleObject(boxLock, INFINITE);
211 #endif
212   Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for update");
213
214   int z;
215   for (z = 0; z < numBoxes; z++)
216   {
217     if (boxes[z] == toUpdate) break;
218   }
219
220   if (z == numBoxes)
221   {
222     // not a Box we have!
223     return;
224   }
225
226   // get the region for the whole box, could be less than that
227   // for smaller updates
228
229   Region r = toUpdate->area;
230
231   if (regionToUpdate)
232   {
233     // Can be null if the whole box should be updated
234     // If this is given the numbers are relative to the size of the box, not the screen
235
236     r.x += regionToUpdate->x;
237     r.y += regionToUpdate->y;
238     r.w = regionToUpdate->w;
239     r.h = regionToUpdate->h;
240   }
241
242   RegionList rl;
243
244   Region r2;
245   boxSplit(r, z+1, numBoxes, 1, rl);
246   while(!rl.empty())
247   {
248     r2 = rl.front();
249     r2.z = z;
250     boxes[z]->blt(r2);
251     rl.pop_front();
252   }
253   
254 #ifndef WIN32
255   pthread_mutex_unlock(&boxLock);
256 #else
257   ReleaseMutex(boxLock);
258 #endif  
259   Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for update");
260 }
261
262 void BoxStack::repaintRevealed(int x, Region r)
263 {
264   RegionList rl;
265   boxSplit(r, x - 1, -1, -1, rl);
266
267   Region r2;
268   while(!rl.empty())
269   {
270     r2 = rl.front();
271     boxes[r2.z]->blt(r2);
272     rl.pop_front();
273   }
274 }
275
276 void BoxStack::boxSplit(Region r, int start, int end, int direction, RegionList& rl)
277 {
278 //  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);
279
280   for(int z = start; z != end; z += direction)
281   {
282     if (r.overlappedBy(boxes[z]->area))
283     {
284 //      printf("Z=%i S=%i E=%i D=%i: %i overlaps\n", z, start, end, direction, z);
285
286       int top = r.y;
287       int btm = r.y2();
288
289       if (boxes[z]->area.y > r.y)
290       {
291 //        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);
292         top = boxes[z]->area.y;
293         Region newR;
294         newR.x = r.x;
295         newR.y = r.y;
296         newR.w = r.w;
297         newR.h = boxes[z]->area.y - r.y;
298         boxSplit(newR, z + direction, end, direction, rl);
299
300         if (direction == -1)
301         {
302           Region newR2;
303           newR2.x = r.x;
304           newR2.y = boxes[z]->area.y;
305           newR2.w = r.w;
306           newR2.h = r.h - newR.h;
307           boxSplit(newR2, z, end, -1, rl);
308           return;
309         }
310       }
311
312       if (boxes[z]->area.y2() < r.y2())
313       {
314 //        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);
315         btm = boxes[z]->area.y2();
316         Region newR;
317         newR.x = r.x;
318         newR.y = boxes[z]->area.y2() + 1;
319         newR.w = r.w;
320         newR.h = r.y2() - newR.y + 1;
321         boxSplit(newR, z + direction, end, direction, rl);
322
323         if (direction == -1)
324         {
325           Region newR2;
326           newR2.x = r.x;
327           newR2.y = r.y;
328           newR2.w = r.w;
329           newR2.h = r.h - newR.h;
330           boxSplit(newR2, z, end, -1, rl);
331           return;
332         }
333       }
334
335       if (boxes[z]->area.x > r.x)
336       {
337 //        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);
338         Region newR;
339         newR.x = r.x;
340         newR.y = top;
341         newR.w = boxes[z]->area.x - r.x;
342         newR.h = btm - top + 1;
343         boxSplit(newR, z + direction, end, direction, rl);
344
345         if (direction == -1)
346         {
347           Region newR2;
348           newR2.x = r.x + newR.w;
349           newR2.y = r.y;
350           newR2.w = r.w - newR.w;
351           newR2.h = r.h;
352           boxSplit(newR2, z, end, -1, rl);
353           return;
354         }
355       }
356
357       if (boxes[z]->area.x2() < r.x2())
358       {
359 //        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);
360         Region newR;
361         newR.x = boxes[z]->area.x2() + 1;
362         newR.y = top;
363         newR.w = r.x2() - newR.x + 1;
364         newR.h = btm - top + 1;
365         boxSplit(newR, z + direction, end, direction, rl);
366
367         if (direction == -1)
368         {
369           Region newR2;
370           newR2.x = r.x;
371           newR2.y = r.y;
372           newR2.w = r.w - newR.w;
373           newR2.h = r.h;
374           boxSplit(newR2, z, end, -1, rl);
375           return;
376         }
377       }
378
379       if (direction == -1)
380       {
381         // we are going down the stack
382         // r is underlapped by boxes[z]
383         // but we have not split
384         // Therefore this region under test is
385         // completely covering boxes[z]
386
387         // don't go any further down, generate a region and quit
388
389 //        printf("Repaint region: %i %i %i %i\n", r.x, r.y, r.w, r.h);
390         r.z = z;
391         rl.push_front(r);
392       }
393
394 //      printf("Returning from Z=%i\n", z);
395       return;
396     }
397     else
398     {
399 //      printf("Z=%i S=%i E=%i D=%i: %i does not overlap\n", z, start, end, direction, z);
400     }
401   }
402
403   // if direction = 1 then we have come to a region that is
404   // entirely clear of higher boxes and needs to be redrawn
405
406   // if direction = -1 then we have come to a region that is on
407   // the very bottom with nothing below it to repaint.
408   // do nothing. stale window data will be left on screen?
409
410   if (direction == 1)
411   {
412     rl.push_front(r);
413   }
414 }
415
416 /////////////////////////////////////////////////////////////////////////////
417 // END NEW STUFF
418 /////////////////////////////////////////////////////////////////////////////
419
420 // ---------------------------------------------------- END OF REMOVE CODE
421
422
423 void BoxStack::removeAll()
424 {
425   // 1.. Don't delete wallpaper. No point.
426
427   // Need locking on this one??
428   
429   // This is pretty silly now that preDelete needs mutex unlocked
430   
431   Boxx* toDel = NULL;
432   
433   while(numBoxes > 1)
434   {
435     #ifndef WIN32
436       pthread_mutex_lock(&boxLock);
437     #else
438       WaitForSingleObject(boxLock, INFINITE);
439     #endif  
440
441     if (numBoxes == 1)
442     {
443       #ifndef WIN32
444         pthread_mutex_unlock(&boxLock);
445       #else
446         ReleaseMutex(boxLock);
447       #endif
448       break;
449     }    
450   
451     toDel = boxes[numBoxes - 1];
452
453     #ifndef WIN32
454       pthread_mutex_unlock(&boxLock);
455     #else
456       ReleaseMutex(boxLock);
457     #endif
458
459     toDel->preDelete();
460     
461     #ifndef WIN32
462       pthread_mutex_lock(&boxLock);
463     #else
464       WaitForSingleObject(boxLock, INFINITE);
465     #endif  
466
467     // If boxes[numBoxes - 1] isn't toDel then there's a problem
468     if (boxes[numBoxes - 1] == toDel)
469     {
470       delete toDel;
471       --numBoxes;
472     }
473
474     #ifndef WIN32
475       pthread_mutex_unlock(&boxLock);
476     #else
477       ReleaseMutex(boxLock);
478     #endif
479
480   }
481
482 }
483
484 int BoxStack::handleCommand(int command)
485 {
486   int retVal;
487   int retVal2 = 0;
488   int i;
489
490   if (command != Remote::NA_NONE)
491   {
492     // handle command return values
493     // 0 - drop through to next box
494     // 1 - dont drop to next box, but not handled
495     // 2 - handled - stop command here
496     // 4 - handled - delete this box
497
498     for (i=numBoxes-1; i>=0; i--)
499     {
500 //      Log::getInstance()->log("BoxStack", Log::DEBUG, "Giving command to i=%i", i);
501       retVal = boxes[i]->handleCommand(command);
502       if (retVal == 1)
503       {
504         // not handled but don't give to any more boxes
505         return 0;
506       }
507
508       if (retVal == 2)
509       {
510         // command handled
511         retVal2 = 1;
512         break;
513       }
514       else if (retVal == 4)
515       {
516 //        Log::getInstance()->log("BoxStack", Log::DEBUG, "Return 4: i=%i, boxes[i]=%p", i, boxes[i]);
517         remove(boxes[i]);
518         retVal2 = 1;
519         break;
520       }
521     }
522   }
523   else
524   {
525     // fake the return code
526     retVal2 = 2;
527   }
528
529   return retVal2;
530 }
531
532 void BoxStack::processMessage(Message* m)
533 {
534   if (m->to != this)
535   {
536     for (int i = numBoxes-1; i >= 0; i--)
537     {
538       if (boxes[i] == m->to)
539       {
540         Log::getInstance()->log("BoxStack", Log::DEBUG, "sending message from box %p to box %p %lu", m->from, m->to, m->message);
541         boxes[i]->processMessage(m);
542         return;
543       }
544     }
545     return;
546   }
547
548   /* Handle mouse events*/
549   // They come in with m->to = NULL? and just need to be delivered to top box?
550   if ((numBoxes > 1) && ((m->message == Message::MOUSE_MOVE) || (m->message == Message::MOUSE_LBDOWN)))
551   {
552     boxes[numBoxes-1]->processMessage(m);
553     return;
554   }
555
556   Log::getInstance()->log("BoxStack", Log::DEBUG, "it's for meeee!");
557
558   switch(m->message)
559   {
560     case Message::CLOSE_ME:
561     {
562       remove((Boxx*)m->from);
563       break;
564     }
565     case Message::ADD_VIEW: // currently not used by anything but it might come in useful again
566     {
567       Boxx* toAdd = (Boxx*)m->parameter;
568       add(toAdd);
569       toAdd->draw();
570       update(toAdd);
571       break;
572     }
573   }
574 }