(n)curse backend for wineconsole

Eric Pouech eric.pouech at wanadoo.fr
Sat Nov 9 12:40:46 CST 2002


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+
-------------- next part --------------
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


More information about the wine-patches mailing list