78c9fbcfec4123f947b8932383e86ab1baab04e1
[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         
231         comp->m_dirty_region = gRegion();
232 }
233
234 void eWidgetDesktop::paint()
235 {
236         m_require_redraw = 0;
237         
238                 /* walk all root windows. */
239         for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
240         {
241                 eWidgetDesktopCompBuffer *comp = (m_comp_mode == cmImmediate) ? &m_screen : i->m_comp_buffer;
242                 
243                 if (!(i->m_vis & eWidget::wVisShow))
244                         continue;
245                 
246                 {
247                         gPainter painter(comp->m_dc);
248                         painter.moveOffset(-comp->m_position);
249                         i->doPaint(painter, comp->m_dirty_region);
250                         painter.resetOffset();
251                 }
252
253                 if (m_comp_mode != cmImmediate)
254                         paintBackground(comp);
255         }
256         
257         if (m_comp_mode == cmImmediate)
258                 paintBackground(&m_screen);
259         
260         if (m_comp_mode == cmBuffered)
261         {
262 //              redrawComposition(0);
263         }
264 }
265
266 void eWidgetDesktop::setDC(gDC *dc)
267 {
268         m_screen.m_dc = dc;
269         if (m_comp_mode == cmBuffered)
270                 redrawComposition(1);
271 }
272
273 void eWidgetDesktop::setRedrawTask(eMainloop &ml)
274 {
275         if (m_mainloop)
276         {
277                 delete m_timer;
278                 m_timer = 0;
279                 m_mainloop = 0;
280         }
281         m_mainloop = &ml;
282         m_timer = new eTimer(m_mainloop);
283         CONNECT(m_timer->timeout, eWidgetDesktop::paint);
284         
285         if (m_require_redraw)
286                 m_timer->start(0, 1);
287 }
288
289 void eWidgetDesktop::makeCompatiblePixmap(ePtr<gPixmap> &pm)
290 {
291         makeCompatiblePixmap(*(pm.operator->()));
292 }
293
294 void eWidgetDesktop::makeCompatiblePixmap(gPixmap &pm)
295 {
296         if (m_comp_mode != cmImmediate)
297                 return;
298         
299 //      eDebug("widgetDesktop: make compatible pixmap of %p", &pm);
300         if (!m_screen.m_dc)
301         {
302                 eWarning("eWidgetDesktop: no DC to make pixmap compatible with!");
303                 return;
304         }
305
306         ePtr<gPixmap> target_pixmap;
307         m_screen.m_dc->getPixmap(target_pixmap);
308         
309         assert(target_pixmap);
310         
311         if (target_pixmap->surface && target_pixmap->surface->bpp > 8)
312                 return;
313
314         ePtr<gDC> pixmap_dc = new gDC(&pm);
315         gPainter pixmap_painter(pixmap_dc);
316         
317         pixmap_painter.mergePalette(target_pixmap);
318 }
319
320 void eWidgetDesktop::setCompositionMode(int mode)
321 {
322         m_comp_mode = mode;
323         
324         if (mode == cmBuffered)
325                 for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
326                         createBufferForWidget(*i);
327         else
328                 for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
329                         removeBufferForWidget(*i);
330 }
331
332 eWidgetDesktop::eWidgetDesktop(eSize size): m_mainloop(0), m_timer(0)
333 {
334         m_screen.m_dirty_region = gRegion(eRect(ePoint(0, 0), size));
335         m_screen.m_screen_size = size;
336         m_require_redraw = 0;
337         m_style_id = 0;
338
339         CONNECT(gRC::getInstance()->notify, eWidgetDesktop::notify);
340         setCompositionMode(cmImmediate);
341 }
342
343 eWidgetDesktop::~eWidgetDesktop()
344 {
345                 /* tell registered root windows that they no longer have a desktop. */
346         for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); )
347         {
348                 i->m_desktop = 0;
349                 i = m_root.erase(i);
350         }
351                 /* destroy all buffers */
352         setCompositionMode(-1);
353 }
354
355 void eWidgetDesktop::createBufferForWidget(eWidget *widget)
356 {
357         removeBufferForWidget(widget);
358         
359         eWidgetDesktopCompBuffer *comp = widget->m_comp_buffer = new eWidgetDesktopCompBuffer;
360         
361         eDebug("create buffer for widget, %d x %d\n", widget->size().width(), widget->size().height());
362         
363         eRect bbox = eRect(widget->position(), widget->size());
364         comp->m_position = bbox.topLeft();
365         comp->m_dirty_region = gRegion(eRect(ePoint(0, 0), bbox.size()));
366         comp->m_screen_size = bbox.size();
367                 /* TODO: configurable bit depth. */
368         
369                 /* clone palette. FIXME. */
370         ePtr<gPixmap> pm = new gPixmap(comp->m_screen_size, 32, 1), pm_screen;
371         pm->surface->clut.data = new gRGB[256];
372         pm->surface->clut.colors = 256;
373         pm->surface->clut.start = 0;
374         
375         
376         m_screen.m_dc->getPixmap(pm_screen);
377         
378         memcpy(pm->surface->clut.data, pm_screen->surface->clut.data, 256 * sizeof(gRGB));
379
380         comp->m_dc = new gDC(pm);
381 }
382
383 void eWidgetDesktop::removeBufferForWidget(eWidget *widget)
384 {
385         if (widget->m_comp_buffer)
386         {
387                 delete widget->m_comp_buffer;
388                 widget->m_comp_buffer = 0;
389         }
390 }
391
392 void eWidgetDesktop::redrawComposition(int notified)
393 {
394         if (m_comp_mode != cmBuffered)
395                 return;
396         
397         assert(m_screen.m_dc);
398         
399         gPainter p(m_screen.m_dc);
400         p.resetClip(eRect(ePoint(0, 0), m_screen.m_screen_size));
401         p.setBackgroundColor(m_screen.m_background_color);
402         p.clear();
403         
404         for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
405         {
406                 if (!i->isVisible())
407                         continue;
408                 ePtr<gPixmap> pm;
409                 ASSERT(i->m_comp_buffer);
410                 i->m_comp_buffer->m_dc->getPixmap(pm);
411                 p.blit(pm, i->m_comp_buffer->m_position, eRect(), gPixmap::blitAlphaBlend);
412         }
413
414                 // flip activates on next vsync.        
415         p.flip();
416         p.waitVSync();
417
418         if (notified)
419                 p.notify();
420
421         for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
422                 if (i->m_animation.m_active)
423                         i->m_animation.tick(1);
424 }
425
426 void eWidgetDesktop::notify()
427 {
428         redrawComposition(1);
429 }
430
431 void eWidgetDesktop::clearVisibility(eWidget *widget)
432 {
433         widget->m_visible_with_childs = gRegion();
434         for (ePtrList<eWidget>::iterator i(widget->m_childs.begin()); i != widget->m_childs.end(); ++i)
435                 clearVisibility(*i);
436 }