widget.c - ltk - GUI toolkit for X11 (WIP)
HTML git clone git://lumidify.org/ltk.git (fast, but not encrypted)
HTML git clone https://lumidify.org/ltk.git (encrypted, but very slow)
HTML git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltk.git (over tor)
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
widget.c (17833B)
---
1 /*
2 * Copyright (c) 2021-2024 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 <string.h>
18
19 #include "khash.h"
20
21 #include "err.h"
22 #include "ltkd.h"
23 #include "widget.h"
24 #include "proto_types.h"
25
26 #include <ltk/ltk.h>
27 #include <ltk/event.h>
28 #include <ltk/eventdefs.h>
29 #include <ltk/memory.h>
30 #include <ltk/rect.h>
31 #include <ltk/util.h>
32
33 KHASH_MAP_INIT_STR(widget, ltkd_widget *)
34 static khash_t(widget) *widget_hash = NULL;
35 /* Hack to make ltkd_destroy_widget_hash work */
36 /* FIXME: any better way to do this? */
37 static int hash_locked = 0;
38
39 static int ltkd_widget_button_event(ltk_widget *widget_unused, ltk_callback_arglist args, ltk_callback_arg arg);
40 static int ltkd_widget_motion_notify(ltk_widget *widget_unused, ltk_callback_arglist args, ltk_callback_arg arg);
41 static int ltkd_widget_scroll_event(ltk_widget *widget_unused, ltk_callback_arglist args, ltk_callback_arg arg);
42 static int ltkd_widget_resize(ltk_widget *widget_unused, ltk_callback_arglist args, ltk_callback_arg arg);
43 static int ltkd_widget_change_state(ltk_widget *widget_unused, ltk_callback_arglist args, ltk_callback_arg arg);
44
45 /* FIXME: generic event handling functions that take the actual uint32_t event type, etc. */
46 int
47 ltkd_widget_queue_specific_event(ltkd_widget *widget, const char *type, uint32_t mask, const char *data) {
48 for (size_t i = 0; i < widget->masks_num; i++) {
49 if (widget->event_masks[i].lwmask & mask) {
50 ltkd_queue_sock_write_fmt(
51 widget->event_masks[i].client,
52 "eventl %s %s %s\n", widget->id, type, data
53 );
54 if (ltkd_handle_lock_client(widget->widget->window, widget->event_masks[i].client))
55 return 1;
56 } else if (widget->event_masks[i].wmask & mask) {
57 ltkd_queue_sock_write_fmt(
58 widget->event_masks[i].client,
59 "event %s %s %s\n", widget->id, type, data
60 );
61 }
62 }
63 return 0;
64 }
65
66 static int
67 ltkd_widget_resize(ltk_widget *widget_unused, ltk_callback_arglist args, ltk_callback_arg arg) {
68 (void)widget_unused;
69 (void)args;
70 ltkd_widget *widget = LTK_CAST_ARG_VOIDP(arg);
71 for (size_t i = 0; i < widget->masks_num; i++) {
72 if (widget->event_masks[i].lmask & LTKD_PEVENTMASK_RESIZE) {
73 ltkd_queue_sock_write_fmt(
74 widget->event_masks[i].client,
75 "eventl %s widget configure %d %d %d %d\n",
76 widget->id, widget->widget->lrect.x, widget->widget->lrect.y,
77 widget->widget->lrect.w, widget->widget->lrect.h
78 );
79 if (ltkd_handle_lock_client(widget->widget->window, widget->event_masks[i].client))
80 return 1;
81 } else if (widget->event_masks[i].mask & LTKD_PEVENTMASK_RESIZE) {
82 ltkd_queue_sock_write_fmt(
83 widget->event_masks[i].client,
84 "event %s widget configure %d %d %d %d\n",
85 widget->id, widget->widget->lrect.x, widget->widget->lrect.y,
86 widget->widget->lrect.w, widget->widget->lrect.h
87 );
88 }
89 }
90 return 0;
91 }
92
93 static int
94 ltkd_widget_change_state(ltk_widget *widget_unused, ltk_callback_arglist args, ltk_callback_arg arg) {
95 (void)widget_unused;
96 (void)args;
97 ltkd_widget *widget = LTK_CAST_ARG_VOIDP(arg);
98 /* FIXME: give old and new state in event */
99 for (size_t i = 0; i < widget->masks_num; i++) {
100 if (widget->event_masks[i].lmask & LTKD_PEVENTMASK_STATECHANGE) {
101 ltkd_queue_sock_write_fmt(
102 widget->event_masks[i].client,
103 "eventl %s widget statechange\n", widget->id
104 );
105 if (ltkd_handle_lock_client(widget->widget->window, widget->event_masks[i].client))
106 return 1;
107 } else if (widget->event_masks[i].mask & LTKD_PEVENTMASK_STATECHANGE) {
108 ltkd_queue_sock_write_fmt(
109 widget->event_masks[i].client,
110 "event %s widget statechange\n", widget->id
111 );
112 }
113 }
114 return 0;
115 }
116
117 static void
118 ltkd_destroy_widget_hash(void) {
119 hash_locked = 1;
120 khint_t k;
121 ltkd_widget *ptr;
122 for (k = kh_begin(widget_hash); k != kh_end(widget_hash); k++) {
123 if (kh_exist(widget_hash, k)) {
124 ptr = kh_value(widget_hash, k);
125 ltk_free((char *)kh_key(widget_hash, k));
126 ltkd_widget_destroy(ptr, 1);
127 }
128 }
129 kh_destroy(widget, widget_hash);
130 widget_hash = NULL;
131 hash_locked = 0;
132 }
133
134 void
135 ltkd_widgets_cleanup(void) {
136 if (widget_hash)
137 ltkd_destroy_widget_hash();
138 }
139
140 static client_event_mask *
141 get_mask_struct(ltkd_widget *widget, int client) {
142 for (size_t i = 0; i < widget->masks_num; i++) {
143 if (widget->event_masks[i].client == client)
144 return &widget->event_masks[i];
145 }
146 widget->masks_alloc = ideal_array_size(widget->masks_alloc, widget->masks_num + 1);
147 widget->event_masks = ltk_reallocarray(widget->event_masks, widget->masks_alloc, sizeof(client_event_mask));
148 client_event_mask *m = &widget->event_masks[widget->masks_num];
149 widget->masks_num++;
150 m->client = client;
151 m->mask = m->lmask = m->wmask = m->lwmask = 0;
152 return m;
153 }
154
155 static ltkd_event_handler widget_handlers[] = {
156 {<kd_widget_button_event, LTK_WIDGET_SIGNAL_MOUSE_PRESS},
157 {<kd_widget_button_event, LTK_WIDGET_SIGNAL_MOUSE_RELEASE},
158 {<kd_widget_motion_notify, LTK_WIDGET_SIGNAL_MOTION_NOTIFY},
159 {<kd_widget_scroll_event, LTK_WIDGET_SIGNAL_MOUSE_SCROLL},
160 {NULL, LTK_WIDGET_SIGNAL_KEY_PRESS}, /* FIXME: add key press here */
161 {NULL, LTK_WIDGET_SIGNAL_KEY_RELEASE},
162 {<kd_widget_resize, LTK_WIDGET_SIGNAL_RESIZE},
163 {<kd_widget_change_state, LTK_WIDGET_SIGNAL_CHANGE_STATE},
164 };
165
166 static uint32_t
167 get_widget_mask(ltkd_widget *widget) {
168 uint32_t cur_mask = 0;
169 for (size_t i = 0; i < widget->masks_num; i++) {
170 cur_mask |= widget->event_masks[i].mask;
171 cur_mask |= widget->event_masks[i].lmask;
172 }
173 return cur_mask;
174 }
175
176 static uint32_t
177 get_widget_special_mask(ltkd_widget *widget) {
178 uint32_t cur_mask = 0;
179 for (size_t i = 0; i < widget->masks_num; i++) {
180 cur_mask |= widget->event_masks[i].wmask;
181 cur_mask |= widget->event_masks[i].lwmask;
182 }
183 return cur_mask;
184 }
185
186 static void
187 set_event_handlers(ltkd_widget *widget, uint32_t before, uint32_t after, ltkd_event_handler *handlers, size_t num_handlers) {
188 for (size_t i = 0; i < num_handlers; i++) {
189 if (!(before & 1) && (after & 1)) {
190 if (handlers[i].callback) {
191 ltk_widget_register_signal_handler(
192 widget->widget, handlers[i].type,
193 handlers[i].callback, LTK_MAKE_ARG_VOIDP(widget)
194 );
195 }
196 } else if ((before & 1) && !(after & 1)) {
197 ltk_widget_remove_signal_handler_by_callback(widget->widget, handlers[i].callback);
198 }
199 before >>= 1;
200 after >>= 1;
201 }
202 }
203
204 static void
205 ltkd_widget_set_event_handlers(ltkd_widget *widget, uint32_t *set_mask, uint32_t new_mask) {
206 uint32_t before = get_widget_mask(widget);
207 *set_mask = new_mask;
208 uint32_t after = get_widget_mask(widget);
209 set_event_handlers(widget, before, after, widget_handlers, LENGTH(widget_handlers));
210 }
211
212 static void
213 ltkd_widget_set_special_event_handlers(ltkd_widget *widget, uint32_t *set_mask, uint32_t new_mask) {
214 uint32_t before = get_widget_special_mask(widget);
215 *set_mask = new_mask;
216 uint32_t after = get_widget_special_mask(widget);
217 set_event_handlers(widget, before, after, widget->event_handlers, widget->num_event_handlers);
218 }
219
220 void
221 ltkd_widget_set_event_mask(ltkd_widget *widget, int client, uint32_t mask) {
222 client_event_mask *m = get_mask_struct(widget, client);
223 ltkd_widget_set_event_handlers(widget, &m->mask, mask);
224 }
225
226 void
227 ltkd_widget_set_event_lmask(ltkd_widget *widget, int client, uint32_t mask) {
228 client_event_mask *m = get_mask_struct(widget, client);
229 ltkd_widget_set_event_handlers(widget, &m->lmask, mask);
230 }
231
232 void
233 ltkd_widget_set_event_wmask(ltkd_widget *widget, int client, uint32_t mask) {
234 client_event_mask *m = get_mask_struct(widget, client);
235 ltkd_widget_set_special_event_handlers(widget, &m->wmask, mask);
236 }
237
238 void
239 ltkd_widget_set_event_lwmask(ltkd_widget *widget, int client, uint32_t mask) {
240 client_event_mask *m = get_mask_struct(widget, client);
241 ltkd_widget_set_special_event_handlers(widget, &m->lwmask, mask);
242 }
243
244 void
245 ltkd_widget_add_to_event_mask(ltkd_widget *widget, int client, uint32_t mask) {
246 client_event_mask *m = get_mask_struct(widget, client);
247 ltkd_widget_set_event_handlers(widget, &m->mask, m->mask | mask);
248 }
249
250 void
251 ltkd_widget_add_to_event_lmask(ltkd_widget *widget, int client, uint32_t mask) {
252 client_event_mask *m = get_mask_struct(widget, client);
253 ltkd_widget_set_event_handlers(widget, &m->lmask, m->lmask | mask);
254 }
255
256 void
257 ltkd_widget_add_to_event_wmask(ltkd_widget *widget, int client, uint32_t mask) {
258 client_event_mask *m = get_mask_struct(widget, client);
259 ltkd_widget_set_special_event_handlers(widget, &m->wmask, m->wmask | mask);
260 }
261
262 void
263 ltkd_widget_add_to_event_lwmask(ltkd_widget *widget, int client, uint32_t mask) {
264 client_event_mask *m = get_mask_struct(widget, client);
265 ltkd_widget_set_special_event_handlers(widget, &m->lwmask, m->lwmask | mask);
266 }
267
268 void
269 ltkd_widget_remove_from_event_mask(ltkd_widget *widget, int client, uint32_t mask) {
270 client_event_mask *m = get_mask_struct(widget, client);
271 ltkd_widget_set_event_handlers(widget, &m->mask, m->mask & ~mask);
272 }
273
274 void
275 ltkd_widget_remove_from_event_lmask(ltkd_widget *widget, int client, uint32_t mask) {
276 client_event_mask *m = get_mask_struct(widget, client);
277 ltkd_widget_set_event_handlers(widget, &m->lmask, m->lmask & ~mask);
278 }
279
280 void
281 ltkd_widget_remove_from_event_wmask(ltkd_widget *widget, int client, uint32_t mask) {
282 client_event_mask *m = get_mask_struct(widget, client);
283 ltkd_widget_set_special_event_handlers(widget, &m->wmask, m->wmask & ~mask);
284 }
285
286 void
287 ltkd_widget_remove_from_event_lwmask(ltkd_widget *widget, int client, uint32_t mask) {
288 client_event_mask *m = get_mask_struct(widget, client);
289 ltkd_widget_set_special_event_handlers(widget, &m->lwmask, m->lwmask & ~mask);
290 }
291
292 /* FIXME: any way to optimize the whole event mask handling a bit? */
293 void
294 ltkd_widget_remove_client(int client) {
295 khint_t k;
296 ltkd_widget *ptr;
297 for (k = kh_begin(widget_hash); k != kh_end(widget_hash); k++) {
298 if (kh_exist(widget_hash, k)) {
299 ptr = kh_value(widget_hash, k);
300 for (size_t i = 0; i < ptr->masks_num; i++) {
301 if (ptr->event_masks[i].client == client) {
302 uint32_t before = get_widget_mask(ptr);
303 uint32_t befores = get_widget_special_mask(ptr);
304 memmove(ptr->event_masks + i, ptr->event_masks + i + 1, ptr->masks_num - i - 1);
305 ptr->masks_num--;
306 /* FIXME: maybe reset to NULL in that case? */
307 if (ptr->masks_num > 0) {
308 size_t sz = ideal_array_size(ptr->masks_alloc, ptr->masks_num);
309 if (sz != ptr->masks_alloc) {
310 ptr->masks_alloc = sz;
311 ptr->event_masks = ltk_reallocarray(ptr->event_masks, sz, sizeof(client_event_mask));
312 }
313 }
314 uint32_t after = get_widget_mask(ptr);
315 uint32_t afters = get_widget_special_mask(ptr);
316 set_event_handlers(ptr, before, after, widget_handlers, LENGTH(widget_handlers));
317 set_event_handlers(ptr, befores, afters, ptr->event_handlers, ptr->num_event_handlers);
318 break;
319 }
320 }
321 }
322 }
323 }
324
325 void
326 ltkd_widgets_init() {
327 widget_hash = kh_init(widget);
328 if (!widget_hash) ltkd_fatal_errno("Unable to initialize widget hash table.\n");
329 }
330
331 /* FIXME: fix global and local coordinates! */
332 static int
333 queue_mouse_event(ltkd_widget *widget, ltk_event_type type, int x, int y) {
334 uint32_t mask;
335 char *typename;
336 switch (type) {
337 case LTK_MOTION_EVENT:
338 mask = LTKD_PEVENTMASK_MOUSEMOTION;
339 typename = "mousemotion";
340 break;
341 case LTK_2BUTTONPRESS_EVENT:
342 mask = LTKD_PEVENTMASK_MOUSEPRESS;
343 typename = "2mousepress";
344 break;
345 case LTK_3BUTTONPRESS_EVENT:
346 mask = LTKD_PEVENTMASK_MOUSEPRESS;
347 typename = "3mousepress";
348 break;
349 case LTK_BUTTONRELEASE_EVENT:
350 mask = LTKD_PEVENTMASK_MOUSERELEASE;
351 typename = "mouserelease";
352 break;
353 case LTK_2BUTTONRELEASE_EVENT:
354 mask = LTKD_PEVENTMASK_MOUSERELEASE;
355 typename = "2mouserelease";
356 break;
357 case LTK_3BUTTONRELEASE_EVENT:
358 mask = LTKD_PEVENTMASK_MOUSERELEASE;
359 typename = "3mouserelease";
360 break;
361 case LTK_BUTTONPRESS_EVENT:
362 default:
363 mask = LTKD_PEVENTMASK_MOUSEPRESS;
364 typename = "mousepress";
365 break;
366 }
367 for (size_t i = 0; i < widget->masks_num; i++) {
368 if (widget->event_masks[i].lmask & mask) {
369 ltkd_queue_sock_write_fmt(
370 widget->event_masks[i].client,
371 "eventl %s widget %s %d %d %d %d\n",
372 widget->id, typename, x, y, x, y
373 /* x - widget->rect.x, y - widget->rect.y */
374 );
375 if (ltkd_handle_lock_client(widget->widget->window, widget->event_masks[i].client))
376 return 1;
377 } else if (widget->event_masks[i].mask & mask) {
378 ltkd_queue_sock_write_fmt(
379 widget->event_masks[i].client,
380 "event %s widget %s %d %d %d %d\n",
381 widget->id, typename, x, y, x, y
382 /* x - widget->rect.x, y - widget->rect.y */
383 );
384 }
385 }
386 return 0;
387 }
388
389 static int
390 ltkd_widget_button_event(ltk_widget *widget_unused, ltk_callback_arglist args, ltk_callback_arg arg) {
391 (void)widget_unused;
392 ltkd_widget *widget = LTK_CAST_ARG_VOIDP(arg);
393 ltk_button_event *event = LTK_GET_ARG_BUTTON_EVENT(args, 0);
394 return queue_mouse_event(widget, event->type, event->x, event->y);
395 }
396
397 static int
398 ltkd_widget_motion_notify(ltk_widget *widget_unused, ltk_callback_arglist args, ltk_callback_arg arg) {
399 (void)widget_unused;
400 ltkd_widget *widget = LTK_CAST_ARG_VOIDP(arg);
401 ltk_motion_event *event = LTK_GET_ARG_MOTION_EVENT(args, 0);
402 return queue_mouse_event(widget, event->type, event->x, event->y);
403 }
404
405 /* FIXME: global/local coords (like above) */
406 static int
407 ltkd_widget_scroll_event(ltk_widget *widget_unused, ltk_callback_arglist args, ltk_callback_arg arg) {
408 (void)widget_unused;
409 ltk_scroll_event *event = LTK_GET_ARG_SCROLL_EVENT(args, 0);
410 ltkd_widget *widget = LTK_CAST_ARG_VOIDP(arg);
411 uint32_t mask = LTKD_PEVENTMASK_MOUSESCROLL;
412 for (size_t i = 0; i < widget->masks_num; i++) {
413 if (widget->event_masks[i].lmask & mask) {
414 ltkd_queue_sock_write_fmt(
415 widget->event_masks[i].client,
416 "eventl %s widget %s %d %d %d %d %d %d\n",
417 widget->id, "mousescroll", event->x, event->y, event->x, event->y, event->dx, event->dy
418 /* x - widget->widget->rect.x, y - widget->widget->rect.y */
419 );
420 if (ltkd_handle_lock_client(widget->widget->window, widget->event_masks[i].client))
421 return 1;
422 } else if (widget->event_masks[i].mask & mask) {
423 ltkd_queue_sock_write_fmt(
424 widget->event_masks[i].client,
425 "event %s widget %s %d %d %d %d %d %d\n",
426 widget->id, "mousescroll", event->x, event->y, event->x, event->y, event->dx, event->dy
427 /* x - widget->widget->rect.x, y - widget->widget->rect.y */
428 );
429 }
430 }
431 return 0;
432 }
433
434 static int
435 ltkd_widget_id_free(const char *id) {
436 khint_t k;
437 k = kh_get(widget, widget_hash, id);
438 if (k != kh_end(widget_hash)) {
439 return 0;
440 }
441 return 1;
442 }
443
444 ltkd_widget *
445 ltkd_get_widget(const char *id, ltk_widget_type type, ltkd_error *err) {
446 khint_t k;
447 ltkd_widget *widget;
448 k = kh_get(widget, widget_hash, id);
449 if (k == kh_end(widget_hash)) {
450 err->type = ERR_INVALID_WIDGET_ID;
451 return NULL;
452 }
453 widget = kh_value(widget_hash, k);
454 if (type != LTK_WIDGET_UNKNOWN && widget->widget->vtable->type != type) {
455 err->type = ERR_INVALID_WIDGET_TYPE;
456 return NULL;
457 }
458 return widget;
459 }
460
461 static void
462 ltkd_set_widget(ltkd_widget *widget, const char *id) {
463 int ret;
464 khint_t k;
465 /* FIXME: make sure no widget is overwritten here */
466 char *tmp = ltk_strdup(id);
467 k = kh_put(widget, widget_hash, tmp, &ret);
468 kh_value(widget_hash, k) = widget;
469 }
470
471 static void
472 ltkd_remove_widget(const char *id) {
473 if (hash_locked)
474 return;
475 khint_t k;
476 k = kh_get(widget, widget_hash, id);
477 if (k != kh_end(widget_hash)) {
478 ltk_free((char *)kh_key(widget_hash, k));
479 kh_del(widget, widget_hash, k);
480 }
481 }
482
483 ltkd_widget *
484 ltkd_widget_create(
485 ltk_widget *widget, const char *id,
486 ltkd_event_handler *event_handlers, size_t num_event_handlers, ltkd_error *err){
487 if (!ltkd_widget_id_free(id)) {
488 err->type = ERR_WIDGET_ID_IN_USE;
489 return NULL;
490 }
491 ltkd_widget *w = ltk_malloc(sizeof(ltkd_widget));
492 w->widget = widget;
493 w->id = ltk_strdup(id);
494 w->event_masks = NULL;
495 w->masks_num = w->masks_alloc = 0;
496 w->event_handlers = event_handlers;
497 w->num_event_handlers = num_event_handlers;
498 ltkd_set_widget(w, id);
499 return w;
500 }
501
502 void
503 ltkd_widget_destroy(ltkd_widget *widget, int shallow) {
504 ltkd_remove_widget(widget->id);
505 ltk_free(widget->id);
506 widget->id = NULL;
507 ltk_free(widget->event_masks);
508 widget->event_masks = NULL;
509 ltk_widget_destroy(widget->widget, shallow);
510 ltk_free(widget);
511 }
512
513 int
514 ltkd_widget_destroy_cmd(
515 ltk_window *window,
516 ltkd_cmd_token *tokens,
517 size_t num_tokens,
518 ltkd_error *err) {
519 (void)window;
520 int shallow = 1;
521 if (num_tokens != 2 && num_tokens != 3) {
522 err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS;
523 err->arg = -1;
524 return 1;
525 }
526 if (tokens[1].contains_nul) {
527 err->type = ERR_INVALID_ARGUMENT;
528 err->arg = 1;
529 return 1;
530 } else if (num_tokens == 3 && tokens[2].contains_nul) {
531 err->type = ERR_INVALID_ARGUMENT;
532 err->arg = 2;
533 return 1;
534 }
535 if (num_tokens == 3) {
536 if (strcmp(tokens[2].text, "deep") == 0) {
537 shallow = 0;
538 } else if (strcmp(tokens[2].text, "shallow") == 0) {
539 shallow = 1;
540 } else {
541 err->type = ERR_INVALID_ARGUMENT;
542 err->arg = 2;
543 return 1;
544 }
545 }
546 ltkd_widget *widget = ltkd_get_widget(tokens[1].text, LTK_WIDGET_UNKNOWN, err);
547 if (!widget) {
548 err->arg = 1;
549 return 1;
550 }
551 ltkd_widget_destroy(widget, shallow);
552 return 0;
553 }