2 Copyright 2004-2005 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, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 ViewMan* ViewMan::instance = NULL;
32 autoDeleteThreadRun = 0;
41 ViewMan* ViewMan::getInstance()
48 if (initted) return 0;
49 pthread_mutex_init(&viewManLock, NULL);
50 pthread_cond_init(&autoDeleteThreadSignal, NULL);
51 if (!startAutoDeleteThread()) return 0;
56 int ViewMan::shutdown()
58 if (!initted) return 0;
60 // FIXME don't think this can work properly, removeAll leaves the wallpaper there!
63 // get the lock here to ensure that the thread is waiting on the cond
64 pthread_mutex_lock(&viewManLock);
65 autoDeleteThreadRun = 0;
66 pthread_cond_signal(&autoDeleteThreadSignal);
67 pthread_mutex_unlock(&viewManLock);
68 pthread_join(autoDeleteThread, NULL);
73 int ViewMan::add(View* v)
75 if (!initted) return 0;
76 if (numViews == 20) return 0;
78 pthread_mutex_lock(&viewManLock);
80 views[numViews++] = v;
83 pthread_mutex_unlock(&viewManLock);
88 int ViewMan::addNoLock(View* v)
90 if (!initted) return 0;
91 if (numViews == 20) return 0;
93 views[numViews++] = v;
101 // ---------------------------------------------------- REMOVE CODE
103 int ViewMan::removeView(View* toDelete, int noLock)
105 if (!initted) return 0;
106 if (numViews == 0) return 0;
108 if (!noLock) pthread_mutex_lock(&viewManLock);
110 Log::getInstance()->log("ViewMan", Log::DEBUG, "entering remove, numViews=%i", numViews);
114 if (toDelete == NULL)
116 toDelete = views[numViews-1];
121 // to be deleted view is more likely to be at the top
122 for (i = numViews-1; i >= 0; i--)
124 Log::getInstance()->log("ViewMan", Log::DEBUG, "todel: %p, i=%i, views[i]=%p", toDelete, i, views[i]);
125 if (views[i] == toDelete) break;
130 // not a View we have!
131 if (!noLock) pthread_mutex_unlock(&viewManLock);
136 Log::getInstance()->log("ViewMan", Log::DEBUG, "Starting deleteView");
138 Log::getInstance()->log("ViewMan", Log::DEBUG, "Done deleteView");
140 // Shift the views on top down one
142 for(int j = i; j < numViews; j++) views[j] = views[j+1];
148 if (!noLock) pthread_mutex_unlock(&viewManLock);
153 /////////////////////////////////////////////////////////////////////////////
155 /////////////////////////////////////////////////////////////////////////////
157 void ViewMan::deleteView(int z)
159 Log::getInstance()->log("ViewMan", Log::DEBUG, "Delete view %i of %i", z, numViews);
161 boxSplit(views[z]->area, z + 1, numViews, 1, rl);
164 repaintRevealed(z, rl.front());
170 void ViewMan::update(int z)
172 // get the region for the whole view, could be less than that
173 // for smaller updates
175 Region r = views[z]->area;
181 boxSplit(r, z+1, numViews, 1, rl);
192 void ViewMan::repaintRevealed(int x, Region r)
195 boxSplit(r, x - 1, -1, -1, rl);
201 views[r2.z]->show(r2);
206 void ViewMan::boxSplit(Region r, int start, int end, int direction, RegionList& rl)
208 // 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);
210 for(int z = start; z != end; z += direction)
212 if (r.overlappedBy(views[z]->area))
214 // printf("Z=%i S=%i E=%i D=%i: %i overlaps\n", z, start, end, direction, z);
219 if (views[z]->area.y > r.y)
221 // 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, views[z]->area.x, views[z]->area.y, views[z]->area.w, views[z]->area.h);
222 top = views[z]->area.y;
227 newR.h = views[z]->area.y - r.y;
228 boxSplit(newR, z + direction, end, direction, rl);
234 newR2.y = views[z]->area.y;
236 newR2.h = r.h - newR.h;
237 boxSplit(newR2, z, end, -1, rl);
242 if (views[z]->area.y2() < r.y2())
244 // 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, views[z]->area.x, views[z]->area.y, views[z]->area.w, views[z]->area.h);
245 btm = views[z]->area.y2();
248 newR.y = views[z]->area.y2() + 1;
250 newR.h = r.y2() - newR.y + 1;
251 boxSplit(newR, z + direction, end, direction, rl);
259 newR2.h = r.h - newR.h;
260 boxSplit(newR2, z, end, -1, rl);
265 if (views[z]->area.x > r.x)
267 // 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, views[z]->area.x, views[z]->area.y, views[z]->area.w, views[z]->area.h);
271 newR.w = views[z]->area.x - r.x;
272 newR.h = btm - top + 1;
273 boxSplit(newR, z + direction, end, direction, rl);
278 newR2.x = r.x + newR.w;
280 newR2.w = r.w - newR.w;
282 boxSplit(newR2, z, end, -1, rl);
287 if (views[z]->area.x2() < r.x2())
289 // 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, views[z]->area.x, views[z]->area.y, views[z]->area.w, views[z]->area.h);
291 newR.x = views[z]->area.x2() + 1;
293 newR.w = r.x2() - newR.x + 1;
294 newR.h = btm - top + 1;
295 boxSplit(newR, z + direction, end, direction, rl);
302 newR2.w = r.w - newR.w;
304 boxSplit(newR2, z, end, -1, rl);
311 // we are going down the stack
312 // r is underlapped by views[z]
313 // but we have not split
314 // Therefore this region under test is
315 // completely covering views[z]
317 // don't go any further down, generate a region and quit
319 // printf("Repaint region: %i %i %i %i\n", r.x, r.y, r.w, r.h);
324 // printf("Returning from Z=%i\n", z);
329 // printf("Z=%i S=%i E=%i D=%i: %i does not overlap\n", z, start, end, direction, z);
333 // if direction = 1 then we have come to a region that is
334 // entirely clear of higher views and needs to be redrawn
336 // if direction = -1 then we have come to a region that is on
337 // the very bottom with nothing below it to repaint.
338 // do nothing. stale window data will be left on screen?
347 void ViewMan::drawBlack(Region& r)
349 Surface* surface = Surface::getScreen();
350 surface->fillblt(r.x, r.y, r.w, r.h, surface->rgba(0, 0, 0, 255));
353 /////////////////////////////////////////////////////////////////////////////
355 /////////////////////////////////////////////////////////////////////////////
357 // ---------------------------------------------------- END OF REMOVE CODE
360 void ViewMan::removeAll()
362 pthread_mutex_lock(&viewManLock);
364 // 1.. Don't delete wallpaper. No point.
365 for (; numViews > 1; --numViews)
367 delete views[numViews-1];
371 pthread_mutex_unlock(&viewManLock);
374 int ViewMan::handleCommand(UCHAR command)
376 pthread_mutex_lock(&viewManLock);
382 if (command != Remote::NA_NONE)
384 // handle command return values
385 // 0 - drop through to next view
386 // 1 - dont drop to next view, but not handled
387 // 2 - handled - stop command here
388 // 4 - handled - delete this view
390 for (i=numViews-1; i>=0; i--)
392 Log::getInstance()->log("ViewMan", Log::DEBUG, "Giving command to i=%i", i);
393 retVal = views[i]->handleCommand(command);
396 // not handled but don't give to any more views
397 pthread_mutex_unlock(&viewManLock);
404 if (views[i]->seconds)
406 struct timespec currentTime;
407 clock_gettime(CLOCK_REALTIME, ¤tTime);
408 views[i]->delSec = currentTime.tv_sec + views[i]->seconds;
409 views[i]->delNSec = currentTime.tv_nsec;
412 pthread_mutex_unlock(&viewManLock);
416 else if (retVal == 4)
418 // removeNoLock(views[i]);
421 Log::getInstance()->log("ViewMan", Log::DEBUG, "Return 4: i=%i, views[i]=%p", i, views[i]);
422 removeView(views[i], 1);
423 pthread_mutex_unlock(&viewManLock);
431 // fake the return code
435 Log::getInstance()->log("ViewMan", Log::DEBUG, "out of handlecommand code, now on to messages");
437 processMessageQueue();
439 pthread_mutex_unlock(&viewManLock);
443 void ViewMan::processMessage(Message* m)
447 for (int i = numViews-1; i >= 0; i--)
449 if (views[i] == m->to)
451 Log::getInstance()->log("ViewMan", Log::DEBUG, "sending message to view");
452 Log::getInstance()->log("ViewMan", Log::DEBUG, "%p %p %lu", m->from, m->to, m->message);
453 views[i]->processMessage(m);
459 Log::getInstance()->log("ViewMan", Log::DEBUG, "it's for meeee!");
463 case Message::CLOSE_ME:
465 removeView((View*)m->from, 1);
468 case Message::SWAP_ME_FOR:
470 View* toReplace = (View*) m->parameter;
472 removeView((View*)m->from, 1);
474 views[numViews] = toReplace;
476 if (toReplace->seconds)
478 struct timespec currentTime;
479 clock_gettime(CLOCK_REALTIME, ¤tTime);
480 toReplace->delSec = currentTime.tv_sec + toReplace->seconds;
481 toReplace->delNSec = currentTime.tv_nsec;
488 case Message::ADD_VIEW:
490 View* toAdd = (View*)m->parameter;
498 int ViewMan::timedDelete(View* v, int seconds, int lockMutex)
502 if (lockMutex) pthread_mutex_lock(&viewManLock);
504 struct timespec currentTime;
505 clock_gettime(CLOCK_REALTIME, ¤tTime);
511 v->seconds = seconds;
512 v->delSec = currentTime.tv_sec + seconds;
513 v->delNSec = currentTime.tv_nsec;
517 // for cancelling the delete
530 if (lockMutex) pthread_mutex_unlock(&viewManLock);
536 // THE THREAD CODE STARTS HERE /////////////////////////////////////////////////////////////
538 void ViewMan::resetThread()
540 // must be called with mutex already locked
542 pthread_cond_signal(&autoDeleteThreadSignal);
545 // undeclared function
546 void startAutoDeleteThread2(void *arg)
548 ViewMan *v = (ViewMan *)arg;
549 v->startAutoDeleteThread3();
551 int ViewMan::startAutoDeleteThread()
553 pthread_mutex_lock(&viewManLock);
555 autoDeleteThreadRun = 1;
556 if (pthread_create(&autoDeleteThread, NULL, (void*(*)(void*))startAutoDeleteThread2, (void *)this) == -1) return 0;
560 void ViewMan::startAutoDeleteThread3()
562 struct timespec nextTime;
563 View* nextToDelete = NULL;
565 // I don't want signals
568 pthread_sigmask(SIG_BLOCK, &sigs, NULL);
578 // Work out the next View to be deleted
581 nextTime.tv_nsec = 0;
584 for(int i = 0; i < numViews; i++)
586 if ((views[i]->delSec > 0) && (views[i]->delNSec > 0))
588 if ((nextTime.tv_sec == 0) && (nextTime.tv_nsec == 0))
590 nextTime.tv_sec = views[i]->delSec;
591 nextTime.tv_nsec = views[i]->delNSec;
592 nextToDelete = views[i];
596 if (views[i]->delSec < nextTime.tv_sec)
598 nextTime.tv_sec = views[i]->delSec;
599 nextTime.tv_nsec = views[i]->delNSec;
600 nextToDelete = views[i];
602 else if (views[i]->delSec == nextTime.tv_sec)
604 if (views[i]->delNSec < nextTime.tv_nsec)
606 nextTime.tv_sec = views[i]->delNSec;
607 nextTime.tv_nsec = views[i]->delNSec;
608 nextToDelete = views[i];
618 if (nextTime.tv_sec == 0)
620 pthread_cond_wait(&autoDeleteThreadSignal, &viewManLock);
624 pthread_cond_timedwait(&autoDeleteThreadSignal, &viewManLock, &nextTime);
627 // ok. we have been signalled or the time has run out
629 // see if we have been signalled. we only get signalled if we
630 // are to reset the timer or if we are to die completely
631 if (!autoDeleteThreadRun)
634 // exiting thread with mutex locked
637 if (resetThreadFlag) continue;
640 Log::getInstance()->log("ViewMan", Log::DEBUG, "About to remove %p", nextToDelete);
641 removeView(nextToDelete, 1); // enter this method with mutex locked