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