changed author email
[guish.git] / src / widget.c
blob9ec47887e5f20fdd2525d0eb016187fa75b2677b
1 /*************************************************************************
2 * Copyright (C) 2024 Francesco Palumbo <phranz.dev@gmail.com>, Naples (Italy)
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 *************************************************************************/
18 #include <unistd.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <sys/time.h>
22 #include <signal.h>
24 #include <X11/Xlib.h>
25 #include <X11/Xatom.h>
27 #include "dectypes.h"
28 #include "widgets.h"
29 #include "widget.h"
30 #include "syntax.h"
31 #include "evaluator.h"
32 #include "debug.h"
33 #include "main.h"
35 #define SPIXEL 2
37 typedef struct point_t {
38 int x;
39 int y;
40 } point_t;
42 int dirty;
43 extern char buf[BSIZ];
45 extern GC gc;
46 extern Atom delatom;
47 extern Display* display;
48 extern Window root;
49 extern int screen;
50 extern map_strcol_t* colors;
51 extern unsigned long flags;
53 extern map_widloci_t* widlines;
54 extern map_widloci_t* widareas;
55 extern map_widarcssets_t* widarcssets;
56 extern map_widarcssets_t* widarcsareas;
57 extern map_widloci_t* widloci;
58 extern map_widloci_t* widpixels;
59 extern map_widnamedpoints_t* widnamedpoints;
61 extern map_widwidget_t* widgets;
62 extern unsigned long options;
64 static point_t lrpos;
65 static point_t fp;
67 #ifdef ENABLE_CONTROL
69 #include <X11/keysym.h>
70 #include <X11/extensions/XTest.h>
72 static map_charcstr_t* ksyms;
74 static void fillksyms() {
75 if (ksyms)
76 return;
78 ksyms = alloc(map_charcstr_t);
80 ksyms->insert(ksyms, ' ', "space");
81 ksyms->insert(ksyms, '-', "minus");
82 ksyms->insert(ksyms, '+', "plus");
83 ksyms->insert(ksyms, '.', "period");
84 ksyms->insert(ksyms, ',', "comma");
85 ksyms->insert(ksyms, ':', "colon");
86 ksyms->insert(ksyms, ';', "semicolon");
87 ksyms->insert(ksyms, '!', "exclam");
88 ksyms->insert(ksyms, '$', "dollar");
89 ksyms->insert(ksyms, '%', "percent");
90 ksyms->insert(ksyms, '&', "ampersand");
91 ksyms->insert(ksyms, '\'', "apostrophe");
92 ksyms->insert(ksyms, '(', "parenleft");
93 ksyms->insert(ksyms, ')', "parenright");
94 ksyms->insert(ksyms, '*', "asterisk");
95 ksyms->insert(ksyms, '<', "less");
96 ksyms->insert(ksyms, '=', "equal");
97 ksyms->insert(ksyms, '>', "greater");
98 ksyms->insert(ksyms, '?', "question");
99 ksyms->insert(ksyms, '@', "at");
100 ksyms->insert(ksyms, '[', "bracketleft");
101 ksyms->insert(ksyms, '\\', "backslash");
102 ksyms->insert(ksyms, '/', "slash");
103 ksyms->insert(ksyms, ']', "bracketright");
104 ksyms->insert(ksyms, '^', "asciicircum");
105 ksyms->insert(ksyms, '_', "underscore");
106 ksyms->insert(ksyms, '`', "grave");
107 ksyms->insert(ksyms, '{', "braceleft");
108 ksyms->insert(ksyms, '}', "braceright");
109 ksyms->insert(ksyms, '~', "asciitilde");
112 void sendkeys(widget_t* w, const char* s) {
113 if (!s)
114 return;
115 fillksyms();
117 const char* r;
118 char* d;
119 char C[2] = {0, 0};
121 focus(w);
122 for (size_t i=0; i<strlen(s); ++i) {
123 C[0] = s[i];
124 d = sadd(NULL, C);
125 const char* k = ksyms->get(ksyms, s[i], &r) ? r : d;
126 unsigned int kc = XKeysymToKeycode(display, XStringToKeysym(k));
127 zfree(d);
128 XTestFakeKeyEvent(display, kc, True, 0);
129 XTestFakeKeyEvent(display, kc, False, 0);
130 XFlush(display);
132 unsigned int rl;
133 each(w->to_release, rl,
134 XTestFakeKeyEvent(display, rl, False, 0);
136 w->to_release->clear(w->to_release);
139 void sendcontrolkeys(widget_t* w, const char* s) {
140 if (!s)
141 return;
142 fillksyms();
144 char* r;
145 vec_str_t* keys = alloc(vec_str_t);
146 ssplit(s, ',', keys, NULL);
148 focus(w);
149 for (size_t i=0; i<keys->count; ++i) {
150 int keep = 0;
151 keys->get(keys, i, &r);
152 if (r[0] == '+') {
153 keys->remove(keys, i);
154 keep = 1;
156 unsigned int kc = XKeysymToKeycode(display, XStringToKeysym(r));
157 XTestFakeKeyEvent(display, kc, True, 0);
158 if (keep)
159 w->to_release->push(w->to_release, kc);
160 else
161 XTestFakeKeyEvent(display, kc, False, 0);
163 XFlush(display);
164 zfree(r);
166 release(keys);
168 #endif
170 #ifdef ENABLE_IMAGES
171 #include <Imlib2.h>
173 void loadimg(widget_t* w, const char* p) {
174 if (!w || (!w->img && !p))
175 return;
177 if (!p) {
178 if (w->img) {
179 imlib_context_set_image(w->img);
180 imlib_free_image();
181 w->img = NULL;
183 return;
185 if (w->img) {
186 imlib_context_set_image(w->img);
187 imlib_free_image();
189 imlib_context_set_display(display);
190 imlib_context_set_visual(DefaultVisual(display, screen));
191 imlib_context_set_colormap(DefaultColormap(display, screen));
193 w->img = imlib_load_image(p);
196 void updateimg(widget_t* w) {
197 if (!w || !w->img)
198 return;
200 Imlib_Image buffer = imlib_create_image(w->w, w->h);
201 imlib_context_set_image(w->img);
203 int ww = imlib_image_get_width();
204 int hh = imlib_image_get_height();
206 imlib_context_set_blend(1);
207 imlib_context_set_image(buffer);
208 imlib_context_set_color(w->bg->red, w->bg->green, w->bg->blue, 255);
209 imlib_image_fill_rectangle(0, 0, w->w, w->h);
211 imlib_blend_image_onto_image(w->img, 0, 0, 0, ww, hh, 0, 0, w->w, w->h);
213 imlib_context_set_blend(0);
214 Pixmap pix;
215 pix = XCreatePixmap(display, root, w->w, w->h, DefaultDepth(display, screen));
217 imlib_context_set_drawable(pix);
218 imlib_render_image_on_drawable_at_size(0, 0, w->w, w->h);
219 XSetWindowBackgroundPixmap(display, w->wid, pix);
221 imlib_context_set_image(buffer);
222 imlib_free_image();
223 XFreePixmap(display, pix);
226 #endif
228 void pass() {
231 intint_intint_t pcoords() {
232 intint_t C;
233 intint_t c;
234 intint_intint_t r;
236 Window w;
237 unsigned int m;
239 XQueryPointer(display, root, &w, &w, &C.key, &C.val, &c.key, &c.val, &m);
240 r.key = C;
241 r.val = c;
243 return r;
246 static int getwidpid(Window w) {
247 int pid = 0;
248 Atom a = XInternAtom(display, "_NET_WM_PID", True);
249 XTextProperty d;
250 XGetTextProperty(display, w, &d, a);
252 if (d.nitems) {
253 pid = *((int*)(unsigned char*)d.value);
254 XFree(d.value);
256 return pid;
259 static void setwidpid(Window w) {
260 Atom a = XInternAtom(display, "_NET_WM_PID", False);
261 sprintf(buf, "%d", getpid());
262 XChangeProperty(display, w, a, XA_STRING, 8, PropModeReplace, (unsigned char*)buf, strlen(buf));
265 int iswin(Window p, Window id) {
266 if (p == id)
267 return 1;
269 Window rr;
270 Window pr;
271 Window *cl = NULL;
272 unsigned int l = 0;
273 int found = 0;
275 XQueryTree(display, p, &rr, &pr, &cl, &l);
276 for (size_t i=0; i<l && !found; ++i)
277 found = iswin(cl[i], id);
278 XFree(cl);
279 return found;
282 static Window parentof(Window w) {
283 Window rr;
284 Window pr;
285 Window *cl = NULL;
286 unsigned int l = 0;
288 XQueryTree(display, w, &rr, &pr, &cl, &l);
289 XFree(cl);
290 return pr;
293 static intint_t coords(Window w) {
294 XWindowAttributes wa;
295 Window cr;
296 intint_t c;
297 c.key = 0;
298 c.val = 0;
300 Window rr;
301 Window pr;
302 Window *cl = NULL;
303 unsigned int l = 0;
305 XQueryTree(display, w, &rr, &pr, &cl, &l);
306 XTranslateCoordinates(display, w, root, 0, 0, &c.key, &c.val, &cr);
307 XGetWindowAttributes(display, w, &wa);
309 c.key = c.key ? c.key - wa.x : wa.x;
310 c.val = c.val ? c.val - wa.y : wa.y;
311 XFree(cl);
312 return c;
315 Window widpid(Window w, int p) {
316 Window rr;
317 Window pr;
318 Window *cl = NULL;
319 unsigned int l = 0;
320 Window f = 0;
322 int pid = getwidpid(w);
324 debug("pid: %d, p: %d\n", pid, p);
325 if (pid && p == pid)
326 return w;
327 XQueryTree(display, w, &rr, &pr, &cl, &l);
328 for (size_t i=0; i<l; ++i)
329 f = widpid(cl[i], p);
330 XFree(cl);
331 return f;
334 static void setclass(widget_t* w, const char* t) {
335 pid_t pid = getpid();
336 XClassHint ch;
338 ch.res_name = (char*)(t ? t : ename(w->etype));
340 memset(buf, 0, sizeof(buf));
341 sprintf(buf, "guish-%d", pid);
343 ch.res_class = buf;
344 XSetClassHint(display, w->wid, &ch);
347 widget_t* make(int e, unsigned long wid, int ww, int hh) {
348 widget_t* w = NULL;
350 switch (e) {
351 case E_BUTTON:
352 w = (widget_t*)alloc(button_t, ww, hh);
353 break;
354 case E_INPUT:
355 w = (widget_t*)alloc(input_t, ww, hh);
356 break;
357 case E_PAGE:
358 w = (widget_t*)alloc(page_t, ww, hh);
359 break;
360 case E_LABEL:
361 w = (widget_t*)alloc(label_t, ww, hh);
362 break;
363 case E_CHECKBOX:
364 w = (widget_t*)alloc(checkbox_t, ww, hh);
365 break;
366 case E_TRANS:
367 w = (widget_t*)alloc(trans_t, ww, hh);
368 break;
369 default: {
370 if (!wid)
371 break;
372 w = alloc(widget_t);
373 w->wid = wid;
374 w->flags |= F_EXT;
375 w->pid = getwidpid(w->wid);
377 XWindowAttributes wa;
378 XGetWindowAttributes(display, w->wid, &wa);
379 w->w = wa.width;
380 w->h = wa.height;
381 w->b = wa.border_width;
384 if (w) {
385 dirty = 1;
386 if (!gc) {
387 XGCValues v = {0};
388 gc = XCreateGC(display, w->wid, 0, &v);
390 if (!wid) {
391 w->etype = e;
392 setclass(w, NULL);
393 setwidpid(w->wid);
395 if (options & SHOW_ON_CREATE)
396 show(w);
397 if (widgets->full(widgets))
398 widgets->rehash(widgets);
399 center(w);
400 widgets->insert(widgets, w->wid, w);
401 return w;
403 return NULL;
406 void del(unsigned long long id) {
407 widget_t* w;
408 if (widgets->get(widgets, id, &w)) {
409 debug("deleting window %llu\n", id);
410 widgets->remove(widgets, id);
411 if (w) {
412 switch (w->etype) {
413 case E_BUTTON:
414 ((button_t*)w)->free((button_t*)w);
415 break;
416 case E_INPUT:
417 ((input_t*)w)->free((input_t*)w);
418 break;
419 case E_PAGE:
420 ((page_t*)w)->free((page_t*)w);
421 break;
422 case E_LABEL:
423 ((label_t*)w)->free((label_t*)w);
424 break;
425 case E_CHECKBOX:
426 ((checkbox_t*)w)->free((checkbox_t*)w);
427 break;
428 case E_TRANS:
429 ((trans_t*)w)->free((trans_t*)w);
430 break;
431 default:
432 if (w->flags & F_EXT)
433 w->free(w);
439 intint_t textpos(widget_t* w, const char* s, align a, size_t i, size_t t) {
440 intint_t c;
442 size_t l = strlen(s);
443 size_t ls = XTextWidth(w->fs, s, l);
445 c.key = w->m;
446 c.val = w->m;
448 if (w->maxwcont < ls)
449 w->maxwcont = ls;
451 switch (a) {
452 case A_TC:
453 c.key = (w->w - ls) / 2;
454 c.val = (w->fh * (i + 1)) + w->m;
455 break;
456 case A_MC:
457 c.key = (w->w - ls) / 2;
458 c.val = ((w->h - (w->fh * t)) / 2) + (w->fh * (i + 1));
459 break;
460 case A_BC:
461 c.key = (w->w - ls) / 2;
462 c.val = (w->h - ((t - (i + 1)) * w->fh)) - w->m;
463 break;
464 case A_TL:
465 c.key = w->m;
466 c.val = (w->fh * (i + 1)) + w->m;
467 break;
468 case A_ML:
469 c.key = w->m;
470 c.val = ((w->h - (w->fh * t)) / 2) + (w->fh * (i + 1));
471 break;
472 case A_BL:
473 c.key = w->m;
474 c.val = (w->h - ((t - (i + 1)) * w->fh)) - w->m;
475 break;
476 case A_TR:
477 c.key = w->w - ls - w->m;
478 c.val = (w->fh * (i + 1)) + w->m;
479 break;
480 case A_MR:
481 c.key = w->w - ls - w->m;
482 c.val = ((w->h - (w->fh * t)) / 2) + (w->fh * (i + 1));
483 break;
484 case A_BR:
485 c.key = w->w - ls - w->m;
486 c.val = (w->h - ((t - (i + 1)) * w->fh)) - w->m;
487 break;
489 return c;
492 static int getalign(const char* p) {
493 if (!p)
494 return A_TC;
496 return
497 (eq(p, "l") || eq(p, "left") || eq(p, "middle-left")) ? A_ML :
498 (eq(p, "r") || eq(p, "right") || eq(p, "middle-right")) ? A_MR :
499 (eq(p, "c") || eq(p, "center") || eq(p, "middle-center")) ? A_MC :
500 (eq(p, "tl") || eq(p, "top-left")) ? A_TL :
501 (eq(p, "tr") || eq(p, "top-right")) ? A_TR :
502 (eq(p, "t") || eq(p, "top-center")) ? A_TC :
503 (eq(p, "bl") || eq(p, "bottom-left")) ? A_BL :
504 (eq(p, "br") || eq(p, "bottom-right")) ? A_BR :
505 (eq(p, "b") || eq(p, "bottom-center")) ? A_BC :
506 A_TC;
509 static void movealign(widget_t* w, widget_t* r, int a) {
510 int x = 0;
511 int y = 0;
513 switch (a) {
514 case A_ML:
515 x = r->x - w->w - (w->b*2);
516 y = (w->h + (w->b*2)) > (r->h + (r->b*2)) ? (r->y - ((w->h + (w->b*2) - r->h) / 2)) : (r->y + ((r->h - w->h) / 2));
517 break;
518 case A_MR:
519 x = r->x + r->w + (r->b*2);
520 y = (w->h + (w->b*2)) > (r->h + (r->b*2)) ? (r->y - ((w->h + (w->b*2) - r->h) / 2)) : (r->y + ((r->h - w->h) / 2));
521 break;
522 case A_MC:
523 x = (w->w + (w->b*2)) > (r->w + (r->b*2)) ? (r->x - ((w->w + (w->b*2) - r->w) / 2)) : (r->x + ((r->w - w->w) / 2));
524 y = (w->h + (w->b*2)) > (r->h + (r->b*2)) ? (r->y - ((w->h + (w->b*2) - r->h) / 2)) : (r->y + ((r->h - w->h) / 2));
525 break;
526 case A_TL:
527 x = r->x - (w->b*2) - w->w;
528 y = r->y - w->h - (w->b*2);
529 break;
530 case A_TR:
531 x = r->x + r->w + (r->b*2);
532 y = r->y - w->h - (w->b*2);
533 break;
534 case A_TC:
535 x = (w->w + (w->b*2)) > (r->w + (r->b*2)) ? (r->x - ((w->w + (w->b*2) - r->w) / 2)) : (r->x + ((r->w - w->w) / 2));
536 y = r->y - w->h - (w->b*2);
537 break;
538 case A_BL:
539 x = r->x - (w->b*2) - w->w;
540 y = r->y + r->h + (r->b*2);
541 break;
542 case A_BR:
543 x = r->x + r->w + (r->b*2);
544 y = r->y + r->h + (r->b*2);
545 break;
546 case A_BC:
547 x = (w->w + (w->b*2)) > (r->w + (r->b*2)) ? (r->x - ((w->w + (w->b*2) - r->w) / 2)) : (r->x + ((r->w - w->w) / 2));
548 y = r->y + r->h + (r->b*2);
549 break;
550 default:
551 break;
553 XMoveWindow(display, w->wid, x, y);
554 moved(w);
557 void closed(widget_t* w) {
558 hide(w);
559 w->flags |= F_CLOSED;
560 trigger(w->wid, SIG_CLOSED);
561 if (w->etype == E_PAGE) {
562 widget_t* s;
563 each(((page_t*)w)->subs, s, closed(s););
567 void moved(widget_t* w) {
568 trigger(w->wid, SIG_MOVED);
570 if (w->related->count) {
571 widint_t* p;
572 widget_t* x;
574 eachitem(w->related, widint_t, p,
575 if (widgets->get(widgets, p->key, &x))
576 movealign(x, w, p->val);
577 else
578 w->related->remove(w->related, p->key);
583 void resized(widget_t* w) {
584 trigger(w->wid, SIG_RESIZED);
586 if (w->relal->key) {
587 widget_t* r;
588 if (widgets->get(widgets, w->relal->key, &r))
589 movealign(w, r, w->relal->val);
590 else
591 w->relal->key = 0;
593 if (w->related->count) {
594 widint_t* p;
595 widget_t* x;
597 eachitem(w->related, widint_t, p,
598 if (widgets->get(widgets, p->key, &x))
599 movealign(x, w, p->val);
600 else
601 w->related->remove(w->related, p->key);
606 void relaxed(widget_t* w, int c) {
607 if (c && !(w->flags & F_WFIXED) && !(w->flags & F_HFIXED))
608 return;
610 w->flags &= ~(F_WFIXED|F_HFIXED);
612 XSizeHints* h = XAllocSizeHints();
613 h->flags = USPosition;
614 XSetWMSizeHints(display, w->wid, h, XA_WM_NORMAL_HINTS);
615 XFree(h);
618 void fixed(widget_t* w, int c) {
619 if (c && w->flags & F_WFIXED && w->flags & F_HFIXED)
620 return;
622 w->flags |= (F_WFIXED | F_HFIXED);
624 XSizeHints* h = XAllocSizeHints();
625 h->flags = USPosition | PMinSize | PMaxSize;
626 h->min_width = h->max_width = w->w;
627 h->min_height = h->max_height = w->h;
628 XSetWMSizeHints(display, w->wid, h, XA_WM_NORMAL_HINTS);
629 XFree(h);
632 void wfixed(widget_t* w, int c) {
633 if (c && w->flags & F_WFIXED && !(w->flags & F_HFIXED))
634 return;
636 w->flags |= F_WFIXED;
637 w->flags &= ~F_HFIXED;
639 XSizeHints* h = XAllocSizeHints();
640 h->flags = USPosition | PMinSize | PMaxSize;
641 h->min_width = h->max_width = w->w;
642 XSetWMSizeHints(display, w->wid, h, XA_WM_NORMAL_HINTS);
643 XFree(h);
646 void hfixed(widget_t* w, int c) {
647 if (c && w->flags & F_HFIXED && !(w->flags & F_WFIXED))
648 return;
650 w->flags |= F_HFIXED;
651 w->flags &= ~F_WFIXED;
653 XSizeHints* h = XAllocSizeHints();
654 h->flags = USPosition | PMinSize | PMaxSize;
655 h->min_height = h->max_height = w->h;
656 XSetWMSizeHints(display, w->wid, h, XA_WM_NORMAL_HINTS);
657 XFree(h);
660 void fullscreen(widget_t* w) {
661 if (!w)
662 return;
664 XEvent e;
665 e.xclient.type = ClientMessage;
666 e.xclient.serial = 0;
667 e.xclient.send_event = True;
668 e.xclient.display = display;
669 e.xclient.window = w->wid;
670 e.xclient.message_type = XInternAtom(display, "_NET_WM_STATE", False);
671 e.xclient.format = 32;
673 e.xclient.data.l[0] = 1;
674 e.xclient.data.l[1] = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", False);
675 e.xclient.data.l[2] = 0;
676 e.xclient.data.l[3] = 1;
677 e.xclient.data.l[4] = 0;
679 XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask|SubstructureNotifyMask, &e);
680 XSync(display, False);
682 if (w->flags & F_GHOST)
683 ghost(w);
684 w->draw(w);
687 void show(widget_t* w) {
688 if (!w)
689 return;
691 if (w->etype == E_PAGE)
692 showsubs(w);
694 XMapWindow(display, w->wid);
695 w->flags |= F_VISIBLE;
696 if (w->flags & F_CLOSED)
697 w->flags &= ~F_CLOSED;
698 if (w->flags & F_GHOST)
699 ghost(w);
701 if (w->parent || w->flags & F_BYPASS || w->flags & F_TRAYED)
702 return;
703 // wm workaround
704 XResizeWindow(display, w->wid, w->w, w->h);
706 Atom wmstate = XInternAtom(display, "WM_STATE", False);
707 if (wmstate == None)
708 return;
710 Atom type;
711 int format;
712 unsigned long n = 0;
713 unsigned long bafter;
714 unsigned char *props = NULL;
716 while (XGetWindowProperty(display, w->wid, wmstate, 0, ~(0l), False, AnyPropertyType, &type, &format, &n, &bafter, &props) == Success) {
717 if (props && *props != WithdrawnState)
718 break;
719 usleep(1000);
720 XFree(props);
722 XFree(props);
725 void hide(widget_t* w) {
726 if (!w || !(w->flags & (F_VISIBLE|F_HOVERED)))
727 return;
729 XUnmapWindow(display, w->wid);
730 w->flags &= ~(F_VISIBLE|F_HOVERED);
731 if (w->parent || w->flags & F_BYPASS || w->flags & F_TRAYED)
732 return;
734 Atom wmstate = XInternAtom(display, "WM_STATE", False);
735 if (wmstate == None)
736 return;
738 Atom type;
739 int format;
740 unsigned long n;
741 unsigned long bafter;
742 unsigned char *props;
743 size_t count = 0;
744 while (XGetWindowProperty(display, w->wid, wmstate, 0, ~(0l), False, AnyPropertyType, &type, &format, &n, &bafter, &props) == Success) {
745 if ((props && *props == WithdrawnState) || count > 1000)
746 break;
747 usleep(1000);
748 ++count;
749 XFree(props);
751 XFree(props);
753 if (w->etype == E_PAGE)
754 hidesubs(w);
757 void entitle(widget_t* w, const char* t) {
758 if (!w)
759 return;
761 if (w->title) {
762 zfree(w->title);
763 w->title = salloc((char*)t);
765 XStoreName(display, w->wid, t);
766 setclass(w, t);
767 XSync(display, False);
770 void settop(widget_t* w) {
771 if (!w)
772 return;
774 XEvent e;
775 e.xclient.type = ClientMessage;
776 e.xclient.serial = 0;
777 e.xclient.send_event = True;
778 e.xclient.display = display;
779 e.xclient.window = w->wid;
780 e.xclient.message_type = XInternAtom(display, "_NET_WM_STATE", False);
781 e.xclient.format = 32;
783 e.xclient.data.l[0] = 1;
784 e.xclient.data.l[1] = XInternAtom(display, "_NET_WM_STATE_ABOVE", False);
785 e.xclient.data.l[2] = 0;
786 e.xclient.data.l[3] = 1;
787 e.xclient.data.l[4] = 0;
788 XSendEvent(display, root, False, SubstructureRedirectMask|SubstructureNotifyMask, &e);
790 XSync(display, False);
791 if (w->flags & F_VISIBLE)
792 show(w);
795 void setdefault(widget_t* w) {
796 if (!w)
797 return;
799 XEvent e;
800 e.xclient.type = ClientMessage;
801 e.xclient.serial = 0;
802 e.xclient.send_event = True;
803 e.xclient.display = display;
804 e.xclient.window = w->wid;
805 e.xclient.message_type = XInternAtom(display, "_NET_WM_STATE", False);
806 e.xclient.format = 32;
808 e.xclient.data.l[0] = 0;
809 e.xclient.data.l[1] = XInternAtom(display, "_NET_WM_STATE_ABOVE", False);
810 e.xclient.data.l[2] = XInternAtom(display, "_NET_WM_STATE_BELOW", False);
811 e.xclient.data.l[3] = 1;
812 e.xclient.data.l[4] = 0;
814 XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask|SubstructureNotifyMask, &e);
816 e.xclient.data.l[1] = XInternAtom(display, "_NET_WM_STATE_MODAL", False);
817 e.xclient.data.l[2] = XInternAtom(display, "_NET_WM_STATE_STICKY", False);
819 XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask|SubstructureNotifyMask, &e);
821 e.xclient.data.l[1] = XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", False);
822 e.xclient.data.l[2] = XInternAtom(display, "_NET_WM_STATE_SKIP_PAGER", False);
824 XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask|SubstructureNotifyMask, &e);
826 XSync(display, False);
827 if (w->flags & F_VISIBLE)
828 show(w);
831 void bypasswm(widget_t* w, int pass) {
832 XSetWindowAttributes wa;
833 memset(&wa, 0, sizeof(wa));
834 wa.override_redirect = pass;
835 XChangeWindowAttributes(display, w->wid, CWOverrideRedirect, &wa);
836 if (pass)
837 w->flags |= F_BYPASS;
838 else
839 w->flags &= ~F_BYPASS;
842 void setbottom(widget_t* w) {
843 if (!w)
844 return;
846 XEvent e;
847 e.xclient.type = ClientMessage;
848 e.xclient.serial = 0;
849 e.xclient.send_event = True;
850 e.xclient.display = display;
851 e.xclient.window = w->wid;
852 e.xclient.message_type = XInternAtom(display, "_NET_WM_STATE", False);
853 e.xclient.format = 32;
855 e.xclient.data.l[0] = 1;
856 e.xclient.data.l[2] = XInternAtom(display, "_NET_WM_STATE_BELOW", False);
857 e.xclient.data.l[3] = 1;
858 e.xclient.data.l[4] = 0;
860 XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask|SubstructureNotifyMask, &e);
862 XSync(display, False);
863 if (w->flags & F_VISIBLE)
864 show(w);
867 void wclose(widget_t* w) {
868 if (!w)
869 return;
871 hide(w);
872 w->flags &= ~F_VISIBLE;
875 intint_t getcenter(widget_t* w) {
876 intint_t r = {0};
877 r.key = -1;
878 r.val = -1;
879 if (!w)
880 return r;
882 r.key = w->w / 2;
883 r.val = w->h / 2;
884 return r;
887 void center(widget_t* w) {
888 if (!w)
889 return;
891 int x;
892 int y;
894 if (w->parent) {
895 x = ((w->parent->w - (w->b*2)) / 2);
896 y = ((w->parent->h - (w->b*2)) / 2);
897 } else {
898 x = (sw() / 2);
899 y = (sh() / 2);
901 intint_t cpos = getcenter(w);
902 move(w, x-cpos.key, y-cpos.val);
905 void wraise(widget_t* w) {
906 if (!w)
907 return;
909 XRaiseWindow(display, w->wid);
910 XSync(display, False);
913 void lower(widget_t* w) {
914 if (!w)
915 return;
917 XLowerWindow(display, w->wid);
918 XSync(display, False);
921 void maximize(widget_t* w) {
922 if (!w)
923 return;
925 XClientMessageEvent e = {0};
927 e.type = ClientMessage;
928 e.window = w->wid;
929 e.message_type = XInternAtom(display, "_NET_WM_STATE", False);
930 e.format = 32;
931 e.data.l[0] = 1;
932 e.data.l[1] = XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
933 e.data.l[2] = XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
934 e.data.l[3] = 0;
935 e.data.l[4] = 0;
936 XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask|SubstructureNotifyMask, (XEvent*) &e);
937 XSync(display, False);
939 if (w->flags & F_GHOST)
940 ghost(w);
943 void resize(widget_t* W, int w, int h) {
944 if (!w || w <= 0 || h <= 0)
945 return;
947 W->w = w;
948 W->h = h;
949 XResizeWindow(display, W->wid, w, h);
950 XSync(display, False);
952 if (W->flags & F_WFIXED && W->flags & F_HFIXED)
953 fixed(W, 0);
954 else if (W->flags & F_WFIXED && !(W->flags & F_HFIXED))
955 wfixed(W, 0);
956 else if (W->flags & F_HFIXED && !(W->flags & F_WFIXED))
957 hfixed(W, 0);
958 else
959 relaxed(W, 0);
961 trigger(W->wid, SIG_RESIZED);
962 resized(W);
965 static void setattr(widget_t* w, const char* k, const char* v) {
966 if (!strcmp(k, "background") || !strcmp(k, "bg")) {
967 if (0) {
968 #ifdef ENABLE_IMAGES
969 } else if (v[0] == '/') {
970 if (ieq(v, "NULL") && w->img) {
971 imlib_context_set_image(w->img);
972 imlib_free_image();
973 w->img = NULL;
974 w->flags |= F_APPLYHOVER;
975 w->draw(w);
976 } else {
977 w->flags &= ~F_APPLYHOVER;
978 loadimg(w, v+1);
979 w->draw(w);
982 #endif
983 else {
984 w->bg = addcol(v);
985 w->draw(w);
987 } else if (!strcmp(k, "color") || !strcmp(k, "foreground") || !strcmp(k, "fg")) {
988 w->fg = addcol(v);
989 w->draw(w);
990 } else if (!strcmp(k, "pressed-background") || !strcmp(k, "pbg")) {
991 w->pbg = addcol(v);
992 w->draw(w);
993 } else if (!strcmp(k, "pressed-color") || !strcmp(k, "pfg")) {
994 w->pfg = addcol(v);
995 w->draw(w);
996 } else if (!strcmp(k, "hovered-background") || !strcmp(k, "hbg")) {
997 w->mask |= EnterWindowMask|LeaveWindowMask;
998 XSelectInput(display, w->wid, w->mask);
999 w->flags |= F_APPLYHOVER;
1000 w->hbg = addcol(v);
1001 w->draw(w);
1002 } else if (!strcmp(k, "hovered-color") || !strcmp(k, "hfg")) {
1003 w->mask |= EnterWindowMask|LeaveWindowMask;
1004 XSelectInput(display, w->wid, w->mask);
1005 w->hfg = addcol(v);
1006 w->draw(w);
1007 } else if (!strcmp(k, "border-color") || !strcmp(k, "bc")) {
1008 XSetWindowBorder(display, w->wid, addcol(v)->pixel);
1009 } else if (!strcmp(k, "width") || !strcmp(k, "w")) {
1010 w->w = strtoul(v, 0, 10);
1011 resize(w, w->w, w->h);
1012 } else if (!strcmp(k, "height") || !strcmp(k, "h")) {
1013 w->h = strtoul(v, 0, 10);
1014 resize(w, w->w, w->h);
1015 } else if (!strcmp(k, "border") || !strcmp(k, "b")) {
1016 w->b = strtoul(v, 0, 10);
1017 XSetWindowBorderWidth(display, w->wid, w->b);
1018 } else if (!strcmp(k, "margin") || !strcmp(k, "g")) {
1019 w->m = strtoul(v, 0, 10);
1020 w->draw(w);
1021 } else if (!strcmp(k, "line") || !strcmp(k, "l")) {
1022 w->lw = strtoul(v, 0, 10);
1023 XSetLineAttributes(display, gc, w->lw, LineSolid, CapRound, JoinRound);
1024 w->draw(w);
1025 } else if (!strcmp(k, "mode") || !strcmp(k, "m")) {
1026 !strcmp(v, "fixed") || !strcmp(v, "f") ? fixed(w, 1) :
1027 !strcmp(v, "wfixed") || !strcmp(v, "w") ? wfixed(w, 1) :
1028 !strcmp(v, "hfixed") || !strcmp(v, "h") ? hfixed(w, 1) :
1029 !strcmp(v, "relaxed") || !strcmp(v, "r") ? relaxed(w, 1) :
1030 pass();
1031 } else if (!strcmp(k, "align") || !strcmp(k, "a")) {
1032 align al =
1033 (!strcmp(v, "l") || !strcmp(v, "left") || !strcmp(v, "middle-left")) ? A_ML :
1034 (!strcmp(v, "r") || !strcmp(v, "right") || !strcmp(v, "middle-right")) ? A_MR :
1035 (!strcmp(v, "c") || !strcmp(v, "center") || !strcmp(v, "middle-center")) ? A_MC :
1036 (!strcmp(v, "tl") || !strcmp(v, "top-left")) ? A_TL :
1037 (!strcmp(v, "tr") || !strcmp(v, "top-right")) ? A_TR :
1038 (!strcmp(v, "t") || !strcmp(v, "top-center")) ? A_TC :
1039 (!strcmp(v, "bl") || !strcmp(v, "bottom-left")) ? A_BL :
1040 (!strcmp(v, "br") || !strcmp(v, "bottom-right")) ? A_BR :
1041 (!strcmp(v, "b") || !strcmp(v, "bottom-center")) ? A_BC :
1042 A_MC;
1043 if (w->etype == E_PAGE) {
1044 page_t* p = (page_t*)w;
1045 if (p->l == HL)
1046 w->align = al;
1047 else
1048 p->valign = al;
1049 setlayout(p, p->l, 1);
1050 } else {
1051 w->align = al;
1053 w->draw(w);
1054 } else if (!strcmp(k, "f") || !strcmp(k, "font")) {
1055 if (!w->fs && !gc)
1056 return;
1057 XFreeFont(display, w->fs);
1058 debug("trying to load font: '%s'\n", v);
1059 w->fs = XLoadQueryFont(display, v);
1060 if (!w->fs) {
1061 debug("cannot load font '%s'\n", v);
1062 w->fs = XLoadQueryFont(display, "fixed");
1064 if (!w->fs)
1065 return;
1066 w->fh = (w->fs->max_bounds.ascent + w->fs->max_bounds.descent);
1067 w->draw(w);
1068 } else {
1069 fprintf(stderr, "warning, unused attr: %s->%s\n", k, v);
1073 void style(widget_t* w, const char* s) {
1074 if (!w)
1075 return;
1076 const char* p = s;
1078 char v[BSIZ];
1079 char* k = buf;
1080 char* b = buf;
1081 int esc = 0;
1082 size_t i;
1084 for (i=0; *p && (i<BSIZ-1); ++p) {
1085 if (!esc) {
1086 switch (*p) {
1087 case '\\':
1088 if (!esc) {
1089 esc = 1;
1090 continue;
1092 esc = 0;
1093 break;
1094 case ' ':
1095 case '\t':
1096 continue;
1097 case '\n':
1098 case '\r':
1099 case '\v':
1100 case '\f':
1101 case '|':
1102 case ';':
1103 b[i] = 0;
1104 if (i)
1105 setattr(w, k, v);
1106 b = k;
1107 i = 0;
1108 continue;
1109 case ':':
1110 b[i] = 0;
1111 b = v;
1112 i = 0;
1113 continue;
1114 default:
1115 break;
1118 esc = 0;
1119 b[i++] = *p;
1121 if (i) {
1122 b[i] = 0;
1123 setattr(w, k, v);
1127 XColor* addcol(const char* v) {
1128 XColor* c = NULL;
1129 if (!colors->contains(colors, v)) {
1130 c = calloc(1, sizeof(XColor));
1131 if (colors->full(colors))
1132 colors->rehash(colors);
1133 if (XParseColor(display, DefaultColormap(display, screen), v, c) &&
1134 XAllocColor(display, DefaultColormap(display, screen), c))
1136 colors->insert(colors, salloc((char*)v), c);
1137 } else {
1138 zfree(c);
1139 colors->get(colors, "black", &c);
1140 return c;
1143 colors->get(colors, v, &c);
1144 return c;
1147 void freeze(widget_t* w) {
1148 if (!w)
1149 return;
1150 if (w->etype == E_PAGE)
1151 freezesubs(w);
1152 w->flags |= F_FREEZED;
1155 void unfreeze(widget_t* w) {
1156 if (!w)
1157 return;
1158 if (w->etype == E_PAGE)
1159 unfreezesubs(w);
1160 w->flags &= ~F_FREEZED;
1163 void fit(widget_t* w) {
1164 if (!w)
1165 return;
1167 if (w->etype == E_PAGE) {
1168 fit_to_cont(w);
1169 return;
1171 if (!w->fs || !w->data || !*w->data)
1172 return;
1174 resize(w, w->maxwcont+SPIXEL+(w->m*2), w->maxhcont+SPIXEL+(w->m*2));
1177 static void nwidgets(widget_t* w, widgetwidget_t* n) {
1178 if (!w || !w->parent || !n)
1179 return;
1181 page_t* p = (page_t*)w->parent;
1183 n->key = NULL;
1184 n->val = NULL;
1186 for (size_t i=0; i<p->subs->count; ++i) {
1187 widget_t* g;
1188 p->subs->get(p->subs, i, &g);
1189 if (w == g) {
1190 if (i)
1191 p->subs->get(p->subs, i-1, &n->key);
1192 else
1193 n->key = NULL;
1194 if (i+1 < p->subs->count)
1195 p->subs->get(p->subs, i+1, &n->val);
1196 else
1197 n->val = NULL;
1198 break;
1203 void fill_right(widget_t* w, int o) {
1204 if (!w || !w->parent)
1205 return;
1207 page_t* p = (page_t*)w->parent;
1208 widgetwidget_t n;
1209 widget_t* next;
1210 nwidgets(w, &n);
1211 next = n.val;
1212 if (!next || p->l == VL || o)
1213 resize(w, w->parent->w - w->x - (w->b*2) - w->parent->m, w->h);
1214 else
1215 resize(w, next->x - w->x - (w->b*2), w->h);
1218 void fill_bottom(widget_t* w, int o) {
1219 if (!w || !w->parent)
1220 return;
1222 page_t* p = (page_t*)w->parent;
1223 widgetwidget_t n;
1224 widget_t* next;
1225 nwidgets(w, &n);
1226 next = n.val;
1228 if (!next || p->l == HL || o)
1229 resize(w, w->w, w->parent->h - w->y - (w->b*2) - w->parent->m);
1230 else
1231 resize(w, w->w, next->y - w->y - (w->b*2));
1234 void fill(widget_t* w, int o) {
1235 if (!w || !w->parent)
1236 return;
1237 fill_right(w, o);
1238 fill_bottom(w, o);
1241 void tray(widget_t* w) {
1242 if (!w || w->parent)
1243 return;
1245 w->flags |= F_TRAYED;
1247 XEvent ev;
1248 Atom sel = XInternAtom (display, "_NET_SYSTEM_TRAY_S0", False);
1250 if (sel == None)
1251 return;
1252 Window tray = XGetSelectionOwner(display, sel);
1253 if (tray == None)
1254 return;
1256 XSelectInput (display,tray,StructureNotifyMask);
1257 memset(&ev, 0, sizeof(ev));
1259 ev.xclient.type = ClientMessage;
1260 ev.xclient.window = tray;
1261 ev.xclient.message_type = XInternAtom(display, "_NET_SYSTEM_TRAY_OPCODE", False);
1262 ev.xclient.format = 32;
1263 ev.xclient.data.l[0] = CurrentTime;
1264 ev.xclient.data.l[1] = 0;
1265 ev.xclient.data.l[2] = w->wid;
1266 ev.xclient.data.l[3] = 0;
1267 ev.xclient.data.l[4] = 0;
1269 XSendEvent(display, tray, False, NoEventMask, &ev);
1270 XSync(display, False);
1272 while (parentof(w->wid) == root)
1273 usleep(1000);
1274 if (!(w->flags & F_VISIBLE))
1275 XUnmapWindow(display, w->wid);
1277 intint_t tc = coords(w->wid);
1279 w->x = tc.key;
1280 w->y = tc.val;
1283 void ghost(widget_t* w) {
1284 if (!w)
1285 return;
1286 w->flags |= F_GHOST;
1288 XClientMessageEvent e = {0};
1290 e.type = ClientMessage;
1291 e.window = w->wid;
1292 e.message_type = XInternAtom(display, "_NET_WM_STATE", False);
1293 e.format = 32;
1294 e.data.l[0] = 1;
1295 e.data.l[1] = XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", False);
1296 e.data.l[2] = 0;
1297 e.data.l[3] = 0;
1298 e.data.l[4] = 0;
1300 XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask|SubstructureNotifyMask, (XEvent *) &e);
1301 XSync(display, False);
1304 void movealigns(widget_t* w, widget_t* r, const char* p) {
1305 if (!w || !r || !p)
1306 return;
1308 movealign(w, r, getalign(p));
1311 void move(widget_t* w, int x, int y) {
1312 if (!w)
1313 return;
1315 w->x = x;
1316 w->y = y;
1317 XMoveWindow(display, w->wid, x, y);
1318 moved(w);
1321 void focus(widget_t* w) {
1322 if (!w || !(w->flags & F_VISIBLE))
1323 return;
1325 XSetInputFocus(display, w->wid, RevertToNone, CurrentTime);
1328 void settext(widget_t* w, const char* t) {
1329 if (!t || !*t)
1330 return;
1331 if (w->data)
1332 free(w->data);
1333 w->data = salloc((char*)t);
1334 w->draw(w);
1337 void addtext(widget_t* w, const char* t) {
1338 if (!t || !*t)
1339 return;
1340 w->data = w->data ? sadd(w->data, t) : salloc((char*)t);
1341 w->draw(w);
1344 void pressed(widget_t* w, int b) {
1345 w->flags |=
1346 (b == MOUSE_LEFT) ? F_LPRESSED :
1347 (b == MOUSE_RIGHT) ? F_RPRESSED :
1348 (b == MOUSE_MIDDLE) ? F_MPRESSED :
1349 F_LPRESSED;
1351 w->draw(w);
1352 trigger(w->wid,
1353 (b == MOUSE_LEFT) ? SIG_LPRESSED :
1354 (b == MOUSE_RIGHT) ? SIG_RPRESSED :
1355 (b == MOUSE_MIDDLE) ? SIG_MPRESSED :
1356 SIG_LPRESSED);
1358 trigger(w->wid, SIG_PRESSED);
1359 struct timeval now;
1360 time_t msec;
1362 if (!w->tpress.tv_usec) {
1363 gettimeofday(&w->tpress, NULL);
1364 return;
1366 gettimeofday(&now, NULL);
1368 msec = ((now.tv_sec * 1000) + (now.tv_usec / 1000)) -
1369 ((w->tpress.tv_sec * 1000) + (w->tpress.tv_usec / 1000));
1371 if (msec >= 0 && msec <= 300) {
1372 w->flags |=
1373 (b == MOUSE_LEFT) ? F_LDCLICKED :
1374 (b == MOUSE_RIGHT) ? F_RDCLICKED :
1375 (b == MOUSE_MIDDLE) ? F_MDCLICKED :
1376 F_LDCLICKED;
1378 gettimeofday(&w->tpress, NULL);
1381 void released(widget_t* w, int b) {
1382 w->flags &=
1383 (b == MOUSE_LEFT) ? ~F_LPRESSED :
1384 (b == MOUSE_RIGHT) ? ~F_RPRESSED :
1385 (b == MOUSE_MIDDLE) ? ~F_MPRESSED :
1386 ~F_LPRESSED;
1388 w->draw(w);
1389 trigger(w->wid,
1390 (b == MOUSE_LEFT) ? SIG_LRELEASED :
1391 (b == MOUSE_RIGHT) ? SIG_RRELEASED :
1392 (b == MOUSE_MIDDLE) ? SIG_MRELEASED :
1393 SIG_LRELEASED);
1395 trigger(w->wid, SIG_RELEASED);
1396 if (w->flags &
1397 (b == MOUSE_LEFT ? F_LDCLICKED :
1398 b == MOUSE_RIGHT ? F_RDCLICKED :
1399 b == MOUSE_MIDDLE ? F_MDCLICKED :
1400 F_LDCLICKED))
1402 trigger(w->wid,
1403 (b == MOUSE_LEFT) ? SIG_LDCLICKED :
1404 (b == MOUSE_RIGHT) ? SIG_RDCLICKED :
1405 (b == MOUSE_MIDDLE) ? SIG_MDCLICKED :
1406 SIG_LDCLICKED);
1407 trigger(w->wid, SIG_DCLICKED);
1408 w->flags &=
1409 (b == MOUSE_LEFT) ? ~F_LDCLICKED :
1410 (b == MOUSE_RIGHT) ? ~F_RDCLICKED :
1411 (b == MOUSE_MIDDLE) ? ~F_MDCLICKED :
1412 ~F_LDCLICKED;
1413 } else {
1414 trigger(w->wid,
1415 (b == MOUSE_LEFT) ? SIG_LCLICKED :
1416 (b == MOUSE_RIGHT) ? SIG_RCLICKED :
1417 (b == MOUSE_MIDDLE) ? SIG_MCLICKED :
1418 SIG_LCLICKED);
1419 trigger(w->wid, SIG_CLICKED);
1423 void scroll(widget_t* w, int d) {
1424 if (d)
1425 trigger(w->wid, SIG_SCROLLUP);
1426 else
1427 trigger(w->wid, SIG_SCROLLDOWN);
1430 void write_align(widget_t* w, const char* s, align a, intint_t* r) {
1431 if (!s || !gc || !w->fs)
1432 return;
1433 size_t l = 0;
1434 char* ls;
1435 intint_t c = {0};
1437 each(w->ll, ls, zfree(ls););
1438 w->ll->clear(w->ll);
1440 ssplit(s, '\n', w->ll, "");
1442 for (size_t i=0; i<w->ll->count; ++i) {
1443 if (w->ll->get(w->ll, i, &ls)) {
1444 if (*ls) {
1445 char* n;
1446 if (w->ll->get(w->ll, i+1, &n) && !*n) {
1447 zfree(n);
1448 w->ll->remove(w->ll, i+1);
1453 w->maxhcont = w->ll->count * w->fh;
1454 for (size_t i=0; i<w->ll->count; ++i) {
1455 w->ll->get(w->ll, i, &ls);
1456 l = strlen(ls);
1457 c = textpos(w, ls, a, i, w->ll->count);
1458 XDrawString(display, w->wid, gc, c.key, c.val, ls, l);
1460 if (r) {
1461 r->key = c.key;
1462 r->val = c.val;
1466 void addpoints(widget_t* w, char* col, vec_int_t* ps) {
1467 if (!w)
1468 return;
1469 if (!widloci)
1470 widloci = alloc(map_widloci_t);
1472 loci_t* s = NULL;
1473 if (!widloci->get(widloci, w->wid, &s)) {
1474 s = alloc(loci_t);
1475 widloci->insert(widloci, w->wid, s);
1477 points_t* m = alloc(points_t);
1478 m->count = ps->count / 2;
1479 m->ps = malloc(sizeof(XPoint)*(ps->count/2));
1480 if (!m->ps) {
1481 fprintf(stderr, "FATAL ERROR: cannot allocate memory.\n");
1482 exit(EXIT_FAILURE);
1484 s->loci->push(s->loci, m);
1486 int x;
1487 int y;
1488 size_t i = 0;
1489 while (ps->count) {
1490 ps->pop_back(ps, &x);
1491 ps->pop_back(ps, &y);
1492 m->ps[i].x = x;
1493 m->ps[i].y = y;
1494 s->cols->push(s->cols, salloc(col));
1495 ++i;
1497 w->draw(w);
1500 void addpixels(widget_t* w, char* col, vec_int_t* ps) {
1501 if (!w)
1502 return;
1503 if (!widpixels)
1504 widpixels = alloc(map_widloci_t);
1506 loci_t* s = NULL;
1507 if (!widpixels->get(widpixels, w->wid, &s)) {
1508 s = alloc(loci_t);
1509 widpixels->insert(widpixels, w->wid, s);
1511 points_t* m = alloc(points_t);
1512 m->count = ps->count / 2;
1513 m->ps = malloc(sizeof(XPoint)*(ps->count/2));
1514 if (!m->ps) {
1515 fprintf(stderr, "FATAL ERROR: cannot allocate memory.\n");
1516 exit(EXIT_FAILURE);
1518 s->loci->push(s->loci, m);
1520 int x;
1521 int y;
1522 size_t i = 0;
1523 while (ps->count) {
1524 ps->pop_back(ps, &x);
1525 ps->pop_back(ps, &y);
1526 m->ps[i].x = x;
1527 m->ps[i].y = y;
1528 s->cols->push(s->cols, salloc(col));
1529 ++i;
1531 w->draw(w);
1534 void addlines(widget_t* w, char* col, vec_int_t* ps) {
1535 if (!w)
1536 return;
1537 if (!widlines)
1538 widlines = alloc(map_widloci_t);
1540 loci_t* s = NULL;
1541 if (!widlines->get(widlines, w->wid, &s)) {
1542 s = alloc(loci_t);
1543 widlines->insert(widlines, w->wid, s);
1545 XPoint* m = NULL;
1546 m = malloc(sizeof(XPoint)*(ps->count/2));
1547 if (!m) {
1548 fprintf(stderr, "FATAL ERROR: cannot allocate memory.\n");
1549 exit(EXIT_FAILURE);
1551 points_t* p = alloc(points_t);
1552 p->count = ps->count / 2;
1553 p->ps = m;
1555 int x;
1556 int y;
1557 for (size_t i=0; ps->count; ++i) {
1558 ps->pop_back(ps, &x);
1559 ps->pop_back(ps, &y);
1560 m[i].x = x;
1561 m[i].y = y;
1563 s->cols->push(s->cols, salloc(col));
1564 s->loci->push(s->loci, p);
1565 w->draw(w);
1568 void addarcs(widget_t* w, int area, char* col, vec_int_t* xas) {
1569 if (!w)
1570 return;
1571 map_widarcssets_t** as = area ? &widarcsareas : &widarcssets;
1573 if (!*as)
1574 *as = alloc(map_widarcssets_t);
1576 arcssets_t* s = NULL;
1577 if (!(*as)->get(*as, w->wid, &s)) {
1578 s = alloc(arcssets_t);
1579 (*as)->insert(*as, w->wid, s);
1581 XArc* m = NULL;
1582 m = malloc(sizeof(XArc)*(xas->count/6));
1583 if (!m) {
1584 fprintf(stderr, "FATAL ERROR: cannot allocate memory.\n");
1585 exit(EXIT_FAILURE);
1587 arcs_t* a = alloc(arcs_t);
1588 a->count = xas->count / 6;
1589 a->as = m;
1591 for (size_t i=0; xas->count; ++i) {
1592 int x;
1593 int y;
1594 int ww;
1595 int hh;
1596 int alpha;
1597 int beta;
1599 xas->pop_back(xas, &x);
1600 xas->pop_back(xas, &y);
1601 xas->pop_back(xas, &ww);
1602 xas->pop_back(xas, &hh);
1603 xas->pop_back(xas, &alpha);
1604 xas->pop_back(xas, &beta);
1606 m[i].x = x;
1607 m[i].y = y;
1608 m[i].width = ww;
1609 m[i].height = hh;
1610 m[i].angle1 = alpha*64;
1611 m[i].angle2 = beta*64;
1613 s->arcssets->push(s->arcssets, a);
1614 s->cols->push(s->cols, salloc(col));
1615 w->draw(w);
1618 void addarea(widget_t* w, char* col, vec_int_t* ps) {
1619 if (!w)
1620 return;
1621 if (!widareas)
1622 widareas = alloc(map_widloci_t);
1623 loci_t* s = NULL;
1624 if (!widareas->get(widareas, w->wid, &s)) {
1625 s = alloc(loci_t);
1626 widareas->insert(widareas, w->wid, s);
1629 points_t* m = alloc(points_t);
1630 m->ps = malloc(sizeof(XPoint)*(ps->count/2));
1631 m->count = ps->count / 2;
1632 if (!m->ps) {
1633 fprintf(stderr, "FATAL ERROR: cannot allocate memory.\n");
1634 exit(EXIT_FAILURE);
1636 int x;
1637 int y;
1638 for (size_t i=0; ps->count; ++i) {
1639 ps->pop_back(ps, &x);
1640 ps->pop_back(ps, &y);
1641 m->ps[i].x = x;
1642 m->ps[i].y = y;
1644 s->loci->push(s->loci, m);
1645 s->cols->push(s->cols, salloc(col));
1646 w->draw(w);
1649 void addnamedpoint(widget_t* w, char* col, char* n, int x, int y) {
1650 if (!w)
1651 return;
1652 if (!widnamedpoints)
1653 widnamedpoints = alloc(map_widnamedpoints_t);
1654 namedpoints_t* s = NULL;
1655 if (!widnamedpoints->get(widnamedpoints, w->wid, &s)) {
1656 s = alloc(namedpoints_t);
1657 widnamedpoints->insert(widnamedpoints, w->wid, s);
1659 s->ps->push(s->ps, x);
1660 s->ps->push(s->ps, y);
1661 s->names->push(s->names, salloc(n));
1662 s->cols->push(s->cols, salloc(col));
1663 w->draw(w);
1666 static XPoint* points_to_normal_coords(widget_t* w, points_t* ps) {
1667 XPoint* cps = malloc(sizeof(XPoint)*ps->count);
1668 memcpy(cps, ps->ps, sizeof(XPoint)*ps->count);
1669 if (!cps) {
1670 fprintf(stderr, "FATAL ERROR: cannot allocate memory.\n");
1671 exit(EXIT_FAILURE);
1673 for (size_t j=0; j<ps->count; ++j)
1674 cps[j].y = w->h-cps[j].y;
1675 return cps;
1678 static XArc* arcs_to_normal_coords(widget_t* w, arcs_t* as) {
1679 XArc* cas = malloc(sizeof(XArc)*as->count);
1680 memcpy(cas, as->as, sizeof(XArc)*as->count);
1681 if (!cas) {
1682 fprintf(stderr, "FATAL ERROR: cannot allocate memory.\n");
1683 exit(EXIT_FAILURE);
1685 for (size_t i=0; i<as->count; ++i) {
1686 cas[i].x = cas[i].x - (cas[i].width / 2);
1687 cas[i].y = w->h - cas[i].y - (cas[i].height / 2);
1689 return cas;
1692 void drawnamedpoints(widget_t* w) {
1693 namedpoints_t* s;
1694 if (widnamedpoints->get(widnamedpoints, w->wid, &s)) {
1695 XColor* c;
1696 char* col;
1697 char* name;
1698 int x;
1699 int y;
1700 for (size_t i=0; i<s->cols->count; ++i) {
1701 s->cols->get(s->cols, i, &col);
1702 s->names->get(s->names, i, &name);
1703 s->ps->get(s->ps, i*2, &x);
1704 s->ps->get(s->ps, i*2+1, &y);
1705 c = addcol(col);
1706 XSetForeground(display, gc, c->pixel);
1707 XDrawArc(display, w->wid, gc, x, w->h-y, w->lw, w->lw, 0, 360*64);
1708 XDrawString(display, w->wid, gc, x, w->h-y, name, strlen(name));
1713 void drawpixels(widget_t* w) {
1714 loci_t* s;
1715 if (widpixels->get(widpixels, w->wid, &s)) {
1716 XColor* c;
1717 char* col;
1718 points_t* ps;
1719 for (size_t i=0; i<s->cols->count; ++i) {
1720 s->cols->get(s->cols, i, &col);
1721 s->loci->get(s->loci, i, &ps);
1722 c = addcol(col);
1723 XSetForeground(display, gc, c->pixel);
1724 XPoint* cps = points_to_normal_coords(w, ps);
1725 XDrawPoints(display, w->wid, gc, cps, ps->count, CoordModeOrigin);
1726 free(cps);
1731 void drawpoints(widget_t* w) {
1732 loci_t* s;
1733 if (widloci->get(widloci, w->wid, &s)) {
1734 XColor* c;
1735 char* col;
1736 points_t* ps;
1737 for (size_t i=0; i<s->cols->count; ++i) {
1738 s->cols->get(s->cols, i, &col);
1739 s->loci->get(s->loci, i, &ps);
1740 c = addcol(col);
1741 XSetForeground(display, gc, c->pixel);
1742 XArc* cas = malloc(sizeof(XArc)*ps->count);
1743 if (!cas) {
1744 fprintf(stderr, "FATAL ERROR: cannot allocate memory.\n");
1745 exit(EXIT_FAILURE);
1747 for (size_t j=0; j<ps->count; ++j) {
1748 cas[j].x = ps->ps[j].x;
1749 cas[j].y = w->h - ps->ps[j].y;
1750 cas[j].width = w->lw;
1751 cas[j].height = w->lw;
1752 cas[j].angle1 = 0;
1753 cas[j].angle2 = 360*64;
1755 XDrawArcs(display, w->wid, gc, cas, ps->count);
1756 free(cas);
1761 void drawlines(widget_t* w) {
1762 loci_t* s;
1763 if (widlines->get(widlines, w->wid, &s)) {
1764 XColor* c;
1765 char* col;
1766 points_t* ps;
1767 for (size_t i=0; i<s->cols->count; ++i) {
1768 s->cols->get(s->cols, i, &col);
1769 s->loci->get(s->loci, i, &ps);
1770 c = addcol(col);
1771 XSetForeground(display, gc, c->pixel);
1772 XPoint* cps = points_to_normal_coords(w, ps);
1773 XDrawLines(display, w->wid, gc, cps, ps->count, CoordModeOrigin);
1774 free(cps);
1779 static void drawarcs_fill(widget_t* w, int fill) {
1780 arcssets_t* s;
1781 int (*oparc)(Display*, Drawable, GC, XArc*, int);
1782 map_widarcssets_t** set;
1784 if (fill) {
1785 oparc = XFillArcs;
1786 set = &widarcsareas;
1787 } else {
1788 oparc = XDrawArcs;
1789 set = &widarcssets;
1791 if ((*set)->get(*set, w->wid, &s)) {
1792 XColor* c;
1793 char* col;
1794 for (size_t i=0; i<s->cols->count; ++i) {
1795 s->cols->get(s->cols, i, &col);
1796 c = addcol(col);
1797 XSetForeground(display, gc, c->pixel);
1798 for (size_t j=0; j<s->arcssets->count; ++j) {
1799 arcs_t* arcs;
1800 s->arcssets->get(s->arcssets, j, &arcs);
1801 XArc* carcs = arcs_to_normal_coords(w, arcs);
1802 oparc(display, w->wid, gc, carcs, arcs->count);
1803 free(carcs);
1809 void drawarcs(widget_t* w) {
1810 drawarcs_fill(w, 0);
1813 void drawareas(widget_t* w) {
1814 loci_t* s = NULL;
1815 if (!widareas->get(widareas, w->wid, &s))
1816 return;
1818 for (size_t i=0; i<s->cols->count; ++i) {
1819 char* col;
1820 points_t* ps;
1821 s->cols->get(s->cols, i, &col);
1822 s->loci->get(s->loci, i, &ps);
1824 XColor* c = addcol(col);
1825 XSetForeground(display, gc, c->pixel);
1827 XPoint* cps = points_to_normal_coords(w, ps);
1828 XFillPolygon(display, w->wid, gc, cps, ps->count, Complex, CoordModeOrigin);
1829 free(cps);
1833 void drawarcsareas(widget_t* w) {
1834 drawarcs_fill(w, 1);
1837 void clear(widget_t* w) {
1838 if (w->data)
1839 free(w->data);
1841 w->data = salloc("");
1842 w->draw(w);
1843 w->maxwcont = 0;
1844 w->maxhcont = 0;
1847 void tgrip(widget_t* w) {
1848 if (w->flags & F_GRIP) {
1849 w->flags &= ~F_GRIP;
1850 if (!(w->flags & (F_XMOV|F_YMOV))) {
1851 w->mask &= ~ButtonMotionMask;
1852 XSelectInput(display, w->wid, w->mask);
1854 } else {
1855 w->flags |= F_GRIP;
1856 w->flags &= ~(F_XMOV|F_YMOV);
1857 w->mask |= ButtonPressMask|ButtonReleaseMask|ButtonMotionMask;
1858 XSelectInput(display, w->wid, w->mask);
1862 void txmov(widget_t* w) {
1863 if (w->flags & F_XMOV) {
1864 w->flags &= ~F_XMOV;
1865 if (!(w->flags & F_YMOV)) {
1866 w->mask &= ~ButtonMotionMask;
1867 XSelectInput(display, w->wid, w->mask);
1869 } else {
1870 w->flags &= ~F_GRIP;
1871 w->flags |= F_XMOV;
1872 w->mask |= ButtonPressMask|ButtonReleaseMask|ButtonMotionMask;
1873 XSelectInput(display, w->wid, w->mask);
1877 void tymov(widget_t* w) {
1878 if (w->flags & F_YMOV) {
1879 w->flags &= ~F_YMOV;
1880 if (!(w->flags & F_XMOV)) {
1881 w->mask &= ~ButtonMotionMask;
1882 XSelectInput(display, w->wid, w->mask);
1884 } else {
1885 w->flags &= ~F_GRIP;
1886 w->flags |= F_YMOV;
1887 w->mask |= ButtonPressMask|ButtonReleaseMask|ButtonMotionMask;
1888 XSelectInput(display, w->wid, w->mask);
1892 void penter(widget_t* w) {
1893 trigger(w->wid, SIG_ENTER);
1896 void pleave(widget_t* w) {
1897 trigger(w->wid, SIG_LEAVE);
1900 void focused(widget_t* w) {
1901 trigger(w->wid, SIG_FOCUS);
1904 void unfocused(widget_t* w) {
1905 trigger(w->wid, SIG_UNFOCUS);
1908 void event(widget_t* w, XEvent* e) {
1909 if (!w || w->flags & F_GARBAGE)
1910 return;
1911 if (e->type == Expose) {
1912 if (w->flags & F_TRAYED) {
1913 intint_t tc = coords(w->wid);
1914 if (w->x != tc.key || w->y != tc.val) {
1915 w->x = tc.key;
1916 w->y = tc.val;
1917 moved(w);
1920 w->draw(w);
1921 return;
1922 } else if (e->type == ClientMessage && (Atom)e->xclient.data.l[0] == delatom) {
1923 closed(w);
1924 return;
1925 } else if (!w->parent && e->type == ConfigureNotify) {
1926 if (w->w != e->xconfigure.width || w->h != e->xconfigure.height) {
1927 w->w = e->xconfigure.width;
1928 w->h = e->xconfigure.height;
1929 resized(w);
1930 w->draw(w);
1931 } else if (w->x != e->xconfigure.x || w->y != e->xconfigure.y) {
1932 w->x = e->xconfigure.x;
1933 w->y = e->xconfigure.y;
1934 moved(w);
1936 return;
1938 if (w->flags & F_FREEZED)
1939 return;
1940 if (e->type == ButtonPress) {
1941 switch (e->xbutton.button) {
1942 case MOUSE_LEFT:
1943 case MOUSE_RIGHT:
1944 case MOUSE_MIDDLE:
1945 if (w->flags & (F_GRIP|F_XMOV|F_YMOV)) {
1946 lrpos.x = e->xmotion.x_root;
1947 lrpos.y = e->xmotion.y_root;
1948 fp.x = e->xmotion.x;
1949 fp.y = e->xmotion.y;
1951 pressed(w, e->xbutton.button);
1952 break;
1953 case MOUSE_SCROLLUP:
1954 scroll(w, 1);
1955 break;
1956 case MOUSE_SCROLLDOWN:
1957 scroll(w, 0);
1958 break;
1960 } else if (e->type == ButtonRelease) {
1961 switch (e->xbutton.button) {
1962 case MOUSE_LEFT:
1963 case MOUSE_RIGHT:
1964 case MOUSE_MIDDLE:
1965 released(w, e->xbutton.button);
1966 break;
1967 default:
1968 return;
1970 if (w->etype == E_CHECKBOX)
1971 w->flags & F_CHECKED ? uncheck(w) : check(w);
1972 } else if (e->type == KeyPress && w->etype == E_INPUT) {
1973 buildtext(w, e);
1974 w->draw(w);
1975 } else if (e->type == KeyRelease && w->etype == E_INPUT) {
1976 buildtext(w, e);
1977 w->draw(w);
1978 } else if (e->type == MotionNotify && w->parent && w->flags & F_GRIP) {
1979 XMotionEvent* m = (XMotionEvent*)e;
1980 point_t np;
1981 widget_t* r = NULL;
1982 widget_t* p = NULL;
1983 int rx;
1984 int ry;
1985 int tx;
1986 int ty;
1988 if (!w->parent->parent && !(w->parent->flags & F_BYPASS)) {
1989 w->flags &= ~F_GRIP;
1990 return;
1992 Window cr;
1994 p = w->parent;
1995 if (p->parent)
1996 r = p->parent;
1998 XTranslateCoordinates(display, root, r ? r->wid : root, m->x_root, m->y_root, &rx, &ry, &cr);
1999 XTranslateCoordinates(display, w->wid, p->wid, fp.x, fp.y, &tx, &ty, &cr);
2000 np.x = rx - tx;
2001 np.y = ry - ty;
2003 if (r) {
2004 move(p,
2005 (np.x + p->w + (p->b * 2)) > (r->w - r->m) ? r->w - r->m - p->w - (p->b * 2) :
2006 np.x > r->m ? np.x :
2007 r->m,
2008 (np.y + p->h + (p->b * 2)) > (r->h - r->m) ? r->h - r->m - p->h - (p->b * 2) :
2009 np.y > r->m ? np.y :
2010 r->m);
2011 } else {
2012 move(p, np.x, np.y);
2014 lrpos.x = m->x_root;
2015 lrpos.y = m->y_root;
2016 } else if (e->type == MotionNotify && w->flags & (F_XMOV|F_YMOV)) {
2017 XMotionEvent* m = (XMotionEvent*)e;
2018 widget_t* p = NULL;
2019 Window cr;
2020 point_t np;
2021 int rx;
2022 int ry;
2024 if (!w->parent && !(w->flags & F_BYPASS)) {
2025 w->flags &= ~(F_XMOV|F_YMOV);
2026 return;
2028 if (w->parent)
2029 p = w->parent;
2031 XTranslateCoordinates(display, root, p ? p->wid : root, m->x_root, m->y_root, &rx, &ry, &cr);
2032 np.x = rx - fp.x;
2033 np.y = ry - fp.y;
2035 if (p) {
2036 move(w,
2037 w->flags & F_XMOV ?
2038 (np.x + w->w + (w->b * 2)) > (p->w - p->m) ? p->w - p->m - w->w - (w->b * 2) :
2039 np.x > p->m ? np.x :
2040 p->m :
2041 w->x
2043 w->flags & F_YMOV ?
2044 (np.y + w->h + (w->b * 2)) >= (p->h - p->m) ? p->h - p->m - w->h - (w->b * 2):
2045 np.y > p->m ? np.y :
2046 p->m :
2047 w->y
2049 } else {
2050 move(w, w->flags & F_XMOV ? np.x : w->x, w->flags & F_YMOV ? np.y : w->y);
2052 lrpos.x = m->x_root;
2053 lrpos.y = m->y_root;
2054 } else if (e->type == EnterNotify) {
2055 penter(w);
2056 w->flags |= F_HOVERED;
2057 if (w->parent)
2058 focus(w);
2059 w->draw(w);
2060 } else if (e->type == LeaveNotify && e->xcrossing.detail != NotifyInferior) {
2061 pleave(w);
2062 w->flags &= ~F_HOVERED;
2063 w->draw(w);
2064 } else if (e->type == FocusIn) {
2065 if (w->etype == E_INPUT) {
2066 int ret = XGrabKeyboard(display, w->wid, False, GrabModeAsync, GrabModeAsync, CurrentTime);
2067 if (ret) {
2068 fprintf(stderr, "Error: unable to grab keyboard on window %lu, reason: %d\n", w->wid, ret);
2069 exit(EXIT_FAILURE);
2070 } else {
2071 XSync(display, True);
2074 focused(w);
2075 w->flags |= F_FOCUSED;
2076 w->draw(w);
2077 } else if (e->type == FocusOut) {
2078 if (w->etype == E_INPUT)
2079 XUngrabKeyboard(display, CurrentTime);
2080 unfocused(w);
2081 w->flags &= ~F_FOCUSED;
2082 w->draw(w);
2086 void relate(widget_t* w, widget_t* r, const char* p) {
2087 if (!w || !r || !p)
2088 return;
2090 if (eq(p, "0")) {
2091 r->related->remove(r->related, w->wid);
2092 w->relal->key = 0;
2093 return;
2095 if (r->related->contains(r->related, w->wid))
2096 return;
2098 align a = getalign(p);
2099 w->relal->key = r->wid;
2100 w->relal->val = a;
2101 r->related->insert(r->related, w->wid, a);
2102 movealign(w, r, a);
2105 void widget_t_free(widget_t* w) {
2106 #ifdef ENABLE_IMAGES
2107 if (w->img) {
2108 imlib_context_set_image(w->img);
2109 imlib_free_image();
2111 #endif
2112 if (w->parent)
2113 child_going(w->parent, w);
2114 if (!(w->flags & F_EXT))
2115 XDestroyWindow(display, w->wid);
2116 if (w->fs) {
2117 XFreeFont(display, w->fs);
2118 w->fs = NULL;
2120 release(w->relal);
2121 release(w->related);
2122 #ifdef ENABLE_CONTROL
2123 if (w->to_release)
2124 release(w->to_release);
2125 #endif
2127 if (w->data)
2128 zfree(w->data);
2129 if (w->title)
2130 zfree(w->title);
2132 if (w->ll) {
2133 char* s;
2134 each(w->ll, s, free(s););
2135 release(w->ll);
2138 if (w->flags & F_EXT) {
2139 if (options & KILL_EXTS_ON_QUIT && (w->pid != getpid()))
2140 kill(w->pid, SIGTERM);
2141 free(w);
2145 widget_t* widget_t_init(widget_t* w) {
2146 if (!w || !display)
2147 return NULL;
2149 memset(w, 0, sizeof(widget_t));
2150 w->data = salloc("");
2151 w->title = salloc("");
2152 w->align = A_MC;
2153 w->m = 0;
2154 w->lw = 1;
2155 w->relal = alloc(widint_t);
2156 w->related = alloc(map_widint_t);
2157 w->ll = alloc(vec_str_t);
2158 #ifdef ENABLE_CONTROL
2159 w->to_release = alloc(vec_uint_t);
2160 #endif
2162 w->pid = getpid();
2163 w->fs = XLoadQueryFont(display, "fixed");
2164 if (w->fs)
2165 w->fh = (w->fs->max_bounds.ascent + w->fs->max_bounds.descent);
2167 w->free = widget_t_free;
2168 w->draw = NULL;
2169 return w;
2172 void points_t_free(points_t* s) {
2173 free(s->ps);
2174 free(s);
2177 points_t* points_t_init(points_t* s) {
2178 if (!s || !display)
2179 return NULL;
2181 memset(s, 0, sizeof(points_t));
2182 s->free = points_t_free;
2184 return s;
2187 void arcs_t_free(arcs_t* s) {
2188 free(s->as);
2189 free(s);
2192 arcs_t* arcs_t_init(arcs_t* s) {
2193 if (!s || !display)
2194 return NULL;
2196 memset(s, 0, sizeof(arcs_t));
2197 s->free = arcs_t_free;
2199 return s;
2202 void namedpoints_t_free(namedpoints_t* s) {
2203 char* c;
2204 each(s->names, c, free(c););
2205 each(s->cols, c, free(c););
2206 release(s->ps);
2207 release(s->names);
2208 release(s->cols);
2209 free(s);
2212 namedpoints_t* namedpoints_t_init(namedpoints_t* s) {
2213 if (!s || !display)
2214 return NULL;
2216 memset(s, 0, sizeof(namedpoints_t));
2217 s->ps = alloc(vec_int_t);
2218 s->names = alloc(vec_str_t);
2219 s->cols = alloc(vec_str_t);
2220 s->free = namedpoints_t_free;
2222 return s;
2225 void loci_t_free(loci_t* s) {
2226 char* c;
2227 points_t* ps;
2228 each(s->cols, c, free(c););
2229 each(s->loci, ps, release(ps););
2230 release(s->cols);
2231 release(s->loci);
2232 free(s);
2235 loci_t* loci_t_init(loci_t* s) {
2236 if (!s || !display)
2237 return NULL;
2239 memset(s, 0, sizeof(loci_t));
2240 s->loci = alloc(vec_points_t);
2241 s->cols = alloc(vec_str_t);
2242 s->free = loci_t_free;
2244 return s;
2247 void arcssets_t_free(arcssets_t* s) {
2248 char* c;
2249 arcs_t* as;
2250 each(s->cols, c, free(c););
2251 each(s->arcssets, as, release(as););
2252 release(s->cols);
2253 release(s->arcssets);
2254 free(s);
2257 arcssets_t* arcssets_t_init(arcssets_t* s) {
2258 if (!s || !display)
2259 return NULL;
2261 memset(s, 0, sizeof(arcssets_t));
2262 s->arcssets = alloc(vec_arcs_t);
2263 s->cols = alloc(vec_str_t);
2264 s->free = arcssets_t_free;
2266 return s;