source: mainline/uspace/lib/ui/src/ui.c@ 3d10a2f

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

Null display spec for the benefit of unit testing

When testing something that takes a display specification as argument,
it is useful to be able to be able to specify dummy output (so far,
only ui_create_disp() could create a UI with dummy output.

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