Subversion Repositories HelenOS

Rev

Rev 4326 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
3582 rimsky 1
/*
2
 * Copyright (c) 2006 Ondrej Palkovsky
3
 * Copyright (c) 2008 Martin Decky
4
 * Copyright (c) 2008 Pavel Rimsky
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 *
11
 * - Redistributions of source code must retain the above copyright
12
 *   notice, this list of conditions and the following disclaimer.
13
 * - Redistributions in binary form must reproduce the above copyright
14
 *   notice, this list of conditions and the following disclaimer in the
15
 *   documentation and/or other materials provided with the distribution.
16
 * - The name of the author may not be used to endorse or promote products
17
 *   derived from this software without specific prior written permission.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
 */
30
 
31
/**
32
 * @defgroup serial Serial console
3618 rimsky 33
 * @brief    Serial console services (putc, puts, clear screen, cursor goto,...)
3582 rimsky 34
 * @{
35
 */ 
36
 
37
/** @file
38
 */
39
 
40
#include <stdio.h>
3716 svoboda 41
#include <ipc/ipc.h>
42
#include <async.h>
43
#include <ipc/fb.h>
44
#include <bool.h>
45
#include <errno.h>
4457 decky 46
#include <io/color.h>
47
#include <io/style.h>
48
#include <string.h>
3582 rimsky 49
 
4141 svoboda 50
#include "../console/screenbuffer.h"
51
#include "main.h"
3582 rimsky 52
#include "serial_console.h"
53
 
54
#define MAX_CONTROL 20
55
 
3769 svoboda 56
static void serial_sgr(const unsigned int mode);
4235 svoboda 57
void serial_putchar(wchar_t ch);
3769 svoboda 58
 
4141 svoboda 59
static int scr_width;
60
static int scr_height;
3769 svoboda 61
static bool color = true;	/** True if producing color output. */
4235 svoboda 62
static bool utf8 = false;	/** True if producing UTF8 output. */
3582 rimsky 63
static putc_function_t putc_function;
64
 
3716 svoboda 65
/* Allow only 1 connection */
66
static int client_connected = 0;
67
 
3769 svoboda 68
enum sgr_color_index {
69
	CI_BLACK	= 0,
70
	CI_RED		= 1,
71
	CI_GREEN	= 2,
72
	CI_BROWN	= 3,
73
	CI_BLUE		= 4,
74
	CI_MAGENTA	= 5,
75
	CI_CYAN		= 6,
76
	CI_WHITE	= 7,
77
};
78
 
79
enum sgr_command {
80
	SGR_RESET	= 0,
81
	SGR_BOLD	= 1,
82
	SGR_BLINK	= 5,
83
	SGR_REVERSE	= 7,
84
	SGR_NORMAL_INT	= 22,
85
	SGR_BLINK_OFF	= 25,
86
	SGR_REVERSE_OFF = 27,
87
	SGR_FGCOLOR	= 30,
88
	SGR_BGCOLOR	= 40
89
};
90
 
91
static int color_map[] = {
92
	[COLOR_BLACK]	= CI_BLACK,
93
	[COLOR_BLUE]	= CI_RED,
94
	[COLOR_GREEN]	= CI_GREEN,
95
	[COLOR_CYAN]	= CI_CYAN,
96
	[COLOR_RED]	= CI_RED,
97
	[COLOR_MAGENTA] = CI_MAGENTA,
98
	[COLOR_YELLOW]	= CI_BROWN,
99
	[COLOR_WHITE]	= CI_WHITE
100
};
101
 
3582 rimsky 102
void serial_puts(char *str)
103
{
104
	while (*str)
105
		putc_function(*(str++));
106
}
107
 
4211 svoboda 108
void serial_putchar(wchar_t ch)
109
{
4235 svoboda 110
	uint8_t buf[STR_BOUNDS(1)];
111
	size_t offs;
112
	size_t i;
113
 
114
	if (utf8 != true) {
115
		if (ch >= 0 && ch < 128)
116
			(*putc_function)((uint8_t) ch);
117
		else 
118
			(*putc_function)('?');
119
		return;
120
	}
121
 
122
	offs = 0;
123
	if (chr_encode(ch, buf, &offs, STR_BOUNDS(1)) == EOK) {
124
		for (i = 0; i < offs; i++)
125
			(*putc_function)(buf[i]);
126
	} else {
127
		(*putc_function)('?');
128
	}
129
 
4211 svoboda 130
}
131
 
