]> git.vomp.tv Git - vompclient.git/blob - viewman.cc
Channel schedules in live banner
[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   topView = 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   removeAll();
61
62   // get the lock here to ensure that the thread is waiting on the cond
63   pthread_mutex_lock(&viewManLock);
64   autoDeleteThreadRun = 0;
65   pthread_cond_signal(&autoDeleteThreadSignal);
66   pthread_mutex_unlock(&viewManLock);
67   pthread_join(autoDeleteThread, NULL);
68   initted = 0;
69   return 1;
70 }
71
72 int ViewMan::add(View* v)
73 {
74   if (!initted) return 0;
75   if (topView == 10) return 0;
76
77   pthread_mutex_lock(&viewManLock);
78
79   topView++;
80   views[topView] = 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 (topView == 10) return 0;
92
93   topView++;
94   views[topView] = v;
95
96   resetThread();
97
98   return 1;
99 }
100
101
102 // ---------------------------------------------------- REMOVE CODE
103
104 int ViewMan::removeView(View* toDelete, int noLock, int noShow)
105 {
106   if (!initted) return 0;
107   if (topView == 0) return 0;
108
109   if (!noLock) pthread_mutex_lock(&viewManLock);
110
111   Log::getInstance()->log("ViewMan", Log::DEBUG, "entering remove, %u topview", topView);
112
113   int i;
114   int wasTopView = 0;
115   int slotTakenFrom = 0;
116
117   if (toDelete == NULL)
118   {
119     toDelete = views[topView];
120     i = topView;
121     wasTopView = 1;
122   }
123   else
124   {
125     // to be deleted view is more likely to be at the top
126
127
128     for (i = topView; i > 0; i--)
129     {
130       if (views[i] == toDelete)
131       {
132         if (i == topView) wasTopView = 1;
133         break;
134       }
135     }
136
137     if (i == 0)
138     {
139       // not a View we have!
140       if (!noLock) pthread_mutex_unlock(&viewManLock);
141       return 0;
142     }
143   }
144
145   // Save the position we are deleting the view from
146   slotTakenFrom = i;
147
148   // Shift the views on top down one
149   for(; i < topView; i++)
150   {
151     views[i] = views[i + 1];
152   }
153   topView--;
154
155   // Done. Now on to drawing.
156
157   View* newTopBox = views[topView]; // just to make second optimisation easier
158
159   // First optimisation. If there are no views left, don't do anything!
160   if (topView == 0)
161   {
162     Log::getInstance()->log("ViewMan", Log::DEBUG, "re-draw using optimisation 1");
163   }
164
165   // second optimisation. if view being deleted is entirely within the view underneath it,
166   // and was the top most box,
167   // only need to redraw the one underneath
168   else if (   wasTopView
169            && (toDelete->getScreenX() >= newTopBox->getScreenX())
170            && (toDelete->getScreenY() >= newTopBox->getScreenY())
171            && ((toDelete->getScreenX() + toDelete->getWidth()) <= (newTopBox->getScreenX() + newTopBox->getWidth()))
172            && ((toDelete->getScreenY() + toDelete->getHeight()) <= (newTopBox->getScreenY() + newTopBox->getHeight()))
173           )
174   {
175     newTopBox->draw();
176     newTopBox->show();
177     Log::getInstance()->log("ViewMan", Log::DEBUG, "re-draw using optimisation 2");
178   }
179
180   // third optimisation. if the box being deleted is totally within one above it, don't do anything
181   else if ((slotTakenFrom <= topView) && isTotallyCovered(toDelete, slotTakenFrom))
182   {
183     Log::getInstance()->log("ViewMan", Log::DEBUG, "re-draw using optimisation 3");
184   }
185
186   // no optimisations left, redo everything.
187   else
188   {
189     redrawAll(noShow);
190     Log::getInstance()->log("ViewMan", Log::DEBUG, "re-draw using no optimisation");
191   }
192
193   // Delete the view
194   delete toDelete;
195
196   resetThread();
197   if (!noLock) pthread_mutex_unlock(&viewManLock);
198
199   return 1;
200 }
201
202 int ViewMan::isTotallyCovered(View* toDelete, int slotTakenFrom)
203 {
204   int todelx1 = toDelete->getScreenX();
205   int todelx2 = toDelete->getScreenX() + toDelete->getWidth();
206   int todely1 = toDelete->getScreenY();
207   int todely2 = toDelete->getScreenY() + toDelete->getHeight();
208
209   int x1 = 999999;
210   int x2 = 0;
211   int y1 = 999999;
212   int y2 = 0;
213
214   int currentx1;
215   int currentx2;
216   int currenty1;
217   int currenty2;
218
219   for (int i = slotTakenFrom; i <= topView; i++)
220   {
221     currentx1 = views[i]->getScreenX();
222     currentx2 = currentx1 + views[i]->getWidth();
223     currenty1 = views[i]->getScreenY();
224     currenty2 = currenty1 + views[i]->getHeight();
225
226 //    printf("Iteration in tc before. i=%i x1=%i x2=%i y1=%i y2=%i cx1=%i cx2=%i cy1=%i cy2=%i\n", i, x1, x2, y1, y2, currentx1, currentx2, currenty1, currenty2);
227
228     if (currentx1 < x1) x1 = currentx1;
229     if (currentx2 > x2) x2 = currentx2;
230     if (currenty1 < y1) y1 = currenty1;
231     if (currenty2 > y2) y2 = currenty2;
232
233 //    printf("Iteration in tc after . i=%i x1=%i x2=%i y1=%i y2=%i\n", i, x1, x2, y1, y2);
234   }
235
236   // k, now x1 x2 y1 y2 contain the dimensions of the biggest box over the deleted slot
237
238   if (   (x1 <= todelx1)
239       && (x2 >= todelx2)
240       && (y1 <= todely1)
241       && (y2 >= todely2)
242      )
243   {
244     return 1;
245   }
246   else
247   {
248     return 0;
249   }
250 }
251
252 void ViewMan::redrawAll(int noShow)
253 {
254   for (int i = 1; i <= topView; i++)
255   {
256     views[i]->draw();
257   }
258
259   if (!noShow) Box::showAll();
260 }
261
262 // ---------------------------------------------------- END OF REMOVE CODE
263
264
265 void ViewMan::removeAll()
266 {
267   pthread_mutex_lock(&viewManLock);
268
269   // FIXME for don't delete wallpaper cos surface destroy doesn't work
270
271   for (; topView > 1; topView--)
272   {
273     delete views[topView];
274   }
275
276   resetThread();
277   pthread_mutex_unlock(&viewManLock);
278 }
279
280 int ViewMan::handleCommand(UCHAR command)
281 {
282   pthread_mutex_lock(&viewManLock);
283
284   int retVal;
285   int retVal2 = 0;
286   int i;
287
288   // handle command return values
289   // 0 - drop through to next view
290   // 1 - dont drop to next view, but not handled
291   // 2 - handled - stop command here
292   // 4 - handled - delete this view
293
294   for (i=topView; i>0; i--)
295   {
296     retVal = views[i]->handleCommand(command);
297     if (retVal == 1)
298     {
299       // not handled but don't give to any more views
300       pthread_mutex_unlock(&viewManLock);
301       return 0;
302     }
303
304     if (retVal == 2)
305     {
306       // command handled
307       if (views[i]->seconds)
308       {
309         struct timespec currentTime;
310         clock_gettime(CLOCK_REALTIME, &currentTime);
311         views[i]->delSec = currentTime.tv_sec + views[i]->seconds;
312         views[i]->delNSec = currentTime.tv_nsec;
313         resetThread();
314       }
315       pthread_mutex_unlock(&viewManLock);
316       retVal2 = 1;
317       break;
318     }
319     else if (retVal == 4)
320     {
321 //      removeNoLock(views[i]);
322 //      Box::showAll();
323 //      resetThread();
324       removeView(views[i], 1);
325       pthread_mutex_unlock(&viewManLock);
326       retVal2 = 1;
327       break;
328     }
329   }
330
331   Log::getInstance()->log("ViewMan", Log::DEBUG, "out of handlecommand code, now on to messages");
332
333   processMessageQueue();
334
335   pthread_mutex_unlock(&viewManLock);
336   return retVal2;
337 }
338
339 void ViewMan::processMessage(Message* m)
340 {
341   if (m->to != this)
342   {
343     for (int i = topView; i > 0; i--)
344     {
345       if (views[i] == m->to)
346       {
347         Log::getInstance()->log("ViewMan", Log::DEBUG, "sending message to view");
348         Log::getInstance()->log("ViewMan", Log::DEBUG, "%p %p %lu", m->from, m->to, m->message);
349         views[i]->processMessage(m);
350         return;
351       }
352     }
353   }
354
355   Log::getInstance()->log("ViewMan", Log::DEBUG, "it's for meeee!");
356
357   switch(m->message)
358   {
359     case Message::CLOSE_ME:
360     {
361       removeView((View*)m->from, 1, 1);
362       break;
363     }
364     case Message::UPDATE_SCREEN:
365     {
366       Box::showAll();
367       break;
368     }
369     case Message::SWAP_ME_FOR:
370     {
371       View* toReplace = (View*) m->parameter;
372
373       removeView((View*)m->from, 1, 1);
374
375       topView++;
376       views[topView] = toReplace;
377       if (toReplace->seconds)
378       {
379         struct timespec currentTime;
380         clock_gettime(CLOCK_REALTIME, &currentTime);
381         toReplace->delSec = currentTime.tv_sec + toReplace->seconds;
382         toReplace->delNSec = currentTime.tv_nsec;
383       }
384       toReplace->draw();
385       Box::showAll();
386       resetThread();
387       break;
388     }
389     case Message::ADD_VIEW:
390     {
391       View* toAdd = (View*)m->parameter;
392       addNoLock(toAdd);
393       toAdd->draw();
394       Box::showAll();
395     }
396   }
397 }
398
399 int ViewMan::timedDelete(View* v, int seconds, int lock)
400 {
401   int success;
402
403   if (lock) pthread_mutex_lock(&viewManLock);
404
405   struct timespec currentTime;
406   clock_gettime(CLOCK_REALTIME, &currentTime);
407
408   if (v)
409   {
410     if (seconds)
411     {
412       v->seconds = seconds;
413       v->delSec = currentTime.tv_sec + seconds;
414       v->delNSec = currentTime.tv_nsec;
415     }
416     else
417     {
418       // for cancelling the delete
419       v->seconds = 0;
420       v->delSec = 0;
421       v->delNSec = 0;
422     }
423     success = 1;
424   }
425   else
426   {
427     success = 0;
428   }
429
430   resetThread();
431   if (lock) pthread_mutex_unlock(&viewManLock);
432
433   return success;
434 }
435
436
437 // THE THREAD CODE STARTS HERE /////////////////////////////////////////////////////////////
438
439 void ViewMan::resetThread()
440 {
441   // must be called with mutex already locked
442   resetThreadFlag = 1;
443   pthread_cond_signal(&autoDeleteThreadSignal);
444 }
445
446 // undeclared function
447 void startAutoDeleteThread2(void *arg)
448 {
449   ViewMan *v = (ViewMan *)arg;
450   v->startAutoDeleteThread3();
451 }
452 int ViewMan::startAutoDeleteThread()
453 {
454   pthread_mutex_lock(&viewManLock);
455   resetThreadFlag = 1;
456   autoDeleteThreadRun = 1;
457   if (pthread_create(&autoDeleteThread, NULL, (void*(*)(void*))startAutoDeleteThread2, (void *)this) == -1) return 0;
458   return 1;
459 }
460
461 void ViewMan::startAutoDeleteThread3()
462 {
463   struct timespec nextTime;
464   View* nextToDelete = NULL;
465
466   // I don't want signals
467   sigset_t sigset;
468   sigfillset(&sigset);
469   pthread_sigmask(SIG_BLOCK, &sigset, NULL);
470
471   // locked here
472
473   while(1)
474   {
475     if (resetThreadFlag)
476     {
477       resetThreadFlag = 0;
478
479       // Work out the next View to be deleted
480
481       nextTime.tv_sec = 0;
482       nextTime.tv_nsec = 0;
483       nextToDelete = NULL;
484
485       for(int i = 1; i <= topView; i++)
486       {
487         if ((views[i]->delSec > 0) && (views[i]->delNSec > 0))
488         {
489           if ((nextTime.tv_sec == 0) && (nextTime.tv_nsec == 0))
490           {
491             nextTime.tv_sec = views[i]->delSec;
492             nextTime.tv_nsec = views[i]->delNSec;
493             nextToDelete = views[i];
494           }
495           else
496           {
497             if (views[i]->delSec < nextTime.tv_sec)
498             {
499               nextTime.tv_sec = views[i]->delSec;
500               nextTime.tv_nsec = views[i]->delNSec;
501               nextToDelete = views[i];
502             }
503             else if (views[i]->delSec == nextTime.tv_sec)
504             {
505               if (views[i]->delNSec < nextTime.tv_nsec)
506               {
507                 nextTime.tv_sec = views[i]->delNSec;
508                 nextTime.tv_nsec = views[i]->delNSec;
509                 nextToDelete = views[i];
510               }
511             }
512           }
513         }
514         // no case
515       }
516       // end
517     }
518
519     if (nextTime.tv_sec == 0)
520     {
521       pthread_cond_wait(&autoDeleteThreadSignal, &viewManLock);
522     }
523     else
524     {
525       pthread_cond_timedwait(&autoDeleteThreadSignal, &viewManLock, &nextTime);
526     }
527
528     // ok. we have been signalled or the time has run out
529
530     // see if we have been signalled. we only get signalled if we
531     // are to reset the timer or if we are to die completely
532     if (!autoDeleteThreadRun)
533     {
534       pthread_exit(NULL);
535       // exiting thread with mutex locked
536     }
537
538     if (resetThreadFlag) continue;
539
540     // timer ran out
541     Log::getInstance()->log("ViewMan", Log::DEBUG, "About to remove %p", nextToDelete);
542     removeView(nextToDelete, 1);  // enter this method with mutex locked
543
544     resetThreadFlag = 1;
545   }
546 }
547