/[gxemul]/upstream/0.3.2/src/console.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /upstream/0.3.2/src/console.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 5 - (show annotations)
Mon Oct 8 16:18:06 2007 UTC (16 years, 7 months ago) by dpavlin
File MIME type: text/plain
File size: 18066 byte(s)
0.3.2
1 /*
2 * Copyright (C) 2003-2005 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * $Id: console.c,v 1.6 2005/03/05 12:17:53 debug Exp $
29 *
30 * Generic console support functions.
31 *
32 * This is used by individual device drivers, for example serial controllers,
33 * to attach stdin/stdout of the host system to a specific device.
34 *
35 * NOTE: This stuff is non-reentrant.
36 */
37
38 #include <errno.h>
39 #include <signal.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <termios.h>
44 #include <unistd.h>
45 #include <sys/time.h>
46 #include <sys/types.h>
47
48 #include "console.h"
49 #include "emul.h"
50 #include "machine.h"
51 #include "memory.h"
52 #include "misc.h"
53
54
55 extern char *progname;
56
57
58 static struct termios console_oldtermios;
59 static struct termios console_curtermios;
60
61 /* For 'slave' mode: */
62 static struct termios console_slave_tios;
63 static int console_slave_outputd;
64
65 static int console_initialized = 0;
66 static int console_stdout_pending;
67
68 #define CONSOLE_FIFO_LEN 4096
69
70 /* Mouse coordinates: */
71 static int console_framebuffer_mouse_x; /* absolute x, 0-based */
72 static int console_framebuffer_mouse_y; /* absolute y, 0-based */
73 static int console_framebuffer_mouse_fb_nr; /* fb_number of last
74 framebuffer cursor update */
75
76 static int console_mouse_x; /* absolute x, 0-based */
77 static int console_mouse_y; /* absolute y, 0-based */
78 static int console_mouse_fb_nr; /* framebuffer number of
79 host movement, 0-based */
80 static int console_mouse_buttons; /* left=4, middle=2, right=1 */
81
82 static int allow_slaves = 0;
83
84 struct console_handle {
85 int in_use;
86 int using_xterm;
87 int inputonly;
88
89 char *name;
90
91 int w_descriptor;
92 int r_descriptor;
93
94 unsigned char fifo[CONSOLE_FIFO_LEN];
95 int fifo_head;
96 int fifo_tail;
97 };
98
99 #define NOT_USING_XTERM 0
100 #define USING_XTERM_BUT_NOT_YET_OPEN 1
101 #define USING_XTERM 2
102
103 /* A simple array of console_handles */
104 static struct console_handle *console_handles = NULL;
105 static int n_console_handles = 0;
106
107
108 /*
109 * console_deinit():
110 *
111 * Restore host's console settings.
112 */
113 void console_deinit(void)
114 {
115 if (!console_initialized)
116 return;
117
118 tcsetattr(STDIN_FILENO, TCSANOW, &console_oldtermios);
119
120 console_initialized = 0;
121 }
122
123
124 /*
125 * console_sigcont():
126 *
127 * If the user presses CTRL-Z (to stop the emulator process) and then
128 * continues, we have to make sure that the right termios settings are
129 * active. (This should be set as the SIGCONT signal handler in src/emul.c.)
130 */
131 void console_sigcont(int x)
132 {
133 if (!console_initialized)
134 return;
135
136 /* Make sure our 'current' termios setting is active: */
137 tcsetattr(STDIN_FILENO, TCSANOW, &console_curtermios);
138
139 /* Reset the signal handler: */
140 signal(SIGCONT, console_sigcont);
141 }
142
143
144 /*
145 * start_xterm():
146 *
147 * When using X11, this routine tries to start up an xterm, with another
148 * copy of gxemul inside. The other gxemul copy is given arguments
149 * that will cause it to run console_slave().
150 */
151 static void start_xterm(int handle)
152 {
153 int filedes[2];
154 int filedesB[2];
155 int res;
156 char **a;
157 pid_t p;
158
159 res = pipe(filedes);
160 if (res) {
161 printf("[ start_xterm(): pipe(): %i ]\n", errno);
162 exit(1);
163 }
164
165 res = pipe(filedesB);
166 if (res) {
167 printf("[ start_xterm(): pipe(): %i ]\n", errno);
168 exit(1);
169 }
170
171 /* printf("filedes = %i,%i\n", filedes[0], filedes[1]); */
172 /* printf("filedesB = %i,%i\n", filedesB[0], filedesB[1]); */
173
174 /* NOTE/warning: Hardcoded max nr of args! */
175 a = malloc(sizeof(char *) * 20);
176 if (a == NULL) {
177 fprintf(stderr, "console_start_slave(): out of memory\n");
178 exit(1);
179 }
180
181 a[0] = getenv("XTERM");
182 if (a[0] == NULL)
183 a[0] = "xterm";
184 a[1] = "-title";
185 a[2] = console_handles[handle].name;
186 a[3] = "-e";
187 a[4] = progname;
188 a[5] = malloc(50);
189 sprintf(a[5], "-WW@S%i,%i", filedes[0], filedesB[1]);
190 a[6] = NULL;
191
192 p = fork();
193 if (p == -1) {
194 printf("[ console_start_slave(): ERROR while trying to "
195 "fork(): %i ]\n", errno);
196 exit(1);
197 } else if (p == 0) {
198 close(filedes[1]);
199 close(filedesB[0]);
200
201 p = setsid();
202 if (p < 0)
203 printf("[ console_start_slave(): ERROR while trying "
204 "to do a setsid(): %i ]\n", errno);
205
206 res = execvp(a[0], a);
207 printf("[ console_start_slave(): ERROR while trying to "
208 "execvp(\"");
209 while (a[0] != NULL) {
210 printf("%s", a[0]);
211 if (a[1] != NULL)
212 printf(" ");
213 a++;
214 }
215 printf("\"): %i ]\n", errno);
216 exit(1);
217 }
218
219 /* TODO: free a and a[*] */
220
221 close(filedes[0]);
222 close(filedesB[1]);
223
224 console_handles[handle].using_xterm = USING_XTERM;
225
226 /*
227 * write to filedes[1], read from filedesB[0]
228 */
229
230 console_handles[handle].w_descriptor = filedes[1];
231 console_handles[handle].r_descriptor = filedesB[0];
232 }
233
234
235 /*
236 * d_avail():
237 *
238 * Returns 1 if anything is available on a descriptor.
239 */
240 static int d_avail(int d)
241 {
242 fd_set rfds;
243 struct timeval tv;
244
245 FD_ZERO(&rfds);
246 FD_SET(d, &rfds);
247 tv.tv_sec = 0;
248 tv.tv_usec = 0;
249 return select(d+1, &rfds, NULL, NULL, &tv);
250 }
251
252
253 /*
254 * console_makeavail():
255 *
256 * Put a character in the queue, so that it will be avaiable,
257 * by inserting it into the char fifo.
258 */
259 void console_makeavail(int handle, char ch)
260 {
261 console_handles[handle].fifo[
262 console_handles[handle].fifo_head] = ch;
263 console_handles[handle].fifo_head = (
264 console_handles[handle].fifo_head + 1) % CONSOLE_FIFO_LEN;
265
266 if (console_handles[handle].fifo_head ==
267 console_handles[handle].fifo_tail)
268 fatal("[ WARNING: console fifo overrun, handle %i ]\n", handle);
269 }
270
271
272 /*
273 * console_stdin_avail():
274 *
275 * Returns 1 if a char is available from a handle's read descriptor,
276 * 0 otherwise.
277 */
278 static int console_stdin_avail(int handle)
279 {
280 if (!allow_slaves)
281 return d_avail(STDIN_FILENO);
282
283 if (console_handles[handle].using_xterm ==
284 USING_XTERM_BUT_NOT_YET_OPEN)
285 return 0;
286
287 return d_avail(console_handles[handle].r_descriptor);
288 }
289
290
291 /*
292 * console_charavail():
293 *
294 * Returns 1 if a char is available in the fifo, 0 otherwise.
295 */
296 int console_charavail(int handle)
297 {
298 while (console_stdin_avail(handle)) {
299 unsigned char ch[100]; /* = getchar(); */
300 ssize_t len;
301 int i, d;
302
303 if (!allow_slaves)
304 d = STDIN_FILENO;
305 else
306 d = console_handles[handle].r_descriptor;
307
308 len = read(d, ch, sizeof(ch));
309
310 for (i=0; i<len; i++) {
311 /* printf("[ %i: %i ]\n", i, ch[i]); */
312
313 if (!allow_slaves) {
314 /* Ugly hack: convert ctrl-b into ctrl-c.
315 (TODO: fix) */
316 if (ch[i] == 2)
317 ch[i] = 3;
318 }
319
320 console_makeavail(handle, ch[i]);
321 }
322 }
323
324 if (console_handles[handle].fifo_head ==
325 console_handles[handle].fifo_tail)
326 return 0;
327
328 return 1;
329 }
330
331
332 /*
333 * console_readchar():
334 *
335 * Returns 0..255 if a char was available, -1 otherwise.
336 */
337 int console_readchar(int handle)
338 {
339 int ch;
340
341 if (!console_charavail(handle))
342 return -1;
343
344 ch = console_handles[handle].fifo[console_handles[handle].fifo_tail];
345 console_handles[handle].fifo_tail ++;
346 console_handles[handle].fifo_tail %= CONSOLE_FIFO_LEN;
347
348 return ch;
349 }
350
351
352 /*
353 * console_putchar():
354 *
355 * Prints a char to stdout, and sets the console_stdout_pending flag.
356 */
357 void console_putchar(int handle, int ch)
358 {
359 char buf[1];
360
361 if (!allow_slaves) {
362 /* stdout: */
363 putchar(ch);
364
365 /* Assume flushes by OS or libc on newlines: */
366 if (ch == '\n')
367 console_stdout_pending = 0;
368 else
369 console_stdout_pending = 1;
370
371 return;
372 }
373
374 if (!console_handles[handle].in_use) {
375 printf("[ console_putchar(): handle %i not in"
376 " use! ]\n", handle);
377 return;
378 }
379
380 if (console_handles[handle].using_xterm ==
381 USING_XTERM_BUT_NOT_YET_OPEN)
382 start_xterm(handle);
383
384 buf[0] = ch;
385 write(console_handles[handle].w_descriptor, buf, 1);
386 }
387
388
389 /*
390 * console_flush():
391 *
392 * Flushes stdout, if necessary, and resets console_stdout_pending to zero.
393 */
394 void console_flush(void)
395 {
396 if (console_stdout_pending)
397 fflush(stdout);
398
399 console_stdout_pending = 0;
400 }
401
402
403 /*
404 * console_mouse_coordinates():
405 *
406 * Sets mouse coordinates. Called by for example an X11 event handler.
407 * x and y are absolute coordinates, fb_nr is where the mouse movement
408 * took place.
409 */
410 void console_mouse_coordinates(int x, int y, int fb_nr)
411 {
412 /* TODO: fb_nr isn't used yet. */
413
414 console_mouse_x = x;
415 console_mouse_y = y;
416 console_mouse_fb_nr = fb_nr;
417 }
418
419
420 /*
421 * console_mouse_button():
422 *
423 * Sets a mouse button to be pressed or released. Called by for example an
424 * X11 event handler. button is 1 (left), 2 (middle), or 3 (right), and
425 * pressed = 1 for pressed, 0 for not pressed.
426 */
427 void console_mouse_button(int button, int pressed)
428 {
429 int mask = 1 << (3-button);
430
431 if (pressed)
432 console_mouse_buttons |= mask;
433 else
434 console_mouse_buttons &= ~mask;
435 }
436
437
438 /*
439 * console_get_framebuffer_mouse():
440 *
441 * TODO: Comment
442 */
443 void console_get_framebuffer_mouse(int *x, int *y, int *fb_nr)
444 {
445 *x = console_framebuffer_mouse_x;
446 *y = console_framebuffer_mouse_y;
447 *fb_nr = console_framebuffer_mouse_fb_nr;
448 }
449
450
451 /*
452 * console_set_framebuffer_mouse():
453 *
454 * A framebuffer device calls this function when it sets the
455 * position of a cursor (ie a mouse cursor).
456 */
457 void console_set_framebuffer_mouse(int x, int y, int fb_nr)
458 {
459 console_framebuffer_mouse_x = x;
460 console_framebuffer_mouse_y = y;
461 console_framebuffer_mouse_fb_nr = fb_nr;
462 }
463
464
465 /*
466 * console_getmouse():
467 *
468 * Puts current mouse data into the variables pointed to by
469 * the arguments.
470 */
471 void console_getmouse(int *x, int *y, int *buttons, int *fb_nr)
472 {
473 *x = console_mouse_x;
474 *y = console_mouse_y;
475 *buttons = console_mouse_buttons;
476 *fb_nr = console_mouse_fb_nr;
477 }
478
479
480 /*
481 * console_slave_sigint():
482 */
483 static void console_slave_sigint(int x)
484 {
485 char buf[1];
486
487 /* Send a ctrl-c: */
488 buf[0] = 3;
489 write(console_slave_outputd, buf, sizeof(buf));
490
491 /* Reset the signal handler: */
492 signal(SIGINT, console_slave_sigint);
493 }
494
495
496 /*
497 * console_slave_sigcont():
498 *
499 * See comment for console_sigcont. This is for used by console_slave().
500 */
501 static void console_slave_sigcont(int x)
502 {
503 /* Make sure our 'current' termios setting is active: */
504 tcsetattr(STDIN_FILENO, TCSANOW, &console_slave_tios);
505
506 /* Reset the signal handler: */
507 signal(SIGCONT, console_slave_sigcont);
508 }
509
510
511 /*
512 * console_slave():
513 *
514 * This function is used when running with X11, and gxemul opens up
515 * separate xterms for each emulated terminal or serial port.
516 */
517 void console_slave(char *arg)
518 {
519 int inputd;
520 int len;
521 char *p;
522 char buf[400];
523
524 /* arg = '3,6' or similar, input and output descriptors */
525 /* printf("console_slave(): arg = '%s'\n", arg); */
526
527 inputd = atoi(arg);
528 p = strchr(arg, ',');
529 if (p == NULL) {
530 printf("console_slave(): bad arg '%s'\n", arg);
531 sleep(5);
532 exit(1);
533 }
534 console_slave_outputd = atoi(p+1);
535
536 /* Set the terminal to raw mode: */
537 tcgetattr(STDIN_FILENO, &console_slave_tios);
538
539 console_slave_tios.c_lflag &= ~ICANON;
540 console_slave_tios.c_cc[VTIME] = 0;
541 console_slave_tios.c_cc[VMIN] = 1;
542 console_slave_tios.c_lflag &= ~ECHO;
543 console_slave_tios.c_iflag &= ~ICRNL;
544 tcsetattr(STDIN_FILENO, TCSANOW, &console_slave_tios);
545
546 signal(SIGINT, console_slave_sigint);
547 signal(SIGCONT, console_slave_sigcont);
548
549 for (;;) {
550 /* TODO: select() on both inputd and stdin */
551
552 if (d_avail(inputd)) {
553 len = read(inputd, buf, sizeof(buf) - 1);
554 if (len < 1)
555 exit(0);
556 buf[len] = '\0';
557 printf("%s", buf);
558 fflush(stdout);
559 }
560
561 if (d_avail(STDIN_FILENO)) {
562 len = read(STDIN_FILENO, buf, sizeof(buf));
563 if (len < 1)
564 exit(0);
565 write(console_slave_outputd, buf, len);
566 }
567
568 usleep(100);
569 }
570 }
571
572
573 /*
574 * console_new_handle():
575 *
576 * Allocates a new console_handle struct, and returns a pointer to it.
577 *
578 * For internal use.
579 */
580 static struct console_handle *console_new_handle(char *name, int *handlep)
581 {
582 struct console_handle *chp;
583 int i, n, found_free = -1;
584
585 /* Reuse an old slot, if possible: */
586 n = n_console_handles;
587 for (i=0; i<n; i++)
588 if (!console_handles[i].in_use) {
589 found_free = i;
590 break;
591 }
592
593 if (found_free == -1) {
594 /* Let's realloc console_handles[], to make room
595 for the new one: */
596 console_handles = realloc(console_handles, sizeof(
597 struct console_handle) * (n_console_handles + 1));
598 if (console_handles == NULL) {
599 printf("console_new_handle(): out of memory\n");
600 exit(1);
601 }
602 found_free = n_console_handles;
603 n_console_handles ++;
604 }
605
606 chp = &console_handles[found_free];
607 memset(chp, 0, sizeof(struct console_handle));
608
609 chp->in_use = 1;
610 chp->name = strdup(name);
611 if (chp->name == NULL) {
612 printf("console_new_handle(): out of memory\n");
613 exit(1);
614 }
615
616 *handlep = found_free;
617 return chp;
618 }
619
620
621 /*
622 * console_start_slave():
623 *
624 * When using X11:
625 *
626 * This routine tries to start up an xterm, with another copy of gxemul
627 * inside. The other gxemul copy is given arguments that will cause it
628 * to run console_slave().
629 *
630 * When not using X11: Things will seem to work the same way without X11,
631 * but no xterm will actually be started.
632 *
633 * consolename should be something like "serial 0".
634 *
635 * On success, an integer >= 0 is returned. This can then be used as a
636 * 'handle' when writing to or reading from an emulated console.
637 *
638 * On failure, -1 is returned.
639 */
640 int console_start_slave(struct machine *machine, char *consolename)
641 {
642 int handle;
643 struct console_handle *chp;
644
645 if (machine == NULL || consolename == NULL) {
646 printf("console_start_slave(): NULL ptr\n");
647 exit(1);
648 }
649
650 chp = console_new_handle(consolename, &handle);
651
652 chp->name = malloc(strlen(machine->name) + strlen(consolename) + 100);
653 if (chp->name == NULL) {
654 printf("out of memory\n");
655 exit(1);
656 }
657 sprintf(chp->name, "GXemul: '%s' %s", machine->name, consolename);
658
659 #if 0
660 if (!machine->use_x11) {
661 return handle;
662 }
663 #endif
664 chp->using_xterm = USING_XTERM_BUT_NOT_YET_OPEN;
665
666 return handle;
667 }
668
669
670 /*
671 * console_start_slave_inputonly():
672 *
673 * Similar to console_start_slave(), but doesn't open an xterm. This is
674 * useful for devices such as keyboard controllers, that need to have an
675 * input queue, but no xterm window associated with it.
676 *
677 * On success, an integer >= 0 is returned. This can then be used as a
678 * 'handle' when writing to or reading from an emulated console.
679 *
680 * On failure, -1 is returned.
681 */
682 int console_start_slave_inputonly(struct machine *machine, char *consolename)
683 {
684 struct console_handle *chp;
685 int handle;
686
687 if (machine == NULL || consolename == NULL) {
688 printf("console_start_slave(): NULL ptr\n");
689 exit(1);
690 }
691
692 chp = console_new_handle(consolename, &handle);
693 chp->inputonly = 1;
694
695 chp->name = malloc(strlen(machine->name) + strlen(consolename) + 100);
696 if (chp->name == NULL) {
697 printf("out of memory\n");
698 exit(1);
699 }
700 sprintf(chp->name, "GXemul: '%s' %s", machine->name, consolename);
701
702 return handle;
703 }
704
705
706 /*
707 * console_init_main():
708 *
709 * Put host's console into single-character (non-canonical) mode.
710 */
711 void console_init_main(struct emul *emul)
712 {
713 int i, tra;
714
715 if (console_initialized)
716 return;
717
718 tcgetattr(STDIN_FILENO, &console_oldtermios);
719 memcpy(&console_curtermios, &console_oldtermios,
720 sizeof (struct termios));
721
722 console_curtermios.c_lflag &= ~ICANON;
723 console_curtermios.c_cc[VTIME] = 0;
724 console_curtermios.c_cc[VMIN] = 1;
725
726 console_curtermios.c_lflag &= ~ECHO;
727
728 /*
729 * Most guest OSes seem to work ok without ~ICRNL, but Linux on
730 * DECstation requires it to be usable. Unfortunately, clearing
731 * out ICRNL makes tracing with '-t ... |more' akward, as you
732 * might need to use CTRL-J instead of the enter key. Hence,
733 * this bit is only cleared if we're not tracing:
734 */
735 tra = 0;
736 for (i=0; i<emul->n_machines; i++)
737 if (emul->machines[i]->show_trace_tree ||
738 emul->machines[i]->instruction_trace ||
739 emul->machines[i]->register_dump)
740 tra = 1;
741 if (!tra)
742 console_curtermios.c_iflag &= ~ICRNL;
743
744 tcsetattr(STDIN_FILENO, TCSANOW, &console_curtermios);
745
746 console_stdout_pending = 1;
747 console_handles[MAIN_CONSOLE].fifo_head = 0;
748 console_handles[MAIN_CONSOLE].fifo_tail = 0;
749
750 console_mouse_x = 0;
751 console_mouse_y = 0;
752 console_mouse_buttons = 0;
753
754 console_initialized = 1;
755 }
756
757
758 /*
759 * console_allow_slaves():
760 *
761 * This function tells the console subsystem whether or not to open up
762 * slave xterms for each emulated serial controller.
763 */
764 void console_allow_slaves(int allow)
765 {
766 allow_slaves = allow;
767 }
768
769
770 /*
771 * console_init():
772 *
773 * This function should be called before any other console_*() function
774 * is used.
775 */
776 void console_init(void)
777 {
778 int handle;
779 struct console_handle *chp;
780
781 chp = console_new_handle("MAIN", &handle);
782 if (handle != MAIN_CONSOLE) {
783 printf("console_init(): fatal error: could not create"
784 " console 0: handle = %i\n", handle);
785 exit(1);
786 }
787 }
788

  ViewVC Help
Powered by ViewVC 1.1.26