Authors: Gavriel State <gavriels@corel.com>, Ulrich Czekalla <ulrichc@corel.com>
[wine] / console / xterm.c
1 /* xterm.c */
2
3 /* This "driver" is designed to go on top of an existing driver
4    to provide support for features only present if using an
5    xterm or compatible program for your console output. 
6    Currently, it supports resizing and separating debug messages from
7    program output.
8    It does not currently support changing the title bar.
9 */
10
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <signal.h>
14 #include <sys/ioctl.h>
15 #include <fcntl.h>
16 #include <unistd.h>
17 #include <termios.h>
18 #include <errno.h>
19
20 #include "console.h"
21 #include "options.h"
22 #include "debugtools.h"
23
24 DEFAULT_DEBUG_CHANNEL(console)
25
26 char console_xterm_prog[80];
27
28 static BOOL wine_create_console(FILE **master, FILE **slave, pid_t *pid);
29 int wine_openpty(int *master, int *slave, char *name,
30                  struct termios *term, struct winsize *winsize);
31
32 /* The console -- I chose to keep the master and slave
33  * (UNIX) file descriptors around in case they are needed for
34  * ioctls later.  The pid is needed to destroy the xterm on close
35  */
36 typedef struct _XTERM_CONSOLE {
37         FILE    *master;                 /* xterm side of pty */
38         FILE    *slave;                  /* wine side of pty */
39         pid_t    pid;                    /* xterm's pid, -1 if no xterm */
40 } XTERM_CONSOLE;
41
42 static XTERM_CONSOLE xterm_console;
43
44 CONSOLE_device chain;
45 FILE *old_in, *old_out;
46
47 void XTERM_Start(void)
48 {
49    /* Here, this is a supplementary driver so we should remember to call
50       the chain. */
51    chain.init = driver.init;
52    driver.init = XTERM_Init;
53
54    chain.close = driver.close;
55    driver.close = XTERM_Close;
56
57    chain.resizeScreen = driver.resizeScreen;
58    driver.resizeScreen = XTERM_ResizeScreen;
59
60    /* Read in driver configuration */
61    PROFILE_GetWineIniString("console", "XtermProg",
62       "xterm", console_xterm_prog, 79); 
63
64 }
65
66 void XTERM_Init()
67 {
68    wine_create_console(&xterm_console.master, &xterm_console.slave,
69       &xterm_console.pid);
70
71    old_in = driver.console_in;
72    driver.console_in = xterm_console.slave;
73
74    old_out = driver.console_out;
75    driver.console_out = xterm_console.slave;
76
77    /* Then call the chain... */
78    if (chain.init)
79       chain.init();
80 }
81
82 void XTERM_Close()
83 {
84    /* Call the chain first... */
85    if (chain.close)
86       chain.close();
87
88    driver.console_in = old_in;
89    driver.console_out = old_out;
90
91    /* make sure a xterm exists to kill */
92    if (xterm_console.pid != -1) {
93       kill(xterm_console.pid, SIGTERM);
94    }
95 }
96
97 void XTERM_ResizeScreen(int x, int y)
98 {
99    char temp[100];
100
101    /* Call the chain first, there shoudln't be any... */
102    if (chain.resizeScreen)
103       chain.resizeScreen(x, y);
104
105    sprintf(temp, "\x1b[8;%d;%dt", y, x);
106    CONSOLE_WriteRawString(temp);
107
108    CONSOLE_NotifyResizeScreen(x, y);
109 }
110
111
112 static BOOL wine_create_console(FILE **master, FILE **slave, pid_t *pid)
113 {
114         /* There is definately a bug in this routine that causes a lot
115            of garbage to be written to the screen, but I can't find it...
116         */
117         struct termios term;
118         char buf[1024];
119         char c = '\0';
120         int status = 0;
121         int i;
122         int tmaster, tslave;
123         char xterm_resolution[10];
124
125         sprintf(xterm_resolution, "%dx%d", driver.x_res,
126            driver.y_res);
127
128         if (tcgetattr(0, &term) < 0) return FALSE;
129         term.c_lflag |= ICANON;
130         term.c_lflag &= ~ECHO;
131         if (wine_openpty(&tmaster, &tslave, NULL, &term, NULL) < 0)
132            return FALSE;
133         *master = fdopen(tmaster, "r+");
134         *slave = fdopen(tslave, "r+");
135
136         if ((*pid=fork()) == 0) {
137                 tcsetattr(fileno(*slave), TCSADRAIN, &term);
138                 sprintf(buf, "-Sxx%d", fileno(*master));
139                 execlp(console_xterm_prog, console_xterm_prog, buf, "-fg",
140                    "white", "-bg", "black", "-g",
141                    xterm_resolution, NULL);
142                 ERR("error creating xterm (file not found?)\n");
143                 exit(1);
144         }
145
146         /* most xterms like to print their window ID when used with -S;
147          * read it and continue before the user has a chance...
148          * NOTE: this is the reason we started xterm with ECHO off,
149          * we'll turn it back on below
150          */
151
152         for (i=0; c!='\n'; (status=fread(&c, 1, 1, *slave)), i++) {
153                 if (status == -1 && c == '\0') {
154                                 /* wait for xterm to be created */
155                         usleep(100);
156                 }
157                 if (i > 10000) {
158                         WARN("can't read xterm WID\n");
159                         kill(*pid, SIGKILL);
160                         return FALSE;
161                 }
162         }
163         term.c_lflag |= ECHO;
164         tcsetattr(fileno(*master), TCSADRAIN, &term);
165
166         return TRUE;
167 }