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

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

Allow console application to set the terminal window caption

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