4457 decky 132
void serial_goto(const unsigned int col, const unsigned int row)
3582 rimsky 133
{
4457 decky 134
	if ((col > scr_width) || (row > scr_height))
3582 rimsky 135
		return;
136
 
3769 svoboda 137
	char control[MAX_CONTROL];
138
	snprintf(control, MAX_CONTROL, "\033[%u;%uf", row + 1, col + 1);
3582 rimsky 139
	serial_puts(control);
140
}
141
 
142
void serial_clrscr(void)
143
{
3769 svoboda 144
	/* Initialize graphic rendition attributes. */
145
	serial_sgr(SGR_RESET);
146
	if (color) {
147
		serial_sgr(SGR_FGCOLOR + CI_BLACK);
148
		serial_sgr(SGR_BGCOLOR + CI_WHITE);
149
	}
150
 
3582 rimsky 151
	serial_puts("\033[2J");
152
}
153
 
154
void serial_scroll(int i)
155
{
156
	if (i > 0) {
4457 decky 157
		serial_goto(0, scr_height - 1);
3582 rimsky 158
		while (i--)
159
			serial_puts("\033D");
160
	} else if (i < 0) {
161
		serial_goto(0, 0);
162
		while (i++)
163
			serial_puts("\033M");
164
	}
165
}
166
 
3767 svoboda 167
/** ECMA-48 Set Graphics Rendition. */
168
static void serial_sgr(const unsigned int mode)
3582 rimsky 169
{
170
	char control[MAX_CONTROL];
171
	snprintf(control, MAX_CONTROL, "\033[%um", mode);
172
	serial_puts(control);
173
}
174
 
3716 svoboda 175
/** Set scrolling region. */
176
void serial_set_scroll_region(unsigned last_row)
177
{
178
	char control[MAX_CONTROL];
179
	snprintf(control, MAX_CONTROL, "\033[0;%ur", last_row);
180
	serial_puts(control);
181
}
182
 
3582 rimsky 183
void serial_cursor_disable(void)
184
{
185
	serial_puts("\033[?25l");
186
}
187
 
188
void serial_cursor_enable(void)
189
{
190
	serial_puts("\033[?25h");
191
}
192
 
193
void serial_console_init(putc_function_t putc_fn, uint32_t w, uint32_t h)
194
{
4141 svoboda 195
	scr_width = w;
196
	scr_height = h;
3582 rimsky 197
	putc_function = putc_fn;
198
}
199
 
4141 svoboda 200
static void serial_set_style(int style)
201
{
202
	if (style == STYLE_EMPHASIS) {
203
		if (color) {
204
			serial_sgr(SGR_RESET);
205
			serial_sgr(SGR_FGCOLOR + CI_RED);
206
			serial_sgr(SGR_BGCOLOR + CI_WHITE);
207
		}
208
		serial_sgr(SGR_BOLD);
209
	} else {
210
		if (color) {
211
			serial_sgr(SGR_RESET);
212
			serial_sgr(SGR_FGCOLOR + CI_BLACK);
213
			serial_sgr(SGR_BGCOLOR + CI_WHITE);
214
		}
215
		serial_sgr(SGR_NORMAL_INT);
216
	}
217
}
218
 
219
static void serial_set_idx(unsigned fgcolor, unsigned bgcolor,
220
    unsigned flags)
221
{
222
	if (color) {
223
		serial_sgr(SGR_RESET);
224
		serial_sgr(SGR_FGCOLOR + color_map[fgcolor]);
225
		serial_sgr(SGR_BGCOLOR + color_map[bgcolor]);
226
	} else {
227
		if (fgcolor < bgcolor)
228
			serial_sgr(SGR_RESET);
229
		else
230
			serial_sgr(SGR_REVERSE);
231
	}	
232
}
233
 
234
static void serial_set_rgb(uint32_t fgcolor, uint32_t bgcolor)
235
{
236
	if (fgcolor < bgcolor)
237
		serial_sgr(SGR_REVERSE_OFF);
238
	else
4457 decky 239
		serial_sgr(SGR_REVERSE);
4141 svoboda 240
}
241
 
