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