04bc85d9059b7b7edb9d57bf9ec764f8f95e1e7f
[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, int top)
9 {
10         assert(!root->m_desktop);
11         
12                 /* buffered mode paints back-to-front, while immediate mode is front-to-back. */
13         if (m_comp_mode == cmBuffered)
14                 top = !top;
15         
16         if (top)
17                 m_root.push_back(root);
18         else
19                 m_root.push_front(root);
20         root->m_desktop = this;
21
22                 /* the creation will be postponed. */
23         root->m_comp_buffer = 0;
24 }
25
26 void eWidgetDesktop::removeRootWidget(eWidget *root)
27 {
28         if (m_comp_mode == cmBuffered)
29                 removeBufferForWidget(root);
30
31         m_root.remove(root);
32 }
33
34 int eWidgetDesktop::movedWidget(eWidget *root)
35 {
36         if ((m_comp_mode == cmBuffered) && (root->m_comp_buffer))
37         {
38                 root->m_comp_buffer->m_position = root->position();
39 //              redrawComposition(0);
40                 return 0;
41         }
42         
43         return -1;
44 }
45
46 void eWidgetDesktop::calcWidgetClipRegion(eWidget *widget, gRegion &parent_visible)
47 {
48                 /* start with our clip region, clipped with the parent's */
49         if (widget->m_vis & eWidget::wVisShow)
50         {
51                 widget->m_visible_region = widget->m_clip_region;
52                 widget->m_visible_region.moveBy(widget->position());
53                 widget->m_visible_region &= parent_visible; // in parent space!
54
55                 if (!widget->isTransparent())
56                                 /* remove everything this widget will contain from parent's visible list, unless widget is transparent. */
57                         parent_visible -= widget->m_visible_region; // will remove child regions too!
58
59                         /* now prepare for recursing to childs */
60                 widget->m_visible_region.moveBy(-widget->position());            // now in local space
61         } else
62                 widget->m_visible_region = gRegion();
63
64         widget->m_visible_with_childs = widget->m_visible_region;
65         
66                         /* add childs in reverse (Z) order - we're going from front-to-bottom here. */
67         ePtrList<eWidget>::iterator i(widget->m_childs.end());
68         
69         for (;;)
70         {
71                 if (i != widget->m_childs.end())
72                 {
73                         if (i->m_vis & eWidget::wVisShow)
74                                 calcWidgetClipRegion(*i, widget->m_visible_region);
75                         else
76                                 clearVisibility(*i);
77                 }
78                 if (i == widget->m_childs.begin())
79                         break;
80                 --i;
81         }
82 }
83
84 void eWidgetDesktop::recalcClipRegions(eWidget *root)
85 {
86         if (m_comp_mode == cmImmediate)
87         {
88                 gRegion background_before = m_screen.m_background_region;
89                 
90                 m_screen.m_background_region = gRegion(eRect(ePoint(0, 0), m_screen.m_screen_size));
91         
92                 for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
93                 {
94                         if (!(i->m_vis & eWidget::wVisShow))
95                         {
96                                 clearVisibility(i);
97                                 continue;
98                         }
99                         
100                         gRegion visible_before = i->m_visible_with_childs;
101
102                         calcWidgetClipRegion(*i, m_screen.m_background_region);
103                         
104                         gRegion redraw = (i->m_visible_with_childs - visible_before) | (visible_before - i->m_visible_with_childs);
105
106                         redraw.moveBy(i->position());
107                         
108                         invalidate(redraw);
109                 }
110                 
111                 gRegion redraw = (background_before - m_screen.m_background_region) | (m_screen.m_background_region - background_before);
112                 invalidate(redraw);
113         } else if (m_comp_mode == cmBuffered)
114         {
115                 if (!root->m_vis & eWidget::wVisShow)
116                 {
117                         clearVisibility(root);
118                         removeBufferForWidget(root);
119                         return;
120                 }
121                 if ((!root->m_comp_buffer) || (root->size() != root->m_comp_buffer->m_screen_size))
122                         createBufferForWidget(root);
123
124                 eWidgetDesktopCompBuffer *comp = root->m_comp_buffer;
125
126                 gRegion visible_before = root->m_visible_with_childs;
127                  
128                 comp->m_background_region = gRegion(eRect(comp->m_position, comp->m_screen_size));
129                 
130                 gRegion visible_new = root->m_visible_with_childs - visible_before;
131                 gRegion visible_lost = visible_before - root->m_visible_with_childs;
132                 visible_new.moveBy(root->position());
133                 visible_lost.moveBy(root->position());
134                 
135                         /* this sucks, obviously. */
136                 invalidate(visible_new);
137                 invalidate(visible_lost);
138                 
139                 calcWidgetClipRegion(root, comp->m_background_region);
140         }
141 }
142
143 void eWidgetDesktop::invalidate(const gRegion &region)
144 {
145         if (region.empty())
146                 return;
147         
148         if (m_timer && !m_require_redraw)
149                 m_timer->start(0, 1); // start singleshot redraw timer
150         
151         m_require_redraw = 1;
152         
153         if (m_comp_mode == cmImmediate)
154                 m_screen.m_dirty_region |= region;
155         else
156                 for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
157                 {
158                         if (!(i->m_vis & eWidget::wVisShow))
159                                 continue;
160                         
161                         eWidgetDesktopCompBuffer *comp = i->m_comp_buffer;
162                         
163                         gRegion mregion = region;
164                         comp->m_dirty_region |= mregion;
165                 }
166 }
167
168 void eWidgetDesktop::setBackgroundColor(eWidgetDesktopCompBuffer *comp, gRGB col)
169 {
170         comp->m_background_color = col;
171         
172                 /* if there's something visible from the background, redraw it with the new color. */
173         if (comp->m_dc && comp->m_background_region.valid() && !comp->m_background_region.empty())
174         {
175                         /* todo: split out "setBackgroundColor / clear"... maybe? */
176                 gPainter painter(comp->m_dc);
177                 painter.resetClip(comp->m_background_region);
178                 painter.setBackgroundColor(comp->m_background_color);
179                 painter.clear();
180         }
181 }
182
183 void eWidgetDesktop::setBackgroundColor(gRGB col)
184 {
185         setBackgroundColor(&m_screen, col);
186
187         if (m_comp_mode == cmBuffered)
188                 for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
189                         setBackgroundColor(i->m_comp_buffer, col);
190 }
191
192 void eWidgetDesktop::setPalette(gPixmap &pm)
193 {
194 //      if (m_comp_mode == cmImmediate)
195         {
196                 ASSERT(m_screen.m_dc);
197                 gPainter painter(m_screen.m_dc);
198                 painter.setPalette(&pm);
199         }
200         
201         if (m_comp_mode == cmBuffered)
202         {
203                 for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
204                 {
205                         ASSERT(i->m_comp_buffer->m_dc);
206                         gPainter painter(i->m_comp_buffer->m_dc);
207                         painter.setPalette(&pm);
208                 }
209         }
210 }
211
212 void eWidgetDesktop::paintBackground(eWidgetDesktopCompBuffer *comp)
213 {
214         comp->m_dirty_region &= comp->m_background_region;
215         
216         gPainter painter(comp->m_dc);
217         
218         painter.resetClip(comp->m_dirty_region);
219         painter.setBackgroundColor(comp->m_background_color);
220         painter.clear();
221         painter.flush();
222         
223         comp->m_dirty_region = gRegion();
224 }
225
226 void eWidgetDesktop::paint()
227 {
228         m_require_redraw = 0;
229         
230                 /* walk all root windows. */
231         for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
232         {
233                 eWidgetDesktopCompBuffer *comp = (m_comp_mode == cmImmediate) ? &m_screen : i->m_comp_buffer;
234                 
235                 if (!(i->m_vis & eWidget::wVisShow))
236                         continue;
237                 
238                 {
239                         gPainter painter(comp->m_dc);
240                         painter.moveOffset(-comp->m_position);
241                         i->doPaint(painter, comp->m_dirty_region);
242                         painter.resetOffset();
243                 }
244
245                 if (m_comp_mode != cmImmediate)
246                         paintBackground(comp);
247         }
248         
249         if (m_comp_mode == cmImmediate)
250                 paintBackground(&m_screen);
251         
252         if (m_comp_mode == cmBuffered)
253         {
254 //              redrawComposition(0);
255         }
256 }
257
258 void eWidgetDesktop::setDC(gDC *dc)
259 {
260         m_screen.m_dc = dc;
261         if (m_comp_mode == cmBuffered)
262                 redrawComposition(1);
263 }
264
265 void eWidgetDesktop::setRedrawTask(eMainloop &ml)
266 {
267         if (m_mainloop)
268         {
269                 delete m_timer;
270                 m_timer = 0;
271                 m_mainloop = 0;
272         }
273         m_mainloop = &ml;
274         m_timer = new eTimer(m_mainloop);
275         CONNECT(m_timer->timeout, eWidgetDesktop::paint);
276         
277         if (m_require_redraw)
278                 m_timer->start(0, 1);
279 }
280
281 void eWidgetDesktop::makeCompatiblePixmap(gPixmap &pm)
282 {
283         if (m_comp_mode != cmImmediate)
284                 return;
285         
286 //      eDebug("widgetDesktop: make compatible pixmap of %p", &pm);
287         if (!m_screen.m_dc)
288         {
289                 eWarning("eWidgetDesktop: no DC to make pixmap compatible with!");
290                 return;
291         }
292
293         ePtr<gDC> pixmap_dc = new gDC(&pm);
294         gPainter pixmap_painter(pixmap_dc);
295         
296         ePtr<gPixmap> target_pixmap;
297         m_screen.m_dc->getPixmap(target_pixmap);
298         
299         assert(target_pixmap);
300         
301         pixmap_painter.mergePalette(target_pixmap);
302 }
303
304 void eWidgetDesktop::setCompositionMode(int mode)
305 {
306         m_comp_mode = mode;
307         
308         if (mode == cmBuffered)
309                 for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
310                         createBufferForWidget(*i);
311         else
312                 for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
313                         removeBufferForWidget(*i);
314 }
315
316 eWidgetDesktop::eWidgetDesktop(eSize size): m_mainloop(0), m_timer(0)
317 {
318         m_screen.m_dirty_region = gRegion(eRect(ePoint(0, 0), size));
319         m_screen.m_screen_size = size;
320         m_require_redraw = 0;
321
322         CONNECT(gRC::getInstance()->notify, eWidgetDesktop::notify);
323         setCompositionMode(cmImmediate);
324 }
325
326 eWidgetDesktop::~eWidgetDesktop()
327 {
328                 /* tell registered root windows that they no longer have a desktop. */
329         for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); )
330         {
331                 i->m_desktop = 0;
332                 i = m_root.erase(i);
333         }
334                 /* destroy all buffers */
335         setCompositionMode(-1);
336 }
337
338 void eWidgetDesktop::createBufferForWidget(eWidget *widget)
339 {
340         removeBufferForWidget(widget);
341         
342         eWidgetDesktopCompBuffer *comp = widget->m_comp_buffer = new eWidgetDesktopCompBuffer;
343         
344         eDebug("create buffer for widget, %d x %d\n", widget->size().width(), widget->size().height());
345         
346         eRect bbox = eRect(widget->position(), widget->size());
347         comp->m_position = bbox.topLeft();
348         comp->m_dirty_region = gRegion(eRect(ePoint(0, 0), bbox.size()));
349         comp->m_screen_size = bbox.size();
350                 /* TODO: configurable bit depth. */
351         
352                 /* clone palette. FIXME. */
353         ePtr<gPixmap> pm = new gPixmap(comp->m_screen_size, 32, 1), pm_screen;
354         pm->surface->clut.data = new gRGB[256];
355         pm->surface->clut.colors = 256;
356         pm->surface->clut.start = 0;
357         
358         
359         m_screen.m_dc->getPixmap(pm_screen);
360         
361         memcpy(pm->surface->clut.data, pm_screen->surface->clut.data, 256 * sizeof(gRGB));
362
363         comp->m_dc = new gDC(pm);
364 }
365
366 void eWidgetDesktop::removeBufferForWidget(eWidget *widget)
367 {
368         if (widget->m_comp_buffer)
369         {
370                 delete widget->m_comp_buffer;
371                 widget->m_comp_buffer = 0;
372         }
373 }
374
375 void eWidgetDesktop::redrawComposition(int notified)
376 {
377         if (m_comp_mode != cmBuffered)
378                 return;
379         
380         assert(m_screen.m_dc);
381         
382         gPainter p(m_screen.m_dc);
383         p.resetClip(eRect(ePoint(0, 0), m_screen.m_screen_size));
384         p.setBackgroundColor(m_screen.m_background_color);
385         p.clear();
386         
387         for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
388         {
389                 if (!i->isVisible())
390                         continue;
391                 ePtr<gPixmap> pm;
392                 ASSERT(i->m_comp_buffer);
393                 i->m_comp_buffer->m_dc->getPixmap(pm);
394                 p.blit(pm, i->m_comp_buffer->m_position, eRect(), gPixmap::blitAlphaBlend);
395         }
396
397                 // flip activates on next vsync.        
398         p.flip();
399         p.waitVSync();
400
401         if (notified)
402                 p.notify();
403
404         for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
405                 if (i->m_animation.m_active)
406                         i->m_animation.tick(1);
407 }
408
409 void eWidgetDesktop::notify()
410 {
411         redrawComposition(1);
412 }
413
414 void eWidgetDesktop::clearVisibility(eWidget *widget)
415 {
416         widget->m_visible_with_childs = gRegion();
417         for (ePtrList<eWidget>::iterator i(widget->m_childs.begin()); i != widget->m_childs.end(); ++i)
418                 clearVisibility(*i);
419 }