3 Copyright 2004-2020 Chris Tallon
5 This file is part of VOMP.
7 VOMP is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 VOMP is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with VOMP. If not, see <https://www.gnu.org/licenses/>.
22 #include "messagequeue.h"
28 static const char* TAG = "BoxStack";
30 BoxStack* BoxStack::instance = NULL;
36 osd = Osd::getInstance();
44 BoxStack* BoxStack::getInstance()
51 if (initted) return 0;
57 int BoxStack::shutdown()
59 if (!initted) return 0;
65 void BoxStack::removeAll()
67 removeAllExceptWallpaper();
68 if (numBoxes == 1) remove(boxes[0]);
71 int BoxStack::addVideoDisplay(Boxx* box,VideoDisplay vd)
74 videoStack.push(std::pair<Boxx*,VideoDisplay>(box,vd));
76 Video::getInstance()->setVideoDisplay(vd);
80 int BoxStack::add(Boxx* v)
82 if (!initted) return 0;
83 LogNT::getInstance()->debug(TAG, "add called");
85 LogNT::getInstance()->debug(TAG, "Locked for add");
89 LogNT::getInstance()->error(TAG, "More than 16 boxes! Unlocked for add");
93 boxes[numBoxes++] = v;
96 if (v->getVideoDisplay(vd)) {
97 LogNT::getInstance()->debug(TAG, "Add video display");
98 addVideoDisplay(v,vd);
103 LogNT::getInstance()->debug(TAG, "Unlocked for add");
108 // ---------------------------------------------------- REMOVE CODE
110 int BoxStack::remove(Boxx* toDelete)
112 if (!initted) return 0;
113 VideoDisplay *display = NULL;
116 LogNT::getInstance()->debug(TAG, "Locked for remove");
121 LogNT::getInstance()->error(TAG, "Unlocked for remove numBoxes == 0");
125 // LogNT::getInstance()->debug(TAG, "entering remove, numBoxes={}", numBoxes);
129 if (toDelete == NULL)
131 toDelete = boxes[numBoxes-1];
136 // to be deleted box is more likely to be at the top
137 for (i = numBoxes-1; i >= 0; i--)
139 // LogNT::getInstance()->debug(TAG, "todel: {}, i={}, boxes[i]={}", (void*)toDelete, i, (void*)boxes[i]);
140 if (boxes[i] == toDelete) break;
145 // not a Box we have!
148 LogNT::getInstance()->error(TAG, "Unlocked for remove - no boxx deleted");
155 toDelete->preDelete();
159 //LogNT::getInstance()->debug(TAG, "Starting repaintRevealed loop");
161 boxSplit(boxes[i]->area, i + 1, numBoxes, 1, rl);
164 repaintRevealed(i, rl.front());
167 //LogNT::getInstance()->debug(TAG, "Done repaintRevealed loop");
169 // Shift the boxes on top down one
171 for(int j = i; j < numBoxes; j++) boxes[j] = boxes[j+1];
173 // If there is only the wallpaper left signal command
176 Message* m = new Message();
177 m->p_to = Message::CONTROL;
178 m->message = Message::LAST_VIEW_CLOSE;
179 MessageQueue::getInstance()->postMessage(m);
182 if (!videoStack.empty() && videoStack.top().first==toDelete) {
184 if (!videoStack.empty()) display=&videoStack.top().second;
187 LogNT::getInstance()->debug(TAG, "Unlocked for remove");
190 //AVO: do this delete outside the lock to allow for recursive calls within the destructor
191 // as this box is not in the stack any more, there is no chance for a second delete
192 LogNT::getInstance()->debug(TAG, "remove: going to delete boxx {}, num {}", static_cast<void*>(toDelete), numBoxes);
198 LogNT::getInstance()->debug(TAG, "setVideoDisplay {} {} {} {} {} {}", display->mode, display->fallbackMode,
199 display->x, display->y, display->width, display->height);
200 Video::getInstance()->setVideoDisplay(*display);
206 void BoxStack::redrawAllBoxes()
210 for (int z = 0; z < numBoxes; z++)
216 update(NULL,NULL); // should blt all
219 void BoxStack::update(Boxx* toUpdate, const Region* regionToUpdate)
221 // LogNT::getInstance()->debug(TAG, "Update called");
222 if (!initted) return; // it is allowed to call this before init
224 // LogNT::getInstance()->debug(TAG, "Locked for update");
226 // Get the z index of the box
230 for (z = 0; z < numBoxes; z++)
232 if (boxes[z] == toUpdate) break;
237 // not a Box we have!
239 LogNT::getInstance()->error(TAG, "Unlocked for update! box not inside boxstack");
250 LogNT::getInstance()->debug(TAG, "Unlocked for update, no box present");
254 // get the region for the whole box, could be less than that
255 // for smaller updates
257 Region r = toUpdate->area;
261 // Can be null if the whole box should be updated
262 // If this is given the numbers are relative to the size of the box, not the screen
264 r.x += regionToUpdate->x;
265 r.y += regionToUpdate->y;
266 r.w = regionToUpdate->w;
267 r.h = regionToUpdate->h;
273 boxSplit(r, z+1, numBoxes, 1, rl);
283 // LogNT::getInstance()->debug(TAG, "Unlocked for update");
286 void BoxStack::repaintRevealed(int x, Region r)
289 boxSplit(r, x - 1, -1, -1, rl);
295 boxes[r2.z]->blt(r2);
300 void BoxStack::boxSplit(Region r, int start, int end, int direction, RegionList& rl)
302 // 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);
304 for(int z = start; z != end; z += direction)
306 if (r.overlappedBy(boxes[z]->area))
308 // printf("Z=%i S=%i E=%i D=%i: %i overlaps\n", z, start, end, direction, z);
313 if (boxes[z]->area.y > r.y)
315 // 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);
316 top = boxes[z]->area.y;
321 newR.h = boxes[z]->area.y - r.y;
322 boxSplit(newR, z + direction, end, direction, rl);
328 newR2.y = boxes[z]->area.y;
330 newR2.h = r.h - newR.h;
331 boxSplit(newR2, z, end, -1, rl);
336 if (boxes[z]->area.y2() < r.y2())
338 // 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);
339 btm = boxes[z]->area.y2();
342 newR.y = boxes[z]->area.y2() + 1;
344 newR.h = r.y2() - newR.y + 1;
345 boxSplit(newR, z + direction, end, direction, rl);
353 newR2.h = r.h - newR.h;
354 boxSplit(newR2, z, end, -1, rl);
359 if (boxes[z]->area.x > r.x)
361 // 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);
365 newR.w = boxes[z]->area.x - r.x;
366 newR.h = btm - top + 1;
367 boxSplit(newR, z + direction, end, direction, rl);
372 newR2.x = r.x + newR.w;
374 newR2.w = r.w - newR.w;
376 boxSplit(newR2, z, end, -1, rl);
381 if (boxes[z]->area.x2() < r.x2())
383 // 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);
385 newR.x = boxes[z]->area.x2() + 1;
387 newR.w = r.x2() - newR.x + 1;
388 newR.h = btm - top + 1;
389 boxSplit(newR, z + direction, end, direction, rl);
396 newR2.w = r.w - newR.w;
398 boxSplit(newR2, z, end, -1, rl);
405 // we are going down the stack
406 // r is underlapped by boxes[z]
407 // but we have not split
408 // Therefore this region under test is
409 // completely covering boxes[z]
411 // don't go any further down, generate a region and quit
413 // printf("Repaint region: %i %i %i %i\n", r.x, r.y, r.w, r.h);
418 // printf("Returning from Z=%i\n", z);
423 // printf("Z=%i S=%i E=%i D=%i: %i does not overlap\n", z, start, end, direction, z);
427 // if direction = 1 then we have come to a region that is
428 // entirely clear of higher boxes and needs to be redrawn
430 // if direction = -1 then we have come to a region that is on
431 // the very bottom with nothing below it to repaint.
432 // do nothing. stale window data will be left on screen?
440 void BoxStack::removeAllExceptWallpaper()
442 // 1.. Don't delete wallpaper. No point.
444 // Need locking on this one??
446 // This is pretty silly now that preDelete needs mutex unlocked
449 VideoDisplay *display = NULL;
461 toDel = boxes[numBoxes - 1];
469 // If boxes[numBoxes - 1] isn't toDel then there's a problem
470 if (boxes[numBoxes - 1] == toDel)
476 LogNT::getInstance()->error(TAG, "Can this actually happen? Why?");
480 // FIXME what is this?
481 if (!videoStack.empty() && videoStack.top().first==toDel) {
483 if (!videoStack.empty()) display=&videoStack.top().second;
487 //AVO: do the delete outside the lock to allow for recursive deletes
488 LogNT::getInstance()->debug(TAG, "removeall: going to delete boxx {}, num={}", static_cast<void*>(toDel), numBoxes);
489 if (display) Video::getInstance()->setVideoDisplay(*display);
491 if (toDel) delete toDel;
495 int BoxStack::handleCommand(int command)
501 LogNT::getInstance()->debug(TAG, "handle command {}", command);
503 if (command != Input::NA_NONE)
505 // handle command return values
506 // 0 - drop through to next box
507 // 1 - dont drop to next box, but not handled
508 // 2 - handled - stop command here
509 // 4 - handled - delete this box
511 for (i=numBoxes-1; i>=0; i--)
513 LogNT::getInstance()->debug(TAG, "Giving command to i={}", i);
514 retVal = boxes[i]->handleCommand(command);
515 if (retVal == ABANDON_COMMAND)
517 // not handled but don't give to any more boxes
521 if (retVal == COMMAND_HANDLED)
526 else if (retVal == DELETE_ME)
528 // LogNT::getInstance()->debug(TAG, "Return 4: i={}, boxes[i]={}", i, (void*)boxes[i]);
537 // fake the return code
544 void BoxStack::processMessage(Message* m)
546 if (m->p_to == Message::MOUSE_RECEIVER)
548 // Handle mouse events
549 // They just need to be delivered to top box
550 if (numBoxes > 1) boxes[numBoxes-1]->processMessage(m);
553 else if (m->p_to == Message::BOXSTACK)
557 case Message::CLOSE_ME:
559 remove(static_cast<Boxx*>(m->from));
562 case Message::ADD_VIEW:
564 Boxx* toAdd = reinterpret_cast<Boxx*>(m->data);