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