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