]> git.vomp.tv Git - vompclient.git/blob - viewman.cc
Cleanup
[vompclient.git] / viewman.cc
1 /*
2     Copyright 2004-2005 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, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "viewman.h"
22
23 ViewMan* ViewMan::instance = NULL;
24
25 ViewMan::ViewMan()
26 {
27   if (instance) return;
28   instance = this;
29   initted = 0;
30   numViews = 0;
31   resetThreadFlag = 0;
32   autoDeleteThreadRun = 0;
33   callFromThread = 0;
34 }
35
36 ViewMan::~ViewMan()
37 {
38   instance = NULL;
39 }
40
41 ViewMan* ViewMan::getInstance()
42 {
43   return instance;
44 }
45
46 int ViewMan::init()
47 {
48   if (initted) return 0;
49   pthread_mutex_init(&viewManLock, NULL);
50   pthread_cond_init(&autoDeleteThreadSignal, NULL);
51   if (!startAutoDeleteThread()) return 0;
52   initted = 1;
53   return 1;
54 }
55
56 int ViewMan::shutdown()
57 {
58   if (!initted) return 0;
59
60   // FIXME don't think this can work properly, removeAll leaves the wallpaper there!
61   removeAll();
62
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);
69   initted = 0;
70   return 1;
71 }
72
73 int ViewMan::add(View* v)
74 {
75   if (!initted) return 0;
76   if (numViews == 20) return 0;
77
78   pthread_mutex_lock(&viewManLock);
79
80   views[numViews++] = v;
81
82   resetThread();
83   pthread_mutex_unlock(&viewManLock);
84
85   return 1;
86 }
87
88 int ViewMan::addNoLock(View* v)
89 {
90   if (!initted) return 0;
91   if (numViews == 20) return 0;
92
93   views[numViews++] = v;
94
95   resetThread();
96
97   return 1;
98 }
99
100
101 // ---------------------------------------------------- REMOVE CODE
102
103 int ViewMan::removeView(View* toDelete, int noLock)
104 {
105   if (!initted) return 0;
106   if (numViews == 0) return 0;
107
108   if (!noLock) pthread_mutex_lock(&viewManLock);
109
110   Log::getInstance()->log("ViewMan", Log::DEBUG, "entering remove, numViews=%i", numViews);
111
112   int i;
113
114   if (toDelete == NULL)
115   {
116     toDelete = views[numViews-1];
117     i = numViews - 1;
118   }
119   else
120   {
121     // to be deleted view is more likely to be at the top
122     for (i = numViews-1; i >= 0; i--)
123     {
124       Log::getInstance()->log("ViewMan", Log::DEBUG, "todel: %p, i=%i, views[i]=%p", toDelete, i, views[i]);
125       if (views[i] == toDelete) break;
126     }
127
128     if (i == -1)
129     {
130       // not a View we have!
131       if (!noLock) pthread_mutex_unlock(&viewManLock);
132       return 0;
133     }
134   }
135
136   Log::getInstance()->log("ViewMan", Log::DEBUG, "Starting deleteView");
137   deleteView(i);
138   Log::getInstance()->log("ViewMan", Log::DEBUG, "Done deleteView");
139
140   // Shift the views on top down one
141   --numViews;
142   for(int j = i; j < numViews; j++) views[j] = views[j+1];
143
144   // Delete the view
145   delete toDelete;
146
147   resetThread();
148   if (!noLock) pthread_mutex_unlock(&viewManLock);
149
150   return 1;
151 }
152
153 /////////////////////////////////////////////////////////////////////////////
154 // NEW STUFF
155 /////////////////////////////////////////////////////////////////////////////
156
157 void ViewMan::deleteView(int z)
158 {
159   Log::getInstance()->log("ViewMan", Log::DEBUG, "Delete view %i of %i", z, numViews);
160   RegionList rl;
161   boxSplit(views[z]->area, z + 1, numViews, 1, rl);
162   while(!rl.empty())
163   {
164     repaintRevealed(z, rl.front());
165     rl.pop_front();
166   }
167 }
168
169 /* For later...
170 void ViewMan::update(int z)
171 {
172   // get the region for the whole view, could be less than that
173   // for smaller updates
174
175   Region r = views[z]->area;
176   RegionList rl;
177
178   r.y += 10;
179
180   Region r2;
181   boxSplit(r, z+1, numViews, 1, rl);
182   while(!rl.empty())
183   {
184     r2 = rl.front();
185     r2.z = z;
186     views[z]->show(r2);
187     rl.pop_front();
188   }
189 }
190 */
191
192 void ViewMan::repaintRevealed(int x, Region r)
193 {
194   RegionList rl;
195   boxSplit(r, x - 1, -1, -1, rl);
196
197   Region r2;
198   while(!rl.empty())
199   {
200     r2 = rl.front();
201     views[r2.z]->show(r2);
202     rl.pop_front();
203   }
204 }
205
206 void ViewMan::boxSplit(Region r, int start, int end, int direction, RegionList& rl)
207 {
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);
209
210   for(int z = start; z != end; z += direction)
211   {
212     if (r.overlappedBy(views[z]->area))
213     {
214 //      printf("Z=%i S=%i E=%i D=%i: %i overlaps\n", z, start, end, direction, z);
215
216       int top = r.y;
217       int btm = r.y2();
218
219       if (views[z]->area.y > r.y)
220       {
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;
223         Region newR;
224         newR.x = r.x;
225         newR.y = r.y;
226         newR.w = r.w;
227         newR.h = views[z]->area.y - r.y;
228         boxSplit(newR, z + direction, end, direction, rl);
229
230         if (direction == -1)
231         {
232           Region newR2;
233           newR2.x = r.x;
234           newR2.y = views[z]->area.y;
235           newR2.w = r.w;
236           newR2.h = r.h - newR.h;
237           boxSplit(newR2, z, end, -1, rl);
238           return;
239         }
240       }
241
242       if (views[z]->area.y2() < r.y2())
243       {
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();
246         Region newR;
247         newR.x = r.x;
248         newR.y = views[z]->area.y2() + 1;
249         newR.w = r.w;
250         newR.h = r.y2() - newR.y + 1;
251         boxSplit(newR, z + direction, end, direction, rl);
252
253         if (direction == -1)
254         {
255           Region newR2;
256           newR2.x = r.x;
257           newR2.y = r.y;
258           newR2.w = r.w;
259           newR2.h = r.h - newR.h;
260           boxSplit(newR2, z, end, -1, rl);
261           return;
262         }
263       }
264
265       if (views[z]->area.x > r.x)
266       {
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);
268         Region newR;
269         newR.x = r.x;
270         newR.y = top;
271         newR.w = views[z]->area.x - r.x;
272         newR.h = btm - top + 1;
273         boxSplit(newR, z + direction, end, direction, rl);
274
275         if (direction == -1)
276         {
277           Region newR2;
278           newR2.x = r.x + newR.w;
279           newR2.y = r.y;
280           newR2.w = r.w - newR.w;
281           newR2.h = r.h;
282           boxSplit(newR2, z, end, -1, rl);
283           return;
284         }
285       }
286
287       if (views[z]->area.x2() < r.x2())
288       {
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);
290         Region newR;
291         newR.x = views[z]->area.x2() + 1;
292         newR.y = top;
293         newR.w = r.x2() - newR.x + 1;
294         newR.h = btm - top + 1;
295         boxSplit(newR, z + direction, end, direction, rl);
296
297         if (direction == -1)
298         {
299           Region newR2;
300           newR2.x = r.x;
301           newR2.y = r.y;
302           newR2.w = r.w - newR.w;
303           newR2.h = r.h;
304           boxSplit(newR2, z, end, -1, rl);
305           return;
306         }
307       }
308
309       if (direction == -1)
310       {
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]
316
317         // don't go any further down, generate a region and quit
318
319 //        printf("Repaint region: %i %i %i %i\n", r.x, r.y, r.w, r.h);
320         r.z = z;
321         rl.push_front(r);
322       }
323
324 //      printf("Returning from Z=%i\n", z);
325       return;
326     }
327     else
328     {
329 //      printf("Z=%i S=%i E=%i D=%i: %i does not overlap\n", z, start, end, direction, z);
330     }
331   }
332
333   // if direction = 1 then we have come to a region that is
334   // entirely clear of higher views and needs to be redrawn
335
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?
339
340   if (direction == 1)
341   {
342     rl.push_front(r);
343   }
344 }
345
346 // TEMP
347 void ViewMan::drawBlack(Region& r)
348 {
349   Surface* surface = Surface::getScreen();
350   surface->fillblt(r.x, r.y, r.w, r.h, surface->rgba(0, 0, 0, 255));
351 }
352
353 /////////////////////////////////////////////////////////////////////////////
354 // END NEW STUFF
355 /////////////////////////////////////////////////////////////////////////////
356
357 // ---------------------------------------------------- END OF REMOVE CODE
358
359
360 void ViewMan::removeAll()
361 {
362   pthread_mutex_lock(&viewManLock);
363
364   // 1.. Don't delete wallpaper. No point.
365   for (; numViews > 1; --numViews)
366   {
367     delete views[numViews-1];
368   }
369
370   resetThread();
371   pthread_mutex_unlock(&viewManLock);
372 }
373
374 int ViewMan::handleCommand(UCHAR command)
375 {
376   pthread_mutex_lock(&viewManLock);
377
378   int retVal;
379   int retVal2 = 0;
380   int i;
381
382   if (command != Remote::NA_NONE)
383   {
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
389
390     for (i=numViews-1; i>=0; i--)
391     {
392       Log::getInstance()->log("ViewMan", Log::DEBUG, "Giving command to i=%i", i);
393       retVal = views[i]->handleCommand(command);
394       if (retVal == 1)
395       {
396         // not handled but don't give to any more views
397         pthread_mutex_unlock(&viewManLock);
398         return 0;
399       }
400
401       if (retVal == 2)
402       {
403         // command handled
404         if (views[i]->seconds)
405         {
406           struct timespec currentTime;
407           clock_gettime(CLOCK_REALTIME, &currentTime);
408           views[i]->delSec = currentTime.tv_sec + views[i]->seconds;
409           views[i]->delNSec = currentTime.tv_nsec;
410           resetThread();
411         }
412         pthread_mutex_unlock(&viewManLock);
413         retVal2 = 1;
414         break;
415       }
416       else if (retVal == 4)
417       {
418   //      removeNoLock(views[i]);
419   //      Box::showAll();
420   //      resetThread();
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);
424         retVal2 = 1;
425         break;
426       }
427     }
428   }
429   else
430   {
431     // fake the return code
432     retVal2 = 2;
433   }
434
435   Log::getInstance()->log("ViewMan", Log::DEBUG, "out of handlecommand code, now on to messages");
436
437   processMessageQueue();
438
439   pthread_mutex_unlock(&viewManLock);
440   return retVal2;
441 }
442
443 void ViewMan::processMessage(Message* m)
444 {
445   if (m->to != this)
446   {
447     for (int i = numViews-1; i >= 0; i--)
448     {
449       if (views[i] == m->to)
450       {
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);
454         return;
455       }
456     }
457   }
458
459   Log::getInstance()->log("ViewMan", Log::DEBUG, "it's for meeee!");
460
461   switch(m->message)
462   {
463     case Message::CLOSE_ME:
464     {
465       removeView((View*)m->from, 1);
466       break;
467     }
468     case Message::SWAP_ME_FOR:
469     {
470       View* toReplace = (View*) m->parameter;
471
472       removeView((View*)m->from, 1);
473
474       views[numViews] = toReplace;
475       ++numViews;
476       if (toReplace->seconds)
477       {
478         struct timespec currentTime;
479         clock_gettime(CLOCK_REALTIME, &currentTime);
480         toReplace->delSec = currentTime.tv_sec + toReplace->seconds;
481         toReplace->delNSec = currentTime.tv_nsec;
482       }
483       toReplace->draw();
484       toReplace->show();
485       resetThread();
486       break;
487     }
488     case Message::ADD_VIEW:
489     {
490       View* toAdd = (View*)m->parameter;
491       addNoLock(toAdd);
492       toAdd->draw();
493       toAdd->show();
494     }
495   }
496 }
497
498 int ViewMan::timedDelete(View* v, int seconds, int lockMutex)
499 {
500   int success;
501
502   if (lockMutex) pthread_mutex_lock(&viewManLock);
503
504   struct timespec currentTime;
505   clock_gettime(CLOCK_REALTIME, &currentTime);
506
507   if (v)
508   {
509     if (seconds)
510     {
511       v->seconds = seconds;
512       v->delSec = currentTime.tv_sec + seconds;
513       v->delNSec = currentTime.tv_nsec;
514     }
515     else
516     {
517       // for cancelling the delete
518       v->seconds = 0;
519       v->delSec = 0;
520       v->delNSec = 0;
521     }
522     success = 1;
523   }
524   else
525   {
526     success = 0;
527   }
528
529   resetThread();
530   if (lockMutex) pthread_mutex_unlock(&viewManLock);
531
532   return success;
533 }
534
535
536 // THE THREAD CODE STARTS HERE /////////////////////////////////////////////////////////////
537
538 void ViewMan::resetThread()
539 {
540   // must be called with mutex already locked
541   resetThreadFlag = 1;
542   pthread_cond_signal(&autoDeleteThreadSignal);
543 }
544
545 // undeclared function
546 void startAutoDeleteThread2(void *arg)
547 {
548   ViewMan *v = (ViewMan *)arg;
549   v->startAutoDeleteThread3();
550 }
551 int ViewMan::startAutoDeleteThread()
552 {
553   pthread_mutex_lock(&viewManLock);
554   resetThreadFlag = 1;
555   autoDeleteThreadRun = 1;
556   if (pthread_create(&autoDeleteThread, NULL, (void*(*)(void*))startAutoDeleteThread2, (void *)this) == -1) return 0;
557   return 1;
558 }
559
560 void ViewMan::startAutoDeleteThread3()
561 {
562   struct timespec nextTime;
563   View* nextToDelete = NULL;
564
565   // I don't want signals
566   sigset_t sigs;
567   sigfillset(&sigs);
568   pthread_sigmask(SIG_BLOCK, &sigs, NULL);
569
570   // locked here
571
572   while(1)
573   {
574     if (resetThreadFlag)
575     {
576       resetThreadFlag = 0;
577
578       // Work out the next View to be deleted
579
580       nextTime.tv_sec = 0;
581       nextTime.tv_nsec = 0;
582       nextToDelete = NULL;
583
584       for(int i = 0; i < numViews; i++)
585       {
586         if ((views[i]->delSec > 0) && (views[i]->delNSec > 0))
587         {
588           if ((nextTime.tv_sec == 0) && (nextTime.tv_nsec == 0))
589           {
590             nextTime.tv_sec = views[i]->delSec;
591             nextTime.tv_nsec = views[i]->delNSec;
592             nextToDelete = views[i];
593           }
594           else
595           {
596             if (views[i]->delSec < nextTime.tv_sec)
597             {
598               nextTime.tv_sec = views[i]->delSec;
599               nextTime.tv_nsec = views[i]->delNSec;
600               nextToDelete = views[i];
601             }
602             else if (views[i]->delSec == nextTime.tv_sec)
603             {
604               if (views[i]->delNSec < nextTime.tv_nsec)
605               {
606                 nextTime.tv_sec = views[i]->delNSec;
607                 nextTime.tv_nsec = views[i]->delNSec;
608                 nextToDelete = views[i];
609               }
610             }
611           }
612         }
613         // no case
614       }
615       // end
616     }
617
618     if (nextTime.tv_sec == 0)
619     {
620       pthread_cond_wait(&autoDeleteThreadSignal, &viewManLock);
621     }
622     else
623     {
624       pthread_cond_timedwait(&autoDeleteThreadSignal, &viewManLock, &nextTime);
625     }
626
627     // ok. we have been signalled or the time has run out
628
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)
632     {
633       pthread_exit(NULL);
634       // exiting thread with mutex locked
635     }
636
637     if (resetThreadFlag) continue;
638
639     // timer ran out
640     Log::getInstance()->log("ViewMan", Log::DEBUG, "About to remove %p", nextToDelete);
641     removeView(nextToDelete, 1);  // enter this method with mutex locked
642
643     resetThreadFlag = 1;
644   }
645 }
646