picexif: fix buffer overflow
[enigma2.git] / lib / gui / ewidgetdesktop.cpp
1 #include <lib/gui/ewidgetdesktop.h>
2 #include <lib/gui/ewidget.h>
3 #include <lib/base/ebase.h>
4 #include <lib/gdi/grc.h>
5
6 extern void dumpRegion(const gRegion &region);
7
8 void eWidgetDesktop::addRootWidget(eWidget *root)
9 {
10         ASSERT(!root->m_desktop);
11         
12         int invert_sense = 0;
13                 /* buffered mode paints back-to-front, while immediate mode is front-to-back. */
14         if (m_comp_mode == cmBuffered)
15                 invert_sense = 1;
16         
17         ePtrList<eWidget>::iterator insert_position = m_root.begin();
18         
19         for (;;)
20         {
21                 if ((insert_position == m_root.end()) || (invert_sense ^ (insert_position->m_z_position < root->m_z_position)))
22                 {
23                         m_root.insert(insert_position, root);
24                         break;
25                 }
26                 ++insert_position;
27         }
28         
29         root->m_desktop = this;
30
31                 /* the creation will be postponed. */
32         for (int i = 0; i < MAX_LAYER; ++i)
33                 root->m_comp_buffer[i] = 0;
34 }
35
36 void eWidgetDesktop::removeRootWidget(eWidget *root)
37 {
38         if (m_comp_mode == cmBuffered)
39         {
40                 for (int i = 0; i < MAX_LAYER; ++i)
41                         removeBufferForWidget(root, i);
42         }
43
44         m_root.remove(root);
45 }
46
47 int eWidgetDesktop::movedWidget(eWidget *root)
48 {
49         if (m_comp_mode != cmBuffered)
50                 return -1; /* native move not supported */
51
52         for (int i = 0; i < MAX_LAYER; ++i)
53         {
54                 if (root->m_comp_buffer[i])
55                         root->m_comp_buffer[i]->m_position = root->position();
56 //              redrawComposition(0);
57         }
58
59         return 0; /* native move ok */
60 }
61
62 void eWidgetDesktop::calcWidgetClipRegion(eWidget *widget, gRegion &parent_visible)
63 {
64                 /* start with our clip region, clipped with the parent's */
65         if (widget->m_vis & eWidget::wVisShow)
66         {
67                 widget->m_visible_region = widget->m_clip_region;
68                 widget->m_visible_region.moveBy(widget->position());
69                 widget->m_visible_region &= parent_visible; // in parent space!
70
71                 if (!widget->isTransparent())
72                                 /* remove everything this widget will contain from parent's visible list, unless widget is transparent. */
73                         parent_visible -= widget->m_visible_region; // will remove child regions too!
74
75                         /* now prepare for recursing to childs */
76                 widget->m_visible_region.moveBy(-widget->position());            // now in local space
77         } else
78                 widget->m_visible_region = gRegion();
79
80         widget->m_visible_with_childs = widget->m_visible_region;
81         
82                         /* add childs in reverse (Z) order - we're going from front-to-bottom here. */
83         ePtrList<eWidget>::iterator i(widget->m_childs.end());
84         
85         for (;;)
86         {
87                 if (i != widget->m_childs.end())
88                 {
89                         if (i->m_vis & eWidget::wVisShow)
90                                 calcWidgetClipRegion(*i, widget->m_visible_region);
91                         else
92                                 clearVisibility(*i);
93                 }
94                 if (i == widget->m_childs.begin())
95                         break;
96                 --i;
97         }
98 }
99
100 void eWidgetDesktop::recalcClipRegions(eWidget *root)
101 {
102         if (m_comp_mode == cmImmediate)
103         {
104                 gRegion background_before = m_screen.m_background_region;
105                 
106                 m_screen.m_background_region = gRegion(eRect(ePoint(0, 0), m_screen.m_screen_size));
107         
108                 for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
109                 {
110                         if (!(i->m_vis & eWidget::wVisShow))
111                         {
112                                 clearVisibility(i);
113                                 continue;
114                         }
115                         
116                         gRegion visible_before = i->m_visible_with_childs;
117
118                         calcWidgetClipRegion(*i, m_screen.m_background_region);
119                         
120                         gRegion redraw = (i->m_visible_with_childs - visible_before) | (visible_before - i->m_visible_with_childs);
121
122                         redraw.moveBy(i->position());
123                         
124                         invalidate(redraw);
125                 }
126                 
127                 gRegion redraw = (background_before - m_screen.m_background_region) | (m_screen.m_background_region - background_before);
128                 invalidate(redraw);
129         } else if (m_comp_mode == cmBuffered)
130         {
131                 if (!root->m_vis & eWidget::wVisShow)
132                 {
133                         clearVisibility(root);
134                         for (int i = 0; i < MAX_LAYER; ++i)
135                                 removeBufferForWidget(root, i);
136                         return;
137                 }
138
139                 for (int i = 0; i < MAX_LAYER; ++i)
140                 {
141                         eWidgetDesktopCompBuffer *comp = root->m_comp_buffer[i];
142
143                                         /* TODO: layers might not be required to have the screen size, for memory reasons. */
144                         if ((i == 0 && !comp) || (comp && (root->size() != comp->m_screen_size)))
145                                 createBufferForWidget(root, 0);
146
147                         comp = root->m_comp_buffer[i]; /* it might have changed. */
148                         
149                         if (!comp) 
150                                 continue;  /* WAIT, don't we need to invalidate,whatever */
151
152                                         /* CHECKME: don't we need to recalculate everything? after all, our buffer has changed and is likely to be cleared */
153                         gRegion visible_before = root->m_visible_with_childs;
154
155                         comp->m_background_region = gRegion(eRect(comp->m_position, comp->m_screen_size));
156
157                         gRegion visible_new = root->m_visible_with_childs - visible_before;
158                         gRegion visible_lost = visible_before - root->m_visible_with_childs;
159                         visible_new.moveBy(root->position());
160                         visible_lost.moveBy(root->position());
161
162                         invalidate(visible_new, root, i);
163                         invalidate(visible_lost, root, i);
164
165                         calcWidgetClipRegion(root, comp->m_background_region);
166                 }
167         }
168 }
169
170 void eWidgetDesktop::invalidateWidgetLayer(const gRegion &region, const eWidget *widget, int layer)
171 {
172         if (m_comp_mode == cmImmediate)
173         {
174                 invalidate(region);
175                 return;
176         }
177         eWidgetDesktopCompBuffer *comp = widget->m_comp_buffer[layer];
178         if (comp)
179                 comp->m_dirty_region |= region;
180 }
181
182 void eWidgetDesktop::invalidateWidget(const gRegion &region, const eWidget *widget, int layer)
183 {
184         if (m_comp_mode == cmImmediate)
185         {
186                 invalidate(region);
187                 return;
188         }
189
190         if (!(widget->m_vis & eWidget::wVisShow))
191                 return;
192
193         gRegion mregion = region;
194         if (layer == -1)
195                 for (int layer = 0; layer < MAX_LAYER; ++layer)
196                         invalidateWidgetLayer(mregion, widget, layer);
197         else
198                 invalidateWidgetLayer(mregion, widget, layer);
199 }
200
201 void eWidgetDesktop::invalidate(const gRegion &region, const eWidget *widget, int layer)
202 {
203         if (region.empty())
204                 return;
205
206         if (m_timer && !m_require_redraw)
207                 m_timer->start(0, 1); // start singleshot redraw timer
208
209         m_require_redraw = 1;
210
211         if (m_comp_mode == cmImmediate)
212         {
213                         /* in immediate mode, we don't care for widget and layer, we use the topmost. */
214                 m_screen.m_dirty_region |= region;
215         } else
216         {
217                 if (!widget)
218                         for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
219                                 invalidateWidget(region, i);
220                 else
221                         invalidateWidget(region, widget, layer);
222         }
223 }
224
225 void eWidgetDesktop::setBackgroundColor(eWidgetDesktopCompBuffer *comp, gRGB col)
226 {
227         comp->m_background_color = col;
228
229                 /* if there's something visible from the background, redraw it with the new color. */
230         if (comp->m_dc && comp->m_background_region.valid() && !comp->m_background_region.empty())
231         {
232                         /* todo: split out "setBackgroundColor / clear"... maybe? */
233                 gPainter painter(comp->m_dc);
234                 painter.resetClip(comp->m_background_region);
235                 painter.setBackgroundColor(comp->m_background_color);
236                 painter.clear();
237         }
238 }
239
240 void eWidgetDesktop::setBackgroundColor(gRGB col)
241 {
242         setBackgroundColor(&m_screen, col);
243
244         if (m_comp_mode == cmBuffered)
245                 for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
246                 {
247                         for (int l = 0; l < MAX_LAYER; ++l)
248                                 if (i->m_comp_buffer[l])
249                                         setBackgroundColor(i->m_comp_buffer[l], l ? gRGB(0, 0, 0, 0) : col); /* all layers above 0 will have a transparent background */
250                 }
251 }
252
253 void eWidgetDesktop::setPalette(gPixmap &pm)
254 {
255 //      if (m_comp_mode == cmImmediate)
256         {
257                 ASSERT(m_screen.m_dc);
258                 gPainter painter(m_screen.m_dc);
259                 painter.setPalette(&pm);
260         }
261         
262         if (m_comp_mode == cmBuffered)
263         {
264                 for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
265                 {
266                         for (int l = 0; l < MAX_LAYER; ++l)
267                         {
268                                 if (!i->m_comp_buffer[l])
269                                         continue;
270                                 ASSERT(i->m_comp_buffer[l]->m_dc);
271                                 gPainter painter(i->m_comp_buffer[l]->m_dc);
272                                 painter.setPalette(&pm);
273                         }
274                 }
275         }
276 }
277
278 void eWidgetDesktop::paintBackground(eWidgetDesktopCompBuffer *comp)
279 {
280         if (!comp)
281                 return;
282
283         comp->m_dirty_region &= comp->m_background_region;
284         
285         gPainter painter(comp->m_dc);
286         
287         painter.resetClip(comp->m_dirty_region);
288         painter.setBackgroundColor(comp->m_background_color);
289         painter.clear();
290         
291         comp->m_dirty_region = gRegion();
292 }
293
294
295 void eWidgetDesktop::paintLayer(eWidget *widget, int layer)
296 {
297         eWidgetDesktopCompBuffer *comp = (m_comp_mode == cmImmediate) ? &m_screen : widget->m_comp_buffer[layer];
298         if (m_comp_mode == cmImmediate)
299                 ASSERT(layer == 0);
300         if (!comp)
301                 return;
302         gPainter painter(comp->m_dc);
303         painter.moveOffset(-comp->m_position);
304         widget->doPaint(painter, comp->m_dirty_region, layer);
305         painter.resetOffset();
306 }
307
308 void eWidgetDesktop::paint()
309 {
310         m_require_redraw = 0;
311
312                 /* walk all root windows. */
313         for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
314         {
315
316                 if (!(i->m_vis & eWidget::wVisShow))
317                         continue;
318
319                 if (m_comp_mode == cmImmediate)
320                         paintLayer(i, 0);
321                 else
322                         for (int l = 0; l < MAX_LAYER; ++l)
323                         {
324                                 paintLayer(i, l);
325                                 paintBackground(i->m_comp_buffer[l]);
326                         }
327         }
328
329         if (m_comp_mode == cmImmediate)
330                 paintBackground(&m_screen);
331
332         if (m_comp_mode == cmBuffered)
333         {
334 //              redrawComposition(0);
335         } else
336         {
337                 gPainter painter(m_screen.m_dc);
338                 painter.flush();
339         }
340 }
341
342 void eWidgetDesktop::setDC(gDC *dc)
343 {
344         m_screen.m_dc = dc;
345         if (m_comp_mode == cmBuffered)
346                 redrawComposition(1);
347 }
348
349 void eWidgetDesktop::setRedrawTask(eMainloop &ml)
350 {
351         if (m_mainloop)
352         {
353                 m_timer = 0;
354                 m_mainloop = 0;
355         }
356         m_mainloop = &ml;
357         m_timer = eTimer::create(m_mainloop);
358         CONNECT(m_timer->timeout, eWidgetDesktop::paint);
359         
360         if (m_require_redraw)
361                 m_timer->start(0, 1);
362 }
363
364 void eWidgetDesktop::makeCompatiblePixmap(ePtr<gPixmap> &pm)
365 {
366         makeCompatiblePixmap(*(pm.operator->()));
367 }
368
369 void eWidgetDesktop::makeCompatiblePixmap(gPixmap &pm)
370 {
371         if (m_comp_mode != cmImmediate)
372                 return;
373         
374 //      eDebug("widgetDesktop: make compatible pixmap of %p", &pm);
375         if (!m_screen.m_dc)
376         {
377                 eWarning("eWidgetDesktop: no DC to make pixmap compatible with!");
378                 return;
379         }
380
381         ePtr<gPixmap> target_pixmap;
382         m_screen.m_dc->getPixmap(target_pixmap);
383         
384         if (!target_pixmap) {
385                 eDebug("no target pixmap! assuming bpp > 8 for accelerated graphics.");
386                 return;
387         }
388         
389         if (target_pixmap->surface && target_pixmap->surface->bpp > 8)
390                 return;
391
392         ePtr<gDC> pixmap_dc = new gDC(&pm);
393         gPainter pixmap_painter(pixmap_dc);
394         
395         pixmap_painter.mergePalette(target_pixmap);
396 }
397
398 void eWidgetDesktop::setCompositionMode(int mode)
399 {
400         m_comp_mode = mode;
401         
402         if (mode == cmBuffered)
403                 for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
404                         createBufferForWidget(*i, 0);
405         else
406                 for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
407                         for (int l = 0; l < MAX_LAYER; ++l)
408                                 removeBufferForWidget(*i, l);
409 }
410
411 eWidgetDesktop::eWidgetDesktop(eSize size): m_mainloop(0)
412 {
413         m_screen.m_dirty_region = gRegion(eRect(ePoint(0, 0), size));
414         m_screen.m_screen_size = size;
415         m_require_redraw = 0;
416         m_style_id = 0;
417
418         CONNECT(gRC::getInstance()->notify, eWidgetDesktop::notify);
419         setCompositionMode(cmImmediate);
420 }
421
422 eWidgetDesktop::~eWidgetDesktop()
423 {
424                 /* tell registered root windows that they no longer have a desktop. */
425         for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); )
426         {
427                 i->m_desktop = 0;
428                 i = m_root.erase(i);
429         }
430                 /* destroy all buffers */
431         setCompositionMode(-1);
432 }
433
434 void eWidgetDesktop::createBufferForWidget(eWidget *widget, int layer)
435 {
436         removeBufferForWidget(widget, layer);
437
438         eWidgetDesktopCompBuffer *comp = widget->m_comp_buffer[layer] = new eWidgetDesktopCompBuffer;
439
440         eDebug("create buffer for widget layer %d, %d x %d\n", layer, widget->size().width(), widget->size().height());
441
442         eRect bbox = eRect(widget->position(), widget->size());
443         comp->m_position = bbox.topLeft();
444         comp->m_dirty_region = gRegion(eRect(ePoint(0, 0), bbox.size()));
445         comp->m_screen_size = bbox.size();
446                 /* TODO: configurable bit depth. */
447
448                 /* clone palette. FIXME. */
449         ePtr<gPixmap> pm = new gPixmap(comp->m_screen_size, 32, 1), pm_screen;
450         pm->surface->clut.data = new gRGB[256];
451         pm->surface->clut.colors = 256;
452         pm->surface->clut.start = 0;
453
454         m_screen.m_dc->getPixmap(pm_screen);
455
456         memcpy(pm->surface->clut.data, pm_screen->surface->clut.data, 256 * sizeof(gRGB));
457
458         comp->m_dc = new gDC(pm);
459 }
460
461 void eWidgetDesktop::removeBufferForWidget(eWidget *widget, int layer)
462 {
463         if (widget->m_comp_buffer[layer])
464         {
465                 delete widget->m_comp_buffer[layer];
466                 widget->m_comp_buffer[layer] = 0;
467         }
468 }
469
470 void eWidgetDesktop::redrawComposition(int notified)
471 {
472         if (m_comp_mode != cmBuffered)
473                 return;
474         
475         ASSERT(m_screen.m_dc);
476         
477         gPainter p(m_screen.m_dc);
478         p.resetClip(eRect(ePoint(0, 0), m_screen.m_screen_size));
479         p.setBackgroundColor(m_screen.m_background_color);
480         p.clear();
481         
482         for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
483         {
484                 if (!i->isVisible())
485                         continue;
486                 for (int layer = 0; layer < MAX_LAYER; ++layer)
487                 {
488                         ePtr<gPixmap> pm;
489                         if (!i->m_comp_buffer[layer])
490                                 continue;
491                         i->m_comp_buffer[layer]->m_dc->getPixmap(pm);
492                         p.blit(pm, i->m_comp_buffer[layer]->m_position, eRect(), gPixmap::blitAlphaBlend);
493                 }
494         }
495
496                 // flip activates on next vsync.        
497         p.flip();
498         p.waitVSync();
499
500         if (notified)
501                 p.notify();
502
503         for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
504                 if (i->m_animation.m_active)
505                         i->m_animation.tick(1);
506 }
507
508 void eWidgetDesktop::notify()
509 {
510         redrawComposition(1);
511 }
512
513 void eWidgetDesktop::clearVisibility(eWidget *widget)
514 {
515         widget->m_visible_with_childs = gRegion();
516         for (ePtrList<eWidget>::iterator i(widget->m_childs.begin()); i != widget->m_childs.end(); ++i)
517                 clearVisibility(*i);
518 }
519
520 void eWidgetDesktop::resize(eSize size)
521 {
522         m_screen.m_dirty_region = gRegion(eRect(ePoint(0, 0), size));
523         m_screen.m_screen_size = size;
524 }