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