]> git.vomp.tv Git - vompclient.git/blob - boxstack.cc
Vogel recursive delete
[vompclient.git] / boxstack.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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20
21 #include "boxstack.h"
22
23 #include "command.h"
24 #include "remote.h"
25 #include "log.h"
26
27 BoxStack* BoxStack::instance = NULL;
28
29 BoxStack::BoxStack()
30 {
31   if (instance) return;
32   instance = this;
33   initted = 0;
34   numBoxes = 0;
35 }
36
37 BoxStack::~BoxStack()
38 {
39   instance = NULL;
40 }
41
42 BoxStack* BoxStack::getInstance()
43 {
44   return instance;
45 }
46
47 int BoxStack::init()
48 {
49   if (initted) return 0;
50   initted = 1;
51   
52 #ifndef WIN32
53   pthread_mutex_init(&boxLock, NULL);
54 #else
55   boxLock = CreateMutex(NULL,FALSE,NULL);
56 #endif
57
58   return 1;
59 }
60
61 int BoxStack::shutdown()
62 {
63   if (!initted) return 0;
64
65   // FIXME don't think this can work properly, removeAll leaves the wallpaper there!
66   removeAll();
67
68   initted = 0;
69   return 1;
70 }
71
72 int BoxStack::add(Boxx* v)
73 {
74   if (!initted) return 0;
75   
76 #ifndef WIN32
77   pthread_mutex_lock(&boxLock);
78 #else
79   WaitForSingleObject(boxLock, INFINITE);
80 #endif
81   Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for add");  
82   
83   if (numBoxes == 16) return 0;
84   boxes[numBoxes++] = v;
85
86 #ifndef WIN32
87   pthread_mutex_unlock(&boxLock);
88 #else
89   ReleaseMutex(boxLock);
90 #endif
91
92   Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for add");
93   
94   return 1;
95 }
96
97 // ---------------------------------------------------- REMOVE CODE
98
99 int BoxStack::remove(Boxx* toDelete)
100 {
101   if (!initted) return 0;
102
103   #ifndef WIN32
104   pthread_mutex_lock(&boxLock);
105   #else
106   WaitForSingleObject(boxLock, INFINITE);
107   #endif
108   Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for remove");  
109   
110   if (numBoxes == 0)
111   {
112     #ifndef WIN32
113     pthread_mutex_unlock(&boxLock);
114     #else
115     ReleaseMutex(boxLock);
116     #endif
117     Log::getInstance()->log("BoxStack", Log::ERR, "Unlocked for remove numBoxes == 0");  
118     return 0;
119   }
120
121 //  Log::getInstance()->log("BoxStack", Log::DEBUG, "entering remove, numBoxes=%i", numBoxes);
122
123   int i;
124
125   if (toDelete == NULL)
126   {
127     toDelete = boxes[numBoxes-1];
128     i = numBoxes - 1;
129   }
130   else
131   {
132     // to be deleted box is more likely to be at the top
133     for (i = numBoxes-1; i >= 0; i--)
134     {
135 //      Log::getInstance()->log("BoxStack", Log::DEBUG, "todel: %p, i=%i, boxes[i]=%p", toDelete, i, boxes[i]);
136       if (boxes[i] == toDelete) break;
137     }
138
139     if (i == -1)
140     {
141       // not a Box we have!
142       // FIXME
143       #ifndef WIN32
144       pthread_mutex_unlock(&boxLock);
145       #else
146       ReleaseMutex(boxLock);
147       #endif
148       Log::getInstance()->log("BoxStack", Log::ERR, "Unlocked for remove - no boxx deleted");  
149       return 0;
150     }
151   }
152
153 #ifndef WIN32
154   pthread_mutex_unlock(&boxLock);
155 #else
156   ReleaseMutex(boxLock);
157 #endif
158
159 toDelete->preDelete();
160
161 #ifndef WIN32
162   pthread_mutex_lock(&boxLock);
163 #else
164   WaitForSingleObject(boxLock, INFINITE);
165 #endif
166
167 //  Log::getInstance()->log("BoxStack", Log::DEBUG, "Starting deleteBox");
168   deleteBox(i);
169 //  Log::getInstance()->log("BoxStack", Log::DEBUG, "Done deleteBox");
170
171   // Shift the boxes on top down one
172   --numBoxes;
173   for(int j = i; j < numBoxes; j++) boxes[j] = boxes[j+1];
174
175   // If there is only the wallpaper left signal command
176   if (numBoxes == 1)
177   {
178     Message* m = new Message();
179     m->to = Command::getInstance();
180     m->message = Message::LAST_VIEW_CLOSE;
181     Command::getInstance()->postMessageNoLock(m);
182   }
183
184 #ifndef WIN32
185   pthread_mutex_unlock(&boxLock);
186 #else
187   ReleaseMutex(boxLock);
188 #endif
189   Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for remove");  
190  
191   // Delete the box
192   //AVO: do this delete outside the lock to allow for recursive calls within the destructor
193   //     as this box is not in the stack any more, there is no chance for a second delete
194   Log::getInstance()->log("BoxStack", Log::DEBUG, "remove: going to delete boxx %p, num %d", toDelete, numBoxes);  
195   delete toDelete;
196
197   return 1;
198 }
199
200 /////////////////////////////////////////////////////////////////////////////
201 // NEW STUFF
202 /////////////////////////////////////////////////////////////////////////////
203
204 void BoxStack::deleteBox(int z)
205 {
206 //  Log::getInstance()->log("BoxStack", Log::DEBUG, "Delete box %i of %i", z, numBoxes);
207   RegionList rl;
208   boxSplit(boxes[z]->area, z + 1, numBoxes, 1, rl);
209   while(!rl.empty())
210   {
211     repaintRevealed(z, rl.front());
212     rl.pop_front();
213   }
214 }
215
216 void BoxStack::update(Boxx* toUpdate, Region* regionToUpdate)
217 {
218 //  Log::getInstance()->log("BoxStack", Log::DEBUG, "Update called");
219   // Get the z index of the box
220
221 #ifndef WIN32
222   pthread_mutex_lock(&boxLock);
223 #else
224   WaitForSingleObject(boxLock, INFINITE);
225 #endif
226   Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for update");
227
228   int z;
229   for (z = 0; z < numBoxes; z++)
230   {
231     if (boxes[z] == toUpdate) break;
232   }
233
234   if (z == numBoxes)
235   {
236     // not a Box we have!
237     return;
238   }
239
240   // get the region for the whole box, could be less than that
241   // for smaller updates
242
243   Region r = toUpdate->area;
244
245   if (regionToUpdate)
246   {
247     // Can be null if the whole box should be updated
248     // If this is given the numbers are relative to the size of the box, not the screen
249
250     r.x += regionToUpdate->x;
251     r.y += regionToUpdate->y;
252     r.w = regionToUpdate->w;
253     r.h = regionToUpdate->h;
254   }
255
256   RegionList rl;
257
258   Region r2;
259   boxSplit(r, z+1, numBoxes, 1, rl);
260   while(!rl.empty())
261   {
262     r2 = rl.front();
263     r2.z = z;
264     boxes[z]->blt(r2);
265     rl.pop_front();
266   }
267   
268 #ifndef WIN32
269   pthread_mutex_unlock(&boxLock);
270 #else
271   ReleaseMutex(boxLock);
272 #endif  
273   Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for update");
274 }
275
276 void BoxStack::repaintRevealed(int x, Region r)
277 {
278   RegionList rl;
279   boxSplit(r, x - 1, -1, -1, rl);
280
281   Region r2;
282   while(!rl.empty())
283   {
284     r2 = rl.front();
285     boxes[r2.z]->blt(r2);
286     rl.pop_front();
287   }
288 }
289
290 void BoxStack::boxSplit(Region r, int start, int end, int direction, RegionList& rl)
291 {
292 //  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);
293
294   for(int z = start; z != end; z += direction)
295   {
296     if (r.overlappedBy(boxes[z]->area))
297     {
298 //      printf("Z=%i S=%i E=%i D=%i: %i overlaps\n", z, start, end, direction, z);
299
300       int top = r.y;
301       int btm = r.y2();
302
303       if (boxes[z]->area.y > r.y)
304       {
305 //        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, boxes[z]->area.x, boxes[z]->area.y, boxes[z]->area.w, boxes[z]->area.h);
306         top = boxes[z]->area.y;
307         Region newR;
308         newR.x = r.x;
309         newR.y = r.y;
310         newR.w = r.w;
311         newR.h = boxes[z]->area.y - r.y;
312         boxSplit(newR, z + direction, end, direction, rl);
313
314         if (direction == -1)
315         {
316           Region newR2;
317           newR2.x = r.x;
318           newR2.y = boxes[z]->area.y;
319           newR2.w = r.w;
320           newR2.h = r.h - newR.h;
321           boxSplit(newR2, z, end, -1, rl);
322           return;
323         }
324       }
325
326       if (boxes[z]->area.y2() < r.y2())
327       {
328 //        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, boxes[z]->area.x, boxes[z]->area.y, boxes[z]->area.w, boxes[z]->area.h);
329         btm = boxes[z]->area.y2();
330         Region newR;
331         newR.x = r.x;
332         newR.y = boxes[z]->area.y2() + 1;
333         newR.w = r.w;
334         newR.h = r.y2() - newR.y + 1;
335         boxSplit(newR, z + direction, end, direction, rl);
336
337         if (direction == -1)
338         {
339           Region newR2;
340           newR2.x = r.x;
341           newR2.y = r.y;
342           newR2.w = r.w;
343           newR2.h = r.h - newR.h;
344           boxSplit(newR2, z, end, -1, rl);
345           return;
346         }
347       }
348
349       if (boxes[z]->area.x > r.x)
350       {
351 //        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, boxes[z]->area.x, boxes[z]->area.y, boxes[z]->area.w, boxes[z]->area.h);
352         Region newR;
353         newR.x = r.x;
354         newR.y = top;
355         newR.w = boxes[z]->area.x - r.x;
356         newR.h = btm - top + 1;
357         boxSplit(newR, z + direction, end, direction, rl);
358
359         if (direction == -1)
360         {
361           Region newR2;
362           newR2.x = r.x + newR.w;
363           newR2.y = r.y;
364           newR2.w = r.w - newR.w;
365           newR2.h = r.h;
366           boxSplit(newR2, z, end, -1, rl);
367           return;
368         }
369       }
370
371       if (boxes[z]->area.x2() < r.x2())
372       {
373 //        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, boxes[z]->area.x, boxes[z]->area.y, boxes[z]->area.w, boxes[z]->area.h);
374         Region newR;
375         newR.x = boxes[z]->area.x2() + 1;
376         newR.y = top;
377         newR.w = r.x2() - newR.x + 1;
378         newR.h = btm - top + 1;
379         boxSplit(newR, z + direction, end, direction, rl);
380
381         if (direction == -1)
382         {
383           Region newR2;
384           newR2.x = r.x;
385           newR2.y = r.y;
386           newR2.w = r.w - newR.w;
387           newR2.h = r.h;
388           boxSplit(newR2, z, end, -1, rl);
389           return;
390         }
391       }
392
393       if (direction == -1)
394       {
395         // we are going down the stack
396         // r is underlapped by boxes[z]
397         // but we have not split
398         // Therefore this region under test is
399         // completely covering boxes[z]
400
401         // don't go any further down, generate a region and quit
402
403 //        printf("Repaint region: %i %i %i %i\n", r.x, r.y, r.w, r.h);
404         r.z = z;
405         rl.push_front(r);
406       }
407
408 //      printf("Returning from Z=%i\n", z);
409       return;
410     }
411     else
412     {
413 //      printf("Z=%i S=%i E=%i D=%i: %i does not overlap\n", z, start, end, direction, z);
414     }
415   }
416
417   // if direction = 1 then we have come to a region that is
418   // entirely clear of higher boxes and needs to be redrawn
419
420   // if direction = -1 then we have come to a region that is on
421   // the very bottom with nothing below it to repaint.
422   // do nothing. stale window data will be left on screen?
423
424   if (direction == 1)
425   {
426     rl.push_front(r);
427   }
428 }
429
430 /////////////////////////////////////////////////////////////////////////////
431 // END NEW STUFF
432 /////////////////////////////////////////////////////////////////////////////
433
434 // ---------------------------------------------------- END OF REMOVE CODE
435
436
437 void BoxStack::removeAll()
438 {
439   // 1.. Don't delete wallpaper. No point.
440
441   // Need locking on this one??
442   
443   // This is pretty silly now that preDelete needs mutex unlocked
444   
445   Boxx* toDel = NULL;
446   
447   while(numBoxes > 1)
448   {
449     #ifndef WIN32
450       pthread_mutex_lock(&boxLock);
451     #else
452       WaitForSingleObject(boxLock, INFINITE);
453     #endif  
454
455     if (numBoxes == 1)
456     {
457       #ifndef WIN32
458         pthread_mutex_unlock(&boxLock);
459       #else
460         ReleaseMutex(boxLock);
461       #endif
462       break;
463     }    
464   
465     toDel = boxes[numBoxes - 1];
466
467     #ifndef WIN32
468       pthread_mutex_unlock(&boxLock);
469     #else
470       ReleaseMutex(boxLock);
471     #endif
472
473     toDel->preDelete();
474     
475     #ifndef WIN32
476       pthread_mutex_lock(&boxLock);
477     #else
478       WaitForSingleObject(boxLock, INFINITE);
479     #endif  
480
481     // If boxes[numBoxes - 1] isn't toDel then there's a problem
482     if (boxes[numBoxes - 1] == toDel)
483     {
484       --numBoxes;
485     }
486     else
487     {
488       Log::getInstance()->log("BoxStack", Log::ERR, "Can this actually happen? Why?");
489       toDel = NULL;
490     }
491
492     #ifndef WIN32
493       pthread_mutex_unlock(&boxLock);
494     #else
495       ReleaseMutex(boxLock);
496     #endif
497
498     //AVO: do the delete outside the lock to allow for recursive deletes
499     Log::getInstance()->log("BoxStack", Log::DEBUG, "going to delete boxx %p, num=%d", toDel, numBoxes);
500     if (toDel) delete toDel;
501   }
502 }
503
504 int BoxStack::handleCommand(int command)
505 {
506   int retVal;
507   int retVal2 = 0;
508   int i;
509
510   if (command != Remote::NA_NONE)
511   {
512     // handle command return values
513     // 0 - drop through to next box
514     // 1 - dont drop to next box, but not handled
515     // 2 - handled - stop command here
516     // 4 - handled - delete this box
517
518     for (i=numBoxes-1; i>=0; i--)
519     {
520 //      Log::getInstance()->log("BoxStack", Log::DEBUG, "Giving command to i=%i", i);
521       retVal = boxes[i]->handleCommand(command);
522       if (retVal == 1)
523       {
524         // not handled but don't give to any more boxes
525         return 0;
526       }
527
528       if (retVal == 2)
529       {
530         // command handled
531         retVal2 = 1;
532         break;
533       }
534       else if (retVal == 4)
535       {
536 //        Log::getInstance()->log("BoxStack", Log::DEBUG, "Return 4: i=%i, boxes[i]=%p", i, boxes[i]);
537         remove(boxes[i]);
538         retVal2 = 1;
539         break;
540       }
541     }
542   }
543   else
544   {
545     // fake the return code
546     retVal2 = 2;
547   }
548
549   return retVal2;
550 }
551
552 void BoxStack::processMessage(Message* m)
553 {
554   if (m->to != this)
555   {
556     for (int i = numBoxes-1; i >= 0; i--)
557     {
558       if (boxes[i] == m->to)
559       {
560         Log::getInstance()->log("BoxStack", Log::DEBUG, "sending message from box %p to box %p %lu", m->from, m->to, m->message);
561         boxes[i]->processMessage(m);
562         return;
563       }
564     }
565     return;
566   }
567
568   /* Handle mouse events*/
569   // They come in with m->to = NULL? and just need to be delivered to top box?
570   if ((numBoxes > 1) && ((m->message == Message::MOUSE_MOVE) || (m->message == Message::MOUSE_LBDOWN)))
571   {
572     boxes[numBoxes-1]->processMessage(m);
573     return;
574   }
575
576   Log::getInstance()->log("BoxStack", Log::DEBUG, "it's for meeee!");
577
578   switch(m->message)
579   {
580     case Message::CLOSE_ME:
581     {
582       remove((Boxx*)m->from);
583       break;
584     }
585     case Message::ADD_VIEW: // currently not used by anything but it might come in useful again
586     {
587       Boxx* toAdd = (Boxx*)m->parameter;
588       add(toAdd);
589       toAdd->draw();
590       update(toAdd);
591       break;
592     }
593   }
594 }