Changes in uspace/lib/ui/src/ui.c [26edcd6:d55ab823] in mainline
- File:
-
- 1 edited
-
uspace/lib/ui/src/ui.c (modified) (8 diffs)
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/ui/src/ui.c
r26edcd6 rd55ab823 1 1 /* 2 * Copyright (c) 202 6Jiri Svoboda2 * Copyright (c) 2020 Jiri Svoboda 3 3 * All rights reserved. 4 4 * … … 34 34 */ 35 35 36 #include <adt/list.h>37 #include <ctype.h>38 36 #include <display.h> 39 37 #include <errno.h> 40 38 #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>47 39 #include <stdlib.h> 48 #include <str.h>49 40 #include <task.h> 50 #include <types/common.h>51 #include <ui/clickmatic.h>52 41 #include <ui/ui.h> 53 #include <ui/wdecor.h>54 #include <ui/window.h>55 #include "../private/wdecor.h"56 #include "../private/window.h"57 42 #include "../private/ui.h" 58 59 /** Parse output specification.60 *61 * Output specification has the form <proto>@<service> where proto is62 * 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 specification66 * @param ws Place to store window system type (protocol)67 * @param osvc Place to store pointer to output service name68 * @param ridev_id Place to store input device ID69 * @return EOK on success, EINVAL if syntax is invalid, ENOMEM if out of70 * memory71 */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 43 147 44 /** Create new user interface. 148 45 * 149 46 * @param ospec Output specification or @c UI_DISPLAY_DEFAULT to use 150 * the default display service, UI_CONSOLE_DEFAULT to use 151 * the default console service, UI_DISPLAY_NULL to use 152 * dummy output. 47 * the default output 153 48 * @param rui Place to store pointer to new UI 154 49 * @return EOK on success or an error code … … 158 53 errno_t rc; 159 54 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;167 55 ui_t *ui; 168 56 169 rc = ui_ospec_parse(ospec, &ws, &osvc, &idev_id);57 rc = display_open(ospec, &display); 170 58 if (rc != EOK) 171 59 return rc; 172 60 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); 61 rc = ui_create_disp(display, &ui); 265 62 if (rc != EOK) { 266 free(ui);63 display_close(display); 267 64 return rc; 268 65 } 269 66 270 ui->console = console; 271 list_initialize(&ui->windows); 272 fibril_mutex_initialize(&ui->lock); 67 ui->display = display; 68 ui->myoutput = true; 273 69 *rui = ui; 274 70 return EOK; … … 284 80 { 285 81 ui_t *ui; 286 errno_t rc;287 82 288 83 ui = calloc(1, sizeof(ui_t)); … … 290 85 return ENOMEM; 291 86 292 rc = ui_clickmatic_create(ui, &ui->clickmatic);293 if (rc != EOK) {294 free(ui);295 return rc;296 }297 298 87 ui->display = disp; 299 list_initialize(&ui->windows);300 fibril_mutex_initialize(&ui->lock);301 88 *rui = ui; 302 89 return EOK; … … 312 99 return; 313 100 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 101 if (ui->myoutput) 102 display_close(ui->display); 325 103 free(ui); 326 }327 328 /** Resize UI after screen size change.329 *330 * @param ui UI331 * @param rect New screen rectangle.332 */333 static void ui_resize(ui_t *ui, gfx_rect_t *rect)334 {335 ui_window_t *wnd;336 337 ui->rect = *rect;338 339 /* Reposition/resize windows */340 wnd = ui_window_first(ui);341 while (wnd != NULL) {342 ui_window_update_placement(wnd);343 wnd = ui_window_next(wnd);344 }345 346 /*347 * XXX Resizing cleared console GC so we need to repaint the348 * background.349 */350 (void)ui_paint(ui);351 }352 353 static void ui_cons_event_process(ui_t *ui, cons_event_t *event)354 {355 ui_window_t *awnd;356 ui_evclaim_t claim;357 pos_event_t pos;358 sysarg_t cols, rows;359 gfx_rect_t rect;360 errno_t rc;361 362 awnd = ui_window_get_active(ui);363 if (awnd == NULL)364 return;365 366 switch (event->type) {367 case CEV_KEY:368 ui_lock(ui);369 ui_window_send_kbd(awnd, &event->ev.key);370 ui_unlock(ui);371 break;372 case CEV_POS:373 pos = event->ev.pos;374 /* Translate event to window-relative coordinates */375 pos.hpos -= awnd->dpos.x;376 pos.vpos -= awnd->dpos.y;377 378 claim = ui_wdecor_pos_event(awnd->wdecor, &pos);379 /* Note: If event is claimed, awnd might not be valid anymore */380 if (claim == ui_unclaimed) {381 ui_lock(ui);382 ui_window_send_pos(awnd, &pos);383 ui_unlock(ui);384 }385 386 break;387 case CEV_RESIZE:388 rc = console_gc_resize(ui->cgc);389 if (rc != EOK) {390 /* XXX No good way to recover. */391 console_done(ui->console);392 exit(1);393 }394 395 rc = console_get_size(ui->console, &cols, &rows);396 if (rc != EOK) {397 /* XXX No good way to recover. */398 console_done(ui->console);399 exit(1);400 }401 402 ui_lock(ui);403 404 rect.p0.x = 0;405 rect.p0.y = 0;406 rect.p1.x = cols;407 rect.p1.y = rows;408 409 ui_resize(ui, &rect);410 ui_unlock(ui);411 break;412 }413 104 } 414 105 … … 422 113 void ui_run(ui_t *ui) 423 114 { 424 cons_event_t event; 425 usec_t timeout; 426 errno_t rc; 115 task_retval(0); 427 116 428 /* Only return command prompt if we are running in a separate window */ 429 if (ui->display != NULL) 430 task_retval(0); 431 432 while (!ui->quit) { 433 if (ui->console != NULL) { 434 timeout = 100000; 435 rc = console_get_event_timeout(ui->console, 436 &event, &timeout); 437 438 /* Do we actually have an event? */ 439 if (rc == EOK) { 440 ui_cons_event_process(ui, &event); 441 } else if (rc != ETIMEOUT) { 442 /* Error, quit */ 443 break; 444 } 445 } else { 446 fibril_usleep(100000); 447 } 448 } 449 } 450 451 /** Repaint UI (only used in fullscreen mode). 452 * 453 * This is used when an area is exposed in fullscreen mode. 454 * 455 * @param ui UI 456 * @return @c EOK on success or an error code 457 */ 458 errno_t ui_paint(ui_t *ui) 459 { 460 errno_t rc; 461 gfx_context_t *gc; 462 ui_window_t *wnd; 463 gfx_color_t *color = NULL; 464 465 /* In case of null output */ 466 if (ui->cgc == NULL) 467 return EOK; 468 469 gc = console_gc_get_ctx(ui->cgc); 470 471 rc = gfx_color_new_ega(0x11, &color); 472 if (rc != EOK) 473 return rc; 474 475 rc = gfx_set_color(gc, color); 476 if (rc != EOK) { 477 gfx_color_delete(color); 478 return rc; 479 } 480 481 rc = gfx_fill_rect(gc, &ui->rect); 482 if (rc != EOK) { 483 gfx_color_delete(color); 484 return rc; 485 } 486 487 gfx_color_delete(color); 488 489 /* Repaint all windows */ 490 wnd = ui_window_first(ui); 491 while (wnd != NULL) { 492 rc = ui_wdecor_paint(wnd->wdecor); 493 if (rc != EOK) 494 return rc; 495 496 rc = ui_window_paint(wnd); 497 if (rc != EOK) 498 return rc; 499 500 wnd = ui_window_next(wnd); 501 } 502 503 return EOK; 504 } 505 506 /** Free up console for other users. 507 * 508 * Release console resources for another application (that the current 509 * task is starting). After the other application finishes, resume 510 * operation with ui_resume(). No calls to UI must happen inbetween 511 * and no events must be processed (i.e. the calling function must not 512 * return control to UI. 513 * 514 * @param ui UI 515 * @return EOK on success or an error code 516 */ 517 errno_t ui_suspend(ui_t *ui) 518 { 519 errno_t rc; 520 521 assert(!ui->suspended); 522 523 if (ui->cgc == NULL) { 524 ui->suspended = true; 525 return EOK; 526 } 527 528 (void) console_set_caption(ui->console, ""); 529 rc = console_gc_suspend(ui->cgc); 530 if (rc != EOK) 531 return rc; 532 533 ui->suspended = true; 534 return EOK; 535 } 536 537 /** Resume suspended UI. 538 * 539 * Reclaim console resources (after child application has finished running) 540 * and restore UI operation previously suspended by calling ui_suspend(). 541 * 542 * @param ui UI 543 * @return EOK on success or an error code 544 */ 545 errno_t ui_resume(ui_t *ui) 546 { 547 errno_t rc; 548 ui_window_t *awnd; 549 sysarg_t col; 550 sysarg_t row; 551 cons_event_t ev; 552 553 assert(ui->suspended); 554 555 if (ui->cgc == NULL) { 556 ui->suspended = false; 557 return EOK; 558 } 559 560 rc = console_get_pos(ui->console, &col, &row); 561 if (rc != EOK) 562 return rc; 563 564 /* 565 * Here's a little heuristic to help determine if we need 566 * to pause before returning to the UI. If we are in the 567 * top-left corner, chances are the screen is empty and 568 * there is no need to pause. 569 */ 570 if (col != 0 || row != 0) { 571 printf("Press any key or button to continue...\n"); 572 573 while (true) { 574 rc = console_get_event(ui->console, &ev); 575 if (rc != EOK) 576 return EIO; 577 578 if (ev.type == CEV_KEY && ev.ev.key.type == KEY_PRESS) 579 break; 580 581 if (ev.type == CEV_POS && ev.ev.pos.type == POS_PRESS) 582 break; 583 } 584 } 585 586 rc = console_gc_resume(ui->cgc); 587 if (rc != EOK) 588 return rc; 589 590 ui->suspended = false; 591 592 awnd = ui_window_get_active(ui); 593 if (awnd != NULL) 594 (void) console_set_caption(ui->console, awnd->wdecor->caption); 595 596 rc = gfx_cursor_set_visible(console_gc_get_ctx(ui->cgc), false); 597 if (rc != EOK) 598 return rc; 599 600 return EOK; 601 } 602 603 /** Determine if UI is suspended. 604 * 605 * @param ui UI 606 * @return @c true iff UI is suspended 607 */ 608 bool ui_is_suspended(ui_t *ui) 609 { 610 return ui->suspended; 611 } 612 613 /** Lock UI. 614 * 615 * Block UI from calling window callbacks. @c ui_lock() and @c ui_unlock() 616 * must be used when accessing UI resources from a fibril (as opposed to 617 * from a window callback). 618 * 619 * @param ui UI 620 */ 621 void ui_lock(ui_t *ui) 622 { 623 if (ui->display != NULL) 624 display_lock(ui->display); 625 fibril_mutex_lock(&ui->lock); 626 } 627 628 /** Unlock UI. 629 * 630 * Allow UI to call window callbacks. @c ui_lock() and @c ui_unlock() 631 * must be used when accessing window resources from a fibril (as opposed to 632 * from a window callback). 633 * 634 * @param ui UI 635 */ 636 void ui_unlock(ui_t *ui) 637 { 638 fibril_mutex_unlock(&ui->lock); 639 if (ui->display != NULL) 640 display_unlock(ui->display); 117 while (!ui->quit) 118 fibril_usleep(100000); 641 119 } 642 120 … … 654 132 } 655 133 656 /** Determine if we are running in text mode.657 *658 * @param ui User interface659 * @return @c true iff we are running in text mode660 */661 bool ui_is_textmode(ui_t *ui)662 {663 /*664 * XXX Currently console is always text and display is always665 * graphics, but this need not always be true.666 */667 return (ui->console != NULL);668 }669 670 /** Determine if we are emulating windows.671 *672 * @param ui User interface673 * @return @c true iff we are running in text mode674 */675 bool ui_is_fullscreen(ui_t *ui)676 {677 return (ui->display == NULL);678 }679 680 /** Get UI screen rectangle.681 *682 * @param ui User interface683 * @param rect Place to store bounding rectangle684 */685 errno_t ui_get_rect(ui_t *ui, gfx_rect_t *rect)686 {687 display_info_t info;688 sysarg_t cols, rows;689 errno_t rc;690 691 if (ui->display != NULL) {692 rc = display_get_info(ui->display, &info);693 if (rc != EOK)694 return rc;695 696 *rect = info.rect;697 } else if (ui->console != NULL) {698 rc = console_get_size(ui->console, &cols, &rows);699 if (rc != EOK)700 return rc;701 702 rect->p0.x = 0;703 rect->p0.y = 0;704 rect->p1.x = cols;705 rect->p1.y = rows;706 } else {707 return ENOTSUP;708 }709 710 return EOK;711 }712 713 /** Get clickmatic from UI.714 *715 * @pararm ui UI716 * @return Clickmatic717 */718 ui_clickmatic_t *ui_get_clickmatic(ui_t *ui)719 {720 return ui->clickmatic;721 }722 723 134 /** @} 724 135 */
Note:
See TracChangeset
for help on using the changeset viewer.
