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