source: mainline/uspace/lib/ui/src/ui.c@ 2651dc5

Last change on this file since 2651dc5 was 2651dc5, checked in by Jiri Svoboda <jiri@…>, 4 years ago

Unmap and clear console before executing a binary

Fixes running edit, nav, improves fdisk, etc.

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