Changes in uspace/lib/ui/src/ui.c [d55ab823:8279aab] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/ui/src/ui.c
rd55ab823 r8279aab 1 1 /* 2 * Copyright (c) 202 0Jiri Svoboda2 * Copyright (c) 2025 Jiri Svoboda 3 3 * All rights reserved. 4 4 * … … 34 34 */ 35 35 36 #include <adt/list.h> 37 #include <ctype.h> 36 38 #include <display.h> 37 39 #include <errno.h> 38 40 #include <fibril.h> 41 #include <fibril_synch.h> 42 #include <gfx/color.h> 43 #include <gfx/cursor.h> 44 #include <gfx/render.h> 45 #include <io/console.h> 46 #include <stdbool.h> 39 47 #include <stdlib.h> 48 #include <str.h> 40 49 #include <task.h> 50 #include <types/common.h> 51 #include <ui/clickmatic.h> 41 52 #include <ui/ui.h> 53 #include <ui/wdecor.h> 54 #include <ui/window.h> 55 #include "../private/wdecor.h" 56 #include "../private/window.h" 42 57 #include "../private/ui.h" 43 58 59 /** Parse output specification. 60 * 61 * Output specification has the form <proto>@<service> where proto is 62 * eiher 'disp' for display service, 'cons' for console, 'null' 63 * for dummy output. Service is a location ID service name (e.g. hid/display). 64 * 65 * @param ospec Output specification 66 * @param ws Place to store window system type (protocol) 67 * @param osvc Place to store pointer to output service name 68 * @param ridev_id Place to store input device ID 69 * @return EOK on success, EINVAL if syntax is invalid, ENOMEM if out of 70 * memory 71 */ 72 static errno_t ui_ospec_parse(const char *ospec, ui_winsys_t *ws, 73 char **osvc, sysarg_t *ridev_id) 74 { 75 const char *cp; 76 const char *qm; 77 const char *endptr; 78 uint64_t idev_id; 79 errno_t rc; 80 81 *ridev_id = 0; 82 83 cp = ospec; 84 while (isalpha(*cp)) 85 ++cp; 86 87 /* Window system / protocol */ 88 if (*cp == '@') { 89 if (str_lcmp(ospec, "disp@", str_length("disp@")) == 0) { 90 *ws = ui_ws_display; 91 } else if (str_lcmp(ospec, "cons@", str_length("cons@")) == 0) { 92 *ws = ui_ws_console; 93 } else if (str_lcmp(ospec, "null@", str_length("null@")) == 0) { 94 *ws = ui_ws_null; 95 } else if (str_lcmp(ospec, "@", str_length("@")) == 0) { 96 *ws = ui_ws_any; 97 } else { 98 *ws = ui_ws_unknown; 99 } 100 101 ++cp; 102 } else { 103 *ws = ui_ws_display; 104 } 105 106 /* Output service is the part before question mark */ 107 qm = str_chr(cp, '?'); 108 if (qm != NULL) { 109 *osvc = str_ndup(cp, qm - cp); 110 } else { 111 /* No question mark */ 112 *osvc = str_dup(cp); 113 } 114 115 if (*osvc == NULL) 116 return ENOMEM; 117 118 if (qm != NULL) { 119 /* The part after the question mark */ 120 cp = qm + 1; 121 122 /* Input device ID parameter */ 123 if (str_lcmp(cp, "idev=", str_length("idev=")) == 0) { 124 cp += str_length("idev="); 125 126 rc = str_uint64_t(cp, &endptr, 10, false, &idev_id); 127 if (rc != EOK) 128 goto error; 129 130 *ridev_id = idev_id; 131 cp = endptr; 132 } 133 } 134 135 if (*cp != '\0') { 136 rc = EINVAL; 137 goto error; 138 } 139 140 return EOK; 141 error: 142 free(*osvc); 143 *osvc = NULL; 144 return rc; 145 } 146 44 147 /** Create new user interface. 45 148 * 46 149 * @param ospec Output specification or @c UI_DISPLAY_DEFAULT to use 47 * the default output 150 * the default display service, UI_CONSOLE_DEFAULT to use 151 * the default console service, UI_DISPLAY_NULL to use 152 * dummy output. 48 153 * @param rui Place to store pointer to new UI 49 154 * @return EOK on success or an error code … … 53 158 errno_t rc; 54 159 display_t *display; 160 console_ctrl_t *console; 161 console_gc_t *cgc; 162 ui_winsys_t ws; 163 char *osvc; 164 sysarg_t cols; 165 sysarg_t rows; 166 sysarg_t idev_id; 55 167 ui_t *ui; 56 168 57 rc = display_open(ospec, &display);169 rc = ui_ospec_parse(ospec, &ws, &osvc, &idev_id); 58 170 if (rc != EOK) 59 171 return rc; 60 172 61 rc = ui_create_disp(display, &ui); 173 if (ws == ui_ws_display || ws == ui_ws_any) { 174 rc = display_open((str_cmp(osvc, "") != 0) ? osvc : 175 DISPLAY_DEFAULT, &display); 176 if (rc != EOK) 177 goto disp_fail; 178 179 rc = ui_create_disp(display, &ui); 180 if (rc != EOK) { 181 display_close(display); 182 goto disp_fail; 183 } 184 185 free(osvc); 186 ui->myoutput = true; 187 ui->idev_id = idev_id; 188 *rui = ui; 189 return EOK; 190 } 191 192 disp_fail: 193 if (ws == ui_ws_console || ws == ui_ws_any) { 194 console = console_init(stdin, stdout); 195 if (console == NULL) 196 goto cons_fail; 197 198 rc = console_get_size(console, &cols, &rows); 199 if (rc != EOK) { 200 console_done(console); 201 goto cons_fail; 202 } 203 204 console_cursor_visibility(console, false); 205 206 /* ws == ui_ws_console */ 207 rc = ui_create_cons(console, &ui); 208 if (rc != EOK) { 209 console_done(console); 210 goto cons_fail; 211 } 212 213 rc = console_gc_create(console, NULL, &cgc); 214 if (rc != EOK) { 215 ui_destroy(ui); 216 console_done(console); 217 goto cons_fail; 218 } 219 220 free(osvc); 221 222 ui->cgc = cgc; 223 ui->rect.p0.x = 0; 224 ui->rect.p0.y = 0; 225 ui->rect.p1.x = cols; 226 ui->rect.p1.y = rows; 227 228 (void) ui_paint(ui); 229 ui->myoutput = true; 230 *rui = ui; 231 return EOK; 232 } 233 234 cons_fail: 235 if (ws == ui_ws_null) { 236 free(osvc); 237 rc = ui_create_disp(NULL, &ui); 238 if (rc != EOK) 239 return rc; 240 241 ui->myoutput = true; 242 *rui = ui; 243 return EOK; 244 } 245 246 free(osvc); 247 return EINVAL; 248 } 249 250 /** Create new user interface using console service. 251 * 252 * @param rui Place to store pointer to new UI 253 * @return EOK on success or an error code 254 */ 255 errno_t ui_create_cons(console_ctrl_t *console, ui_t **rui) 256 { 257 ui_t *ui; 258 errno_t rc; 259 260 ui = calloc(1, sizeof(ui_t)); 261 if (ui == NULL) 262 return ENOMEM; 263 264 rc = ui_clickmatic_create(ui, &ui->clickmatic); 62 265 if (rc != EOK) { 63 display_close(display); 64 return rc; 65 } 66 67 ui->display = display; 68 ui->myoutput = true; 266 free(ui); 267 return rc; 268 } 269 270 ui->console = console; 271 list_initialize(&ui->windows); 272 fibril_mutex_initialize(&ui->lock); 69 273 *rui = ui; 70 274 return EOK; … … 80 284 { 81 285 ui_t *ui; 286 errno_t rc; 82 287 83 288 ui = calloc(1, sizeof(ui_t)); … … 85 290 return ENOMEM; 86 291 292 rc = ui_clickmatic_create(ui, &ui->clickmatic); 293 if (rc != EOK) { 294 free(ui); 295 return rc; 296 } 297 87 298 ui->display = disp; 299 list_initialize(&ui->windows); 300 fibril_mutex_initialize(&ui->lock); 88 301 *rui = ui; 89 302 return EOK; … … 99 312 return; 100 313 101 if (ui->myoutput) 102 display_close(ui->display); 314 if (ui->myoutput) { 315 if (ui->cgc != NULL) 316 console_gc_delete(ui->cgc); 317 if (ui->console != NULL) { 318 console_cursor_visibility(ui->console, true); 319 console_done(ui->console); 320 } 321 if (ui->display != NULL) 322 display_close(ui->display); 323 } 324 103 325 free(ui); 326 } 327 328 static void ui_cons_event_process(ui_t *ui, cons_event_t *event) 329 { 330 ui_window_t *awnd; 331 ui_evclaim_t claim; 332 pos_event_t pos; 333 334 awnd = ui_window_get_active(ui); 335 if (awnd == NULL) 336 return; 337 338 switch (event->type) { 339 case CEV_KEY: 340 ui_lock(ui); 341 ui_window_send_kbd(awnd, &event->ev.key); 342 ui_unlock(ui); 343 break; 344 case CEV_POS: 345 pos = event->ev.pos; 346 /* Translate event to window-relative coordinates */ 347 pos.hpos -= awnd->dpos.x; 348 pos.vpos -= awnd->dpos.y; 349 350 claim = ui_wdecor_pos_event(awnd->wdecor, &pos); 351 /* Note: If event is claimed, awnd might not be valid anymore */ 352 if (claim == ui_unclaimed) { 353 ui_lock(ui); 354 ui_window_send_pos(awnd, &pos); 355 ui_unlock(ui); 356 } 357 358 break; 359 case CEV_RESIZE: 360 ui_lock(ui); 361 ui_window_send_resize(awnd); 362 ui_unlock(ui); 363 break; 364 } 104 365 } 105 366 … … 113 374 void ui_run(ui_t *ui) 114 375 { 115 task_retval(0); 116 117 while (!ui->quit) 118 fibril_usleep(100000); 376 cons_event_t event; 377 usec_t timeout; 378 errno_t rc; 379 380 /* Only return command prompt if we are running in a separate window */ 381 if (ui->display != NULL) 382 task_retval(0); 383 384 while (!ui->quit) { 385 if (ui->console != NULL) { 386 timeout = 100000; 387 rc = console_get_event_timeout(ui->console, 388 &event, &timeout); 389 390 /* Do we actually have an event? */ 391 if (rc == EOK) { 392 ui_cons_event_process(ui, &event); 393 } else if (rc != ETIMEOUT) { 394 /* Error, quit */ 395 break; 396 } 397 } else { 398 fibril_usleep(100000); 399 } 400 } 401 } 402 403 /** Repaint UI (only used in fullscreen mode). 404 * 405 * This is used when an area is exposed in fullscreen mode. 406 * 407 * @param ui UI 408 * @return @c EOK on success or an error code 409 */ 410 errno_t ui_paint(ui_t *ui) 411 { 412 errno_t rc; 413 gfx_context_t *gc; 414 ui_window_t *awnd; 415 gfx_color_t *color = NULL; 416 417 /* In case of null output */ 418 if (ui->cgc == NULL) 419 return EOK; 420 421 gc = console_gc_get_ctx(ui->cgc); 422 423 rc = gfx_color_new_ega(0x11, &color); 424 if (rc != EOK) 425 return rc; 426 427 rc = gfx_set_color(gc, color); 428 if (rc != EOK) { 429 gfx_color_delete(color); 430 return rc; 431 } 432 433 rc = gfx_fill_rect(gc, &ui->rect); 434 if (rc != EOK) { 435 gfx_color_delete(color); 436 return rc; 437 } 438 439 gfx_color_delete(color); 440 441 /* XXX Should repaint all windows */ 442 awnd = ui_window_get_active(ui); 443 if (awnd == NULL) 444 return EOK; 445 446 rc = ui_wdecor_paint(awnd->wdecor); 447 if (rc != EOK) 448 return rc; 449 450 return ui_window_paint(awnd); 451 } 452 453 /** Free up console for other users. 454 * 455 * Release console resources for another application (that the current 456 * task is starting). After the other application finishes, resume 457 * operation with ui_resume(). No calls to UI must happen inbetween 458 * and no events must be processed (i.e. the calling function must not 459 * return control to UI. 460 * 461 * @param ui UI 462 * @return EOK on success or an error code 463 */ 464 errno_t ui_suspend(ui_t *ui) 465 { 466 errno_t rc; 467 468 assert(!ui->suspended); 469 470 if (ui->cgc == NULL) { 471 ui->suspended = true; 472 return EOK; 473 } 474 475 (void) console_set_caption(ui->console, ""); 476 rc = console_gc_suspend(ui->cgc); 477 if (rc != EOK) 478 return rc; 479 480 ui->suspended = true; 481 return EOK; 482 } 483 484 /** Resume suspended UI. 485 * 486 * Reclaim console resources (after child application has finished running) 487 * and restore UI operation previously suspended by calling ui_suspend(). 488 * 489 * @param ui UI 490 * @return EOK on success or an error code 491 */ 492 errno_t ui_resume(ui_t *ui) 493 { 494 errno_t rc; 495 ui_window_t *awnd; 496 sysarg_t col; 497 sysarg_t row; 498 cons_event_t ev; 499 500 assert(ui->suspended); 501 502 if (ui->cgc == NULL) { 503 ui->suspended = false; 504 return EOK; 505 } 506 507 rc = console_get_pos(ui->console, &col, &row); 508 if (rc != EOK) 509 return rc; 510 511 /* 512 * Here's a little heuristic to help determine if we need 513 * to pause before returning to the UI. If we are in the 514 * top-left corner, chances are the screen is empty and 515 * there is no need to pause. 516 */ 517 if (col != 0 || row != 0) { 518 printf("Press any key or button to continue...\n"); 519 520 while (true) { 521 rc = console_get_event(ui->console, &ev); 522 if (rc != EOK) 523 return EIO; 524 525 if (ev.type == CEV_KEY && ev.ev.key.type == KEY_PRESS) 526 break; 527 528 if (ev.type == CEV_POS && ev.ev.pos.type == POS_PRESS) 529 break; 530 } 531 } 532 533 rc = console_gc_resume(ui->cgc); 534 if (rc != EOK) 535 return rc; 536 537 ui->suspended = false; 538 539 awnd = ui_window_get_active(ui); 540 if (awnd != NULL) 541 (void) console_set_caption(ui->console, awnd->wdecor->caption); 542 543 rc = gfx_cursor_set_visible(console_gc_get_ctx(ui->cgc), false); 544 if (rc != EOK) 545 return rc; 546 547 return EOK; 548 } 549 550 /** Determine if UI is suspended. 551 * 552 * @param ui UI 553 * @return @c true iff UI is suspended 554 */ 555 bool ui_is_suspended(ui_t *ui) 556 { 557 return ui->suspended; 558 } 559 560 /** Lock UI. 561 * 562 * Block UI from calling window callbacks. @c ui_lock() and @c ui_unlock() 563 * must be used when accessing UI resources from a fibril (as opposed to 564 * from a window callback). 565 * 566 * @param ui UI 567 */ 568 void ui_lock(ui_t *ui) 569 { 570 if (ui->display != NULL) 571 display_lock(ui->display); 572 fibril_mutex_lock(&ui->lock); 573 } 574 575 /** Unlock UI. 576 * 577 * Allow UI to call window callbacks. @c ui_lock() and @c ui_unlock() 578 * must be used when accessing window resources from a fibril (as opposed to 579 * from a window callback). 580 * 581 * @param ui UI 582 */ 583 void ui_unlock(ui_t *ui) 584 { 585 fibril_mutex_unlock(&ui->lock); 586 if (ui->display != NULL) 587 display_unlock(ui->display); 119 588 } 120 589 … … 132 601 } 133 602 603 /** Determine if we are running in text mode. 604 * 605 * @param ui User interface 606 * @return @c true iff we are running in text mode 607 */ 608 bool ui_is_textmode(ui_t *ui) 609 { 610 /* 611 * XXX Currently console is always text and display is always 612 * graphics, but this need not always be true. 613 */ 614 return (ui->console != NULL); 615 } 616 617 /** Determine if we are emulating windows. 618 * 619 * @param ui User interface 620 * @return @c true iff we are running in text mode 621 */ 622 bool ui_is_fullscreen(ui_t *ui) 623 { 624 return (ui->display == NULL); 625 } 626 627 /** Get UI screen rectangle. 628 * 629 * @param ui User interface 630 * @param rect Place to store bounding rectangle 631 */ 632 errno_t ui_get_rect(ui_t *ui, gfx_rect_t *rect) 633 { 634 display_info_t info; 635 sysarg_t cols, rows; 636 errno_t rc; 637 638 if (ui->display != NULL) { 639 rc = display_get_info(ui->display, &info); 640 if (rc != EOK) 641 return rc; 642 643 *rect = info.rect; 644 } else if (ui->console != NULL) { 645 rc = console_get_size(ui->console, &cols, &rows); 646 if (rc != EOK) 647 return rc; 648 649 rect->p0.x = 0; 650 rect->p0.y = 0; 651 rect->p1.x = cols; 652 rect->p1.y = rows; 653 } else { 654 return ENOTSUP; 655 } 656 657 return EOK; 658 } 659 660 /** Get clickmatic from UI. 661 * 662 * @pararm ui UI 663 * @return Clickmatic 664 */ 665 ui_clickmatic_t *ui_get_clickmatic(ui_t *ui) 666 { 667 return ui->clickmatic; 668 } 669 134 670 /** @} 135 671 */
Note:
See TracChangeset
for help on using the changeset viewer.