this patch implement a curse backend for wineconsole, meaning you can use the unix term as the playground for any CUI application (including wcmd, winedbg...) how to use it: - when a console is created(*) for a CUI program, the backend to use is determined (in this order of priority) 1/ content of WINECONSOLE_BACKEND environment variable (it's usage is more for debugging purposes) 2/ content of app defaults key 'Backend' for the application creating the console 3/ content of app defaults key 'Backend' the backend name can be either: a/ wineconsole what existed before this patch b/ Curse (n)curse display, using current Unix terminal (if input hasn't been redirected) c/ XTerm+Curse as above, but in a specific xterm window if the name of a backend cannot be found in the list above, wineconsole is used there are still a few bugs around, the most important one (and known) is that, when using wcmd for example, the terminal isn't properly reset when the CUI app exits this is caused because we have the following parent/child relationship shell -> CUI app -> wineconsole in this case, the CUI app dies before wineconsole, so wineconsole is put in the background by the shell a possible (and logical fix) would be to make wineconsole (in Curse mode) a program group leader, and make any CUI program attached to this console in the wineconsole program group. of course, if the CUI program detaches itself from the console, it should be put back in the shell pgm group (note, that this doesn't append if wineconsole launches the CUI app, so if you want to terminal neat on exit, you'd better use this method of creation) I didn't really start to implement this. so if someone has good ideas around this issue, please be welcome (*) console creation this patch doesn't force a wineconsole creation in every case: - when any process starts, it gets by default window-std handles hooked to the unix std streams - only when it actually requests, either a console creation (AllocConsole, or CreateProcess with CREATE_NEW_CONSOLE flag set) or launching the program with wineconsole instead of wine, does the hereabove mentionned processus take place A+
Name: wc_curse ChangeLog: License: X11 GenDate: 2002/11/09 18:26:10 UTC ModifiedFiles: programs/wineconsole/Makefile.in programs/wineconsole/registry.c programs/wineconsole/user.c programs/wineconsole/winecon_private.h programs/wineconsole/wineconsole.c AddedFiles: programs/wineconsole/curses.c =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/programs/wineconsole/Makefile.in,v retrieving revision 1.6 diff -u -u -r1.6 Makefile.in --- programs/wineconsole/Makefile.in 21 May 2002 19:42:30 -0000 1.6 +++ programs/wineconsole/Makefile.in 21 May 2002 20:08:24 -0000 @@ -4,11 +4,13 @@ SRCDIR = @srcdir@ VPATH = @srcdir@ MODULE = wineconsole.exe +EXTRALIBS = @CURSESLIBS@ APPMODE = gui IMPORTS = gdi32 user32 advapi32 kernel32 ntdll DELAYIMPORTS = comctl32 C_SRCS = \ + curses.c \ dialog.c \ registry.c \ user.c \ Index: programs/wineconsole/registry.c =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/programs/wineconsole/registry.c,v retrieving revision 1.9 diff -u -u -r1.9 registry.c --- programs/wineconsole/registry.c 4 Sep 2002 18:41:52 -0000 1.9 +++ programs/wineconsole/registry.c 28 Oct 2002 06:13:29 -0000 @@ -160,7 +160,7 @@ cfg->cursor_size = 25; cfg->cursor_visible = 1; cfg->exit_on_die = 1; - cfg->face_name[0] = 0; + memset(cfg->face_name, 0, sizeof(cfg->face_name)); cfg->cell_height = 12; cfg->cell_width = 8; cfg->font_weight = 0; Index: programs/wineconsole/user.c =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/programs/wineconsole/user.c,v retrieving revision 1.17 diff -u -u -r1.17 user.c --- programs/wineconsole/user.c 28 Oct 2002 23:51:27 -0000 1.17 +++ programs/wineconsole/user.c 29 Oct 2002 08:50:06 -0000 @@ -532,7 +532,7 @@ { LOGFONT lf; struct font_chooser fc; - + WINE_TRACE_(wc_font)("=> %s h=%u w=%u\n", wine_dbgstr_wn(font, -1), height, weight); Index: programs/wineconsole/winecon_private.h =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/programs/wineconsole/winecon_private.h,v retrieving revision 1.8 diff -u -u -r1.8 winecon_private.h --- programs/wineconsole/winecon_private.h 4 Sep 2002 18:41:52 -0000 1.8 +++ programs/wineconsole/winecon_private.h 28 Oct 2002 06:01:01 -0000 @@ -49,13 +49,14 @@ struct config_data curcfg; CHAR_INFO* cells; /* local copy of cells (sb_width * sb_height) */ - COORD cursor; /* position in cells of cursor */ HANDLE hConIn; /* console input handle */ HANDLE hConOut; /* screen buffer handle: has to be changed when active sb changes */ HANDLE hSynchro; /* waitable handle signalled by server when something in server has been modified */ + WCHAR backend[32]; /* name of the backend to be used */ + int (*fnMainLoop)(struct inner_data* data); void (*fnPosCursor)(const struct inner_data* data); void (*fnShapeCursor)(struct inner_data* data, int size, int vis, BOOL force); @@ -90,3 +91,4 @@ /* backends... */ extern BOOL WCUSER_InitBackend(struct inner_data* data); extern BOOL WCCURSE_InitBackend(struct inner_data* data); +extern BOOL WCCURSE_InitBackendXTerm(struct inner_data* data); Index: programs/wineconsole/wineconsole.c =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/programs/wineconsole/wineconsole.c,v retrieving revision 1.17 diff -u -u -r1.17 wineconsole.c --- programs/wineconsole/wineconsole.c 3 Oct 2002 19:54:57 -0000 1.17 +++ programs/wineconsole/wineconsole.c 8 Nov 2002 21:56:45 -0000 @@ -23,7 +23,6 @@ #include <stdio.h> #include "wine/server.h" -#include "wine/unicode.h" #include "winecon_private.h" #include "wine/debug.h" @@ -428,28 +427,80 @@ { if (!data) return; + if (data->fnDeleteBackend) data->fnDeleteBackend(data); if (data->hConIn) CloseHandle(data->hConIn); if (data->hConOut) CloseHandle(data->hConOut); if (data->hSynchro) CloseHandle(data->hSynchro); if (data->cells) HeapFree(GetProcessHeap(), 0, data->cells); - if (data->fnDeleteBackend) data->fnDeleteBackend(data); HeapFree(GetProcessHeap(), 0, data); } /****************************************************************** + * WINECON_GetBackendName + * + * + */ +static void WINECON_GetBackendName(WCHAR* dst, DWORD sz) +{ + static const WCHAR wszWCBackend[] = {'W','I','N','E','C','O','N','S','O','L','E','_','B','A','C','K','E','N','D',0}; + static const WCHAR wszAppConfig[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\','W','i','n','e','\\', + 'C','o','n','f','i','g','\\','A','p','p','D','e','f','a','u','l','t','s',0}; + static const WCHAR wszBackend[] = {'B','a','c','k','e','n','d',0}; + + if (GetEnvironmentVariable(wszWCBackend, dst, sz / sizeof(WCHAR)) == 0) + { + WCHAR buffer[MAX_PATH]; + HKEY hDefaultKey, hAppKey = 0; + DWORD count; + + /* open the app-specific key */ + if (GetModuleFileName(0, buffer, MAX_PATH)) + { + WCHAR* appname; + + for (appname = buffer + lstrlenW(buffer); appname > buffer; appname--) + { + if (*appname == '/' || *appname == '\\') + { + appname++; + break; + } + } + if (!RegOpenKey(HKEY_LOCAL_MACHINE, wszAppConfig, &hDefaultKey) && + RegOpenKey(hDefaultKey, appname, &hAppKey)) + hAppKey = 0; + } + + /* get the display name */ + count = sz; + if (RegQueryValueExW(hAppKey, wszBackend, 0, NULL, (void*)dst, &count)) + { + count = sz; + if (RegQueryValueExW(hDefaultKey, wszBackend, 0, NULL, (void*)dst, &count)) + dst[0] = 0; + } + if (hAppKey) RegCloseKey(hAppKey); + RegCloseKey(hDefaultKey); + } +} + +/****************************************************************** * WINECON_Init * * Initialisation part I. Creation of server object (console input and * active screen buffer) */ -static struct inner_data* WINECON_Init(HINSTANCE hInst, DWORD pid, LPCWSTR appname, - BOOL (*backend)(struct inner_data*)) +static struct inner_data* WINECON_Init(HINSTANCE hInst, DWORD pid, LPCWSTR appname) { + static WCHAR wszCurse[] = {'C','u','r','s','e',0}; + static WCHAR wszXTermCurse[] = {'X','T','e','r','m','+','C','u','r','s','e',0}; struct inner_data* data = NULL; DWORD ret; struct config_data cfg; STARTUPINFOW si; - + WCHAR backend_name[32]; + BOOL (*backend)(struct inner_data*); + data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data)); if (!data) return 0; @@ -499,7 +550,7 @@ { req->handle = (obj_handle_t)data->hConIn; req->mask = SET_CONSOLE_INPUT_INFO_TITLE; - wine_server_add_data( req, appname, strlenW(appname) * sizeof(WCHAR) ); + wine_server_add_data( req, appname, lstrlenW(appname) * sizeof(WCHAR) ); ret = !wine_server_call_err( req ); } SERVER_END_REQ; @@ -518,6 +569,13 @@ if (!ret) goto error; WINE_TRACE("using hConOut %p\n", data->hConOut); + WINECON_GetBackendName(backend_name, sizeof(backend_name)); + + WINE_TRACE("using backend %s\n", wine_dbgstr_w(backend_name)); + if (lstrcmpW(backend_name, wszCurse) == 0) backend = WCCURSE_InitBackend; + else if (lstrcmpW(backend_name, wszXTermCurse) == 0) backend = WCCURSE_InitBackendXTerm; + else backend = WCUSER_InitBackend; + /* filling data->curcfg from cfg */ if ((*backend)(data)) { @@ -600,14 +658,11 @@ struct inner_data* data; int ret = 1; unsigned evt; - BOOL (*backend)(struct inner_data*); - - backend = WCUSER_InitBackend; /* case of wineconsole <evt>, signal process that created us that we're up and running */ if (WINECON_HasEvent(lpCmdLine, &evt)) { - if (!(data = WINECON_Init(hInst, 0, NULL, backend))) return 0; + if (!(data = WINECON_Init(hInst, 0, NULL))) return 0; ret = SetEvent((HANDLE)evt); if (!ret) { @@ -635,7 +690,7 @@ while (*src && *src != ' ') *dst++ = *src++; *dst = 0; - if (!(data = WINECON_Init(hInst, GetCurrentProcessId(), buffer, backend))) return 0; + if (!(data = WINECON_Init(hInst, GetCurrentProcessId(), buffer))) return 0; ret = WINECON_Spawn(data, wcmdLine); if (!ret) { --- /dev/null Thu Jan 1 01:00:00 1970 +++ programs/wineconsole/curses.c Sat Nov 9 19:25:38 2002 @@ -0,0 +1,876 @@ +/* + * a GUI application for displaying a console + * (N)Curses back end + * + * Copyright 2002 Eric Pouech + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Known issues & FIXME: + * - when exiting, the old screen is screwed up (new to reset it) + * => seems caused by parent process (the one allocating the console) + * termination, and the we're put in the background ?? + * - not all key mapping functions have been written + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#ifdef HAVE_CURSES_H +#include <curses.h> +#endif +#ifdef HAVE_NCURSES_H +#include <ncurses.h> +#endif +#ifdef HAVE_TERMIOS_H +#include <termios.h> +#endif +#include <unistd.h> +#include <signal.h> +#ifdef HAVE_PTY_H +# include <pty.h> +#endif +#include <winnls.h> +#include "winecon_private.h" + +#include "wine/server.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(wineconsole); + +struct inner_data_curse +{ + pid_t xtermpid; + SCREEN* screen; + mmask_t initial_mouse_mask; + HANDLE hInput; + HANDLE hOutput; +}; + +#define PRIVATE(data) ((struct inner_data_curse*)((data)->private)) + +#if defined(HAVE_CURSES_H) || defined(HAVE_NCURSES_H) + +/****************************************************************** + * WCUSER_ResizeScreenBuffer + * + * + */ +static void WCCURSE_ResizeScreenBuffer(struct inner_data* data) +{ +} + +/****************************************************************** + * WCCURSE_PosCursor + * + * Set a new position for the cursor + */ +static void WCCURSE_PosCursor(const struct inner_data* data) +{ + move(data->cursor.Y, data->cursor.X); + refresh(); +} + +/****************************************************************** + * WCCURSE_ShapeCursor + * + * Sets a new shape for the cursor + */ +void WCCURSE_ShapeCursor(struct inner_data* data, int size, int vis, BOOL force) +{ +} + +/****************************************************************** + * INECON_ComputePositions + * + * Recomputes all the components (mainly scroll bars) positions + */ +void WCCURSE_ComputePositions(struct inner_data* data) +{ + if (PRIVATE(data)->xtermpid) + { + char buffer[64]; + DWORD n; + + resizeterm(data->curcfg.win_height, data->curcfg.win_width); + /* FIXME: this should be a curse function */ + sprintf(buffer, "\x1b[8;%d;%dt", data->curcfg.win_height, data->curcfg.win_width); + WriteFile(PRIVATE(data)->hOutput, buffer, strlen(buffer), &n, NULL); + } + WCCURSE_PosCursor(data); +} + +/****************************************************************** + * WCCURSE_SetTitle + * + * Sets the title to the wine console + */ +static void WCCURSE_SetTitle(const struct inner_data* data) +{ + if (PRIVATE(data)->xtermpid) + { + WCHAR wbuf[256]; + + if (WINECON_GetConsoleTitle(data->hConIn, wbuf, sizeof(wbuf)/sizeof(WCHAR))) + { + char buffer[256]; + DWORD written; + + WriteFile(PRIVATE(data)->hOutput, "\033]2;", 4, &written, NULL); + WideCharToMultiByte(CP_ACP, 0, wbuf, -1, buffer, sizeof(buffer), + NULL, NULL); + WriteFile(PRIVATE(data)->hOutput, buffer, strlen(buffer), &written, NULL); + WriteFile(PRIVATE(data)->hOutput, "\a", 1, &written, NULL); + } + } +} + +/****************************************************************** + * Refresh + * + * + */ +static void WCCURSE_Refresh(const struct inner_data* data, int tp, int bm) +{ + int x, y; + CHAR_INFO* cell; + WORD color; + + tp = max(tp, data->curcfg.win_pos.Y); + bm = min(bm, data->curcfg.win_pos.Y + data->curcfg.win_height - 1); + + for (y = tp; y <= bm; y++) + { + cell = &data->cells[y * data->curcfg.sb_width]; + /* FIXME: no x-scroll */ + for (x = 0; x < min(data->curcfg.sb_width, data->curcfg.win_width); x++) + { + color = 0; + if (cell[x].Attributes & FOREGROUND_RED) color |= COLOR_RED; + if (cell[x].Attributes & FOREGROUND_BLUE) color |= COLOR_BLUE; + if (cell[x].Attributes & FOREGROUND_GREEN) color |= COLOR_GREEN; + if (cell[x].Attributes & BACKGROUND_RED) color |= COLOR_RED << 3; + if (cell[x].Attributes & BACKGROUND_BLUE) color |= COLOR_BLUE << 3; + if (cell[x].Attributes & BACKGROUND_GREEN) color |= COLOR_GREEN << 3; + + mvaddch(y - data->curcfg.win_pos.Y, x, + (unsigned char)cell[x].Char.UnicodeChar | COLOR_PAIR(color)); + } + } + WCCURSE_PosCursor(data); + refresh(); +} + +/****************************************************************** + * WCCURSE_Scroll + * + * + */ +static void WCCURSE_Scroll(struct inner_data* data, int pos, BOOL horz) +{ + if (horz) + { + data->curcfg.win_pos.X = pos; + } + else + { + data->curcfg.win_pos.Y = pos; + } + WCCURSE_Refresh(data, data->curcfg.win_pos.Y, + data->curcfg.win_pos.Y + data->curcfg.win_height - 1); +} + +static void WCCURSE_SetFont(struct inner_data* data, const WCHAR* font, + unsigned height, unsigned weight) +{ + /* FIXME: really not much to do ? */ +} + +/* 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 +}; + +/****************************************************************** + * WCCURSE_FillSimpleChar + * + * + */ +static unsigned WCCURSE_FillSimpleChar(INPUT_RECORD* ir, unsigned inchar) +{ + unsigned vk; + + WINE_TRACE("[%u]\n", inchar); + + if (inchar == 127) inchar = '\b'; + + ir->EventType = KEY_EVENT; + ir->Event.KeyEvent.bKeyDown = 1; + ir->Event.KeyEvent.wRepeatCount = 1; + ir->Event.KeyEvent.dwControlKeyState = 0; + vk = vkkeyscan_table[inchar]; + if (vk & 0x0100) + ir->Event.KeyEvent.dwControlKeyState |= SHIFT_PRESSED; + if (vk & 0x0200) + ir->Event.KeyEvent.dwControlKeyState |= LEFT_CTRL_PRESSED; + if (vk & 0x0400) + ir->Event.KeyEvent.dwControlKeyState |= LEFT_ALT_PRESSED; + ir->Event.KeyEvent.wVirtualKeyCode = vk; + ir->Event.KeyEvent.wVirtualScanCode = mapvkey_0[vk & 0x00ff]; /* VirtualKeyCodes to ScanCode */ + ir->Event.KeyEvent.uChar.UnicodeChar = (unsigned char)inchar; + + return TRUE; +} + +/****************************************************************** + * WCCURSE_FillComplexChar + * + * + */ +static unsigned WCCURSE_FillComplexChar(INPUT_RECORD* ir, WORD vk, WORD kc) +{ + ir->EventType = KEY_EVENT; + ir->Event.KeyEvent.bKeyDown = 1; + ir->Event.KeyEvent.wRepeatCount = 1; + ir->Event.KeyEvent.dwControlKeyState = 0; + + ir->Event.KeyEvent.wVirtualScanCode = vk; + ir->Event.KeyEvent.wVirtualKeyCode = kc; + ir->Event.KeyEvent.dwControlKeyState |= ENHANCED_KEY; + ir->Event.KeyEvent.uChar.UnicodeChar = 0; + + return TRUE; +} + +/****************************************************************** + * WCCURSE_FillMouse + * + * + */ +static unsigned WCCURSE_FillMouse(INPUT_RECORD* ir) +{ + static unsigned bstate /* = 0 */; + static COORD pos /* = {0, 0} */; + + MEVENT mevt; + BOOL ret = 0; + + if (getmouse(&mevt) == ERR) + return FALSE; + + WINE_TRACE("[%u]: (%d, %d) %08lx\n", + mevt.id, mevt.x, mevt.y, (unsigned long)mevt.bstate); + + /* macros to ease mapping ncurse button numbering to windows's one */ +#define BTN1_BIT FROM_LEFT_1ST_BUTTON_PRESSED +#define BTN2_BIT RIGHTMOST_BUTTON_PRESSED +#define BTN3_BIT FROM_LEFT_2ND_BUTTON_PRESSED +#define BTN4_BIT 0 /* not done yet */ + + /* FIXME: to be checked */ + if (mevt.bstate & BUTTON1_PRESSED) bstate |= BTN1_BIT; + if (mevt.bstate & BUTTON1_RELEASED) bstate &= ~BTN1_BIT; + if (mevt.bstate & BUTTON2_PRESSED) bstate |= BTN2_BIT; + if (mevt.bstate & BUTTON2_RELEASED) bstate &= ~BTN2_BIT; + if (mevt.bstate & BUTTON3_PRESSED) bstate |= BTN3_BIT; + if (mevt.bstate & BUTTON3_RELEASED) bstate &= ~BTN3_BIT; + + /* for the clicked & double click events, since we'll generate automatically + * the release event, we don't have to store the state + */ + if ((mevt.bstate & (BUTTON1_CLICKED|BUTTON1_DOUBLE_CLICKED)) && !(bstate & BTN1_BIT)) + { + ret = BTN1_BIT; + } + if ((mevt.bstate & (BUTTON2_CLICKED|BUTTON2_DOUBLE_CLICKED)) && !(bstate & BTN2_BIT)) + { + ret = BTN2_BIT; + } + if ((mevt.bstate & (BUTTON3_CLICKED|BUTTON3_DOUBLE_CLICKED)) && !(bstate & BTN3_BIT)) + { + ret = BTN3_BIT; + } + + ir->EventType = MOUSE_EVENT; + ir->Event.MouseEvent.dwMousePosition.X = mevt.x; + ir->Event.MouseEvent.dwMousePosition.Y = mevt.y; + + ir->Event.MouseEvent.dwButtonState = (bstate | ret); + + /* partial conversion */ + ir->Event.MouseEvent.dwControlKeyState = 0; + if (mevt.bstate & BUTTON_SHIFT) ir->Event.MouseEvent.dwControlKeyState |= SHIFT_PRESSED; + /* choose to map to left ctrl... could use both ? */ + if (mevt.bstate & BUTTON_CTRL) ir->Event.MouseEvent.dwControlKeyState |= LEFT_CTRL_PRESSED; + /* choose to map to left alt... could use both ? */ + if (mevt.bstate & BUTTON_ALT) ir->Event.MouseEvent.dwControlKeyState |= LEFT_ALT_PRESSED; + /* FIXME: unsupported yet flags: CAPSLOCK_ON, ENHANCED_KEY (??), NUMLOCK_ON, SCROLLLOCK_ON + * could be reported from the key events... + */ + + ir->Event.MouseEvent.dwEventFlags = 0; + if ((mevt.bstate & BUTTON1_DOUBLE_CLICKED) && ((bstate|ret) & BTN1_BIT)) + ir->Event.MouseEvent.dwEventFlags |= DOUBLE_CLICK; + if ((mevt.bstate & BUTTON2_DOUBLE_CLICKED) && ((bstate|ret) & BTN2_BIT)) + ir->Event.MouseEvent.dwEventFlags |= DOUBLE_CLICK; + if ((mevt.bstate & BUTTON3_DOUBLE_CLICKED) && ((bstate|ret) & BTN3_BIT)) + ir->Event.MouseEvent.dwEventFlags |= DOUBLE_CLICK; + if (mevt.x != pos.X || mevt.y != pos.Y) + { + ir->Event.MouseEvent.dwEventFlags |= MOUSE_MOVED; + } + pos.X = mevt.x; pos.Y = mevt.y; + + return ret; +} + +/****************************************************************** + * WCCURSE_FillCode + * + * + */ +static unsigned WCCURSE_FillCode(INPUT_RECORD* ir, int inchar) +{ + unsigned secondEvent = 0; + + switch (inchar) + { + case KEY_BREAK: + goto notFound; + case KEY_DOWN: + secondEvent = WCCURSE_FillComplexChar(ir, 0x50, 0x28); + break; + case KEY_UP: + secondEvent = WCCURSE_FillComplexChar(ir, 0x48, 0x26); + break; + case KEY_LEFT: + secondEvent = WCCURSE_FillComplexChar(ir, 0x4b, 0x25); + break; + case KEY_RIGHT: + secondEvent = WCCURSE_FillComplexChar(ir, 0x4d, 0x27); + break; + case KEY_HOME: + secondEvent = WCCURSE_FillComplexChar(ir, 0x47, 0x24); + break; + case KEY_BACKSPACE: + secondEvent = WCCURSE_FillSimpleChar(ir, '\b'); + break; + + case KEY_F0: /* up to F63 */ + goto notFound; + + case KEY_F( 1): + case KEY_F( 2): + case KEY_F( 3): + case KEY_F( 4): + case KEY_F( 5): + case KEY_F( 6): + case KEY_F( 7): + case KEY_F( 8): + case KEY_F( 9): + case KEY_F(10): + secondEvent = WCCURSE_FillComplexChar(ir, 0x3b + inchar - KEY_F(1), 0); + break; + case KEY_F(11): + case KEY_F(12): + secondEvent = WCCURSE_FillComplexChar(ir, 0xd9 + inchar - KEY_F(11), 0); + break; + + case KEY_DL: + case KEY_IL: + case KEY_DC: + case KEY_IC: + case KEY_EIC: + case KEY_CLEAR: + case KEY_EOS: + case KEY_EOL: + case KEY_SF: + case KEY_SR: + goto notFound; + + case KEY_NPAGE: + secondEvent = WCCURSE_FillComplexChar(ir, 0x51, 0x22); + break; + case KEY_PPAGE: + secondEvent = WCCURSE_FillComplexChar(ir, 0x49, 0x21); + break; + + case KEY_STAB: + case KEY_CTAB: + case KEY_CATAB: + case KEY_ENTER: + case KEY_SRESET: + case KEY_RESET: + case KEY_PRINT: + case KEY_LL: + case KEY_A1: + case KEY_A3: + case KEY_B2: + case KEY_C1: + case KEY_C3: + case KEY_BTAB: + case KEY_BEG: + case KEY_CANCEL: + case KEY_CLOSE: + case KEY_COMMAND: + case KEY_COPY: + case KEY_CREATE: + case KEY_END: + case KEY_EXIT: + case KEY_FIND: + case KEY_HELP: + case KEY_MARK: + case KEY_MESSAGE: + goto notFound; + + case KEY_MOUSE: + secondEvent = WCCURSE_FillMouse(ir); + break; + + case KEY_MOVE: + case KEY_NEXT: + case KEY_OPEN: + case KEY_OPTIONS: + case KEY_PREVIOUS: + case KEY_REDO: + case KEY_REFERENCE: + case KEY_REFRESH: + case KEY_REPLACE: + case KEY_RESIZE: + case KEY_RESTART: + case KEY_RESUME: + case KEY_SAVE: + case KEY_SBEG: + case KEY_SCANCEL: + case KEY_SCOMMAND: + case KEY_SCOPY: + case KEY_SCREATE: + case KEY_SDC: + case KEY_SDL: + case KEY_SELECT: + case KEY_SEND: + case KEY_SEOL: + case KEY_SEXIT: + case KEY_SFIND: + case KEY_SHELP: + case KEY_SHOME: + case KEY_SIC: + case KEY_SLEFT: + case KEY_SMESSAGE: + case KEY_SMOVE: + case KEY_SNEXT: + case KEY_SOPTIONS: + case KEY_SPREVIOUS: + case KEY_SPRINT: + case KEY_SREDO: + case KEY_SREPLACE: + case KEY_SRIGHT: + case KEY_SRSUME: + case KEY_SSAVE: + case KEY_SSUSPEND: + case KEY_SUNDO: + case KEY_SUSPEND: + case KEY_UNDO: + notFound: + WINE_FIXME("Not done yet (%d)\n", inchar); + break; + default: + WINE_ERR("Unknown val (%d)\n", inchar); + break; + } + return secondEvent; +} + +/****************************************************************** + * WCCURSE_GetEvents + * + * + */ +static void WCCURSE_GetEvents(struct inner_data* data) +{ + int inchar; + INPUT_RECORD ir[2]; + unsigned secondEvent = 0; + DWORD n; + + if ((inchar = wgetch(stdscr)) == ERR) {WINE_FIXME("Ooch. somebody beat us\n");return;} + + WINE_TRACE("Got %d\n", inchar); + + ir->EventType = 0; + + if (inchar & KEY_CODE_YES) + { + secondEvent = WCCURSE_FillCode(ir, inchar); + } + else + { + secondEvent = WCCURSE_FillSimpleChar(ir, inchar); + } + + if (secondEvent != 0) + { + ir[1] = ir[0]; + + switch (ir[1].EventType) + { + case KEY_EVENT: + ir[1].Event.KeyEvent.bKeyDown = 0; + break; + case MOUSE_EVENT: + ir[1].Event.MouseEvent.dwButtonState &= ~secondEvent; + break; + default: + WINE_FIXME("oooo\n"); + break; + } + } + if (ir[0].EventType != 0) + WriteConsoleInput(data->hConIn, ir, secondEvent ? 2 : 1, &n); +} + +/****************************************************************** + * openpty + * + * + */ +int openpty(int* master, int* slave, char* name, + struct termios* term, struct winsize* winsize) +{ + const char *ptr1, *ptr2; + char pts_name[512]; + + strcpy(pts_name, "/dev/ptyXY"); + + for (ptr1 = "pqrstuvwxyzPQRST"; *ptr1 != 0; ptr1++) { + pts_name[8] = *ptr1; + for (ptr2 = "0123456789abcdef"; *ptr2 != 0; ptr2++) { + pts_name[9] = *ptr2; + + if ((*master = open(pts_name, O_RDWR)) < 0) { + if (errno == ENOENT) + return -1; + else + continue; + } + pts_name[5] = 't'; + if ((*slave = open(pts_name, O_RDWR)) < 0) { + pts_name[5] = 'p'; + close(*master); + continue; + } + + if (term != NULL) + tcsetattr(*slave, TCSANOW, term); + if (winsize != NULL) + ioctl(*slave, TIOCSWINSZ, winsize); + if (name != NULL) + strcpy(name, pts_name); + return *slave; + } + } + errno = EMFILE; + return -1; +} + +/****************************************************************** + * WCCURSE_SpawnXTerm + * + * + */ +static BOOL WCCURSE_SpawnXTerm(struct inner_data* data) +{ + struct termios term; + char buf[256]; + int i, master, slave, flags; + FILE* fout; + FILE* fin; + + if (tcgetattr(0, &term) < 0) + { + /* ignore failure, or we can run from a script */ + WINE_ERR("Issue\n"); + } + term.c_lflag = ~(ECHO|ICANON); + + if (openpty(&master, &slave, NULL, &term, NULL) < 0) + return FALSE; + + switch (PRIVATE(data)->xtermpid = fork()) + { + case (pid_t)-1: /* error */ + PRIVATE(data)->xtermpid = 0; + close(master); close(slave); + return FALSE; + case 0: /* child */ + 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", "+cm", 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++) + { + char c; + 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; + } + + fcntl(slave, F_GETFL, &flags); + flags |= O_NDELAY; + fcntl(slave, F_SETFL, flags); + + fout = fdopen(slave, "w"); + fin = fdopen(slave, "r"); + + PRIVATE(data)->screen = newterm("xterm", fout, fin); + return !wine_server_fd_to_handle(slave, GENERIC_READ|SYNCHRONIZE, FALSE, + (obj_handle_t*)&PRIVATE(data)->hInput) && + !wine_server_fd_to_handle(slave, GENERIC_WRITE|SYNCHRONIZE, FALSE, + (obj_handle_t*)&PRIVATE(data)->hOutput); +} + +/****************************************************************** + * WCCURSE_InheritUnixConsole + * + * + */ +static BOOL WCCURSE_InheritUnixConsole(struct inner_data* data) +{ + int fdin, fdout; + FILE* fin; + FILE* fout; + + fdin = open("/dev/tty", O_RDONLY); + fdout = open("/dev/tty", O_WRONLY); + + if (wine_server_fd_to_handle(fdin, GENERIC_READ|SYNCHRONIZE, FALSE, + (obj_handle_t*)&PRIVATE(data)->hInput)) + WINE_FIXME("Cannot open 0\n"); + if (wine_server_fd_to_handle(fdout, GENERIC_WRITE|SYNCHRONIZE, FALSE, + (obj_handle_t*)&PRIVATE(data)->hOutput)) + WINE_FIXME("Cannot open 1\n"); + + if (!(fin = fdopen(fdin, "r")) || !(fout = fdopen(fdout, "w"))) + {WINE_FIXME("Cannot fdopen 0/1\n"); return FALSE;} + + PRIVATE(data)->screen = newterm(getenv("TERM"), fout, fin); + PRIVATE(data)->xtermpid = 0; + + /* avoid for this process, when spawning child process attached to this console, + * to go in the background + * keep stderr open for error logging + */ + fclose(stdin); close(0); + fclose(stdout); close(1); + + return TRUE; +} + +/****************************************************************** + * WCCURSE_DeleteBackend + * + * + */ +static void WCCURSE_DeleteBackend(struct inner_data* data) +{ + if (!PRIVATE(data)) return; + + WINE_FIXME("Deleting screen\n"); + + CloseHandle(PRIVATE(data)->hInput); + CloseHandle(PRIVATE(data)->hOutput); + + refresh(); + if (endwin() != OK) + { + WINE_FIXME("failed endwin: %s\n", + tcgetpgrp(0) == getpgrp() ? "foreground" : "background"); + } + delscreen(PRIVATE(data)->screen); + + if (PRIVATE(data)->xtermpid) + kill(PRIVATE(data)->xtermpid, SIGQUIT); + + HeapFree(GetProcessHeap(), 0, PRIVATE(data)); + PRIVATE(data) = NULL; + WINE_FIXME("Screen deleted\n"); +} + +/****************************************************************** + * WCCURSE_MainLoop + * + * + */ +static int WCCURSE_MainLoop(struct inner_data* data) +{ + HANDLE hin[2]; + + hin[0] = PRIVATE(data)->hInput; + hin[1] = data->hSynchro; + + for (;;) + { + switch (WaitForMultipleObjects(2, hin, FALSE, INFINITE)) + { + case WAIT_OBJECT_0: + WCCURSE_GetEvents(data); + break; + case WAIT_OBJECT_0+1: + if (!WINECON_GrabChanges(data)) return 0; + break; + default: + WINE_ERR("got pb\n"); + /* err */ + break; + } + } +} + +/****************************************************************** + * WCCURSE_Init + * + * + */ +static BOOL WCCURSE_Init(struct inner_data* data, int mode) +{ + BOOL ret; + + data->private = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct inner_data_curse)); + if (!data->private) return FALSE; + + data->fnMainLoop = WCCURSE_MainLoop; + data->fnPosCursor = WCCURSE_PosCursor; + data->fnShapeCursor = WCCURSE_ShapeCursor; + data->fnComputePositions = WCCURSE_ComputePositions; + data->fnRefresh = WCCURSE_Refresh; + data->fnResizeScreenBuffer = WCCURSE_ResizeScreenBuffer; + data->fnSetTitle = WCCURSE_SetTitle; + data->fnScroll = WCCURSE_Scroll; + data->fnSetFont = WCCURSE_SetFont; + data->fnDeleteBackend = WCCURSE_DeleteBackend; + + switch (mode) + { + case 0: ret = WCCURSE_InheritUnixConsole(data); break; + case 1: ret = WCCURSE_SpawnXTerm(data); break; + default: WINE_ERR("Unknown init mode (%d)\n", mode); return FALSE; + } + if (!ret) return ret; + + set_term(PRIVATE(data)->screen); + + /* creating the basic colors - FIXME intensity not handled yet */ + if (has_colors()) + { + int i, j; + + start_color(); + for (i = 0; i < 8; i++) + for (j = 0; j < 8; j++) + init_pair(i | (j << 3), i, j); + } + + /* allow key mapping */ + keypad(stdscr, TRUE); + raw(); + noecho(); + nonl(); + intrflush(stdscr, FALSE); + scrollok(stdscr, FALSE); + nodelay(stdscr, TRUE); + mousemask(0xffffffff, &PRIVATE(data)->initial_mouse_mask); + + return TRUE; +} + +/****************************************************************** + * WCCURSE_InitBackend + * + * Initialisation part II: creation of window. + * + */ +BOOL WCCURSE_InitBackend(struct inner_data* data) +{ + return WCCURSE_Init(data, 0); +} + +/****************************************************************** + * WCCURSE_InitBackend + * + * Initialisation part II: creation of window. + * + */ +BOOL WCCURSE_InitBackendXTerm(struct inner_data* data) +{ + return WCCURSE_Init(data, 1); +} + +#else +BOOL WCCURSE_InitBackend(struct inner_data* data) +{ + return FALSE; +} + +BOOL WCCURSE_InitBackendXTerm(struct inner_data* data) +{ + return FALSE; +} +#endif