]> 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   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 //  Surface::bufferToScreen();
371
372   resetThread();
373   pthread_mutex_unlock(&viewManLock);
374 }
375
376 int ViewMan::handleCommand(UCHAR command)
377 {
378   pthread_mutex_lock(&viewManLock);
379
380   int retVal;
381   int retVal2 = 0;
382   int i;
383
384   if (command != Remote::NA_NONE)
385   {
386     // handle command return values
387     // 0 - drop through to next view
388     // 1 - dont drop to next view, but not handled
389     // 2 - handled - stop command here
390     // 4 - handled - delete this view
391
392     for (i=numViews-1; i>=0; i--)
393     {
394       Log::getInstance()->log("ViewMan", Log::DEBUG, "Giving command to i=%i", i);
395       retVal = views[i]->handleCommand(command);
396       if (retVal == 1)
397       {
398         // not handled but don't give to any more views
399         pthread_mutex_unlock(&viewManLock);
400         return 0;
401       }
402
403       if (retVal == 2)
404       {
405         // command handled
406         if (views[i]->seconds)
407         {
408           struct timespec currentTime;
409           clock_gettime(CLOCK_REALTIME, &currentTime);
410           views[i]->delSec = currentTime.tv_sec + views[i]->seconds;
411           views[i]->delNSec = currentTime.tv_nsec;
412           resetThread();
413         }
414         pthread_mutex_unlock(&viewManLock);
415         retVal2 = 1;
416         break;
417       }
418       else if (retVal == 4)
419       {
420   //      removeNoLock(views[i]);
421   //      Box::showAll();
422   //      resetThread();
423         Log::getInstance()->log("ViewMan", Log::DEBUG, "Return 4: i=%i, views[i]=%p", i, views[i]);
424         removeView(views[i], 1);
425         pthread_mutex_unlock(&viewManLock);
426         retVal2 = 1;
427         break;
428       }
429     }
430   }
431   else
432   {
433     // fake the return code
434     retVal2 = 2;
435   }
436
437   Log::getInstance()->log("ViewMan", Log::DEBUG, "out of handlecommand code, now on to messages");
438
439   processMessageQueue();
440
441   pthread_mutex_unlock(&viewManLock);
442   return retVal2;
443 }
444
445 void ViewMan::processMessage(Message* m)
446 {
447   if (m->to != this)
448   {
449     for (int i = numViews-1; i >= 0; i--)
450     {
451       if (views[i] == m->to)
452       {
453         Log::getInstance()->log("ViewMan", Log::DEBUG, "sending message to view");
454         Log::getInstance()->log("ViewMan", Log::DEBUG, "%p %p %lu", m->from, m->to, m->message);
455         views[i]->processMessage(m);
456         return;
457       }
458     }
459   }
460
461   Log::getInstance()->log("ViewMan", Log::DEBUG, "it's for meeee!");
462
463   switch(m->message)
464   {
465     case Message::CLOSE_ME:
466     {
467       removeView((View*)m->from, 1);
468       break;
469     }
470     case Message::SWAP_ME_FOR:
471     {
472       View* toReplace = (View*) m->parameter;
473
474       removeView((View*)m->from, 1);
475
476       views[numViews] = toReplace;
477       ++numViews;
478       if (toReplace->seconds)
479       {
480         struct timespec currentTime;
481         clock_gettime(CLOCK_REALTIME, &currentTime);
482         toReplace->delSec = currentTime.tv_sec + toReplace->seconds;
483         toReplace->delNSec = currentTime.tv_nsec;
484       }
485       toReplace->draw();
486       toReplace->show();
487       resetThread();
488       break;
489     }
490     case Message::ADD_VIEW:
491     {
492       View* toAdd = (View*)m->parameter;
493       addNoLock(toAdd);
494       toAdd->draw();
495       toAdd->show();
496     }
497   }
498 }
499
500 int ViewMan::timedDelete(View* v, int seconds, int lockMutex)
501 {
502   int success;
503
504   if (lockMutex) pthread_mutex_lock(&viewManLock);
505
506   struct timespec currentTime;
507   clock_gettime(CLOCK_REALTIME, &currentTime);
508
509   if (v)
510   {
511     if (seconds)
512     {
513       v->seconds = seconds;
514       v->delSec = currentTime.tv_sec + seconds;
515       v->delNSec = currentTime.tv_nsec;
516     }
517     else
518     {
519       // for cancelling the delete
520       v->seconds = 0;
521       v->delSec = 0;
522       v->delNSec = 0;
523     }
524     success = 1;
525   }
526   else
527   {
528     success = 0;
529   }
530
531   resetThread();
532   if (lockMutex) pthread_mutex_unlock(&viewManLock);
533
534   return success;
535 }
536
537
538 // THE THREAD CODE STARTS HERE /////////////////////////////////////////////////////////////
539
540 void ViewMan::resetThread()
541 {
542   // must be called with mutex already locked
543   resetThreadFlag = 1;
544   pthread_cond_signal(&autoDeleteThreadSignal);
545 }
546
547 // undeclared function
548 void startAutoDeleteThread2(void *arg)
549 {
550   ViewMan *v = (ViewMan *)arg;
551   v->startAutoDeleteThread3();
552 }
553 int ViewMan::startAutoDeleteThread()
554 {
555   pthread_mutex_lock(&viewManLock);
556   resetThreadFlag = 1;
557   autoDeleteThreadRun = 1;
558   if (pthread_create(&autoDeleteThread, NULL, (void*(*)(void*))startAutoDeleteThread2, (void *)this) == -1) return 0;
559   return 1;
560 }
561
562 void ViewMan::startAutoDeleteThread3()
563 {
564   struct timespec nextTime;
565   View* nextToDelete = NULL;
566
567   // I don't want signals
568   sigset_t sigs;
569   sigfillset(&sigs);
570   pthread_sigmask(SIG_BLOCK, &sigs, NULL);
571
572   // locked here
573
574   while(1)
575   {
576     if (resetThreadFlag)
577     {
578       resetThreadFlag = 0;
579
580       // Work out the next View to be deleted
581
582       nextTime.tv_sec = 0;
583       nextTime.tv_nsec = 0;
584       nextToDelete = NULL;
585
586       for(int i = 0; i < numViews; i++)
587       {
588         if ((views[i]->delSec > 0) && (views[i]->delNSec > 0))
589         {
590           if ((nextTime.tv_sec == 0) && (nextTime.tv_nsec == 0))
591           {
592             nextTime.tv_sec = views[i]->delSec;
593             nextTime.tv_nsec = views[i]->delNSec;
594             nextToDelete = views[i];
595           }
596           else
597           {
598             if (views[i]->delSec < nextTime.tv_sec)
599             {
600               nextTime.tv_sec = views[i]->delSec;
601               nextTime.tv_nsec = views[i]->delNSec;
602               nextToDelete = views[i];
603             }
604             else if (views[i]->delSec == nextTime.tv_sec)
605             {
606               if (views[i]->delNSec < nextTime.tv_nsec)
607               {
608                 nextTime.tv_sec = views[i]->delNSec;
609                 nextTime.tv_nsec = views[i]->delNSec;
610                 nextToDelete = views[i];
611               }
612             }
613           }
614         }
615         // no case
616       }
617       // end
618     }
619
620     if (nextTime.tv_sec == 0)
621     {
622       pthread_cond_wait(&autoDeleteThreadSignal, &viewManLock);
623     }
624     else
625     {
626       pthread_cond_timedwait(&autoDeleteThreadSignal, &viewManLock, &nextTime);
627     }
628
629     // ok. we have been signalled or the time has run out
630
631     // see if we have been signalled. we only get signalled if we
632     // are to reset the timer or if we are to die completely
633     if (!autoDeleteThreadRun)
634     {
635       pthread_exit(NULL);
636       // exiting thread with mutex locked
637     }
638
639     if (resetThreadFlag) continue;
640
641     // timer ran out
642     Log::getInstance()->log("ViewMan", Log::DEBUG, "About to remove %p", nextToDelete);
643     removeView(nextToDelete, 1);  // enter this method with mutex locked
644
645     resetThreadFlag = 1;
646   }
647 }
648