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

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

Avoid infinite loop when console communication is broken

Need to make sure callers of console_get_event_timeout() can distinguish
between timeout and I/O error. Fix all callers of console_get_event()
and console_get_event_timeout() not to enter infinite loop when console
connection is broken. Also avoid setting of errno variable.

  • Property mode set to 100644
File size: 6.3 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 <ctype.h>
37#include <display.h>
38#include <errno.h>
39#include <fibril.h>
40#include <io/console.h>
41#include <stdbool.h>
42#include <stdlib.h>
43#include <str.h>
44#include <task.h>
45#include <ui/ui.h>
46#include <ui/wdecor.h>
47#include "../private/window.h"
48#include "../private/ui.h"
49
50/** Parse output specification.
51 *
52 * Output specification has the form <proto>@<service> where proto is
53 * eiher 'disp' for display service or 'cons' for console. Service
54 * is a location ID service name (e.g. hid/display).
55 *
56 * @param ospec Output specification
57 * @param ws Place to store window system type (protocol)
58 * @param osvc Place to store pointer to output service name
59 */
60static void ui_ospec_parse(const char *ospec, ui_winsys_t *ws,
61 const char **osvc)
62{
63 const char *cp;
64
65 if (ospec == UI_DISPLAY_DEFAULT) {
66 *ws = ui_ws_display;
67 *osvc = DISPLAY_DEFAULT;
68 return;
69 }
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;
80 } else {
81 *ws = ui_ws_unknown;
82 }
83
84 if (cp[1] != '\0')
85 *osvc = cp + 1;
86 else
87 *osvc = NULL;
88 } else {
89 *ws = ui_ws_display;
90 *osvc = ospec;
91 }
92}
93
94/** Create new user interface.
95 *
96 * @param ospec Output specification or @c UI_DISPLAY_DEFAULT to use
97 * the default output
98 * @param rui Place to store pointer to new UI
99 * @return EOK on success or an error code
100 */
101errno_t ui_create(const char *ospec, ui_t **rui)
102{
103 errno_t rc;
104 display_t *display;
105 console_ctrl_t *console;
106 ui_winsys_t ws;
107 const char *osvc;
108 ui_t *ui;
109
110 ui_ospec_parse(ospec, &ws, &osvc);
111
112 if (ws == ui_ws_display) {
113 rc = display_open(osvc, &display);
114 if (rc != EOK)
115 return rc;
116
117 rc = ui_create_disp(display, &ui);
118 if (rc != EOK) {
119 display_close(display);
120 return rc;
121 }
122 } else if (ws == ui_ws_console) {
123 console = console_init(stdin, stdout);
124 if (console == NULL)
125 return EIO;
126
127 /* ws == ui_ws_console */
128 rc = ui_create_cons(console, &ui);
129 if (rc != EOK) {
130 console_done(console);
131 return rc;
132 }
133 } else {
134 return EINVAL;
135 }
136
137 ui->myoutput = true;
138 *rui = ui;
139 return EOK;
140}
141
142/** Create new user interface using console service.
143 *
144 * @param rui Place to store pointer to new UI
145 * @return EOK on success or an error code
146 */
147errno_t ui_create_cons(console_ctrl_t *console, ui_t **rui)
148{
149 ui_t *ui;
150
151 ui = calloc(1, sizeof(ui_t));
152 if (ui == NULL)
153 return ENOMEM;
154
155 ui->console = console;
156 *rui = ui;
157 return EOK;
158}
159
160/** Create new user interface using display service.
161 *
162 * @param disp Display
163 * @param rui Place to store pointer to new UI
164 * @return EOK on success or an error code
165 */
166errno_t ui_create_disp(display_t *disp, ui_t **rui)
167{
168 ui_t *ui;
169
170 ui = calloc(1, sizeof(ui_t));
171 if (ui == NULL)
172 return ENOMEM;
173
174 ui->display = disp;
175 *rui = ui;
176 return EOK;
177}
178
179/** Destroy user interface.
180 *
181 * @param ui User interface or @c NULL
182 */
183void ui_destroy(ui_t *ui)
184{
185 if (ui == NULL)
186 return;
187
188 if (ui->myoutput) {
189 if (ui->console != NULL)
190 console_done(ui->console);
191 if (ui->display != NULL)
192 display_close(ui->display);
193 }
194
195 free(ui);
196}
197
198static void ui_cons_event_process(ui_t *ui, cons_event_t *event)
199{
200 if (ui->root_wnd == NULL)
201 return;
202
203 switch (event->type) {
204 case CEV_KEY:
205 ui_window_send_kbd(ui->root_wnd, &event->ev.key);
206 break;
207 case CEV_POS:
208 ui_wdecor_pos_event(ui->root_wnd->wdecor, &event->ev.pos);
209 ui_window_send_pos(ui->root_wnd, &event->ev.pos);
210 break;
211 }
212}
213
214/** Execute user interface.
215 *
216 * Return task exit code of zero and block unitl the application starts
217 * the termination process by calling ui_quit(@a ui).
218 *
219 * @param ui User interface
220 */
221void ui_run(ui_t *ui)
222{
223 cons_event_t event;
224 usec_t timeout;
225 errno_t rc;
226
227 /* Only return command prompt if we are running in a separate window */
228 if (ui->display != NULL)
229 task_retval(0);
230
231 while (!ui->quit) {
232 if (ui->console != NULL) {
233 timeout = 100000;
234 rc = console_get_event_timeout(ui->console,
235 &event, &timeout);
236
237 /* Do we actually have an event? */
238 if (rc == EOK) {
239 ui_cons_event_process(ui, &event);
240 } else if (rc != ETIMEOUT) {
241 /* Error, quit */
242 break;
243 }
244 } else {
245 fibril_usleep(100000);
246 }
247 }
248}
249
250/** Terminate user interface.
251 *
252 * Calling this function causes the user interface to terminate
253 * (i.e. exit from ui_run()). This would be typically called from
254 * an event handler.
255 *
256 * @param ui User interface
257 */
258void ui_quit(ui_t *ui)
259{
260 ui->quit = true;
261}
262
263/** Determine if we are running in text mode.
264 *
265 * @param ui User interface
266 * @return @c true iff we are running in text mode
267 */
268bool ui_is_textmode(ui_t *ui)
269{
270 /*
271 * XXX Currently console is always text and display is always
272 * graphics, but this need not always be true.
273 */
274 return (ui->console != NULL);
275}
276
277/** @}
278 */
Note: See TracBrowser for help on using the repository browser.