source: mainline/uspace/srv/hid/output/output.c@ bbc6277

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

Chardev output needs buffering.

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