widget.c (18095B)
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() { 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 }