2 Copyright 2004-2020 Chris Tallon
4 This file is part of VOMP.
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.
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.
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/>.
22 #include "messagequeue.h"
26 BoxStack* BoxStack::instance = NULL;
32 osd = Osd::getInstance();
40 BoxStack* BoxStack::getInstance()
47 if (initted) return 0;
53 int BoxStack::shutdown()
55 if (!initted) return 0;
61 void BoxStack::removeAll()
63 removeAllExceptWallpaper();
64 if (numBoxes == 1) remove(boxes[0]);
67 int BoxStack::addVideoDisplay(Boxx* box,VideoDisplay vd)
70 videoStack.push(std::pair<Boxx*,VideoDisplay>(box,vd));
72 Video::getInstance()->setVideoDisplay(vd);
76 int BoxStack::add(Boxx* v)
78 if (!initted) return 0;
79 Log::getInstance()->log("BoxStack", Log::DEBUG, "add called");
81 Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for add");
85 Log::getInstance()->log("BoxStack", Log::ERR, "More than 16 boxes! Unlocked for add");
89 boxes[numBoxes++] = v;
92 if (v->getVideoDisplay(vd)) {
93 Log::getInstance()->log("BoxStack", Log::DEBUG, "Add video display");
94 addVideoDisplay(v,vd);
99 Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for add");
104 // ---------------------------------------------------- REMOVE CODE
106 int BoxStack::remove(Boxx* toDelete)
108 if (!initted) return 0;
109 VideoDisplay *display = NULL;
112 Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for remove");
117 Log::getInstance()->log("BoxStack", Log::ERR, "Unlocked for remove numBoxes == 0");
121 // Log::getInstance()->log("BoxStack", Log::DEBUG, "entering remove, numBoxes=%i", numBoxes);
125 if (toDelete == NULL)
127 toDelete = boxes[numBoxes-1];
132 // to be deleted box is more likely to be at the top
133 for (i = numBoxes-1; i >= 0; i--)
135 // Log::getInstance()->log("BoxStack", Log::DEBUG, "todel: %p, i=%i, boxes[i]=%p", toDelete, i, boxes[i]);
136 if (boxes[i] == toDelete) break;
141 // not a Box we have!
144 Log::getInstance()->log("BoxStack", Log::ERR, "Unlocked for remove - no boxx deleted");
151 toDelete->preDelete();
155 //Log::getInstance()->log("BoxStack", Log::DEBUG, "Starting repaintRevealed loop");
157 boxSplit(boxes[i]->area, i + 1, numBoxes, 1, rl);
160 repaintRevealed(i, rl.front());
163 //Log::getInstance()->log("BoxStack", Log::DEBUG, "Done repaintRevealed loop");
165 // Shift the boxes on top down one
167 for(int j = i; j < numBoxes; j++) boxes[j] = boxes[j+1];
169 // If there is only the wallpaper left signal command
172 Message* m = new Message();
173 m->p_to = Message::CONTROL;
174 m->message = Message::LAST_VIEW_CLOSE;
175 MessageQueue::getInstance()->postMessage(m);
178 if (!videoStack.empty() && videoStack.top().first==toDelete) {
180 if (!videoStack.empty()) display=&videoStack.top().second;
183 Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for remove");
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);
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);
202 void BoxStack::redrawAllBoxes()
206 for (int z = 0; z < numBoxes; z++)
212 update(NULL,NULL); // should blt all
215 void BoxStack::update(Boxx* toUpdate, const Region* regionToUpdate)
217 // Log::getInstance()->log("BoxStack", Log::DEBUG, "Update called");
218 if (!initted) return; // it is allowed to call this before init
220 // Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for update");
222 // Get the z index of the box
226 for (z = 0; z < numBoxes; z++)
228 if (boxes[z] == toUpdate) break;
233 // not a Box we have!
235 Log::getInstance()->log("BoxStack", Log::ERR, "Unlocked for update! box not inside boxstack");
246 Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for update, no box present");
250 // get the region for the whole box, could be less than that
251 // for smaller updates
253 Region r = toUpdate->area;
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
260 r.x += regionToUpdate->x;
261 r.y += regionToUpdate->y;
262 r.w = regionToUpdate->w;
263 r.h = regionToUpdate->h;
269 boxSplit(r, z+1, numBoxes, 1, rl);
279 // Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for update");
282 void BoxStack::repaintRevealed(int x, Region r)
285 boxSplit(r, x - 1, -1, -1, rl);
291 boxes[r2.z]->blt(r2);
296 void BoxStack::boxSplit(Region r, int start, int end, int direction, RegionList& rl)
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);
300 for(int z = start; z != end; z += direction)
302 if (r.overlappedBy(boxes[z]->area))
304 // printf("Z=%i S=%i E=%i D=%i: %i overlaps\n", z, start, end, direction, z);
309 if (boxes[z]->area.y > r.y)
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;
317 newR.h = boxes[z]->area.y - r.y;
318 boxSplit(newR, z + direction, end, direction, rl);
324 newR2.y = boxes[z]->area.y;
326 newR2.h = r.h - newR.h;
327 boxSplit(newR2, z, end, -1, rl);
332 if (boxes[z]->area.y2() < r.y2())
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();
338 newR.y = boxes[z]->area.y2() + 1;
340 newR.h = r.y2() - newR.y + 1;
341 boxSplit(newR, z + direction, end, direction, rl);
349 newR2.h = r.h - newR.h;
350 boxSplit(newR2, z, end, -1, rl);
355 if (boxes[z]->area.x > r.x)
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);
361 newR.w = boxes[z]->area.x - r.x;
362 newR.h = btm - top + 1;
363 boxSplit(newR, z + direction, end, direction, rl);
368 newR2.x = r.x + newR.w;
370 newR2.w = r.w - newR.w;
372 boxSplit(newR2, z, end, -1, rl);
377 if (boxes[z]->area.x2() < r.x2())
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);
381 newR.x = boxes[z]->area.x2() + 1;
383 newR.w = r.x2() - newR.x + 1;
384 newR.h = btm - top + 1;
385 boxSplit(newR, z + direction, end, direction, rl);
392 newR2.w = r.w - newR.w;
394 boxSplit(newR2, z, end, -1, rl);
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]
407 // don't go any further down, generate a region and quit
409 // printf("Repaint region: %i %i %i %i\n", r.x, r.y, r.w, r.h);
414 // printf("Returning from Z=%i\n", z);
419 // printf("Z=%i S=%i E=%i D=%i: %i does not overlap\n", z, start, end, direction, z);
423 // if direction = 1 then we have come to a region that is
424 // entirely clear of higher boxes and needs to be redrawn
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?
436 void BoxStack::removeAllExceptWallpaper()
438 // 1.. Don't delete wallpaper. No point.
440 // Need locking on this one??
442 // This is pretty silly now that preDelete needs mutex unlocked
445 VideoDisplay *display = NULL;
457 toDel = boxes[numBoxes - 1];
465 // If boxes[numBoxes - 1] isn't toDel then there's a problem
466 if (boxes[numBoxes - 1] == toDel)
472 Log::getInstance()->log("BoxStack", Log::ERR, "Can this actually happen? Why?");
476 // FIXME what is this?
477 if (!videoStack.empty() && videoStack.top().first==toDel) {
479 if (!videoStack.empty()) display=&videoStack.top().second;
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);
487 if (toDel) delete toDel;
491 int BoxStack::handleCommand(int command)
497 if (command != Input::NA_NONE)
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
505 for (i=numBoxes-1; i>=0; i--)
507 // Log::getInstance()->log("BoxStack", Log::DEBUG, "Giving command to i=%i", i);
508 retVal = boxes[i]->handleCommand(command);
511 // not handled but don't give to any more boxes
521 else if (retVal == 4)
523 // Log::getInstance()->log("BoxStack", Log::DEBUG, "Return 4: i=%i, boxes[i]=%p", i, boxes[i]);
532 // fake the return code
539 void BoxStack::processMessage(Message* m)
541 if ((m->p_to != Message::BOXSTACK) && (m->to != this))
543 for (int i = numBoxes-1; i >= 0; i--)
545 if (boxes[i] == m->to)
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);
555 // Handle mouse events
556 // They just need to be delivered to top box
557 if (m->p_to == Message::MOUSE_RECEIVER)
559 if (numBoxes > 1) boxes[numBoxes-1]->processMessage(m);
563 Log::getInstance()->log("BoxStack", Log::DEBUG, "it's for meeee!");
567 case Message::CLOSE_ME:
569 remove(static_cast<Boxx*>(m->from));
572 case Message::ADD_VIEW:
574 Boxx* toAdd = reinterpret_cast<Boxx*>(m->data);