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