/[gxemul]/upstream/0.3.6.1/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.6.1/src/console.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.26