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