PATCH: xterm support for wineconsole
Marcus Meissner
marcus at jet.franken.de
Sun Nov 25 15:14:16 CST 2001
Hi,
This patch adds a XTERM backend to wineconsole.
Some problems:
- In full screen text applications (like IDA), the cursor shows sometimes
up in the wrong spot.
- Scrolling does not work yet (so winedbg doesn't really work).
- Uses config.h.
Not quite ready for production use, but mostly for Eric and Alexandre to
review :) I am however running IDAW in it already.
Ciao, Marcus
Changelog:
Added 'xterm' backend to wineconsole.
-------------- next part --------------
Index: Makefile.in
===================================================================
RCS file: /home/wine/wine/programs/wineconsole/Makefile.in,v
retrieving revision 1.2
diff -u -r1.2 Makefile.in
--- Makefile.in 2001/11/24 17:07:13 1.2
+++ Makefile.in 2001/11/25 19:29:04
@@ -8,7 +8,8 @@
C_SRCS = \
dialog.c \
user.c \
- wineconsole.c
+ wineconsole.c \
+ xterm.c
RC_SRCS = \
wineconsole_res.rc
Index: winecon_private.h
===================================================================
RCS file: /home/wine/wine/programs/wineconsole/winecon_private.h,v
retrieving revision 1.1
diff -u -r1.1 winecon_private.h
--- winecon_private.h 2001/11/23 23:05:03 1.1
+++ winecon_private.h 2001/11/25 19:29:15
@@ -44,6 +44,9 @@
BOOL hasSelection; /* a rectangular mouse selection has taken place */
COORD selectPt1; /* start (and end) point of a mouse selection */
COORD selectPt2;
+
+ /* The following fields are used by the xterm backend (should be hidden) */
+ int xtermfd,xtermpid;
};
# ifdef __GNUC__
@@ -68,8 +71,15 @@
extern void WINECON_FetchCells(struct inner_data* data, int upd_tp, int upd_bm);
extern int WINECON_GrabChanges(struct inner_data* data);
+
extern BOOL WCUSER_GetProperties(struct inner_data*);
extern BOOL WCUSER_SetFont(struct inner_data* data, const LOGFONT* font, const TEXTMETRIC* tm);
extern BOOL WCUSER_ValidateFont(const struct inner_data* data, const LOGFONT* lf);
extern BOOL WCUSER_ValidateFontMetric(const struct inner_data* data, const TEXTMETRIC* tm);
extern BOOL WCUSER_InitBackend(struct inner_data* data);
+
+extern BOOL WCXTERM_GetProperties(struct inner_data*);
+extern BOOL WCXTERM_SetFont(struct inner_data* data, const LOGFONT* font, const TEXTMETRIC* tm);
+extern BOOL WCXTERM_ValidateFont(const struct inner_data* data, const LOGFONT* lf);
+extern BOOL WCXTERM_ValidateFontMetric(const struct inner_data* data, const TEXTMETRIC* tm);
+extern BOOL WCXTERM_InitBackend(struct inner_data* data);
Index: wineconsole.c
===================================================================
RCS file: /home/wine/wine/programs/wineconsole/wineconsole.c,v
retrieving revision 1.2
diff -u -r1.2 wineconsole.c
--- wineconsole.c 2001/11/25 00:49:36 1.2
+++ wineconsole.c 2001/11/25 19:29:20
@@ -212,8 +212,7 @@
else num = 0;
}
SERVER_END_VAR_REQ;
- if (!num) {Trace(0, "hmm renderer signaled but no events available\n"); return 1;}
-
+
/* FIXME: should do some event compression here (cursor pos, update) */
Trace(1, "Change notification:");
for (i = 0; i < num; i++)
-------------- next part --------------
/*
* a GUI application for displaying a console
* XTERM back end
* Copyright 2001 Marcus Meissner
*/
#include "config.h"
#ifdef HAVE_OPENPTY /* use this as trigger whether we can use it or not */
#include <assert.h>
#include <fcntl.h>
#include <sys/time.h>
#include <errno.h>
#include <sys/errno.h>
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#ifdef HAVE_PTY_H
# include <pty.h>
#endif
#include "winecon_private.h"
/* Ascii -> VK, generated by calling VkKeyScanA(i) */
static int vkkeyscan_table[256] = {
0,0,0,0,0,0,0,0,8,9,0,0,0,13,0,0,0,0,0,19,145,556,0,0,0,0,0,27,0,0,0,
0,32,305,478,307,308,309,311,222,313,304,312,443,188,189,190,191,48,
49,50,51,52,53,54,55,56,57,442,186,444,187,446,447,306,321,322,323,
324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,
341,342,343,344,345,346,219,220,221,310,445,192,65,66,67,68,69,70,71,
72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,475,476,477,
448,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,400,0,0,0,0,0,0
};
static int mapvkey_0[256]={
0,0,0,0,0,0,0,0,14,15,0,0,0,28,0,0,42,29,56,69,58,0,0,0,0,0,0,1,0,0,
0,0,57,73,81,79,71,75,72,77,80,0,0,0,55,82,83,0,11,2,3,4,5,6,7,8,9,
10,0,0,0,0,0,0,0,30,48,46,32,18,33,34,35,23,36,37,38,50,49,24,25,16,
19,31,20,22,47,17,45,21,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55,78,0,74,
0,53,59,60,61,62,63,64,65,66,67,68,87,88,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,69,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,13,51,12,52,53,41,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,43,27,40,76,96,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};
static int mapvkey_1[256]={
0,27,49,50,51,52,53,54,55,56,57,48,189,187,8,9,81,87,69,82,84,89,85,
73,79,80,219,221,13,17,65,83,68,70,71,72,74,75,76,186,222,192,16,220,
90,88,67,86,66,78,77,188,190,191,16,106,18,32,20,112,113,114,115,116,
117,118,119,120,121,144,145,36,38,33,109,37,223,39,107,35,40,34,45,
46,0,0,0,122,123,0,0,0,0,0,0,0,224,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0
};
int xwrite(int fd,caddr_t buf,size_t size) {
int curwritten = 0;
while (curwritten < size) {
int res = write(fd,buf+curwritten,size-curwritten);
if (res == -1)
return -1;
curwritten += res;
}
return curwritten;
}
/******************************************************************
* WCXTERM_PosCursor
*
* Set a new position for the cursor
*/
static void WCXTERM_PosCursor(const struct inner_data* data)
{
char buf[30];
sprintf(buf,"%c[%d;%dH",27,data->cursor.Y+1,data->cursor.X+1);
xwrite(data->xtermfd,buf,strlen(buf));
}
/******************************************************************
* WCXTERM_FillMemDC
*
* Fills the Mem DC with current cells values
*/
static void WCXTERM_FillMemDC(const struct inner_data* data, int upd_tp, int upd_bm)
{
unsigned i, j, k;
CHAR_INFO* cell;
WORD attr;
CHAR* line;
char outbuf[30];
const int colormap[8] = { 0,4,2,6, 1,5,3,7, };
sprintf(outbuf,"%c7",27);xwrite(data->xtermfd,outbuf,2);
if (!(line = HeapAlloc(GetProcessHeap(), 0, data->sb_width*sizeof(CHAR))))
{Trace(0, "OOM\n"); return;}
for (j = upd_tp; j <= upd_bm; j++)
{
cell = &data->cells[j * data->sb_width];
for (i = 0; i < data->win_width; i++)
{
attr = cell[i].Attributes;
sprintf(outbuf,"%c[0;%s3%d;4%dm%c[%d;%dH",
27,
(attr & FOREGROUND_INTENSITY)?"1;":"",
colormap[attr&7],
colormap[(attr&0x70)>>4],
27,
j+1,
i+1
);
xwrite(data->xtermfd,outbuf,strlen(outbuf));
for (k = i; k < data->win_width && cell[k].Attributes == attr; k++){
WCHAR c = cell[k].Char.UnicodeChar;
/* Escape special vt100 escape codes with 8 bit set */
switch (c) {
case 132:case 133:case 136:case 141:case 142:case 143:case 144:
case 150:case 151:case 155:case 156:case 157:case 158:case 159:
c='?';break;
default: break;
}
line[k - i] = c;
}
xwrite(data->xtermfd,line,k-i);
i = k - 1;
}
}
sprintf(outbuf,"%c8",27);xwrite(data->xtermfd,outbuf,2);
}
/******************************************************************
* WCXTERM_NewBitmap
*
* Either the font geometry or the sb geometry has changed.
*/
static void WCXTERM_NewBitmap(struct inner_data* data, BOOL fill)
{
if (!data->sb_width || !data->sb_height)
return;
if (fill)
WCXTERM_FillMemDC(data, 0, data->sb_height - 1);
}
/******************************************************************
* WCXTERM_ResizeScreenBuffer
*
*
*/
static void WCXTERM_ResizeScreenBuffer(struct inner_data* data)
{
WCXTERM_NewBitmap(data, FALSE);
}
/******************************************************************
* WCXTERM_SetTitle
*
* Sets the title to the wine console
*/
static void WCXTERM_SetTitle(const struct inner_data* data)
{
WCHAR buffer[256];
if (WINECON_GetConsoleTitle(data->hConIn, buffer, sizeof(buffer))) {
WCHAR *w;
char abuffer[256],*s;
strcpy( abuffer, "\033]2;" );
s = abuffer+strlen(abuffer);
w = buffer;
while ((*s++ = *w++))
/*EMPTY*/;
s--; /* step back to \0 */
*s++='\a';
*s++='\0';
xwrite(data->xtermfd,abuffer,strlen(abuffer));
}
}
static void WCXTERM_Refresh(const struct inner_data* data, int tp, int bm)
{
if (data->win_pos.Y <= bm && data->win_pos.Y + data->win_height >= tp)
{
WCXTERM_FillMemDC(data, tp, bm);
}
}
/******************************************************************
* WCXTERM_DeleteBackend
*
*
*/
void WCXTERM_DeleteBackend(struct inner_data* data)
{
fprintf(stderr,"WCXTERM_DeleteBackend\n");
}
/****************************************************************************
* WCXTERM_string_to_IR [internal]
*
* Transfers a string read from XTERM to INPUT_RECORDs and adds them to the
* queue. Does translation of vt100 style function keys and xterm-mouse clicks.
*/
static void
WCXTERM_string_to_IR( struct inner_data *data,unsigned char *buf,int len) {
int j,k;
INPUT_RECORD ir;
DWORD junk;
for (j=0;j<len;j++) {
unsigned char inchar = buf[j];
memset(&ir,0,sizeof(ir));
if (inchar!=27) { /* no escape -> 'normal' keyboard event */
ir.EventType = 1; /* Key_event */
ir.Event.KeyEvent.bKeyDown = 1;
ir.Event.KeyEvent.wRepeatCount = 0;
ir.Event.KeyEvent.dwControlKeyState = 0;
if (inchar & 0x80) {
ir.Event.KeyEvent.dwControlKeyState|=LEFT_ALT_PRESSED;
inchar &= ~0x80;
}
ir.Event.KeyEvent.wVirtualKeyCode = vkkeyscan_table[inchar];
if (ir.Event.KeyEvent.wVirtualKeyCode & 0x0100)
ir.Event.KeyEvent.dwControlKeyState|=SHIFT_PRESSED;
if (ir.Event.KeyEvent.wVirtualKeyCode & 0x0200)
ir.Event.KeyEvent.dwControlKeyState|=LEFT_CTRL_PRESSED;
if (ir.Event.KeyEvent.wVirtualKeyCode & 0x0400)
ir.Event.KeyEvent.dwControlKeyState|=LEFT_ALT_PRESSED;
ir.Event.KeyEvent.wVirtualScanCode = mapvkey_0[
ir.Event.KeyEvent.wVirtualKeyCode & 0x00ff
]; /* VirtualKeyCodes to ScanCode */
ir.Event.KeyEvent.uChar.AsciiChar = inchar;
if ((inchar==127)||(inchar=='\b')) { /* backspace */
ir.Event.KeyEvent.uChar.AsciiChar = '\b'; /* FIXME: hmm */
ir.Event.KeyEvent.wVirtualScanCode = 0x0e;
ir.Event.KeyEvent.wVirtualKeyCode = VK_BACK;
} else {
if ((inchar=='\n')||(inchar=='\r')) {
ir.Event.KeyEvent.uChar.AsciiChar = '\r';
ir.Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
ir.Event.KeyEvent.wVirtualScanCode = 0x1c;
ir.Event.KeyEvent.dwControlKeyState = 0;
} else {
if (inchar<' ') {
/* FIXME: find good values for ^X */
ir.Event.KeyEvent.wVirtualKeyCode = 0xdead;
ir.Event.KeyEvent.wVirtualScanCode = 0xbeef;
}
}
}
assert(WriteConsoleInputA( data->hConIn, &ir, 1, &junk ));
ir.Event.KeyEvent.bKeyDown = 0;
assert(WriteConsoleInputA( data->hConIn, &ir, 1, &junk ));
continue;
}
/* inchar is ESC */
if ((j==len-1) || (buf[j+1]!='[')) {/* add ESCape on its own */
ir.EventType = 1; /* Key_event */
ir.Event.KeyEvent.bKeyDown = 1;
ir.Event.KeyEvent.wRepeatCount = 0;
ir.Event.KeyEvent.wVirtualKeyCode = VK_ESCAPE;
ir.Event.KeyEvent.wVirtualScanCode = mapvkey_0[
ir.Event.KeyEvent.wVirtualKeyCode
];
ir.Event.KeyEvent.dwControlKeyState = 0;
ir.Event.KeyEvent.uChar.AsciiChar = 27;
assert(WriteConsoleInputA( data->hConIn, &ir, 1, &junk ));
ir.Event.KeyEvent.bKeyDown = 0;
assert(WriteConsoleInputA( data->hConIn, &ir, 1, &junk ));
continue;
}
for (k=j;k<len;k++) {
if (((buf[k]>='A') && (buf[k]<='Z')) ||
((buf[k]>='a') && (buf[k]<='z')) ||
(buf[k]=='~')
)
break;
}
if (k<len) {
int subid,scancode=0;
ir.EventType = 1; /* Key_event */
ir.Event.KeyEvent.bKeyDown = 1;
ir.Event.KeyEvent.wRepeatCount = 0;
ir.Event.KeyEvent.dwControlKeyState = 0;
ir.Event.KeyEvent.wVirtualKeyCode = 0xad; /* FIXME */
ir.Event.KeyEvent.wVirtualScanCode = 0xad; /* FIXME */
ir.Event.KeyEvent.uChar.AsciiChar = 0;
switch (buf[k]) {
case '~':
sscanf(&buf[j+2],"%d",&subid);
switch (subid) {
case 2:/*INS */scancode = 0xe052;break;
case 3:/*DEL */scancode = 0xe053;break;
case 6:/*PGDW*/scancode = 0xe051;break;
case 5:/*PGUP*/scancode = 0xe049;break;
case 11:/*F1 */scancode = 0x003b;break;
case 12:/*F2 */scancode = 0x003c;break;
case 13:/*F3 */scancode = 0x003d;break;
case 14:/*F4 */scancode = 0x003e;break;
case 15:/*F5 */scancode = 0x003f;break;
case 17:/*F6 */scancode = 0x0040;break;
case 18:/*F7 */scancode = 0x0041;break;
case 19:/*F8 */scancode = 0x0042;break;
case 20:/*F9 */scancode = 0x0043;break;
case 21:/*F10 */scancode = 0x0044;break;
case 23:/*F11 */scancode = 0x00d9;break;
case 24:/*F12 */scancode = 0x00da;break;
/* FIXME: Shift-Fx */
default:
fprintf(stderr,"parse ESC[%d~\n",subid);
break;
}
break;
case 'A': /* Cursor Up */scancode = 0xe048;break;
case 'B': /* Cursor Down */scancode = 0xe050;break;
case 'D': /* Cursor Left */scancode = 0xe04b;break;
case 'C': /* Cursor Right */scancode = 0xe04d;break;
case 'F': /* End */scancode = 0xe04f;break;
case 'H': /* Home */scancode = 0xe047;break;
case 'M':
/* Mouse Button Press (ESCM<button+'!'><x+'!'><y+'!'>) or
* Release (ESCM#<x+'!'><y+'!'>
*/
if (k<len-3) {
ir.EventType = MOUSE_EVENT;
ir.Event.MouseEvent.dwMousePosition.X = buf[k+2]-'!';
ir.Event.MouseEvent.dwMousePosition.Y = buf[k+3]-'!';
if (buf[k+1]=='#')
ir.Event.MouseEvent.dwButtonState = 0;
else
ir.Event.MouseEvent.dwButtonState = 1<<(buf[k+1]-' ');
ir.Event.MouseEvent.dwEventFlags = 0; /* FIXME */
assert(WriteConsoleInputA( data->hConIn, &ir, 1, &junk));
j=k+3;
}
break;
case 'c':
j=k;
break;
}
if (scancode) {
ir.Event.KeyEvent.wVirtualScanCode = scancode;
ir.Event.KeyEvent.wVirtualKeyCode = mapvkey_1[scancode];
assert(WriteConsoleInputA( data->hConIn, &ir, 1, &junk ));
ir.Event.KeyEvent.bKeyDown = 0;
assert(WriteConsoleInputA( data->hConIn, &ir, 1, &junk ));
j=k;
continue;
}
}
}
}
/******************************************************************
* WCXTERM_MainLoop
*
*
*/
static int
WCXTERM_MainLoop( struct inner_data *data )
{
char *buf = HeapAlloc(GetProcessHeap(),0,1);
int len = 0, escape_seen = 0;
while (1)
{
DWORD res;
char inchar;
struct timeval tv;
fd_set readfds;
memset(&tv,0,sizeof(tv));
/* If we have at one time seen escape in this loop, we are
* within an Escape sequence, so wait for a bit more input for the
* rest of the loop.
*/
if (escape_seen) {
tv.tv_sec = 0;
tv.tv_usec = 50000;
} else {
tv.tv_sec = 0;
tv.tv_usec = 100000;
}
FD_ZERO(&readfds);
FD_SET(data->xtermfd,&readfds);
if (-1==(res=select(data->xtermfd+1,&readfds,NULL,NULL,&tv))) {
perror("select");
return 1;
}
/* Get events from server */
if (!WINECON_GrabChanges(data))
return 0;
if (res>0) {
while (1) {
res = read( data->xtermfd, &inchar, 1 );
switch (res) {
case -1:if (errno==EAGAIN)
break; /* expected error for non blocking */
perror("break");
return 1;
case 0:fprintf(stderr,"read returned 0, EOF on xterm?\n");
break;
default:break;
}
if (res==0)
return 0;
if (res == -1) /* no more input, wait */
break;
buf = HeapReAlloc(GetProcessHeap(),0,buf,len+1);
buf[len++]=inchar;
if (inchar == 27) {
if (len>1) {
/* If we spot an ESC, we flush all up to it
* since we can be sure that we have a complete
* sequence.
*/
WCXTERM_string_to_IR(data,buf,len-1);
buf = HeapReAlloc(GetProcessHeap(),0,buf,1);
buf[0] = 27;
len = 1;
}
escape_seen = 1;
continue;
}
}
} else {
/* we did not have a character above */
if (buf) {
WCXTERM_string_to_IR(data,buf,len);
len = 0;
HeapFree(GetProcessHeap(),0,buf);
buf = NULL;
escape_seen = 0;
}
}
}
return 0;
}
static BOOL CONSOLE_make_complex(struct inner_data *data)
{
struct termios term;
char buf[256];
char c = '\0';
int i,master,slave,flags;
if (tcgetattr(0, &term) < 0) {
/* ignore failure, or we can't run from a script */
}
term.c_lflag = ~(ECHO|ICANON);
if (openpty(&master, &slave, NULL, &term, NULL) < 0)
return FALSE;
if ((data->xtermpid=fork()) == 0) {
tcsetattr(slave, TCSADRAIN, &term);
close( slave );
sprintf(buf, "-Sxx%d", master);
/* "-fn vga" for VGA font. Harmless if vga is not present:
* xterm: unable to open font "vga", trying "fixed"....
*/
execlp("xterm", "xterm", buf, "-fn","vga",NULL);
perror("error creating AllocConsole xterm");
exit(1);
}
close( master );
/* most xterms like to print their window ID when used with -S;
* read it and continue before the user has a chance...
*/
for (i = 0; i < 10000; i++)
{
if (read( slave, &c, 1 ) == 1)
{
if (c == '\n') break;
}
else usleep(100); /* wait for xterm to be created */
}
if (i == 10000)
{
fprintf(stderr,"can't read xterm WID\n");
close( slave );
return FALSE;
}
data->xtermfd = slave;
fcntl(data->xtermfd,F_GETFL,&flags);
flags |= O_NDELAY;
fcntl(data->xtermfd,F_SETFL,flags);
/* Enable mouseclicks, disable 8bit charactercontrols */
strcpy( buf, "\033[?1002h\033 F" );
xwrite(data->xtermfd,buf,strlen(buf));
return TRUE;
}
static void WCXTERM_Scroll(struct inner_data* data, int pos, BOOL horz) {
fprintf(stderr,"WCXTERM_Scroll(%p,%d,%d), stub.\n",data,pos,horz);
}
static void WCXTERM_ShapeCursor(struct inner_data* data, int size, int vis, BOOL force) {
char *escseq;
if (vis)
escseq="\033[?25h";
else
escseq="\033[?25l";
data->cursor_visible = vis;
xwrite(data->xtermfd,escseq,strlen(escseq));
}
static void WCXTERM_ComputePositions(struct inner_data* data) {
fprintf(stderr,"WCXTERM_ComputePositions(%p), stub.\n",data);
}
/******************************************************************
* WCXTERM_InitBackend
*
* Initialisation part II: creation of window.
*
*/
BOOL WCXTERM_InitBackend(struct inner_data* data)
{
data->fnMainLoop = WCXTERM_MainLoop;
data->fnPosCursor = WCXTERM_PosCursor;
data->fnShapeCursor = WCXTERM_ShapeCursor;
data->fnComputePositions = WCXTERM_ComputePositions;
data->fnRefresh = WCXTERM_Refresh;
data->fnResizeScreenBuffer = WCXTERM_ResizeScreenBuffer;
data->fnSetTitle = WCXTERM_SetTitle;
data->fnScroll = WCXTERM_Scroll;
data->fnDeleteBackend = WCXTERM_DeleteBackend;
CONSOLE_make_complex(data);
/* force update of current data */
WINECON_GrabChanges(data);
return TRUE;
}
#endif
More information about the wine-patches
mailing list