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 = <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 }