add ability to lock/unlock the framebuffer (fix locking of lcd.. i hope)
[enigma2.git] / lib / gdi / grc.cpp
1 // for debugging use:
2 // #define SYNC_PAINT
3 #include <unistd.h>
4 #ifndef SYNC_PAINT
5 #include <pthread.h>
6 #endif
7
8 #include <lib/gdi/grc.h>
9 #include <lib/gdi/font.h>
10 #include <lib/base/init.h>
11 #include <lib/base/init_num.h>
12
13 #ifndef SYNC_PAINT
14 void *gRC::thread_wrapper(void *ptr)
15 {
16         return ((gRC*)ptr)->thread();
17 }
18 #endif
19
20 gRC *gRC::instance=0;
21
22 gRC::gRC(): rp(0), wp(0)
23 #ifdef SYNC_PAINT
24 ,m_notify_pump(eApp, 0)
25 #else
26 ,m_notify_pump(eApp, 1)
27 #endif
28 {
29         ASSERT(!instance);
30         instance=this;
31         CONNECT(m_notify_pump.recv_msg, gRC::recv_notify);
32 #ifndef SYNC_PAINT
33         pthread_mutex_init(&mutex, 0);
34         pthread_cond_init(&cond, 0);
35         int res = pthread_create(&the_thread, 0, thread_wrapper, this);
36         if (res)
37                 eFatal("RC thread couldn't be created");
38         else
39                 eDebug("RC thread created successfully");
40 #endif
41 }
42
43 DEFINE_REF(gRC);
44
45 gRC::~gRC()
46 {
47         instance=0;
48
49         gOpcode o;
50         o.opcode=gOpcode::shutdown;
51         submit(o);
52 #ifndef SYNC_PAINT
53         eDebug("waiting for gRC thread shutdown");
54         pthread_join(the_thread, 0);
55         eDebug("gRC thread has finished");
56 #endif
57 }
58
59 void gRC::submit(const gOpcode &o)
60 {
61         while(1)
62         {
63 #ifndef SYNC_PAINT
64                 pthread_mutex_lock(&mutex);
65 #endif
66                 int tmp=wp;
67                 tmp+=1;
68                 if ( tmp == MAXSIZE )
69                         tmp=0;
70                 if ( tmp == rp )
71                 {
72 #ifndef SYNC_PAINT
73                         pthread_mutex_unlock(&mutex);
74 #else
75                         thread();
76 #endif
77                         //printf("render buffer full...\n");
78                         //fflush(stdout);
79                         usleep(1000);  // wait 1 msec
80                         continue;
81                 }
82                 int free=rp-wp;
83                 if ( free <= 0 )
84                         free+=MAXSIZE;
85                 queue[wp++]=o;
86                 if ( wp == MAXSIZE )
87                         wp = 0;
88                 if (o.opcode==gOpcode::flush||o.opcode==gOpcode::shutdown||o.opcode==gOpcode::notify)
89 #ifndef SYNC_PAINT
90                         pthread_cond_signal(&cond);  // wakeup gdi thread
91                 pthread_mutex_unlock(&mutex);
92 #else
93                         thread(); // paint
94 #endif
95                 break;
96         }
97 }
98
99 void *gRC::thread()
100 {
101         int need_notify = 0;
102 #ifndef SYNC_PAINT
103         while (1)
104         {
105                 singleLock s(mutex);
106 #else
107         while (rp != wp)
108         {
109 #endif
110                 if ( rp != wp )
111                 {
112                         gOpcode& o(queue[rp]);
113                         if (o.opcode==gOpcode::shutdown)
114                                 break;
115                         else if (o.opcode==gOpcode::notify)
116                                 need_notify = 1;
117                         else
118                                 o.dc->exec(&o);
119                         rp++;
120                         if ( rp == MAXSIZE )
121                                 rp=0;
122                 }
123                 else
124                 {
125                         if (need_notify)
126                         {
127                                 need_notify = 0;
128                                 m_notify_pump.send(1);
129                         }
130 #ifndef SYNC_PAINT
131                         pthread_cond_wait(&cond, &mutex);
132 #endif
133                 }
134         }
135 #ifndef SYNC_PAINT
136         pthread_exit(0);
137 #endif
138         return 0;
139 }
140
141 void gRC::recv_notify(const int &i)
142 {
143         notify();
144 }
145
146 gRC *gRC::getInstance()
147 {
148         return instance;
149 }
150
151 static int gPainter_instances;
152
153 gPainter::gPainter(gDC *dc, eRect rect): m_dc(dc), m_rc(gRC::getInstance())
154 {
155 //      ASSERT(!gPainter_instances);
156         gPainter_instances++;
157 //      begin(rect);
158 }
159
160 gPainter::~gPainter()
161 {
162         end();
163         gPainter_instances--;
164 }
165
166 void gPainter::setBackgroundColor(const gColor &color)
167 {
168         if ( m_dc->islocked() )
169                 return;
170         gOpcode o;
171         o.opcode = gOpcode::setBackgroundColor;
172         o.dc = m_dc.grabRef();
173         o.parm.setColor = new gOpcode::para::psetColor;
174         o.parm.setColor->color = color;
175
176         m_rc->submit(o);
177 }
178
179 void gPainter::setForegroundColor(const gColor &color)
180 {
181         if ( m_dc->islocked() )
182                 return;
183         gOpcode o;
184         o.opcode = gOpcode::setForegroundColor;
185         o.dc = m_dc.grabRef();
186         o.parm.setColor = new gOpcode::para::psetColor;
187         o.parm.setColor->color = color;
188
189         m_rc->submit(o);
190 }
191
192 void gPainter::setBackgroundColor(const gRGB &color)
193 {
194         if ( m_dc->islocked() )
195                 return;
196         gOpcode o;
197         o.opcode = gOpcode::setBackgroundColorRGB;
198         o.dc = m_dc.grabRef();
199         o.parm.setColorRGB = new gOpcode::para::psetColorRGB;
200         o.parm.setColorRGB->color = color;
201
202         m_rc->submit(o);
203 }
204
205 void gPainter::setForegroundColor(const gRGB &color)
206 {
207         if ( m_dc->islocked() )
208                 return;
209         gOpcode o;
210         o.opcode = gOpcode::setForegroundColorRGB;
211         o.dc = m_dc.grabRef();
212         o.parm.setColorRGB = new gOpcode::para::psetColorRGB;
213         o.parm.setColorRGB->color = color;
214
215         m_rc->submit(o);
216 }
217
218 void gPainter::setFont(gFont *font)
219 {
220         if ( m_dc->islocked() )
221                 return;
222         gOpcode o;
223         o.opcode = gOpcode::setFont;
224         o.dc = m_dc.grabRef();
225         font->AddRef();
226         o.parm.setFont = new gOpcode::para::psetFont;
227         o.parm.setFont->font = font;
228
229         m_rc->submit(o);
230 }
231
232 void gPainter::renderText(const eRect &pos, const std::string &string, int flags)
233 {
234         if ( m_dc->islocked() )
235                 return;
236         gOpcode o;
237         o.opcode=gOpcode::renderText;
238         o.dc = m_dc.grabRef();
239         o.parm.renderText = new gOpcode::para::prenderText;
240         o.parm.renderText->area = pos;
241         o.parm.renderText->text = string;
242         o.parm.renderText->flags = flags;
243         m_rc->submit(o);
244 }
245
246 void gPainter::renderPara(eTextPara *para, ePoint offset)
247 {
248         if ( m_dc->islocked() )
249                 return;
250         gOpcode o;
251         o.opcode=gOpcode::renderPara;
252         o.dc = m_dc.grabRef();
253         o.parm.renderPara = new gOpcode::para::prenderPara;
254         o.parm.renderPara->offset = offset;
255
256         para->AddRef();
257         o.parm.renderPara->textpara = para;
258         m_rc->submit(o);
259 }
260
261 void gPainter::fill(const eRect &area)
262 {
263         if ( m_dc->islocked() )
264                 return;
265         gOpcode o;
266         o.opcode=gOpcode::fill;
267
268         o.dc = m_dc.grabRef();
269         o.parm.fill = new gOpcode::para::pfillRect;
270         o.parm.fill->area = area;
271         m_rc->submit(o);
272 }
273
274 void gPainter::fill(const gRegion &region)
275 {
276         if ( m_dc->islocked() )
277                 return;
278         gOpcode o;
279         o.opcode=gOpcode::fillRegion;
280
281         o.dc = m_dc.grabRef();
282         o.parm.fillRegion = new gOpcode::para::pfillRegion;
283         o.parm.fillRegion->region = region;
284         m_rc->submit(o);
285 }
286
287 void gPainter::clear()
288 {
289         if ( m_dc->islocked() )
290                 return;
291         gOpcode o;
292         o.opcode=gOpcode::clear;
293         o.dc = m_dc.grabRef();
294         o.parm.fill = new gOpcode::para::pfillRect;
295         o.parm.fill->area = eRect();
296         m_rc->submit(o);
297 }
298
299 void gPainter::blit(gPixmap *pixmap, ePoint pos, const eRect &clip, int flags)
300 {
301         if ( m_dc->islocked() )
302                 return;
303         gOpcode o;
304
305         ASSERT(pixmap);
306
307         o.opcode=gOpcode::blit;
308         o.dc = m_dc.grabRef();
309         pixmap->AddRef();
310         o.parm.blit  = new gOpcode::para::pblit;
311         o.parm.blit->pixmap = pixmap;
312         o.parm.blit->position = pos;
313         o.parm.blit->clip = clip;
314         o.parm.blit->flags=flags;
315         m_rc->submit(o);
316 }
317
318
319 void gPainter::setPalette(gRGB *colors, int start, int len)
320 {
321         if ( m_dc->islocked() )
322                 return;
323         gOpcode o;
324         o.opcode=gOpcode::setPalette;
325         o.dc = m_dc.grabRef();
326         gPalette *p=new gPalette;
327
328         o.parm.setPalette = new gOpcode::para::psetPalette;
329         p->data=new gRGB[len];
330
331         memcpy(p->data, colors, len*sizeof(gRGB));
332         p->start=start;
333         p->colors=len;
334         o.parm.setPalette->palette = p;
335         m_rc->submit(o);
336 }
337
338 void gPainter::setPalette(gPixmap *source)
339 {
340         ASSERT(source);
341         setPalette(source->surface->clut.data, source->surface->clut.start, source->surface->clut.colors);
342 }
343
344 void gPainter::mergePalette(gPixmap *target)
345 {
346         if ( m_dc->islocked() )
347                 return;
348         gOpcode o;
349         o.opcode = gOpcode::mergePalette;
350         o.dc = m_dc.grabRef();
351         target->AddRef();
352         o.parm.mergePalette = new gOpcode::para::pmergePalette;
353         o.parm.mergePalette->target = target;
354         m_rc->submit(o);
355 }
356
357 void gPainter::line(ePoint start, ePoint end)
358 {
359         if ( m_dc->islocked() )
360                 return;
361         gOpcode o;
362         o.opcode=gOpcode::line;
363         o.dc = m_dc.grabRef();
364         o.parm.line = new gOpcode::para::pline;
365         o.parm.line->start = start;
366         o.parm.line->end = end;
367         m_rc->submit(o);
368 }
369
370 void gPainter::setOffset(ePoint val)
371 {
372         if ( m_dc->islocked() )
373                 return;
374         gOpcode o;
375         o.opcode=gOpcode::setOffset;
376         o.dc = m_dc.grabRef();
377         o.parm.setOffset = new gOpcode::para::psetOffset;
378         o.parm.setOffset->rel = 0;
379         o.parm.setOffset->value = val;
380         m_rc->submit(o);
381 }
382
383 void gPainter::moveOffset(ePoint rel)
384 {
385         if ( m_dc->islocked() )
386                 return;
387         gOpcode o;
388         o.opcode=gOpcode::setOffset;
389         o.dc = m_dc.grabRef();
390         o.parm.setOffset = new gOpcode::para::psetOffset;
391         o.parm.setOffset->rel = 1;
392         o.parm.setOffset->value = rel;
393         m_rc->submit(o);
394 }
395
396 void gPainter::resetOffset()
397 {
398         if ( m_dc->islocked() )
399                 return;
400         gOpcode o;
401         o.opcode=gOpcode::setOffset;
402         o.dc = m_dc.grabRef();
403         o.parm.setOffset = new gOpcode::para::psetOffset;
404         o.parm.setOffset->rel = 0;
405         o.parm.setOffset->value = ePoint(0, 0);
406         m_rc->submit(o);
407 }
408
409 void gPainter::resetClip(const gRegion &region)
410 {
411         if ( m_dc->islocked() )
412                 return;
413         gOpcode o;
414         o.opcode = gOpcode::setClip;
415         o.dc = m_dc.grabRef();
416         o.parm.clip = new gOpcode::para::psetClip;
417         o.parm.clip->region = region;
418         m_rc->submit(o);
419 }
420
421 void gPainter::clip(const gRegion &region)
422 {
423         if ( m_dc->islocked() )
424                 return;
425         gOpcode o;
426         o.opcode = gOpcode::addClip;
427         o.dc = m_dc.grabRef();
428         o.parm.clip = new gOpcode::para::psetClip;
429         o.parm.clip->region = region;
430         m_rc->submit(o);
431 }
432
433 void gPainter::clippop()
434 {
435         if ( m_dc->islocked() )
436                 return;
437         gOpcode o;
438         o.opcode = gOpcode::popClip;
439         o.dc = m_dc.grabRef();
440         m_rc->submit(o);
441 }
442
443 void gPainter::flush()
444 {
445         if ( m_dc->islocked() )
446                 return;
447         gOpcode o;
448         o.opcode = gOpcode::flush;
449         o.dc = m_dc.grabRef();
450         m_rc->submit(o);
451 }
452
453 void gPainter::waitVSync()
454 {
455         if ( m_dc->islocked() )
456                 return;
457         gOpcode o;
458         o.opcode = gOpcode::waitVSync;
459         o.dc = m_dc.grabRef();
460         m_rc->submit(o);
461 }
462
463 void gPainter::flip()
464 {
465         if ( m_dc->islocked() )
466                 return;
467         gOpcode o;
468         o.opcode = gOpcode::flip;
469         o.dc = m_dc.grabRef();
470         m_rc->submit(o);
471 }
472
473 void gPainter::notify()
474 {
475         if ( m_dc->islocked() )
476                 return;
477         gOpcode o;
478         o.opcode = gOpcode::notify;
479         o.dc = m_dc.grabRef();
480         m_rc->submit(o);
481 }
482
483 void gPainter::end()
484 {
485         if ( m_dc->islocked() )
486                 return;
487         gOpcode o;
488         o.opcode = gOpcode::flush;
489         o.dc = m_dc.grabRef();
490         m_rc->submit(o);
491 }
492
493 gDC::gDC()
494 {
495 }
496
497 gDC::gDC(gPixmap *pixmap): m_pixmap(pixmap)
498 {
499 }
500
501 gDC::~gDC()
502 {
503 }
504
505 void gDC::exec(gOpcode *o)
506 {
507         switch (o->opcode)
508         {
509         case gOpcode::setBackgroundColor:
510                 m_background_color = o->parm.setColor->color;
511                 delete o->parm.setColor;
512                 break;
513         case gOpcode::setForegroundColor:
514                 m_foreground_color = o->parm.setColor->color;
515                 delete o->parm.setColor;
516                 break;
517         case gOpcode::setBackgroundColorRGB:
518                 m_background_color = m_pixmap->surface->clut.findColor(o->parm.setColorRGB->color);
519                 delete o->parm.setColorRGB;
520                 break;
521         case gOpcode::setForegroundColorRGB:
522                 m_foreground_color = m_pixmap->surface->clut.findColor(o->parm.setColorRGB->color);
523                 delete o->parm.setColorRGB;
524                 break;
525         case gOpcode::setFont:
526                 m_current_font = o->parm.setFont->font;
527                 o->parm.setFont->font->Release();
528                 delete o->parm.setFont;
529                 break;
530         case gOpcode::renderText:
531         {
532                 ePtr<eTextPara> para = new eTextPara(o->parm.renderText->area);
533                 int flags = o->parm.renderText->flags;
534                 assert(m_current_font);
535                 para->setFont(m_current_font);
536                 para->renderString(o->parm.renderText->text, (flags & gPainter::RT_WRAP) ? RS_WRAP : 0);
537                 
538                 if (flags & gPainter::RT_HALIGN_RIGHT)
539                         para->realign(eTextPara::dirRight);
540                 else if (flags & gPainter::RT_HALIGN_CENTER)
541                         para->realign(eTextPara::dirCenter);
542                 else if (flags & gPainter::RT_HALIGN_BLOCK)
543                         para->realign(eTextPara::dirBlock);
544                 
545                 ePoint offset = m_current_offset;
546                 
547                 if (o->parm.renderText->flags & gPainter::RT_VALIGN_CENTER)
548                 {
549                         eRect bbox = para->getBoundBox();
550                         int vcentered_top = o->parm.renderText->area.top() + ((o->parm.renderText->area.height() - bbox.height()) / 2);
551                         int correction = vcentered_top - bbox.top();
552                         offset += ePoint(0, correction);
553                 }
554                 para->blit(*this, offset, getRGB(m_background_color), getRGB(m_foreground_color));
555                 delete o->parm.renderText;
556                 break;
557         }
558         case gOpcode::renderPara:
559         {
560                 o->parm.renderPara->textpara->blit(*this, o->parm.renderPara->offset + m_current_offset, getRGB(m_background_color), getRGB(m_foreground_color));
561                 o->parm.renderPara->textpara->Release();
562                 delete o->parm.renderPara;
563                 break;
564         }
565         case gOpcode::fill:
566         {
567                 eRect area = o->parm.fill->area;
568                 area.moveBy(m_current_offset);
569                 gRegion clip = m_current_clip & area;
570                 m_pixmap->fill(clip, m_foreground_color);
571                 delete o->parm.fill;
572                 break;
573         }
574         case gOpcode::fillRegion:
575         {
576                 o->parm.fillRegion->region.moveBy(m_current_offset);
577                 gRegion clip = m_current_clip & o->parm.fillRegion->region;
578                 m_pixmap->fill(clip, m_foreground_color);
579                 delete o->parm.fillRegion;
580                 break;
581         }
582         case gOpcode::clear:
583                 m_pixmap->fill(m_current_clip, m_background_color);
584                 delete o->parm.fill;
585                 break;
586         case gOpcode::blit:
587         {
588                 gRegion clip;
589                                 // this code should be checked again but i'm too tired now
590                 
591                 o->parm.blit->position += m_current_offset;
592                 
593                 if (o->parm.blit->clip.valid())
594                 {
595                         o->parm.blit->clip.moveBy(m_current_offset);
596                         clip.intersect(gRegion(o->parm.blit->clip), m_current_clip);
597                 } else
598                         clip = m_current_clip;
599                 
600                 m_pixmap->blit(*o->parm.blit->pixmap, o->parm.blit->position, clip, o->parm.blit->flags);
601                 o->parm.blit->pixmap->Release();
602                 delete o->parm.blit;
603                 break;
604         }
605         case gOpcode::setPalette:
606                 if (o->parm.setPalette->palette->start > m_pixmap->surface->clut.colors)
607                         o->parm.setPalette->palette->start = m_pixmap->surface->clut.colors;
608                 if (o->parm.setPalette->palette->colors > (m_pixmap->surface->clut.colors-o->parm.setPalette->palette->start))
609                         o->parm.setPalette->palette->colors = m_pixmap->surface->clut.colors-o->parm.setPalette->palette->start;
610                 if (o->parm.setPalette->palette->colors)
611                         memcpy(m_pixmap->surface->clut.data+o->parm.setPalette->palette->start, o->parm.setPalette->palette->data, o->parm.setPalette->palette->colors*sizeof(gRGB));
612                 
613                 delete[] o->parm.setPalette->palette->data;
614                 delete o->parm.setPalette->palette;
615                 delete o->parm.setPalette;
616                 break;
617         case gOpcode::mergePalette:
618                 m_pixmap->mergePalette(*o->parm.mergePalette->target);
619                 o->parm.mergePalette->target->Release();
620                 delete o->parm.mergePalette;
621                 break; 
622         case gOpcode::line:
623         {
624                 ePoint start = o->parm.line->start + m_current_offset, end = o->parm.line->end + m_current_offset;
625                 m_pixmap->line(m_current_clip, start, end, m_foreground_color);
626                 delete o->parm.line;
627                 break;
628         }
629         case gOpcode::addClip:
630                 m_clip_stack.push(m_current_clip);
631                 o->parm.clip->region.moveBy(m_current_offset);
632                 m_current_clip &= o->parm.clip->region;
633                 delete o->parm.clip;
634                 break;
635         case gOpcode::setClip:
636                 o->parm.clip->region.moveBy(m_current_offset);
637                 m_current_clip = o->parm.clip->region & eRect(ePoint(0, 0), m_pixmap->size());
638                 delete o->parm.clip;
639                 break;
640         case gOpcode::popClip:
641                 if (!m_clip_stack.empty())
642                 {
643                         m_current_clip = m_clip_stack.top();
644                         m_clip_stack.pop();
645                 }
646                 break;
647         case gOpcode::setOffset:
648                 if (o->parm.setOffset->rel)
649                         m_current_offset += o->parm.setOffset->value;
650                 else
651                         m_current_offset  = o->parm.setOffset->value;
652                 delete o->parm.setOffset;
653                 break;
654         case gOpcode::waitVSync:
655                 break;
656         case gOpcode::flip:
657                 break;
658         case gOpcode::flush:
659                 break;
660         default:
661                 eFatal("illegal opcode %d. expect memory leak!", o->opcode);
662         }
663 }
664
665 gRGB gDC::getRGB(gColor col)
666 {
667         if ((!m_pixmap) || (!m_pixmap->surface->clut.data))
668                 return gRGB(col, col, col);
669         if (col<0)
670         {
671                 eFatal("bla transp");
672                 return gRGB(0, 0, 0, 0xFF);
673         }
674         return m_pixmap->surface->clut.data[col];
675 }
676
677 DEFINE_REF(gDC);
678
679 eAutoInitPtr<gRC> init_grc(eAutoInitNumbers::graphic, "gRC");