source: mainline/uspace/lib/ui/src/ui.c@ 14b4577

ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 14b4577 was 3fd38b2, checked in by Jiri Svoboda <jiri@…>, 3 years ago

Size taskbar based on display size, fix text mode

  • Property mode set to 100644
File size: 12.3 KB
RevLine 
[f7a90df]1/*
[8965860c]2 * Copyright (c) 2022 Jiri Svoboda
[f7a90df]3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libui
30 * @{
31 */
32/**
33 * @file User interface
34 */
35
[252d03c]36#include <adt/list.h>
[77ffa01]37#include <ctype.h>
[f7a90df]38#include <display.h>
39#include <errno.h>
[d284ce9]40#include <fibril.h>
[8965860c]41#include <fibril_synch.h>
[1ebcb791]42#include <gfx/color.h>
[c632c96]43#include <gfx/cursor.h>
[1ebcb791]44#include <gfx/render.h>
[77ffa01]45#include <io/console.h>
[87822ce]46#include <stdbool.h>
[f7a90df]47#include <stdlib.h>
[77ffa01]48#include <str.h>
[d55ab823]49#include <task.h>
[8965860c]50#include <ui/clickmatic.h>
[f7a90df]51#include <ui/ui.h>
[77ffa01]52#include <ui/wdecor.h>
[252d03c]53#include <ui/window.h>
[b48e680f]54#include "../private/wdecor.h"
[77ffa01]55#include "../private/window.h"
[f7a90df]56#include "../private/ui.h"
57
[77ffa01]58/** Parse output specification.
59 *
60 * Output specification has the form <proto>@<service> where proto is
[3d10a2f]61 * eiher 'disp' for display service, 'cons' for console, 'null'
62 * for dummy output. Service is a location ID service name (e.g. hid/display).
[77ffa01]63 *
64 * @param ospec Output specification
65 * @param ws Place to store window system type (protocol)
66 * @param osvc Place to store pointer to output service name
67 */
68static void ui_ospec_parse(const char *ospec, ui_winsys_t *ws,
69 const char **osvc)
70{
71 const char *cp;
72
73 cp = ospec;
74 while (isalpha(*cp))
75 ++cp;
76
77 if (*cp == '@') {
78 if (str_lcmp(ospec, "disp@", str_length("disp@")) == 0) {
79 *ws = ui_ws_display;
80 } else if (str_lcmp(ospec, "cons@", str_length("cons@")) == 0) {
81 *ws = ui_ws_console;
[3d10a2f]82 } else if (str_lcmp(ospec, "null@", str_length("null@")) == 0) {
83 *ws = ui_ws_null;
[552b69f]84 } else if (str_lcmp(ospec, "@", str_length("@")) == 0) {
85 *ws = ui_ws_any;
[77ffa01]86 } else {
87 *ws = ui_ws_unknown;
88 }
89
90 if (cp[1] != '\0')
91 *osvc = cp + 1;
92 else
93 *osvc = NULL;
94 } else {
95 *ws = ui_ws_display;
96 *osvc = ospec;
97 }
98}
99
[f7a90df]100/** Create new user interface.
101 *
102 * @param ospec Output specification or @c UI_DISPLAY_DEFAULT to use
[3d10a2f]103 * the default display service, UI_CONSOLE_DEFAULT to use
104 * the default console service, UI_DISPLAY_NULL to use
105 * dummy output.
[f7a90df]106 * @param rui Place to store pointer to new UI
107 * @return EOK on success or an error code
108 */
109errno_t ui_create(const char *ospec, ui_t **rui)
110{
111 errno_t rc;
112 display_t *display;
[77ffa01]113 console_ctrl_t *console;
[252d03c]114 console_gc_t *cgc;
[77ffa01]115 ui_winsys_t ws;
116 const char *osvc;
[1ebcb791]117 sysarg_t cols;
118 sysarg_t rows;
[f7a90df]119 ui_t *ui;
120
[77ffa01]121 ui_ospec_parse(ospec, &ws, &osvc);
122
[552b69f]123 if (ws == ui_ws_display || ws == ui_ws_any) {
124 rc = display_open(osvc != NULL ? osvc : DISPLAY_DEFAULT,
125 &display);
[77ffa01]126 if (rc != EOK)
[552b69f]127 goto disp_fail;
[77ffa01]128
129 rc = ui_create_disp(display, &ui);
130 if (rc != EOK) {
131 display_close(display);
[552b69f]132 goto disp_fail;
[77ffa01]133 }
[552b69f]134
135 ui->myoutput = true;
136 *rui = ui;
137 return EOK;
138 }
139
140disp_fail:
141 if (ws == ui_ws_console || ws == ui_ws_any) {
[77ffa01]142 console = console_init(stdin, stdout);
143 if (console == NULL)
[552b69f]144 goto cons_fail;
[f7a90df]145
[1ebcb791]146 rc = console_get_size(console, &cols, &rows);
147 if (rc != EOK) {
148 console_done(console);
[552b69f]149 goto cons_fail;
[1ebcb791]150 }
151
[bb14312]152 console_cursor_visibility(console, false);
153
[77ffa01]154 /* ws == ui_ws_console */
155 rc = ui_create_cons(console, &ui);
156 if (rc != EOK) {
157 console_done(console);
[552b69f]158 goto cons_fail;
[77ffa01]159 }
[252d03c]160
161 rc = console_gc_create(console, NULL, &cgc);
162 if (rc != EOK) {
163 ui_destroy(ui);
164 console_done(console);
[552b69f]165 goto cons_fail;
[252d03c]166 }
167
168 ui->cgc = cgc;
[1ebcb791]169 ui->rect.p0.x = 0;
170 ui->rect.p0.y = 0;
171 ui->rect.p1.x = cols;
172 ui->rect.p1.y = rows;
173
174 (void) ui_paint(ui);
[552b69f]175 ui->myoutput = true;
176 *rui = ui;
177 return EOK;
178 }
179
180cons_fail:
181 if (ws == ui_ws_null) {
[3d10a2f]182 rc = ui_create_disp(NULL, &ui);
183 if (rc != EOK)
184 return rc;
[552b69f]185
186 ui->myoutput = true;
187 *rui = ui;
188 return EOK;
[f7a90df]189 }
190
[552b69f]191 return EINVAL;
[f7a90df]192}
193
[77ffa01]194/** Create new user interface using console service.
195 *
196 * @param rui Place to store pointer to new UI
197 * @return EOK on success or an error code
198 */
199errno_t ui_create_cons(console_ctrl_t *console, ui_t **rui)
200{
201 ui_t *ui;
[8965860c]202 errno_t rc;
[77ffa01]203
204 ui = calloc(1, sizeof(ui_t));
205 if (ui == NULL)
206 return ENOMEM;
207
[8965860c]208 rc = ui_clickmatic_create(ui, &ui->clickmatic);
209 if (rc != EOK) {
210 free(ui);
211 return rc;
212 }
213
[77ffa01]214 ui->console = console;
[252d03c]215 list_initialize(&ui->windows);
[8965860c]216 fibril_mutex_initialize(&ui->lock);
[77ffa01]217 *rui = ui;
218 return EOK;
219}
220
[f7a90df]221/** Create new user interface using display service.
222 *
223 * @param disp Display
224 * @param rui Place to store pointer to new UI
225 * @return EOK on success or an error code
226 */
227errno_t ui_create_disp(display_t *disp, ui_t **rui)
228{
229 ui_t *ui;
[8965860c]230 errno_t rc;
[f7a90df]231
232 ui = calloc(1, sizeof(ui_t));
233 if (ui == NULL)
234 return ENOMEM;
235
[8965860c]236 rc = ui_clickmatic_create(ui, &ui->clickmatic);
237 if (rc != EOK) {
238 free(ui);
239 return rc;
240 }
241
[f7a90df]242 ui->display = disp;
[252d03c]243 list_initialize(&ui->windows);
[8965860c]244 fibril_mutex_initialize(&ui->lock);
[f7a90df]245 *rui = ui;
246 return EOK;
247}
248
249/** Destroy user interface.
250 *
251 * @param ui User interface or @c NULL
252 */
253void ui_destroy(ui_t *ui)
254{
255 if (ui == NULL)
256 return;
257
[77ffa01]258 if (ui->myoutput) {
[252d03c]259 if (ui->cgc != NULL)
260 console_gc_delete(ui->cgc);
[bb14312]261 if (ui->console != NULL) {
262 console_cursor_visibility(ui->console, true);
[77ffa01]263 console_done(ui->console);
[bb14312]264 }
[77ffa01]265 if (ui->display != NULL)
266 display_close(ui->display);
267 }
268
[f7a90df]269 free(ui);
270}
271
[77ffa01]272static void ui_cons_event_process(ui_t *ui, cons_event_t *event)
273{
[252d03c]274 ui_window_t *awnd;
275 ui_evclaim_t claim;
[3c3657c]276 pos_event_t pos;
[252d03c]277
278 awnd = ui_window_get_active(ui);
279 if (awnd == NULL)
[77ffa01]280 return;
281
282 switch (event->type) {
283 case CEV_KEY:
[252d03c]284 ui_window_send_kbd(awnd, &event->ev.key);
[77ffa01]285 break;
286 case CEV_POS:
[3c3657c]287 pos = event->ev.pos;
[8ce56a6]288 /* Translate event to window-relative coordinates */
[3c3657c]289 pos.hpos -= awnd->dpos.x;
290 pos.vpos -= awnd->dpos.y;
291
292 claim = ui_wdecor_pos_event(awnd->wdecor, &pos);
[252d03c]293 /* Note: If event is claimed, awnd might not be valid anymore */
294 if (claim == ui_unclaimed)
[3c3657c]295 ui_window_send_pos(awnd, &pos);
296
[77ffa01]297 break;
298 }
299}
300
[d284ce9]301/** Execute user interface.
302 *
[d55ab823]303 * Return task exit code of zero and block unitl the application starts
304 * the termination process by calling ui_quit(@a ui).
[d284ce9]305 *
306 * @param ui User interface
307 */
308void ui_run(ui_t *ui)
309{
[77ffa01]310 cons_event_t event;
311 usec_t timeout;
[87822ce]312 errno_t rc;
[d55ab823]313
[77ffa01]314 /* Only return command prompt if we are running in a separate window */
315 if (ui->display != NULL)
316 task_retval(0);
317
318 while (!ui->quit) {
319 if (ui->console != NULL) {
320 timeout = 100000;
[87822ce]321 rc = console_get_event_timeout(ui->console,
[77ffa01]322 &event, &timeout);
[87822ce]323
324 /* Do we actually have an event? */
325 if (rc == EOK) {
[77ffa01]326 ui_cons_event_process(ui, &event);
[87822ce]327 } else if (rc != ETIMEOUT) {
328 /* Error, quit */
329 break;
330 }
[77ffa01]331 } else {
332 fibril_usleep(100000);
333 }
334 }
[d284ce9]335}
336
[252d03c]337/** Repaint UI (only used in fullscreen mode).
338 *
339 * This is used when an area is exposed in fullscreen mode.
340 *
341 * @param ui UI
342 * @return @c EOK on success or an error code
343 */
344errno_t ui_paint(ui_t *ui)
345{
346 errno_t rc;
[1ebcb791]347 gfx_context_t *gc;
[252d03c]348 ui_window_t *awnd;
[1ebcb791]349 gfx_color_t *color = NULL;
350
[6d172f6]351 /* In case of null output */
352 if (ui->cgc == NULL)
353 return EOK;
354
[1ebcb791]355 gc = console_gc_get_ctx(ui->cgc);
356
357 rc = gfx_color_new_ega(0x11, &color);
358 if (rc != EOK)
359 return rc;
360
361 rc = gfx_set_color(gc, color);
362 if (rc != EOK) {
363 gfx_color_delete(color);
364 return rc;
365 }
366
367 rc = gfx_fill_rect(gc, &ui->rect);
368 if (rc != EOK) {
369 gfx_color_delete(color);
370 return rc;
371 }
372
373 gfx_color_delete(color);
[252d03c]374
375 /* XXX Should repaint all windows */
376 awnd = ui_window_get_active(ui);
377 if (awnd == NULL)
378 return EOK;
379
380 rc = ui_wdecor_paint(awnd->wdecor);
381 if (rc != EOK)
382 return rc;
383
384 return ui_window_paint(awnd);
385}
386
[c632c96]387/** Free up console for other users.
388 *
389 * Release console resources for another application (that the current
390 * task is starting). After the other application finishes, resume
391 * operation with ui_resume(). No calls to UI must happen inbetween
392 * and no events must be processed (i.e. the calling function must not
393 * return control to UI.
394 *
395 * @param ui UI
396 * @return EOK on success or an error code
397 */
398errno_t ui_suspend(ui_t *ui)
399{
400 if (ui->cgc == NULL)
401 return EOK;
402
[b48e680f]403 (void) console_set_caption(ui->console, "");
[c632c96]404 return console_gc_suspend(ui->cgc);
405}
406
407/** Resume suspended UI.
408 *
409 * Reclaim console resources (after child application has finished running)
410 * and restore UI operation previously suspended by calling ui_suspend().
411 *
412 * @param ui UI
413 * @return EOK on success or an error code
414 */
415errno_t ui_resume(ui_t *ui)
416{
417 errno_t rc;
[b48e680f]418 ui_window_t *awnd;
[e3e64f6]419 sysarg_t col;
420 sysarg_t row;
421 cons_event_t ev;
[c632c96]422
423 if (ui->cgc == NULL)
424 return EOK;
425
[e3e64f6]426 rc = console_get_pos(ui->console, &col, &row);
427 if (rc != EOK)
428 return rc;
429
430 /*
431 * Here's a little heuristic to help determine if we need
432 * to pause before returning to the UI. If we are in the
433 * top-left corner, chances are the screen is empty and
434 * there is no need to pause.
435 */
436 if (col != 0 || row != 0) {
437 printf("Press any key or button to continue...\n");
438
439 while (true) {
440 rc = console_get_event(ui->console, &ev);
441 if (rc != EOK)
442 return EIO;
443
444 if (ev.type == CEV_KEY && ev.ev.key.type == KEY_PRESS)
445 break;
446
447 if (ev.type == CEV_POS && ev.ev.pos.type == POS_PRESS)
448 break;
449 }
450 }
451
[c632c96]452 rc = console_gc_resume(ui->cgc);
453 if (rc != EOK)
454 return rc;
455
[b48e680f]456 awnd = ui_window_get_active(ui);
457 if (awnd != NULL)
458 (void) console_set_caption(ui->console, awnd->wdecor->caption);
459
[c632c96]460 return gfx_cursor_set_visible(console_gc_get_ctx(ui->cgc), false);
461}
462
[8965860c]463/** Lock UI.
464 *
465 * Block UI from calling window callbacks. @c ui_lock() and @c ui_unlock()
466 * must be used when accessing UI resources from a fibril (as opposed to
467 * from a window callback).
468 *
469 * @param ui UI
470 */
471void ui_lock(ui_t *ui)
472{
473 fibril_mutex_lock(&ui->lock);
474}
475
476/** Unlock UI.
477 *
478 * Allow UI to call window callbacks. @c ui_lock() and @c ui_unlock()
479 * must be used when accessing window resources from a fibril (as opposed to
480 * from a window callback).
481 *
482 * @param ui UI
483 */
484void ui_unlock(ui_t *ui)
485{
486 fibril_mutex_unlock(&ui->lock);
487}
488
[d284ce9]489/** Terminate user interface.
490 *
491 * Calling this function causes the user interface to terminate
492 * (i.e. exit from ui_run()). This would be typically called from
493 * an event handler.
494 *
495 * @param ui User interface
496 */
497void ui_quit(ui_t *ui)
498{
499 ui->quit = true;
500}
501
[9c7dc8e]502/** Determine if we are running in text mode.
503 *
504 * @param ui User interface
505 * @return @c true iff we are running in text mode
506 */
507bool ui_is_textmode(ui_t *ui)
508{
509 /*
510 * XXX Currently console is always text and display is always
511 * graphics, but this need not always be true.
512 */
513 return (ui->console != NULL);
514}
515
[252d03c]516/** Determine if we are emulating windows.
517 *
518 * @param ui User interface
519 * @return @c true iff we are running in text mode
520 */
521bool ui_is_fullscreen(ui_t *ui)
522{
523 return (ui->display == NULL);
524}
525
[3fd38b2]526/** Get UI screen rectangle.
527 *
528 * @param ui User interface
529 * @param rect Place to store bounding rectangle
530 */
531errno_t ui_get_rect(ui_t *ui, gfx_rect_t *rect)
532{
533 display_info_t info;
534 sysarg_t cols, rows;
535 errno_t rc;
536
537 if (ui->display != NULL) {
538 rc = display_get_info(ui->display, &info);
539 if (rc != EOK)
540 return rc;
541
542 *rect = info.rect;
543 } else if (ui->console != NULL) {
544 rc = console_get_size(ui->console, &cols, &rows);
545 if (rc != EOK)
546 return rc;
547
548 rect->p0.x = 0;
549 rect->p0.y = 0;
550 rect->p1.x = cols;
551 rect->p1.y = rows;
552 } else {
553 return ENOTSUP;
554 }
555
556 return EOK;
557}
558
[8965860c]559/** Get clickmatic from UI.
560 *
561 * @pararm ui UI
562 * @return Clickmatic
563 */
564ui_clickmatic_t *ui_get_clickmatic(ui_t *ui)
565{
566 return ui->clickmatic;
567}
568
[f7a90df]569/** @}
570 */
Note: See TracBrowser for help on using the repository browser.