242
static void serial_set_attrs(const attrs_t *a)
243
{
244
	switch (a->t) {
4457 decky 245
	case at_style:
246
		serial_set_style(a->a.s.style);
247
		break;
248
	case at_rgb:
249
		serial_set_rgb(a->a.r.fg_color, a->a.r.bg_color);
250
		break;
251
	case at_idx:
252
		serial_set_idx(a->a.i.fg_color,
253
		    a->a.i.bg_color, a->a.i.flags);
254
		break;
255
	default:
256
		break;
4141 svoboda 257
	}
258
}
259
 
4167 svoboda 260
/** Draw text data to viewport.
261
 *
262
 * @param vport Viewport id
263
 * @param data  Text data.
264
 * @param x	Leftmost column of the area.
265
 * @param y	Topmost row of the area.
266
 * @param w	Number of rows.
267
 * @param h	Number of columns.
268
 */
269
static void draw_text_data(keyfield_t *data, unsigned int x,
270
    unsigned int y, unsigned int w, unsigned int h)
4141 svoboda 271
{
4167 svoboda 272
	unsigned int i, j;
273
	keyfield_t *field;
4141 svoboda 274
	attrs_t *a0, *a1;
275
 
4457 decky 276
	serial_goto(x, y);
4141 svoboda 277
	a0 = &data[0].attrs;
278
	serial_set_attrs(a0);
279
 
4167 svoboda 280
	for (j = 0; j < h; j++) {
281
		if (j > 0 && w != scr_width)
4457 decky 282
			serial_goto(x, j);
4167 svoboda 283
 
284
		for (i = 0; i < w; i++) {
285
			field = &data[j * w + i];
286
 
287
			a1 = &field->attrs;
4141 svoboda 288
			if (!attrs_same(*a0, *a1))
289
				serial_set_attrs(a1);
4211 svoboda 290
			serial_putchar(field->character);
4141 svoboda 291
			a0 = a1;
292
		}
293
	}
294
}
295
 
4164 svoboda 296
int lastcol = 0;
297
int lastrow = 0;
298
 
3716 svoboda 299
/**
300
 * Main function of the thread serving client connections.
301
 */
