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 }