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