302
void serial_client_connection(ipc_callid_t iid, ipc_call_t *icall)
303
{
304
	int retval;
305
	ipc_callid_t callid;
306
	ipc_call_t call;
4141 svoboda 307
	keyfield_t *interbuf = NULL;
308
	size_t intersize = 0;
309
 
4211 svoboda 310
	wchar_t c;
4167 svoboda 311
	int col, row, w, h;
3716 svoboda 312
	int i;
4141 svoboda 313
 
4325 svoboda 314
	attrs_t cur_attr;
3716 svoboda 315
 
316
	if (client_connected) {
317
		ipc_answer_0(iid, ELIMIT);
318
		return;
319
	}
320
 
321
	client_connected = 1;
322
	ipc_answer_0(iid, EOK);
4325 svoboda 323
 
324
	cur_attr.t = at_style;
325
	cur_attr.a.s.style = STYLE_NORMAL;
3716 svoboda 326
 
327
	/* Clear the terminal, set scrolling region
328
	   to 0 - height rows. */
329
	serial_clrscr();
330
	serial_goto(0, 0);
4141 svoboda 331
	serial_set_scroll_region(scr_height);
3716 svoboda 332
 
333
	while (true) {
334
		callid = async_get_call(&call);
335
		switch (IPC_GET_METHOD(call)) {
336
		case IPC_M_PHONE_HUNGUP:
337
			client_connected = 0;
338
			ipc_answer_0(callid, EOK);
339
			return;
4141 svoboda 340
		case IPC_M_SHARE_OUT:
341
			/* We accept one area for data interchange */
342
			intersize = IPC_GET_ARG2(call);
343
			if (intersize >= scr_width * scr_height *
344
			    sizeof(*interbuf)) {
345
				receive_comm_area(callid, &call,
346
				    (void *) &interbuf);
347
				continue;
348
			}
349
			retval = EINVAL;
350
			break;
351
		case FB_DRAW_TEXT_DATA:
4167 svoboda 352
			col = IPC_GET_ARG1(call);
353
			row = IPC_GET_ARG2(call);
354
			w = IPC_GET_ARG3(call);
355
			h = IPC_GET_ARG4(call);
4141 svoboda 356
			if (!interbuf) {
357
				retval = EINVAL;
358
				break;
359
			}
4167 svoboda 360
			if (col + w > scr_width || row + h > scr_height) {
361
				retval = EINVAL;
362
				break;
363
			}
364
			draw_text_data(interbuf, col, row, w, h);
4457 decky 365
			lastcol = col + w;
4167 svoboda 366
			lastrow = row + h - 1;
4141 svoboda 367
			retval = 0;
368
			break;
3716 svoboda 369
		case FB_PUTCHAR:
370
			c = IPC_GET_ARG1(call);
4457 decky 371
			col = IPC_GET_ARG2(call);
372
			row = IPC_GET_ARG3(call);
4167 svoboda 373
			if ((lastcol != col) || (lastrow != row))
4457 decky 374
				serial_goto(col, row);
4167 svoboda 375
			lastcol = col + 1;
376
			lastrow = row;
4211 svoboda 377
			serial_putchar(c);
3716 svoboda 378
			retval = 0;
379
			break;
380
		case FB_CURSOR_GOTO:
4457 decky 381
			col = IPC_GET_ARG1(call);
382
			row = IPC_GET_ARG2(call);
383
			serial_goto(col, row);
384
			lastcol = col;
4167 svoboda 385
			lastrow = row;
3716 svoboda 386
			retval = 0;
387
			break;
388
		case FB_GET_CSIZE:
4457 decky 389
			ipc_answer_2(callid, EOK, scr_width, scr_height);
3716 svoboda 390
			continue;
391
		case FB_CLEAR:
392
			serial_clrscr();
393
			retval = 0;
394
			break;
395
		case FB_SET_STYLE:
4325 svoboda 396
			cur_attr.t = at_style;
397
			cur_attr.a.s.style = IPC_GET_ARG1(call);
398
			cur_attr.a.i.bg_color = IPC_GET_ARG2(call);
399
			serial_set_attrs(&cur_attr);
400
 
3767 svoboda 401
			retval = 0;
402
			break;
403
		case FB_SET_COLOR:
4325 svoboda 404
			cur_attr.t = at_idx;
405
			cur_attr.a.i.fg_color = IPC_GET_ARG1(call);
406
			cur_attr.a.i.bg_color = IPC_GET_ARG2(call);
407
			cur_attr.a.i.flags = IPC_GET_ARG3(call);
408
			serial_set_attrs(&cur_attr);
3769 svoboda 409
 
3716 svoboda 410
			retval = 0;
411
			break;
3767 svoboda 412
		case FB_SET_RGB_COLOR:
4325 svoboda 413
			cur_attr.t = at_rgb;
414
			cur_attr.a.i.fg_color = IPC_GET_ARG1(call);
415
			cur_attr.a.i.bg_color = IPC_GET_ARG2(call);
416
			serial_set_attrs(&cur_attr);
4141 svoboda 417
 
3767 svoboda 418
			retval = 0;
419
			break;
3716 svoboda 420
		case FB_SCROLL:
421
			i = IPC_GET_ARG1(call);
4141 svoboda 422
			if ((i > scr_height) || (i < -scr_height)) {
3716 svoboda 423
				retval = EINVAL;
424
				break;
425
			}
426
			serial_scroll(i);
4457 decky 427
			serial_goto(lastcol, lastrow);
3716 svoboda 428
			retval = 0;
429
			break;
430
		case FB_CURSOR_VISIBILITY:
431
			if(IPC_GET_ARG1(call))
432
				serial_cursor_enable();
433
			else
434
				serial_cursor_disable();
435
			retval = 0;
436
			break;
4326 svoboda 437
		case FB_SCREEN_YIELD:
4325 svoboda 438
			serial_sgr(SGR_RESET);
439
			serial_puts("\033[2J");
440
			serial_goto(0, 0);
441
			serial_cursor_enable();
442
			retval = 0;
443
			break;
4326 svoboda 444
		case FB_SCREEN_RECLAIM:
445
			serial_clrscr();
446
			serial_set_attrs(&cur_attr);
447
			retval = 0;
448
			break;
3716 svoboda 449
		default:
450
			retval = ENOENT;
451
		}
452
		ipc_answer_0(callid, retval);
453
	}
454
}
455
 
4457 decky 456
/**
3582 rimsky 457
 * @}
3618 rimsky 458
 */