]> git.decadent.org.uk Git - ion3.git/blob - ioncore/gr.c
794f1f76306a54061412681cba35c9b1fcf01501
[ion3.git] / ioncore / gr.c
1 /*
2  * ion/ioncore/gr.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2008. 
5  *
6  * See the included file LICENSE for details.
7  */
8
9 #include <string.h>
10
11 #include <libtu/objp.h>
12 #include <libtu/minmax.h>
13 #include <libextl/readconfig.h>
14 #include <libmainloop/hooks.h>
15 #include "common.h"
16 #include "global.h"
17 #include "modules.h"
18 #include "gr.h"
19
20
21 /*{{{ Lookup and registration */
22
23 INTRSTRUCT(GrEngine);
24
25 DECLSTRUCT(GrEngine){
26     char *name;
27     GrGetBrushFn *fn;
28     GrEngine *next, *prev;
29 };
30
31
32 static GrEngine *engines=NULL, *current_engine=NULL;
33
34
35 bool gr_register_engine(const char *engine, GrGetBrushFn *fn)
36 {
37     GrEngine *eng;
38     
39     if(engine==NULL || fn==NULL)
40         return FALSE;
41     
42     eng=ALLOC(GrEngine);
43     
44     if(eng==NULL)
45         return FALSE;
46     
47     eng->name=scopy(engine);
48     
49     if(eng->name==NULL){
50         free(eng);
51         return FALSE;
52     }
53     
54     eng->fn=fn;
55     
56     LINK_ITEM(engines, eng, next, prev);
57
58     return TRUE;
59 }
60
61
62 void gr_unregister_engine(const char *engine)
63 {
64     GrEngine *eng;
65     
66     for(eng=engines; eng!=NULL; eng=eng->next){
67         if(strcmp(eng->name, engine)==0)
68             break;
69     }
70     
71     if(eng==NULL)
72         return;
73     
74     UNLINK_ITEM(engines, eng, next, prev);
75     free(eng->name);
76     if(current_engine==eng)
77         current_engine=NULL;
78     free(eng);
79 }
80
81
82 static bool gr_do_select_engine(const char *engine)
83 {
84     GrEngine *eng;
85
86     for(eng=engines; eng!=NULL; eng=eng->next){
87         if(strcmp(eng->name, engine)==0){
88             current_engine=eng;
89             return TRUE;
90         }
91     }
92     
93     return FALSE;
94 }
95
96
97 /*EXTL_DOC
98  * Future requests for ``brushes'' are to be forwarded to the drawing engine
99  * \var{engine}. If no engine of such name is known, a module with that name
100  * is attempted to be loaded. This function is only intended to be called from
101  * colour scheme etc. configuration files and can not be used to change the
102  * look of existing objects; for that use \fnref{gr.read_config}.
103  */
104 EXTL_EXPORT_AS(gr, select_engine)
105 bool gr_select_engine(const char *engine)
106 {
107     if(engine==NULL)
108         return FALSE;
109     
110     if(gr_do_select_engine(engine))
111         return TRUE;
112     
113     if(!ioncore_load_module(engine))
114         return FALSE;
115     
116     if(!gr_do_select_engine(engine)){
117         warn(TR("Drawing engine %s is not registered!"), engine);
118         return FALSE;
119     }
120     
121     return TRUE;
122 }
123
124
125 GrBrush *gr_get_brush(Window win, WRootWin *rootwin, const char *style)
126 {
127     GrEngine *eng=(current_engine!=NULL ? current_engine : engines);
128     GrBrush *ret;
129     
130     if(eng==NULL || eng->fn==NULL)
131         return NULL;
132     
133     ret=(eng->fn)(win, rootwin, style);
134     
135     if(ret==NULL)
136         warn(TR("Unable to find brush for style '%s'."), style);
137         
138     return ret;
139 }
140
141
142 /*}}}*/
143
144
145 /*{{{ Scoring */
146
147
148 static GrAttr star_id=STRINGID_NONE;
149
150
151 static int cmp(const void *a_, const void *b_)
152 {
153     StringId a=*(const StringId*)a_;
154     StringId b=((const GrAttrScore*)b_)->attr;
155     
156     return (a < b ? -1 : ((a == b) ? 0 : 1));
157 }
158
159
160 static uint scorefind(const GrStyleSpec *attr, const GrAttrScore *spec)
161 {
162     GrAttrScore *res;
163     
164     if(attr->attrs==NULL)
165         return 0;
166     
167     if(star_id==STRINGID_NONE)
168         star_id=stringstore_alloc("*");
169     
170     if(spec->attr==star_id){
171         /* Since every item occurs only once on the list, with a score,
172          * return the score of the star in the spec, instead of one.
173          */
174         return spec->score;
175     }
176     
177     res=bsearch(&spec->attr, attr->attrs, attr->n, sizeof(GrAttrScore), cmp);
178     
179     return (res==NULL ? 0 : 2*res->score);
180 }
181
182
183 uint gr_stylespec_score2(const GrStyleSpec *spec, const GrStyleSpec *attr1, 
184                          const GrStyleSpec *attr2)
185 {
186     uint score=0;
187     uint i;
188     
189     for(i=0; i<spec->n; i++){
190         int sc=scorefind(attr1, &spec->attrs[i]);
191         
192         if(attr2!=NULL)
193             sc=maxof(sc, scorefind(attr2, &spec->attrs[i]));
194         
195         if(sc==0){
196             score=0;
197             break;
198         }
199         
200         score+=sc;
201     }
202     
203     return score;
204 }
205
206
207 uint gr_stylespec_score(const GrStyleSpec *spec, const GrStyleSpec *attr)
208 {
209     return gr_stylespec_score2(spec, attr, NULL);
210 }
211
212
213 static uint count_dashes(const char *str)
214 {
215     uint n=0;
216     
217     if(str!=NULL){
218         while(1){
219             const char *p=strchr(str, '-');
220             if(p==NULL)
221                 break;
222             n++;
223             str=p+1;
224         }
225     }
226     
227     return n;
228 }
229     
230     
231 bool gr_stylespec_load_(GrStyleSpec *spec, const char *str, bool no_order_score)
232 {
233     uint score=(no_order_score ? 1 : count_dashes(str)+1);
234     
235     gr_stylespec_init(spec);
236     
237     while(str!=NULL){
238         GrAttr a;
239         const char *p=strchr(str, '-');
240     
241         if(p==NULL){
242             a=stringstore_alloc(str);
243             str=p;
244         }else{
245             a=stringstore_alloc_n(str, p-str);
246             str=p+1;
247         }
248         
249         if(a==STRINGID_NONE)
250             goto fail;
251             
252         if(!gr_stylespec_add(spec, a, score))
253             goto fail;
254             
255         stringstore_free(a);
256         
257         if(!no_order_score)
258             score--;
259     }
260     
261     return TRUE;
262     
263 fail:
264     gr_stylespec_unalloc(spec);
265     
266     return FALSE;
267 }
268
269
270 bool gr_stylespec_load(GrStyleSpec *spec, const char *str)
271 {
272     return gr_stylespec_load_(spec, str, FALSE);
273 }
274
275
276 void gr_stylespec_unalloc(GrStyleSpec *spec)
277 {
278     uint i;
279     
280     for(i=0; i<spec->n; i++)
281         stringstore_free(spec->attrs[i].attr);
282     
283     if(spec->attrs!=NULL){
284         free(spec->attrs);
285         spec->attrs=NULL;
286     }
287     
288     spec->n=0;
289 }
290
291
292 void gr_stylespec_init(GrStyleSpec *spec)
293 {
294     spec->attrs=NULL;
295     spec->n=0;
296 }
297
298
299 static bool gr_stylespec_find_(const GrStyleSpec *spec, GrAttr a, int *idx_ge)
300 {
301     bool found=FALSE;
302     uint i;
303     
304     for(i=0; i<spec->n; i++){
305         if(spec->attrs[i].attr>=a){
306             found=(spec->attrs[i].attr==a);
307             break;
308         }
309     }
310     
311     *idx_ge=i;
312     return found;
313 }
314
315
316 bool gr_stylespec_isset(const GrStyleSpec *spec, GrAttr a)
317 {
318     int idx_ge;
319     
320     return gr_stylespec_find_(spec, a, &idx_ge);
321 }
322
323
324 bool gr_stylespec_add(GrStyleSpec *spec, GrAttr a, uint score)
325 {
326     static const uint sz=sizeof(GrAttrScore);
327     GrAttrScore *idsn;
328     int idx_ge;
329     
330     if(a==GRATTR_NONE || score==0)
331         return TRUE;
332     
333     if(gr_stylespec_find_(spec, a, &idx_ge)){
334         spec->attrs[idx_ge].score+=score;
335         return TRUE;
336     }
337     
338     idsn=(GrAttrScore*)realloc(spec->attrs, (spec->n+1)*sz);
339     
340     if(idsn==NULL)
341         return FALSE;
342         
343     stringstore_ref(a);
344         
345     memmove(idsn+idx_ge+1, idsn+idx_ge, (spec->n-idx_ge)*sz);
346     
347     idsn[idx_ge].attr=a;
348     idsn[idx_ge].score=score;
349     spec->attrs=idsn;
350     spec->n++;
351     
352     return TRUE;
353 }
354
355
356 bool gr_stylespec_set(GrStyleSpec *spec, GrAttr a)
357 {
358     return gr_stylespec_add(spec, a, 1);
359 }
360
361
362 void gr_stylespec_unset(GrStyleSpec *spec, GrAttr a)
363 {
364     static const uint sz=sizeof(GrAttrScore);
365     GrAttrScore *idsn;
366     int idx_ge;
367     
368     if(a==GRATTR_NONE)
369         return;
370     
371     if(!gr_stylespec_find_(spec, a, &idx_ge))
372         return;
373     
374     stringstore_free(spec->attrs[idx_ge].attr);
375     
376     memmove(spec->attrs+idx_ge, spec->attrs+idx_ge+1,
377             (spec->n-idx_ge-1)*sz);
378             
379     spec->n--;
380     
381     idsn=(GrAttrScore*)realloc(spec->attrs, (spec->n)*sz);
382     
383     if(idsn!=NULL || spec->n==0)
384         spec->attrs=idsn;
385 }
386
387
388 static bool gr_stylespec_do_init_from(GrStyleSpec *dst, const GrStyleSpec *src)
389 {
390     uint i;
391     
392     if(src->n==0)
393         return TRUE;
394         
395     dst->attrs=ALLOC_N(GrAttrScore, src->n);
396     
397     if(dst->attrs==NULL)
398         return FALSE;
399     
400     for(i=0; i<src->n; i++){
401         dst->attrs[i]=src->attrs[i];
402         stringstore_ref(dst->attrs[i].attr);
403     }
404     
405     dst->n=src->n;
406     
407     return TRUE;
408 }
409
410
411 bool gr_stylespec_append(GrStyleSpec *dst, const GrStyleSpec *src)
412 {
413     uint i;
414     bool ok=TRUE;
415     
416     if(dst->attrs==NULL){
417         ok=gr_stylespec_do_init_from(dst, src);
418     }else{
419         for(i=0; i<src->n; i++){
420             if(!gr_stylespec_add(dst, src->attrs[i].attr, src->attrs[i].score))
421                 ok=FALSE;
422         }
423     }
424     
425     return ok;
426 }
427
428
429 bool gr_stylespec_equals(const GrStyleSpec *s1, const GrStyleSpec *s2)
430 {
431     uint i;
432     
433     if(s1->n!=s2->n)
434         return FALSE;
435         
436     for(i=0; i<s1->n; i++){
437         if(s1->attrs[i].attr!=s2->attrs[i].attr)
438             return FALSE;
439     }
440     
441     return TRUE;
442 }
443     
444
445 /*}}}*/
446
447
448 /*{{{ Init, deinit */
449
450
451 bool grbrush_init(GrBrush *brush)
452 {
453     return TRUE;
454 }
455
456
457 void grbrush_deinit(GrBrush *brush)
458 {
459 }
460
461
462 void grbrush_release(GrBrush *brush)
463 {
464     CALL_DYN(grbrush_release, brush, (brush));
465 }
466
467
468 GrBrush *grbrush_get_slave(GrBrush *brush, WRootWin *rootwin, 
469                            const char *style)
470 {
471     GrBrush *slave=NULL;
472     CALL_DYN_RET(slave, GrBrush*, grbrush_get_slave, brush,
473                  (brush, rootwin, style));
474     return slave;
475 }
476
477
478 /*}}}*/
479
480
481 /*{{{ Dynfuns/begin/end/replay */
482
483
484 void grbrush_begin(GrBrush *brush, const WRectangle *geom, int flags)
485 {
486     CALL_DYN(grbrush_begin, brush, (brush, geom, flags));
487 }
488
489
490 void grbrush_end(GrBrush *brush)
491 {
492     CALL_DYN(grbrush_end, brush, (brush));
493 }
494
495
496 /*}}}*/
497
498
499 /*{{{ Dynfuns/values */
500
501
502 void grbrush_get_font_extents(GrBrush *brush, GrFontExtents *fnte)
503 {
504     CALL_DYN(grbrush_get_font_extents, brush, (brush, fnte));
505 }
506
507
508 void grbrush_get_border_widths(GrBrush *brush, GrBorderWidths *bdw)
509 {
510     CALL_DYN(grbrush_get_border_widths, brush, (brush, bdw));
511 }
512
513
514 DYNFUN bool grbrush_get_extra(GrBrush *brush, const char *key, 
515                               char type, void *data)
516 {
517     bool ret=FALSE;
518     CALL_DYN_RET(ret, bool, grbrush_get_extra, brush,
519                  (brush, key, type, data));
520     return ret;
521 }
522
523
524 /*}}}*/
525
526
527 /*{{{ Dynfuns/Borders */
528
529
530 void grbrush_draw_border(GrBrush *brush, const WRectangle *geom)
531 {
532     CALL_DYN(grbrush_draw_border, brush, (brush, geom));
533 }
534
535
536 void grbrush_draw_borderline(GrBrush *brush, const WRectangle *geom,
537                              GrBorderLine line)
538 {
539     CALL_DYN(grbrush_draw_borderline, brush, (brush, geom, line));
540 }
541
542
543 /*}}}*/
544
545
546 /*{{{ Dynfuns/Strings */
547
548
549 void grbrush_draw_string(GrBrush *brush, int x, int y,
550                          const char *str, int len, bool needfill)
551 {
552     CALL_DYN(grbrush_draw_string, brush, (brush, x, y, str, len, needfill));
553 }
554
555
556 uint grbrush_get_text_width(GrBrush *brush, const char *text, uint len)
557 {
558     uint ret=0;
559     CALL_DYN_RET(ret, uint, grbrush_get_text_width, brush, 
560                  (brush, text, len));
561     return ret;
562 }
563
564
565 /*}}}*/
566
567
568 /*{{{ Dynfuns/Textboxes */
569
570
571 void grbrush_draw_textbox(GrBrush *brush, const WRectangle *geom,
572                           const char *text, bool needfill)
573 {
574     CALL_DYN(grbrush_draw_textbox, brush, (brush, geom, text, needfill));
575 }
576
577 void grbrush_draw_textboxes(GrBrush *brush, const WRectangle *geom,
578                             int n, const GrTextElem *elem, 
579                             bool needfill)
580 {
581     CALL_DYN(grbrush_draw_textboxes, brush, (brush, geom, n, elem, needfill));
582 }
583
584
585 /*}}}*/
586
587
588 /*{{{ Dynfuns/Misc */
589
590
591 void grbrush_set_window_shape(GrBrush *brush, bool rough,
592                               int n, const WRectangle *rects)
593 {
594     CALL_DYN(grbrush_set_window_shape, brush, (brush, rough, n, rects));
595 }
596
597
598 void grbrush_enable_transparency(GrBrush *brush, GrTransparency tr)
599 {
600     CALL_DYN(grbrush_enable_transparency, brush, (brush, tr));
601 }
602
603
604 void grbrush_fill_area(GrBrush *brush, const WRectangle *geom)
605 {
606     CALL_DYN(grbrush_fill_area, brush, (brush, geom));
607 }
608
609
610 void grbrush_clear_area(GrBrush *brush, const WRectangle *geom)
611 {
612     CALL_DYN(grbrush_clear_area, brush, (brush, geom));
613 }
614
615
616 void grbrush_init_attr(GrBrush *brush, const GrStyleSpec *spec)
617 {
618     CALL_DYN(grbrush_init_attr, brush, (brush, spec));
619 }
620
621
622 void grbrush_set_attr(GrBrush *brush, GrAttr attr)
623 {
624     CALL_DYN(grbrush_set_attr, brush, (brush, attr));
625 }
626
627
628 void grbrush_unset_attr(GrBrush *brush, GrAttr attr)
629 {
630     CALL_DYN(grbrush_unset_attr, brush, (brush, attr));
631 }
632
633
634 /*}}}*/
635
636
637 /*{{{ ioncore_read_config/refresh */
638
639
640 /*EXTL_DOC
641  * Read drawing engine configuration file \file{look.lua}.
642  */
643 EXTL_EXPORT_AS(gr, read_config)
644 void gr_read_config()
645 {
646     extl_read_config("look", NULL, TRUE);
647     
648     /* If nothing has been loaded, try the default engine with
649      * default settings.
650      */
651     if(engines==NULL){
652         warn(TR("No drawing engines loaded, trying \"de\"."));
653         gr_select_engine("de");
654     }
655 }
656
657
658 /*EXTL_DOC
659  * Refresh objects' brushes to update them to use newly loaded style.
660  */
661 EXTL_EXPORT_AS(gr, refresh)
662 void gr_refresh()
663 {
664     WRootWin *rootwin;
665     
666     FOR_ALL_ROOTWINS(rootwin){
667         region_updategr((WRegion*)rootwin);
668     }
669 }
670
671
672 /*}}}*/
673
674
675 /*{{{ Class implementation */
676
677
678 static DynFunTab grbrush_dynfuntab[]={
679     END_DYNFUNTAB
680 };
681                                        
682
683 IMPLCLASS(GrBrush, Obj, grbrush_deinit, grbrush_dynfuntab);
684
685
686 /*}}}*/
687