ltk

Socket-based GUI for X11 (WIP)
git clone git://lumidify.org/ltk.git (fast, but not encrypted)
git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
Log | Files | Refs | README | LICENSE

draw.c (10979B)


      1 /*
      2  * Copyright (c) 2020 lumidify <nobody@lumidify.org>
      3  *
      4  * Permission to use, copy, modify, and/or distribute this software for any
      5  * purpose with or without fee is hereby granted, provided that the above
      6  * copyright notice and this permission notice appear in all copies.
      7  *
      8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15  */
     16 
     17 #include <stdio.h>
     18 #include <stdlib.h>
     19 #include <string.h>
     20 #include <stdarg.h>
     21 #include <stdint.h>
     22 
     23 #include <X11/Xlib.h>
     24 #include <X11/Xutil.h>
     25 
     26 #include "memory.h"
     27 #include "color.h"
     28 #include "rect.h"
     29 #include "widget.h"
     30 #include "ltk.h"
     31 #include "util.h"
     32 #include "draw.h"
     33 
     34 static void ltk_draw_draw(ltk_widget *self, ltk_rect clip_rect);
     35 static ltk_draw *ltk_draw_create(ltk_window *window,
     36     const char *id, int w, int h, const char *color);
     37 static void ltk_draw_resize(ltk_widget *self);
     38 static void ltk_draw_destroy(ltk_widget *self, int shallow);
     39 static void ltk_draw_clear(ltk_window *window, ltk_draw *draw);
     40 static void ltk_draw_set_color(ltk_window *window, ltk_draw *draw, const char *color);
     41 static void ltk_draw_line(ltk_window *window, ltk_draw *draw, int x1, int y1, int x2, int y2);
     42 static void ltk_draw_rect(ltk_window *window, ltk_draw *draw, int x, int y, int w, int h, int fill);
     43 
     44 static struct ltk_widget_vtable vtable = {
     45 	.draw = &ltk_draw_draw,
     46 	.resize = &ltk_draw_resize,
     47 	.destroy = &ltk_draw_destroy,
     48 	.type = LTK_DRAW,
     49 	.needs_redraw = 1,
     50 	/* FIXME: use the widget pixmap here and store the drawn stuff
     51 	   logically as paths, not just on the pixmap */
     52 	.needs_pixmap = 0
     53 };
     54 
     55 static int ltk_draw_cmd_clear(
     56     ltk_window *window,
     57     char **tokens,
     58     size_t num_tokens,
     59     char **errstr);
     60 static int ltk_draw_cmd_set_color(
     61     ltk_window *window,
     62     char **tokens,
     63     size_t num_tokens,
     64     char **errstr);
     65 static int ltk_draw_cmd_line(
     66     ltk_window *window,
     67     char **tokens,
     68     size_t num_tokens,
     69     char **errstr);
     70 static int ltk_draw_cmd_rect(
     71     ltk_window *window,
     72     char **tokens,
     73     size_t num_tokens,
     74     char **errstr);
     75 static int ltk_draw_cmd_create(
     76     ltk_window *window,
     77     char **tokens,
     78     size_t num_tokens,
     79     char **errstr);
     80 
     81 static void
     82 ltk_draw_draw(ltk_widget *self, ltk_rect clip_rect) {
     83 	(void)clip_rect; /* FIXME: actually use this */
     84 	ltk_draw *draw = (ltk_draw *)self;
     85 	ltk_window *window = draw->widget.window;
     86 	ltk_rect rect = draw->widget.rect;
     87 	XCopyArea(window->dpy, draw->pix, window->drawable, window->gc, 0, 0, rect.w, rect.h, rect.x, rect.y);
     88 }
     89 
     90 
     91 static ltk_draw *
     92 ltk_draw_create(ltk_window *window, const char *id, int w, int h, const char *color) {
     93 	ltk_draw *draw = ltk_malloc(sizeof(ltk_draw));
     94 
     95 	ltk_fill_widget_defaults(&draw->widget, id, window, &vtable, w, h);
     96 	draw->widget.rect.w = w;
     97 	draw->widget.rect.h = h;
     98 	draw->pix = XCreatePixmap(window->dpy, window->drawable, w, h, window->depth);
     99 	if (!ltk_create_xcolor(window, color, &draw->bg)) {
    100 		ltk_free(draw);
    101 		ltk_fatal_errno("Unable to allocate XColor.\n");
    102 	}
    103 	draw->fg = draw->bg;
    104 	XSetForeground(window->dpy, window->gc, draw->bg.pixel);
    105 	XFillRectangle(window->dpy, draw->pix, window->gc, 0, 0, w, h);
    106 
    107 	return draw;
    108 }
    109 
    110 static void
    111 ltk_draw_resize(ltk_widget *self) {
    112 	ltk_draw *draw = (ltk_draw *)self;
    113 	Window win;
    114 	int x, y;
    115 	unsigned int w, h, bw, d;
    116 	unsigned int new_w, new_h;
    117 	ltk_window *window = draw->widget.window;
    118 	ltk_rect rect = draw->widget.rect;
    119 	XGetGeometry(window->dpy, draw->pix, &win, &x, &y, &w, &h, &bw, &d);
    120 
    121 	/* FIXME: get rid of casts */
    122 	new_w = (int)w < rect.w ? rect.w : (int)w;
    123 	new_h = (int)h < rect.h ? rect.h : (int)h;
    124 	if (new_w < w && new_h < h)
    125 		return;
    126 	Pixmap tmp = XCreatePixmap(window->dpy, window->drawable,
    127 	    new_w, new_h, window->depth);
    128 	XSetForeground(window->dpy, window->gc, draw->bg.pixel);
    129 	XFillRectangle(window->dpy, tmp, window->gc, 0, 0, new_w, new_h);
    130 	XCopyArea(window->dpy, draw->pix, tmp, window->gc,
    131 	    0, 0, w, h, 0, 0);
    132 	XFreePixmap(window->dpy, draw->pix);
    133 	draw->pix = tmp;
    134 }
    135 
    136 static void
    137 ltk_draw_destroy(ltk_widget *self, int shallow) {
    138 	(void)shallow;
    139 	ltk_draw *draw = (ltk_draw *)self;
    140 	if (!draw) {
    141 		ltk_warn("Tried to destroy NULL draw.\n");
    142 		return;
    143 	}
    144 	ltk_remove_widget(draw->widget.id);
    145 	ltk_free(draw->widget.id);
    146 	XFreePixmap(draw->widget.window->dpy, draw->pix);
    147 	ltk_free(draw);
    148 }
    149 
    150 static void
    151 ltk_draw_clear(ltk_window *window, ltk_draw *draw) {
    152 	Window win;
    153 	int x, y;
    154 	unsigned int w, h, bw, d;
    155 	XGetGeometry(window->dpy, draw->pix, &win, &x, &y, &w, &h, &bw, &d);
    156 	XSetForeground(window->dpy, window->gc, draw->bg.pixel);
    157 	XFillRectangle(window->dpy, window->drawable, window->gc, 0, 0, w, h);
    158 	ltk_draw_draw((ltk_widget *)draw, draw->widget.rect);
    159 }
    160 
    161 /* FIXME: Error return */
    162 static void
    163 ltk_draw_set_color(ltk_window *window, ltk_draw *draw, const char *color) {
    164 	XColor tmp;
    165 	if (ltk_create_xcolor(window, color, &tmp)) {
    166 		draw->fg = tmp;
    167 	}
    168 }
    169 
    170 static void
    171 ltk_draw_line(ltk_window *window, ltk_draw *draw, int x1, int y1, int x2, int y2) {
    172 	ltk_rect rect = draw->widget.rect;
    173 	XSetForeground(window->dpy, window->gc, draw->fg.pixel);
    174 	XSetLineAttributes(window->dpy, window->gc, 2, LineSolid, CapButt, JoinMiter);
    175 	XDrawLine(window->dpy, draw->pix, window->gc, x1, y1, x2, y2);
    176 	x1 += rect.x;
    177 	y1 += rect.y;
    178 	x2 += rect.x;
    179 	y2 += rect.y;
    180 	if (x1 > rect.x + rect.w) x1 = rect.x + rect.w;
    181 	if (y1 > rect.y + rect.h) y1 = rect.y + rect.h;
    182 	if (x2 > rect.x + rect.w) x2 = rect.x + rect.w;
    183 	if (y2 > rect.y + rect.h) y2 = rect.y + rect.h;
    184 	XDrawLine(window->dpy, window->drawable, window->gc, x1, y1, x2, y2);
    185 }
    186 
    187 static void
    188 ltk_draw_rect(ltk_window *window, ltk_draw *draw, int x, int y, int w, int h, int fill) {
    189 	int x_win, y_win, w_win, h_win;
    190 	ltk_rect rect = draw->widget.rect;
    191 	XSetForeground(window->dpy, window->gc, draw->fg.pixel);
    192 	x_win = x + rect.x;
    193 	y_win = y + rect.y;
    194 	w_win = x + w > rect.w ? rect.w - x : w;
    195 	h_win = y + h > rect.h ? rect.h - y : h;
    196 	if (fill) {
    197 		XFillRectangle(window->dpy, draw->pix, window->gc, x, y, w, h);
    198 		XFillRectangle(window->dpy, window->drawable, window->gc, x_win, y_win, w_win, h_win);
    199 	} else {
    200 		XSetLineAttributes(window->dpy, window->gc, 2, LineSolid, CapButt, JoinMiter);
    201 		XDrawRectangle(window->dpy, draw->pix, window->gc, x, y, w, h);
    202 		XDrawRectangle(window->dpy, window->drawable, window->gc, x_win, y_win, w_win, h_win);
    203 	}
    204 }
    205 
    206 static int
    207 ltk_draw_cmd_clear(
    208     ltk_window *window,
    209     char **tokens,
    210     size_t num_tokens,
    211     char **errstr) {
    212 	ltk_draw *draw;
    213 	if (num_tokens != 3) {
    214 		*errstr = "Invalid number of arguments.\n";
    215 		return 1;
    216 	}
    217 	draw = (ltk_draw *)ltk_get_widget(tokens[1], LTK_DRAW, errstr);
    218 	if (!draw) return 1;
    219 	ltk_draw_clear(window, draw);
    220 
    221 	return 0;
    222 }
    223 
    224 static int
    225 ltk_draw_cmd_set_color(
    226     ltk_window *window,
    227     char **tokens,
    228     size_t num_tokens,
    229     char **errstr) {
    230 	ltk_draw *draw;
    231 	if (num_tokens != 4) {
    232 		*errstr = "Invalid number of arguments.\n";
    233 		return 1;
    234 	}
    235 	draw = (ltk_draw *)ltk_get_widget(tokens[1], LTK_DRAW, errstr);
    236 	if (!draw) return 1;
    237 	ltk_draw_set_color(window, draw, tokens[3]);
    238 
    239 	return 0;
    240 }
    241 
    242 static int
    243 ltk_draw_cmd_line(
    244     ltk_window *window,
    245     char **tokens,
    246     size_t num_tokens,
    247     char **errstr) {
    248 	ltk_draw *draw;
    249 	int x1, y1, x2, y2;
    250 	const char *errstr_num;
    251 	if (num_tokens != 7) {
    252 		*errstr = "Invalid number of arguments.\n";
    253 		return 1;
    254 	}
    255 	draw = (ltk_draw *)ltk_get_widget(tokens[1], LTK_DRAW, errstr);
    256 	if (!draw) return 1;
    257 	x1 = ltk_strtonum(tokens[3], 0, 100000, &errstr_num);
    258 	if (errstr_num) {
    259 		*errstr = "Invalid x1.\n";
    260 		return 1;
    261 	}
    262 	y1 = ltk_strtonum(tokens[4], 0, 100000, &errstr_num);
    263 	if (errstr_num) {
    264 		*errstr = "Invalid y1.\n";
    265 		return 1;
    266 	}
    267 	x2 = ltk_strtonum(tokens[5], 0, 100000, &errstr_num);
    268 	if (errstr_num) {
    269 		*errstr = "Invalid x2.\n";
    270 		return 1;
    271 	}
    272 	y2 = ltk_strtonum(tokens[6], 0, 100000, &errstr_num);
    273 	if (errstr_num) {
    274 		*errstr = "Invalid y2.\n";
    275 		return 1;
    276 	}
    277 	ltk_draw_line(window, draw, x1, y1, x2, y2);
    278 
    279 	return 0;
    280 }
    281 
    282 static int
    283 ltk_draw_cmd_rect(
    284     ltk_window *window,
    285     char **tokens,
    286     size_t num_tokens,
    287     char **errstr) {
    288 	ltk_draw *draw;
    289 	const char *errstr_num;
    290 	int x, y, w, h, fill;
    291 	if (num_tokens != 8) {
    292 		*errstr = "Invalid number of arguments.\n";
    293 		return 1;
    294 	}
    295 	draw = (ltk_draw *)ltk_get_widget(tokens[1], LTK_DRAW, errstr);
    296 	if (!draw) return 1;
    297 	x = ltk_strtonum(tokens[3], 0, 100000, &errstr_num);
    298 	if (errstr_num) {
    299 		*errstr = "Invalid x.\n";
    300 		return 1;
    301 	}
    302 	y = ltk_strtonum(tokens[4], 0, 100000, &errstr_num);
    303 	if (errstr_num) {
    304 		*errstr = "Invalid y.\n";
    305 		return 1;
    306 	}
    307 	w = ltk_strtonum(tokens[5], 1, 100000, &errstr_num);
    308 	if (errstr_num) {
    309 		*errstr = "Invalid width.\n";
    310 		return 1;
    311 	}
    312 	h = ltk_strtonum(tokens[6], 1, 100000, &errstr_num);
    313 	if (errstr_num) {
    314 		*errstr = "Invalid height.\n";
    315 		return 1;
    316 	}
    317 	fill = ltk_strtonum(tokens[7], 0, 1, &errstr_num);
    318 	if (errstr_num) {
    319 		*errstr = "Invalid fill bool.\n";
    320 		return 1;
    321 	}
    322 	ltk_draw_rect(window, draw, x, y, w, h, fill);
    323 
    324 	return 0;
    325 }
    326 
    327 /* draw <draw id> create <width> <height> <color> */
    328 static int
    329 ltk_draw_cmd_create(
    330     ltk_window *window,
    331     char **tokens,
    332     size_t num_tokens,
    333     char **errstr) {
    334 	ltk_draw *draw;
    335 	int w, h;
    336 	const char *errstr_num;
    337 	if (num_tokens != 6) {
    338 		*errstr = "Invalid number of arguments.\n";
    339 		return 1;
    340 	}
    341 	if (!ltk_widget_id_free(tokens[1])) {
    342 		*errstr = "Widget ID already taken.\n";
    343 		return 1;
    344 	}
    345 	w = ltk_strtonum(tokens[3], 1, 100000, &errstr_num);
    346 	if (errstr_num) {
    347 		*errstr = "Invalid width.\n";
    348 		return 1;
    349 	}
    350 	h = ltk_strtonum(tokens[4], 1, 100000, &errstr_num);
    351 	if (errstr_num) {
    352 		*errstr = "Invalid height.\n";
    353 		return 1;
    354 	}
    355 	draw = ltk_draw_create(window, tokens[1], w, h, tokens[5]);
    356 	ltk_set_widget((ltk_widget *)draw, tokens[1]);
    357 
    358 	return 0;
    359 }
    360 
    361 /* draw <draw id> <command> ... */
    362 int
    363 ltk_draw_cmd(
    364     ltk_window *window,
    365     char **tokens,
    366     size_t num_tokens,
    367     char **errstr) {
    368 	if (num_tokens < 3) {
    369 		*errstr = "Invalid number of arguments.\n";
    370 		return 1;
    371 	}
    372 	if (strcmp(tokens[2], "create") == 0) {
    373 		return ltk_draw_cmd_create(window, tokens, num_tokens, errstr);
    374 	} else if (strcmp(tokens[2], "clear") == 0) {
    375 		return ltk_draw_cmd_clear(window, tokens, num_tokens, errstr);
    376 	} else if (strcmp(tokens[2], "set-color") == 0) {
    377 		return ltk_draw_cmd_set_color(window, tokens, num_tokens, errstr);
    378 	} else if (strcmp(tokens[2], "line") == 0) {
    379 		return ltk_draw_cmd_line(window, tokens, num_tokens, errstr);
    380 	} else if (strcmp(tokens[2], "rect") == 0) {
    381 		return ltk_draw_cmd_rect(window, tokens, num_tokens, errstr);
    382 	} else {
    383 		*errstr = "Invalid command.\n";
    384 		return 1;
    385 	}
    386 
    387 	return 0;
    388 }