Manpage formatting
[notion.git] / ioncore / gr.c
blobb1712a1afe83fd47de4c5a7379a8596b24b0aaa7
1 /*
2 * ion/ioncore/gr.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
9 #include <string.h>
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"
21 /*{{{ Lookup and registration */
23 INTRSTRUCT(GrEngine);
25 DECLSTRUCT(GrEngine){
26 char *name;
27 GrGetBrushFn *fn;
28 GrEngine *next, *prev;
32 static GrEngine *engines=NULL, *current_engine=NULL;
35 bool gr_register_engine(const char *engine, GrGetBrushFn *fn)
37 GrEngine *eng;
39 if(engine==NULL || fn==NULL)
40 return FALSE;
42 eng=ALLOC(GrEngine);
44 if(eng==NULL)
45 return FALSE;
47 eng->name=scopy(engine);
49 if(eng->name==NULL){
50 free(eng);
51 return FALSE;
54 eng->fn=fn;
56 LINK_ITEM(engines, eng, next, prev);
58 return TRUE;
62 void gr_unregister_engine(const char *engine)
64 GrEngine *eng;
66 for(eng=engines; eng!=NULL; eng=eng->next){
67 if(strcmp(eng->name, engine)==0)
68 break;
71 if(eng==NULL)
72 return;
74 UNLINK_ITEM(engines, eng, next, prev);
75 free(eng->name);
76 if(current_engine==eng)
77 current_engine=NULL;
78 free(eng);
82 static bool gr_do_select_engine(const char *engine)
84 GrEngine *eng;
86 for(eng=engines; eng!=NULL; eng=eng->next){
87 if(strcmp(eng->name, engine)==0){
88 current_engine=eng;
89 return TRUE;
93 return FALSE;
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}.
104 EXTL_EXPORT_AS(gr, select_engine)
105 bool gr_select_engine(const char *engine)
107 if(engine==NULL)
108 return FALSE;
110 if(gr_do_select_engine(engine))
111 return TRUE;
113 if(!ioncore_load_module(engine))
114 return FALSE;
116 if(!gr_do_select_engine(engine)){
117 warn(TR("Drawing engine %s is not registered!"), engine);
118 return FALSE;
121 return TRUE;
125 GrBrush *gr_get_brush(Window win, WRootWin *rootwin, const char *style)
127 GrEngine *eng=(current_engine!=NULL ? current_engine : engines);
128 GrBrush *ret;
130 if(eng==NULL || eng->fn==NULL)
131 return NULL;
133 ret=(eng->fn)(win, rootwin, style);
135 if(ret==NULL)
136 warn(TR("Unable to find brush for style '%s'."), style);
138 return ret;
142 /*}}}*/
145 /*{{{ Scoring */
148 static GrAttr star_id=STRINGID_NONE;
151 static int cmp(const void *a_, const void *b_)
153 StringId a=*(const StringId*)a_;
154 StringId b=((const GrAttrScore*)b_)->attr;
156 return (a < b ? -1 : ((a == b) ? 0 : 1));
160 static uint scorefind(const GrStyleSpec *attr, const GrAttrScore *spec)
162 GrAttrScore *res;
164 if(attr->attrs==NULL)
165 return 0;
167 if(star_id==STRINGID_NONE)
168 star_id=stringstore_alloc("*");
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.
174 return spec->score;
177 res=bsearch(&spec->attr, attr->attrs, attr->n, sizeof(GrAttrScore), cmp);
179 return (res==NULL ? 0 : 2*res->score);
183 uint gr_stylespec_score2(const GrStyleSpec *spec, const GrStyleSpec *attr1,
184 const GrStyleSpec *attr2)
186 uint score=0;
187 uint i;
189 for(i=0; i<spec->n; i++){
190 int sc=scorefind(attr1, &spec->attrs[i]);
192 if(attr2!=NULL)
193 sc=maxof(sc, scorefind(attr2, &spec->attrs[i]));
195 if(sc==0){
196 score=0;
197 break;
200 score+=sc;
203 return score;
207 uint gr_stylespec_score(const GrStyleSpec *spec, const GrStyleSpec *attr)
209 return gr_stylespec_score2(spec, attr, NULL);
213 static uint count_dashes(const char *str)
215 uint n=0;
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;
227 return n;
231 bool gr_stylespec_load_(GrStyleSpec *spec, const char *str, bool no_order_score)
233 uint score=(no_order_score ? 1 : count_dashes(str)+1);
235 gr_stylespec_init(spec);
237 while(str!=NULL){
238 GrAttr a;
239 const char *p=strchr(str, '-');
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;
249 if(a==STRINGID_NONE)
250 goto fail;
252 if(!gr_stylespec_add(spec, a, score))
253 goto fail;
255 stringstore_free(a);
257 if(!no_order_score)
258 score--;
261 return TRUE;
263 fail:
264 gr_stylespec_unalloc(spec);
266 return FALSE;
270 bool gr_stylespec_load(GrStyleSpec *spec, const char *str)
272 return gr_stylespec_load_(spec, str, FALSE);
276 void gr_stylespec_unalloc(GrStyleSpec *spec)
278 uint i;
280 for(i=0; i<spec->n; i++)
281 stringstore_free(spec->attrs[i].attr);
283 if(spec->attrs!=NULL){
284 free(spec->attrs);
285 spec->attrs=NULL;
288 spec->n=0;
292 void gr_stylespec_init(GrStyleSpec *spec)
294 spec->attrs=NULL;
295 spec->n=0;
299 static bool gr_stylespec_find_(const GrStyleSpec *spec, GrAttr a, int *idx_ge)
301 bool found=FALSE;
302 uint i;
304 for(i=0; i<spec->n; i++){
305 if(spec->attrs[i].attr>=a){
306 found=(spec->attrs[i].attr==a);
307 break;
311 *idx_ge=i;
312 return found;
316 bool gr_stylespec_isset(const GrStyleSpec *spec, GrAttr a)
318 int idx_ge;
320 return gr_stylespec_find_(spec, a, &idx_ge);
324 bool gr_stylespec_add(GrStyleSpec *spec, GrAttr a, uint score)
326 static const uint sz=sizeof(GrAttrScore);
327 GrAttrScore *idsn;
328 int idx_ge;
330 if(a==GRATTR_NONE || score==0)
331 return TRUE;
333 if(gr_stylespec_find_(spec, a, &idx_ge)){
334 spec->attrs[idx_ge].score+=score;
335 return TRUE;
338 idsn=(GrAttrScore*)realloc(spec->attrs, (spec->n+1)*sz);
340 if(idsn==NULL)
341 return FALSE;
343 stringstore_ref(a);
345 memmove(idsn+idx_ge+1, idsn+idx_ge, (spec->n-idx_ge)*sz);
347 idsn[idx_ge].attr=a;
348 idsn[idx_ge].score=score;
349 spec->attrs=idsn;
350 spec->n++;
352 return TRUE;
356 bool gr_stylespec_set(GrStyleSpec *spec, GrAttr a)
358 return gr_stylespec_add(spec, a, 1);
362 void gr_stylespec_unset(GrStyleSpec *spec, GrAttr a)
364 static const uint sz=sizeof(GrAttrScore);
365 GrAttrScore *idsn;
366 int idx_ge;
368 if(a==GRATTR_NONE)
369 return;
371 if(!gr_stylespec_find_(spec, a, &idx_ge))
372 return;
374 stringstore_free(spec->attrs[idx_ge].attr);
376 memmove(spec->attrs+idx_ge, spec->attrs+idx_ge+1,
377 (spec->n-idx_ge-1)*sz);
379 spec->n--;
381 idsn=(GrAttrScore*)realloc(spec->attrs, (spec->n)*sz);
383 if(idsn!=NULL || spec->n==0)
384 spec->attrs=idsn;
388 static bool gr_stylespec_do_init_from(GrStyleSpec *dst, const GrStyleSpec *src)
390 uint i;
392 if(src->n==0)
393 return TRUE;
395 dst->attrs=ALLOC_N(GrAttrScore, src->n);
397 if(dst->attrs==NULL)
398 return FALSE;
400 for(i=0; i<src->n; i++){
401 dst->attrs[i]=src->attrs[i];
402 stringstore_ref(dst->attrs[i].attr);
405 dst->n=src->n;
407 return TRUE;
411 bool gr_stylespec_append(GrStyleSpec *dst, const GrStyleSpec *src)
413 uint i;
414 bool ok=TRUE;
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;
425 return ok;
429 bool gr_stylespec_equals(const GrStyleSpec *s1, const GrStyleSpec *s2)
431 uint i;
433 if(s1->n!=s2->n)
434 return FALSE;
436 for(i=0; i<s1->n; i++){
437 if(s1->attrs[i].attr!=s2->attrs[i].attr)
438 return FALSE;
441 return TRUE;
445 /*}}}*/
448 /*{{{ Init, deinit */
451 bool grbrush_init(GrBrush *brush)
453 return TRUE;
457 void grbrush_deinit(GrBrush *brush)
462 void grbrush_release(GrBrush *brush)
464 CALL_DYN(grbrush_release, brush, (brush));
468 GrBrush *grbrush_get_slave(GrBrush *brush, WRootWin *rootwin,
469 const char *style)
471 GrBrush *slave=NULL;
472 CALL_DYN_RET(slave, GrBrush*, grbrush_get_slave, brush,
473 (brush, rootwin, style));
474 return slave;
478 /*}}}*/
481 /*{{{ Dynfuns/begin/end/replay */
484 void grbrush_begin(GrBrush *brush, const WRectangle *geom, int flags)
486 CALL_DYN(grbrush_begin, brush, (brush, geom, flags));
490 void grbrush_end(GrBrush *brush)
492 CALL_DYN(grbrush_end, brush, (brush));
496 /*}}}*/
499 /*{{{ Dynfuns/values */
502 void grbrush_get_font_extents(GrBrush *brush, GrFontExtents *fnte)
504 CALL_DYN(grbrush_get_font_extents, brush, (brush, fnte));
508 void grbrush_get_border_widths(GrBrush *brush, GrBorderWidths *bdw)
510 CALL_DYN(grbrush_get_border_widths, brush, (brush, bdw));
514 DYNFUN bool grbrush_get_extra(GrBrush *brush, const char *key,
515 char type, void *data)
517 bool ret=FALSE;
518 CALL_DYN_RET(ret, bool, grbrush_get_extra, brush,
519 (brush, key, type, data));
520 return ret;
524 /*}}}*/
527 /*{{{ Dynfuns/Borders */
530 void grbrush_draw_border(GrBrush *brush, const WRectangle *geom)
532 CALL_DYN(grbrush_draw_border, brush, (brush, geom));
536 void grbrush_draw_borderline(GrBrush *brush, const WRectangle *geom,
537 GrBorderLine line)
539 CALL_DYN(grbrush_draw_borderline, brush, (brush, geom, line));
543 /*}}}*/
546 /*{{{ Dynfuns/Strings */
549 void grbrush_draw_string(GrBrush *brush, int x, int y,
550 const char *str, int len, bool needfill)
552 CALL_DYN(grbrush_draw_string, brush, (brush, x, y, str, len, needfill));
556 uint grbrush_get_text_width(GrBrush *brush, const char *text, uint len)
558 uint ret=0;
559 CALL_DYN_RET(ret, uint, grbrush_get_text_width, brush,
560 (brush, text, len));
561 return ret;
565 /*}}}*/
568 /*{{{ Dynfuns/Textboxes */
571 void grbrush_draw_textbox(GrBrush *brush, const WRectangle *geom,
572 const char *text, bool needfill)
574 CALL_DYN(grbrush_draw_textbox, brush, (brush, geom, text, needfill));
577 void grbrush_draw_textboxes(GrBrush *brush, const WRectangle *geom,
578 int n, const GrTextElem *elem,
579 bool needfill)
581 CALL_DYN(grbrush_draw_textboxes, brush, (brush, geom, n, elem, needfill));
585 /*}}}*/
588 /*{{{ Dynfuns/Misc */
591 void grbrush_set_window_shape(GrBrush *brush, bool rough,
592 int n, const WRectangle *rects)
594 CALL_DYN(grbrush_set_window_shape, brush, (brush, rough, n, rects));
598 void grbrush_enable_transparency(GrBrush *brush, GrTransparency tr)
600 CALL_DYN(grbrush_enable_transparency, brush, (brush, tr));
604 void grbrush_fill_area(GrBrush *brush, const WRectangle *geom)
606 CALL_DYN(grbrush_fill_area, brush, (brush, geom));
610 void grbrush_clear_area(GrBrush *brush, const WRectangle *geom)
612 CALL_DYN(grbrush_clear_area, brush, (brush, geom));
616 void grbrush_init_attr(GrBrush *brush, const GrStyleSpec *spec)
618 CALL_DYN(grbrush_init_attr, brush, (brush, spec));
622 void grbrush_set_attr(GrBrush *brush, GrAttr attr)
624 CALL_DYN(grbrush_set_attr, brush, (brush, attr));
628 void grbrush_unset_attr(GrBrush *brush, GrAttr attr)
630 CALL_DYN(grbrush_unset_attr, brush, (brush, attr));
634 /*}}}*/
637 /*{{{ ioncore_read_config/refresh */
640 /*EXTL_DOC
641 * Read drawing engine configuration file \file{look.lua}.
643 EXTL_EXPORT_AS(gr, read_config)
644 void gr_read_config()
646 extl_read_config("look", NULL, TRUE);
648 /* If nothing has been loaded, try the default engine with
649 * default settings.
651 if(engines==NULL){
652 warn(TR("No drawing engines loaded, trying \"de\"."));
653 gr_select_engine("de");
658 /*EXTL_DOC
659 * Refresh objects' brushes to update them to use newly loaded style.
661 EXTL_EXPORT_AS(gr, refresh)
662 void gr_refresh()
664 WRootWin *rootwin;
666 FOR_ALL_ROOTWINS(rootwin){
667 region_updategr((WRegion*)rootwin);
672 /*}}}*/
675 /*{{{ Class implementation */
678 static DynFunTab grbrush_dynfuntab[]={
679 END_DYNFUNTAB
683 IMPLCLASS(GrBrush, Obj, grbrush_deinit, grbrush_dynfuntab);
686 /*}}}*/