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