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.
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++)
/* * 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