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