tdraw.c - ltk - Socket-based GUI for X11 (WIP)
HTML git clone git://lumidify.org/ltk.git (fast, but not encrypted)
HTML git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
tdraw.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 = <k_draw_draw,
46 .resize = <k_draw_resize,
47 .destroy = <k_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 }