source: mainline/uspace/srv/hid/output/output.c@ 36aec61e

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

Display device backend for output server

This provides console on a grahpical device, similar to the old kfb
backend. It talks to the userspace kfb driver, instead of mapping
the kfb directly. You can get graphical console by configuring
CONFIG_FB=y and CONFIG_WINSYS=n

  • Property mode set to 100644
File size: 11.2 KB
Line 
1/*
2 * Copyright (c) 2011 Martin Decky
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 output
30 * @{
31 */
32
33#include <errno.h>
34#include <stddef.h>
35#include <stdlib.h>
36#include <macros.h>
37#include <as.h>
38#include <task.h>
39#include <ipc/output.h>
40#include <config.h>
41#include "port/ega.h"
42#include "port/chardev.h"
43#include "port/ddev.h"
44#include "output.h"
45
46#define MAX_COLS 128
47#define MAX_ROWS 128
48
49typedef struct {
50 link_t link;
51
52 size_t size;
53 unsigned int flags;
54 void *data;
55} frontbuf_t;
56
57static LIST_INITIALIZE(outdevs);
58static LIST_INITIALIZE(frontbufs);
59
60outdev_t *outdev_register(outdev_ops_t *ops, void *data)
61{
62 assert(ops->get_dimensions);
63
64 outdev_t *dev = (outdev_t *) malloc(sizeof(outdev_t));
65 if (dev == NULL)
66 return NULL;
67
68 link_initialize(&dev->link);
69
70 dev->ops = *ops;
71 dev->data = data;
72
73 ops->get_dimensions(dev, &dev->cols, &dev->rows);
74 dev->backbuf = chargrid_create(dev->cols, dev->rows,
75 CHARGRID_FLAG_NONE);
76 if (dev->backbuf == NULL) {
77 free(dev);
78 return NULL;
79 }
80
81 list_append(&dev->link, &outdevs);
82 return dev;
83}
84
85static void srv_yield(ipc_call_t *icall)
86{
87 errno_t ret = EOK;
88
89 list_foreach(outdevs, link, outdev_t, dev) {
90 assert(dev->ops.yield);
91
92 errno_t rc = dev->ops.yield(dev);
93 if (rc != EOK)
94 ret = rc;
95 }
96
97 async_answer_0(icall, ret);
98}
99
100static void srv_claim(ipc_call_t *icall)
101{
102 errno_t ret = EOK;
103
104 list_foreach(outdevs, link, outdev_t, dev) {
105 assert(dev->ops.claim);
106
107 errno_t rc = dev->ops.claim(dev);
108 if (rc != EOK)
109 ret = rc;
110 }
111
112 async_answer_0(icall, ret);
113}
114
115static void srv_get_dimensions(ipc_call_t *icall)
116{
117 sysarg_t cols = MAX_COLS;
118 sysarg_t rows = MAX_ROWS;
119
120 list_foreach(outdevs, link, outdev_t, dev) {
121 cols = min(cols, dev->cols);
122 rows = min(rows, dev->rows);
123 }
124
125 async_answer_2(icall, EOK, cols, rows);
126}
127
128static void srv_get_caps(ipc_call_t *icall)
129{
130 console_caps_t caps = 0;
131
132 list_foreach(outdevs, link, outdev_t, dev) {
133 assert(dev->ops.get_caps);
134
135 caps |= dev->ops.get_caps(dev);
136 }
137
138 async_answer_1(icall, EOK, caps);
139}
140
141static frontbuf_t *resolve_frontbuf(sysarg_t handle, ipc_call_t *icall)
142{
143 frontbuf_t *frontbuf = NULL;
144 list_foreach(frontbufs, link, frontbuf_t, cur) {
145 if (cur == (frontbuf_t *) handle) {
146 frontbuf = cur;
147 break;
148 }
149 }
150
151 if (frontbuf == NULL) {
152 async_answer_0(icall, ENOENT);
153 return NULL;
154 }
155
156 return frontbuf;
157}
158
159static void srv_frontbuf_create(ipc_call_t *icall)
160{
161 frontbuf_t *frontbuf = (frontbuf_t *) malloc(sizeof(frontbuf_t));
162 if (frontbuf == NULL) {
163 async_answer_0(icall, ENOMEM);
164 return;
165 }
166
167 link_initialize(&frontbuf->link);
168
169 ipc_call_t call;
170 if (!async_share_out_receive(&call, &frontbuf->size,
171 &frontbuf->flags)) {
172 free(frontbuf);
173 async_answer_0(icall, EINVAL);
174 return;
175 }
176
177 errno_t rc = async_share_out_finalize(&call, &frontbuf->data);
178 if ((rc != EOK) || (frontbuf->data == AS_MAP_FAILED)) {
179 free(frontbuf);
180 async_answer_0(icall, ENOMEM);
181 return;
182 }
183
184 list_append(&frontbuf->link, &frontbufs);
185 async_answer_1(icall, EOK, (sysarg_t) frontbuf);
186}
187
188static void srv_frontbuf_destroy(ipc_call_t *icall)
189{
190 frontbuf_t *frontbuf = resolve_frontbuf(ipc_get_arg1(icall), icall);
191 if (frontbuf == NULL)
192 return;
193
194 list_remove(&frontbuf->link);
195 as_area_destroy(frontbuf->data);
196 free(frontbuf);
197
198 async_answer_0(icall, EOK);
199}
200
201static void srv_cursor_update(ipc_call_t *icall)
202{
203 frontbuf_t *frontbuf = resolve_frontbuf(ipc_get_arg1(icall), icall);
204 if (frontbuf == NULL)
205 return;
206
207 chargrid_t *buf = (chargrid_t *) frontbuf->data;
208 bool visible = chargrid_get_cursor_visibility(buf);
209
210 sysarg_t col;
211 sysarg_t row;
212 chargrid_get_cursor(buf, &col, &row);
213
214 list_foreach(outdevs, link, outdev_t, dev) {
215 assert(dev->ops.cursor_update);
216
217 sysarg_t prev_col;
218 sysarg_t prev_row;
219 chargrid_get_cursor(dev->backbuf, &prev_col, &prev_row);
220
221 chargrid_set_cursor(dev->backbuf, col, row);
222 chargrid_set_cursor_visibility(dev->backbuf, visible);
223
224 dev->ops.cursor_update(dev, prev_col, prev_row, col, row,
225 visible);
226 dev->ops.flush(dev);
227
228 }
229
230 async_answer_0(icall, EOK);
231}
232
233static void srv_set_style(ipc_call_t *icall)
234{
235 list_foreach(outdevs, link, outdev_t, dev) {
236 dev->attrs.type = CHAR_ATTR_STYLE;
237 dev->attrs.val.style =
238 (console_style_t) ipc_get_arg1(icall);
239 }
240
241 async_answer_0(icall, EOK);
242}
243
244static void srv_set_color(ipc_call_t *icall)
245{
246 list_foreach(outdevs, link, outdev_t, dev) {
247 dev->attrs.type = CHAR_ATTR_INDEX;
248 dev->attrs.val.index.bgcolor =
249 (console_color_t) ipc_get_arg1(icall);
250 dev->attrs.val.index.fgcolor =
251 (console_color_t) ipc_get_arg2(icall);
252 dev->attrs.val.index.attr =
253 (console_color_attr_t) ipc_get_arg3(icall);
254 }
255
256 async_answer_0(icall, EOK);
257}
258
259static void srv_set_rgb_color(ipc_call_t *icall)
260{
261 list_foreach(outdevs, link, outdev_t, dev) {
262 dev->attrs.type = CHAR_ATTR_RGB;
263 dev->attrs.val.rgb.bgcolor = ipc_get_arg1(icall);
264 dev->attrs.val.rgb.fgcolor = ipc_get_arg2(icall);
265 }
266
267 async_answer_0(icall, EOK);
268}
269
270static bool srv_update_scroll(outdev_t *dev, chargrid_t *buf)
271{
272 assert(dev->ops.char_update);
273
274 sysarg_t top_row = chargrid_get_top_row(buf);
275
276 if (dev->top_row == top_row)
277 return false;
278
279 dev->top_row = top_row;
280
281 for (sysarg_t y = 0; y < dev->rows; y++) {
282 for (sysarg_t x = 0; x < dev->cols; x++) {
283 charfield_t *front_field =
284 chargrid_charfield_at(buf, x, y);
285 charfield_t *back_field =
286 chargrid_charfield_at(dev->backbuf, x, y);
287 bool update = false;
288
289 if (front_field->ch != back_field->ch) {
290 back_field->ch = front_field->ch;
291 update = true;
292 }
293
294 if (!attrs_same(front_field->attrs, back_field->attrs)) {
295 back_field->attrs = front_field->attrs;
296 update = true;
297 }
298
299 front_field->flags &= ~CHAR_FLAG_DIRTY;
300
301 if (update)
302 dev->ops.char_update(dev, x, y);
303 }
304 }
305
306 return true;
307}
308
309static void srv_update(ipc_call_t *icall)
310{
311 frontbuf_t *frontbuf = resolve_frontbuf(ipc_get_arg1(icall), icall);
312 if (frontbuf == NULL)
313 return;
314
315 chargrid_t *buf = (chargrid_t *) frontbuf->data;
316
317 list_foreach(outdevs, link, outdev_t, dev) {
318 assert(dev->ops.char_update);
319
320 if (srv_update_scroll(dev, buf))
321 continue;
322
323 for (sysarg_t y = 0; y < dev->rows; y++) {
324 for (sysarg_t x = 0; x < dev->cols; x++) {
325 charfield_t *front_field =
326 chargrid_charfield_at(buf, x, y);
327 charfield_t *back_field =
328 chargrid_charfield_at(dev->backbuf, x, y);
329 bool update = false;
330
331 if ((front_field->flags & CHAR_FLAG_DIRTY) ==
332 CHAR_FLAG_DIRTY) {
333 if (front_field->ch != back_field->ch) {
334 back_field->ch = front_field->ch;
335 update = true;
336 }
337
338 if (!attrs_same(front_field->attrs,
339 back_field->attrs)) {
340 back_field->attrs = front_field->attrs;
341 update = true;
342 }
343
344 front_field->flags &= ~CHAR_FLAG_DIRTY;
345 }
346
347 if (update)
348 dev->ops.char_update(dev, x, y);
349 }
350 }
351
352 dev->ops.flush(dev);
353 }
354
355 async_answer_0(icall, EOK);
356}
357
358static void srv_damage(ipc_call_t *icall)
359{
360 frontbuf_t *frontbuf = resolve_frontbuf(ipc_get_arg1(icall), icall);
361 if (frontbuf == NULL)
362 return;
363
364 chargrid_t *buf = (chargrid_t *) frontbuf->data;
365
366 list_foreach(outdevs, link, outdev_t, dev) {
367 assert(dev->ops.char_update);
368
369 if (srv_update_scroll(dev, buf))
370 continue;
371
372 sysarg_t col = ipc_get_arg2(icall);
373 sysarg_t row = ipc_get_arg3(icall);
374
375 sysarg_t cols = ipc_get_arg4(icall);
376 sysarg_t rows = ipc_get_arg5(icall);
377
378 for (sysarg_t y = 0; y < rows; y++) {
379 for (sysarg_t x = 0; x < cols; x++) {
380 charfield_t *front_field =
381 chargrid_charfield_at(buf, col + x, row + y);
382 charfield_t *back_field =
383 chargrid_charfield_at(dev->backbuf, col + x, row + y);
384
385 back_field->ch = front_field->ch;
386 back_field->attrs = front_field->attrs;
387 front_field->flags &= ~CHAR_FLAG_DIRTY;
388 dev->ops.char_update(dev, col + x, row + y);
389 }
390 }
391 dev->ops.flush(dev);
392
393 }
394
395 async_answer_0(icall, EOK);
396}
397
398static void client_connection(ipc_call_t *icall, void *arg)
399{
400 /* Accept the connection */
401 async_accept_0(icall);
402
403 while (true) {
404 ipc_call_t call;
405 async_get_call(&call);
406
407 if (!ipc_get_imethod(&call)) {
408 async_answer_0(&call, EOK);
409 break;
410 }
411
412 switch (ipc_get_imethod(&call)) {
413 case OUTPUT_YIELD:
414 srv_yield(&call);
415 break;
416 case OUTPUT_CLAIM:
417 srv_claim(&call);
418 break;
419 case OUTPUT_GET_DIMENSIONS:
420 srv_get_dimensions(&call);
421 break;
422 case OUTPUT_GET_CAPS:
423 srv_get_caps(&call);
424 break;
425
426 case OUTPUT_FRONTBUF_CREATE:
427 srv_frontbuf_create(&call);
428 break;
429 case OUTPUT_FRONTBUF_DESTROY:
430 srv_frontbuf_destroy(&call);
431 break;
432
433 case OUTPUT_CURSOR_UPDATE:
434 srv_cursor_update(&call);
435 break;
436 case OUTPUT_SET_STYLE:
437 srv_set_style(&call);
438 break;
439 case OUTPUT_SET_COLOR:
440 srv_set_color(&call);
441 break;
442 case OUTPUT_SET_RGB_COLOR:
443 srv_set_rgb_color(&call);
444 break;
445 case OUTPUT_UPDATE:
446 srv_update(&call);
447 break;
448 case OUTPUT_DAMAGE:
449 srv_damage(&call);
450 break;
451
452 default:
453 async_answer_0(&call, EINVAL);
454 }
455 }
456}
457
458static void usage(char *name)
459{
460 printf("Usage: %s <service_name>\n", name);
461}
462
463int main(int argc, char *argv[])
464{
465 if (argc < 2) {
466 usage(argv[0]);
467 return 1;
468 }
469
470 printf("%s: HelenOS output service\n", NAME);
471
472 /* Register server */
473 async_set_fallback_port_handler(client_connection, NULL);
474 errno_t rc = loc_server_register(NAME);
475 if (rc != EOK) {
476 printf("%s: Unable to register driver\n", NAME);
477 return rc;
478 }
479
480 service_id_t service_id;
481 rc = loc_service_register(argv[1], &service_id);
482 if (rc != EOK) {
483 printf("%s: Unable to register service %s\n", NAME, argv[1]);
484 return rc;
485 }
486
487 if (!config_key_exists("console")) {
488 ega_init();
489#if defined(CONFIG_FB) && !defined(CONFIG_WINSYS)
490 /*
491 * NOTE: We don't have a good way of detecting the absence
492 * of a KFB display device at run time because of the
493 * asunchronous nature of device discovery
494 */
495 output_ddev_init();
496#endif
497 }
498
499 chardev_init();
500
501 printf("%s: Accepting connections\n", NAME);
502 task_retval(0);
503 async_manager();
504
505 /* Never reached */
506 return 0;
507}
508
509/** @}
510 */
Note: See TracBrowser for help on using the repository browser.