source: mainline/uspace/lib/ui/src/ui.c@ bad765a

serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since bad765a was 552b69f, checked in by Jiri Svoboda <jiri@…>, 4 years ago

Dual-mode applications should automatically fall back to console

  • Property mode set to 100644
File size: 10.0 KB
RevLine 
[f7a90df]1/*
[77ffa01]2 * Copyright (c) 2021 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>
[1ebcb791]41#include <gfx/color.h>
[c632c96]42#include <gfx/cursor.h>
[1ebcb791]43#include <gfx/render.h>
[77ffa01]44#include <io/console.h>
[87822ce]45#include <stdbool.h>
[f7a90df]46#include <stdlib.h>
[77ffa01]47#include <str.h>
[d55ab823]48#include <task.h>
[f7a90df]49#include <ui/ui.h>
[77ffa01]50#include <ui/wdecor.h>
[252d03c]51#include <ui/window.h>
[b48e680f]52#include "../private/wdecor.h"
[77ffa01]53#include "../private/window.h"
[f7a90df]54#include "../private/ui.h"
55
[77ffa01]56/** Parse output specification.
57 *
58 * Output specification has the form <proto>@<service> where proto is
[3d10a2f]59 * eiher 'disp' for display service, 'cons' for console, 'null'
60 * for dummy output. Service is a location ID service name (e.g. hid/display).
[77ffa01]61 *
62 * @param ospec Output specification
63 * @param ws Place to store window system type (protocol)
64 * @param osvc Place to store pointer to output service name
65 */
66static void ui_ospec_parse(const char *ospec, ui_winsys_t *ws,
67 const char **osvc)
68{
69 const char *cp;
70
71 cp = ospec;
72 while (isalpha(*cp))
73 ++cp;
74
75 if (*cp == '@') {
76 if (str_lcmp(ospec, "disp@", str_length("disp@")) == 0) {
77 *ws = ui_ws_display;
78 } else if (str_lcmp(ospec, "cons@", str_length("cons@")) == 0) {
79 *ws = ui_ws_console;
[3d10a2f]80 } else if (str_lcmp(ospec, "null@", str_length("null@")) == 0) {
81 *ws = ui_ws_null;
[552b69f]82 } else if (str_lcmp(ospec, "@", str_length("@")) == 0) {
83 *ws = ui_ws_any;
[77ffa01]84 } else {
85 *ws = ui_ws_unknown;
86 }
87
88 if (cp[1] != '\0')
89 *osvc = cp + 1;
90 else
91 *osvc = NULL;
92 } else {
93 *ws = ui_ws_display;
94 *osvc = ospec;
95 }
96}
97
[f7a90df]98/** Create new user interface.
99 *
100 * @param ospec Output specification or @c UI_DISPLAY_DEFAULT to use
[3d10a2f]101 * the default display service, UI_CONSOLE_DEFAULT to use
102 * the default console service, UI_DISPLAY_NULL to use
103 * dummy output.
[f7a90df]104 * @param rui Place to store pointer to new UI
105 * @return EOK on success or an error code
106 */
107errno_t ui_create(const char *ospec, ui_t **rui)
108{
109 errno_t rc;
110 display_t *display;
[77ffa01]111 console_ctrl_t *console;
[252d03c]112 console_gc_t *cgc;
[77ffa01]113 ui_winsys_t ws;
114 const char *osvc;
[1ebcb791]115 sysarg_t cols;
116 sysarg_t rows;
[f7a90df]117 ui_t *ui;
118
[77ffa01]119 ui_ospec_parse(ospec, &ws, &osvc);
120
[552b69f]121 if (ws == ui_ws_display || ws == ui_ws_any) {
122 rc = display_open(osvc != NULL ? osvc : DISPLAY_DEFAULT,
123 &display);
[77ffa01]124 if (rc != EOK)
[552b69f]125 goto disp_fail;
[77ffa01]126
127 rc = ui_create_disp(display, &ui);
128 if (rc != EOK) {
129 display_close(display);
[552b69f]130 goto disp_fail;
[77ffa01]131 }
[552b69f]132
133 ui->myoutput = true;
134 *rui = ui;
135 return EOK;
136 }
137
138disp_fail:
139 if (ws == ui_ws_console || ws == ui_ws_any) {
[77ffa01]140 console = console_init(stdin, stdout);
141 if (console == NULL)
[552b69f]142 goto cons_fail;
[f7a90df]143
[1ebcb791]144 rc = console_get_size(console, &cols, &rows);
145 if (rc != EOK) {
146 console_done(console);
[552b69f]147 goto cons_fail;
[1ebcb791]148 }
149
[bb14312]150 console_cursor_visibility(console, false);
151
[77ffa01]152 /* ws == ui_ws_console */
153 rc = ui_create_cons(console, &ui);
154 if (rc != EOK) {
155 console_done(console);
[552b69f]156 goto cons_fail;
[77ffa01]157 }
[252d03c]158
159 rc = console_gc_create(console, NULL, &cgc);
160 if (rc != EOK) {
161 ui_destroy(ui);
162 console_done(console);
[552b69f]163 goto cons_fail;
[252d03c]164 }
165
166 ui->cgc = cgc;
[1ebcb791]167 ui->rect.p0.x = 0;
168 ui->rect.p0.y = 0;
169 ui->rect.p1.x = cols;
170 ui->rect.p1.y = rows;
171
172 (void) ui_paint(ui);
[552b69f]173 ui->myoutput = true;
174 *rui = ui;
175 return EOK;
176 }
177
178cons_fail:
179 if (ws == ui_ws_null) {
[3d10a2f]180 rc = ui_create_disp(NULL, &ui);
181 if (rc != EOK)
182 return rc;
[552b69f]183
184 ui->myoutput = true;
185 *rui = ui;
186 return EOK;
[f7a90df]187 }
188
[552b69f]189 return EINVAL;
[f7a90df]190}
191
[77ffa01]192/** Create new user interface using console service.
193 *
194 * @param rui Place to store pointer to new UI
195 * @return EOK on success or an error code
196 */
197errno_t ui_create_cons(console_ctrl_t *console, ui_t **rui)
198{
199 ui_t *ui;
200
201 ui = calloc(1, sizeof(ui_t));
202 if (ui == NULL)
203 return ENOMEM;
204
205 ui->console = console;
[252d03c]206 list_initialize(&ui->windows);
[77ffa01]207 *rui = ui;
208 return EOK;
209}
210
[f7a90df]211/** Create new user interface using display service.
212 *
213 * @param disp Display
214 * @param rui Place to store pointer to new UI
215 * @return EOK on success or an error code
216 */
217errno_t ui_create_disp(display_t *disp, ui_t **rui)
218{
219 ui_t *ui;
220
221 ui = calloc(1, sizeof(ui_t));
222 if (ui == NULL)
223 return ENOMEM;
224
225 ui->display = disp;
[252d03c]226 list_initialize(&ui->windows);
[f7a90df]227 *rui = ui;
228 return EOK;
229}
230
231/** Destroy user interface.
232 *
233 * @param ui User interface or @c NULL
234 */
235void ui_destroy(ui_t *ui)
236{
237 if (ui == NULL)
238 return;
239
[77ffa01]240 if (ui->myoutput) {
[252d03c]241 if (ui->cgc != NULL)
242 console_gc_delete(ui->cgc);
[bb14312]243 if (ui->console != NULL) {
244 console_cursor_visibility(ui->console, true);
[77ffa01]245 console_done(ui->console);
[bb14312]246 }
[77ffa01]247 if (ui->display != NULL)
248 display_close(ui->display);
249 }
250
[f7a90df]251 free(ui);
252}
253
[77ffa01]254static void ui_cons_event_process(ui_t *ui, cons_event_t *event)
255{
[252d03c]256 ui_window_t *awnd;
257 ui_evclaim_t claim;
[3c3657c]258 pos_event_t pos;
[252d03c]259
260 awnd = ui_window_get_active(ui);
261 if (awnd == NULL)
[77ffa01]262 return;
263
264 switch (event->type) {
265 case CEV_KEY:
[252d03c]266 ui_window_send_kbd(awnd, &event->ev.key);
[77ffa01]267 break;
268 case CEV_POS:
[3c3657c]269 pos = event->ev.pos;
[8ce56a6]270 /* Translate event to window-relative coordinates */
[3c3657c]271 pos.hpos -= awnd->dpos.x;
272 pos.vpos -= awnd->dpos.y;
273
274 claim = ui_wdecor_pos_event(awnd->wdecor, &pos);
[252d03c]275 /* Note: If event is claimed, awnd might not be valid anymore */
276 if (claim == ui_unclaimed)
[3c3657c]277 ui_window_send_pos(awnd, &pos);
278
[77ffa01]279 break;
280 }
281}
282
[d284ce9]283/** Execute user interface.
284 *
[d55ab823]285 * Return task exit code of zero and block unitl the application starts
286 * the termination process by calling ui_quit(@a ui).
[d284ce9]287 *
288 * @param ui User interface
289 */
290void ui_run(ui_t *ui)
291{
[77ffa01]292 cons_event_t event;
293 usec_t timeout;
[87822ce]294 errno_t rc;
[d55ab823]295
[77ffa01]296 /* Only return command prompt if we are running in a separate window */
297 if (ui->display != NULL)
298 task_retval(0);
299
300 while (!ui->quit) {
301 if (ui->console != NULL) {
302 timeout = 100000;
[87822ce]303 rc = console_get_event_timeout(ui->console,
[77ffa01]304 &event, &timeout);
[87822ce]305
306 /* Do we actually have an event? */
307 if (rc == EOK) {
[77ffa01]308 ui_cons_event_process(ui, &event);
[87822ce]309 } else if (rc != ETIMEOUT) {
310 /* Error, quit */
311 break;
312 }
[77ffa01]313 } else {
314 fibril_usleep(100000);
315 }
316 }
[d284ce9]317}
318
[252d03c]319/** Repaint UI (only used in fullscreen mode).
320 *
321 * This is used when an area is exposed in fullscreen mode.
322 *
323 * @param ui UI
324 * @return @c EOK on success or an error code
325 */
326errno_t ui_paint(ui_t *ui)
327{
328 errno_t rc;
[1ebcb791]329 gfx_context_t *gc;
[252d03c]330 ui_window_t *awnd;
[1ebcb791]331 gfx_color_t *color = NULL;
332
[6d172f6]333 /* In case of null output */
334 if (ui->cgc == NULL)
335 return EOK;
336
[1ebcb791]337 gc = console_gc_get_ctx(ui->cgc);
338
339 rc = gfx_color_new_ega(0x11, &color);
340 if (rc != EOK)
341 return rc;
342
343 rc = gfx_set_color(gc, color);
344 if (rc != EOK) {
345 gfx_color_delete(color);
346 return rc;
347 }
348
349 rc = gfx_fill_rect(gc, &ui->rect);
350 if (rc != EOK) {
351 gfx_color_delete(color);
352 return rc;
353 }
354
355 gfx_color_delete(color);
[252d03c]356
357 /* XXX Should repaint all windows */
358 awnd = ui_window_get_active(ui);
359 if (awnd == NULL)
360 return EOK;
361
362 rc = ui_wdecor_paint(awnd->wdecor);
363 if (rc != EOK)
364 return rc;
365
366 return ui_window_paint(awnd);
367}
368
[c632c96]369/** Free up console for other users.
370 *
371 * Release console resources for another application (that the current
372 * task is starting). After the other application finishes, resume
373 * operation with ui_resume(). No calls to UI must happen inbetween
374 * and no events must be processed (i.e. the calling function must not
375 * return control to UI.
376 *
377 * @param ui UI
378 * @return EOK on success or an error code
379 */
380errno_t ui_suspend(ui_t *ui)
381{
382 if (ui->cgc == NULL)
383 return EOK;
384
[b48e680f]385 (void) console_set_caption(ui->console, "");
[c632c96]386 return console_gc_suspend(ui->cgc);
387}
388
389/** Resume suspended UI.
390 *
391 * Reclaim console resources (after child application has finished running)
392 * and restore UI operation previously suspended by calling ui_suspend().
393 *
394 * @param ui UI
395 * @return EOK on success or an error code
396 */
397errno_t ui_resume(ui_t *ui)
398{
399 errno_t rc;
[b48e680f]400 ui_window_t *awnd;
[c632c96]401
402 if (ui->cgc == NULL)
403 return EOK;
404
405 rc = console_gc_resume(ui->cgc);
406 if (rc != EOK)
407 return rc;
408
[b48e680f]409 awnd = ui_window_get_active(ui);
410 if (awnd != NULL)
411 (void) console_set_caption(ui->console, awnd->wdecor->caption);
412
[c632c96]413 return gfx_cursor_set_visible(console_gc_get_ctx(ui->cgc), false);
414}
415
[d284ce9]416/** Terminate user interface.
417 *
418 * Calling this function causes the user interface to terminate
419 * (i.e. exit from ui_run()). This would be typically called from
420 * an event handler.
421 *
422 * @param ui User interface
423 */
424void ui_quit(ui_t *ui)
425{
426 ui->quit = true;
427}
428
[9c7dc8e]429/** Determine if we are running in text mode.
430 *
431 * @param ui User interface
432 * @return @c true iff we are running in text mode
433 */
434bool ui_is_textmode(ui_t *ui)
435{
436 /*
437 * XXX Currently console is always text and display is always
438 * graphics, but this need not always be true.
439 */
440 return (ui->console != NULL);
441}
442
[252d03c]443/** Determine if we are emulating windows.
444 *
445 * @param ui User interface
446 * @return @c true iff we are running in text mode
447 */
448bool ui_is_fullscreen(ui_t *ui)
449{
450 return (ui->display == NULL);
451}
452
[f7a90df]453/** @}
454 */
Note: See TracBrowser for help on using the repository browser.