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