wineconsole: Move the curses critical section inside the private data.
[wine] / programs / wineconsole / curses.c
1 /*
2  * a GUI application for displaying a console
3  *      (N)Curses back end
4  *
5  * Copyright 2002 Eric Pouech
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 /* Known issues & FIXME:
23  * - not all key mapping functions have been written
24  * - allow dyn loading of curses library (extreme care should be taken for 
25  *   functions which can be implemented as macros)
26  * - finish buffer scrolling (mainly, need to decide of a nice way for 
27  *   requesting the UP/DOWN operations
28  */
29
30 #include "config.h"
31 #include "wine/port.h"
32
33 #include <stdio.h>
34 #include <stdarg.h>
35 #include <stdlib.h>
36 #ifdef HAVE_POLL_H
37 # include <poll.h>
38 #endif
39 #ifdef HAVE_NCURSES_H
40 # include <ncurses.h>
41 #elif defined(HAVE_CURSES_H)
42 # include <curses.h>
43 #endif
44 /* avoid redefinition warnings */
45 #undef KEY_EVENT
46 #undef MOUSE_MOVED
47
48 #ifdef HAVE_UNISTD_H
49 #include <unistd.h>
50 #endif
51 #include <windef.h>
52 #include <winbase.h>
53 #include <winnls.h>
54 #include "winecon_private.h"
55
56 #include "wine/library.h"
57 #include "wine/debug.h"
58
59 WINE_DEFAULT_DEBUG_CHANNEL(curses);
60
61 #define PRIVATE(data)   ((struct inner_data_curse*)((data)->private))
62
63 #if defined(SONAME_LIBCURSES) || defined(SONAME_LIBNCURSES)
64
65 #ifdef HAVE_NCURSES_H
66 # define CURSES_NAME "ncurses"
67 #else
68 # define CURSES_NAME "curses"
69 #endif
70
71 struct inner_data_curse 
72 {
73     unsigned long       initial_mouse_mask;
74     int                 sync_pipe[2];
75     HANDLE              input_thread;
76     CRITICAL_SECTION    lock;
77     WINDOW*             pad;
78     chtype*             line;
79     int                 allow_scroll;
80 };
81
82 static void *nc_handle = NULL;
83
84 #ifdef initscr  /* work around Solaris breakage */
85 #undef initscr
86 extern WINDOW *initscr(void);
87 #endif
88
89 #define MAKE_FUNCPTR(f) static typeof(f) * p_##f;
90
91 MAKE_FUNCPTR(curs_set)
92 MAKE_FUNCPTR(delwin)
93 MAKE_FUNCPTR(endwin)
94 #ifndef getmaxx
95 MAKE_FUNCPTR(getmaxx)
96 #endif
97 #ifndef getmaxy
98 MAKE_FUNCPTR(getmaxy)
99 #endif
100 MAKE_FUNCPTR(getmouse)
101 MAKE_FUNCPTR(has_colors)
102 MAKE_FUNCPTR(init_pair)
103 MAKE_FUNCPTR(initscr)
104 #ifndef intrflush
105 MAKE_FUNCPTR(intrflush)
106 #endif
107 MAKE_FUNCPTR(keypad)
108 MAKE_FUNCPTR(newpad)
109 #ifndef nodelay
110 MAKE_FUNCPTR(nodelay)
111 #endif
112 #ifndef noecho
113 MAKE_FUNCPTR(noecho)
114 #endif
115 MAKE_FUNCPTR(prefresh)
116 MAKE_FUNCPTR(raw)
117 MAKE_FUNCPTR(start_color)
118 MAKE_FUNCPTR(stdscr)
119 MAKE_FUNCPTR(waddchnstr)
120 MAKE_FUNCPTR(wmove)
121 MAKE_FUNCPTR(wgetch)
122 #ifdef HAVE_MOUSEMASK
123 MAKE_FUNCPTR(mouseinterval)
124 MAKE_FUNCPTR(mousemask)
125 #endif
126 MAKE_FUNCPTR(acs_map)
127
128 #undef MAKE_FUNCPTR
129
130 /**********************************************************************/
131
132 static BOOL WCCURSES_bind_libcurses(void)
133 {
134 #ifdef SONAME_LIBNCURSES
135     static const char ncname[] = SONAME_LIBNCURSES;
136 #else
137     static const char ncname[] = SONAME_LIBCURSES;
138 #endif
139
140     nc_handle = wine_dlopen(ncname, RTLD_NOW, NULL, 0);
141     if(!nc_handle)
142     {
143         WINE_MESSAGE("Wine cannot find the " CURSES_NAME " library (%s).\n",
144                      ncname);
145         return FALSE;
146     }
147
148 #define LOAD_FUNCPTR(f)                                      \
149     if((p_##f = wine_dlsym(nc_handle, #f, NULL, 0)) == NULL) \
150     {                                                        \
151         WINE_WARN("Can't find symbol %s\n", #f);             \
152         goto sym_not_found;                                  \
153     }
154
155     LOAD_FUNCPTR(curs_set)
156     LOAD_FUNCPTR(delwin)
157     LOAD_FUNCPTR(endwin)
158 #ifndef getmaxx
159     LOAD_FUNCPTR(getmaxx)
160 #endif
161 #ifndef getmaxy
162     LOAD_FUNCPTR(getmaxy)
163 #endif
164     LOAD_FUNCPTR(getmouse)
165     LOAD_FUNCPTR(has_colors)
166     LOAD_FUNCPTR(init_pair)
167     LOAD_FUNCPTR(initscr)
168 #ifndef intrflush
169     LOAD_FUNCPTR(intrflush)
170 #endif
171     LOAD_FUNCPTR(keypad)
172     LOAD_FUNCPTR(newpad)
173 #ifndef nodelay
174     LOAD_FUNCPTR(nodelay)
175 #endif
176 #ifndef noecho
177     LOAD_FUNCPTR(noecho)
178 #endif
179     LOAD_FUNCPTR(prefresh)
180     LOAD_FUNCPTR(raw)
181     LOAD_FUNCPTR(start_color)
182     LOAD_FUNCPTR(stdscr)
183     LOAD_FUNCPTR(waddchnstr)
184     LOAD_FUNCPTR(wmove)
185     LOAD_FUNCPTR(wgetch)
186 #ifdef HAVE_MOUSEMASK
187     LOAD_FUNCPTR(mouseinterval)
188     LOAD_FUNCPTR(mousemask)
189 #endif
190     LOAD_FUNCPTR(acs_map)
191
192 #undef LOAD_FUNCPTR
193
194     return TRUE;
195
196 sym_not_found:
197     WINE_MESSAGE(
198       "Wine cannot find certain functions that it needs inside the "
199        CURSES_NAME "\nlibrary.  To enable Wine to use " CURSES_NAME 
200       " please upgrade your " CURSES_NAME "\nlibraries\n");
201     wine_dlclose(nc_handle, NULL, 0);
202     nc_handle = NULL;
203     return FALSE;
204 }
205
206 #define curs_set p_curs_set
207 #define delwin p_delwin
208 #define endwin p_endwin
209 #ifndef getmaxx
210 #define getmaxx p_getmaxx
211 #endif
212 #ifndef getmaxy
213 #define getmaxy p_getmaxy
214 #endif
215 #define getmouse p_getmouse
216 #define has_colors p_has_colors
217 #define init_pair p_init_pair
218 #define initscr p_initscr
219 #ifndef intrflush
220 #define intrflush p_intrflush
221 #endif
222 #define keypad p_keypad
223 #define mouseinterval p_mouseinterval
224 #define mousemask p_mousemask
225 #define newpad p_newpad
226 #ifndef nodelay
227 #define nodelay p_nodelay
228 #endif
229 #ifndef noecho
230 #define noecho p_noecho
231 #endif
232 #define prefresh p_prefresh
233 #define raw p_raw
234 #define start_color p_start_color
235 #define stdscr (*p_stdscr)
236 #define waddchnstr p_waddchnstr
237 #define wmove p_wmove
238 #define wgetch p_wgetch
239 #define acs_map (*p_acs_map)
240
241 /******************************************************************
242  *              WCCURSES_ResizeScreenBuffer
243  *
244  *
245  */
246 static void WCCURSES_ResizeScreenBuffer(struct inner_data* data)
247 {
248     /* reallocate a new pad. next event would redraw the whole pad */
249     if (PRIVATE(data)->pad) delwin(PRIVATE(data)->pad);
250     PRIVATE(data)->pad = newpad(data->curcfg.sb_height, data->curcfg.sb_width);
251     if (!PRIVATE(data)->pad)
252         WINE_FIXME("Cannot create pad\n");
253     if (PRIVATE(data)->line) 
254         PRIVATE(data)->line = HeapReAlloc(GetProcessHeap(), 0, PRIVATE(data)->line, 
255                                       sizeof(chtype) * data->curcfg.sb_width);
256     else 
257         PRIVATE(data)->line = HeapAlloc(GetProcessHeap(), 0, 
258                                       sizeof(chtype) * data->curcfg.sb_width);
259 }
260
261 /******************************************************************
262  *              WCCURSES_PosCursor
263  *
264  * Set a new position for the cursor (and refresh any modified part of our pad)
265  */
266 static void     WCCURSES_PosCursor(const struct inner_data* data)
267 {
268     int scr_width;
269     int scr_height;
270
271     if (data->curcfg.cursor_visible &&
272         data->cursor.Y >= data->curcfg.win_pos.Y &&
273         data->cursor.Y < data->curcfg.win_pos.Y + data->curcfg.win_height &&
274         data->cursor.X >= data->curcfg.win_pos.X &&
275         data->cursor.X < data->curcfg.win_pos.X + data->curcfg.win_width)
276     {
277         if (curs_set(2) == ERR) curs_set(1);
278         wmove(PRIVATE(data)->pad, data->cursor.Y, data->cursor.X);
279     }
280     else
281     {
282         curs_set(0);
283     }
284     getmaxyx(stdscr, scr_height, scr_width); 
285     prefresh(PRIVATE(data)->pad,
286              data->curcfg.win_pos.Y, data->curcfg.win_pos.X,
287              0, 0, 
288              min(scr_height, data->curcfg.win_height) - 1, 
289              min(scr_width, data->curcfg.win_width) - 1);
290 }
291
292 /******************************************************************
293  *              WCCURSES_ShapeCursor
294  *
295  * Sets a new shape for the cursor
296  */
297 static void     WCCURSES_ShapeCursor(struct inner_data* data, int size, int vis, BOOL force)
298 {
299     /* we can't do much about the size... */
300     data->curcfg.cursor_size = size;
301     data->curcfg.cursor_visible = vis ? TRUE : FALSE;
302     WCCURSES_PosCursor(data);
303 }
304
305 /******************************************************************
306  *              WCCURSES_ComputePositions
307  *
308  * Recomputes all the components (mainly scroll bars) positions
309  */
310 static void     WCCURSES_ComputePositions(struct inner_data* data)
311 {
312     int         x, y;
313
314     getmaxyx(stdscr, y, x);
315     if ((data->curcfg.win_height && y < data->curcfg.win_height) ||
316         (data->curcfg.win_width && x < data->curcfg.win_width))
317     {
318         SMALL_RECT  pos;
319
320         WINE_WARN("Window too large (%dx%d), adjusting to curses' size (%dx%d)\n",
321                   data->curcfg.win_width, data->curcfg.win_height, x, y);
322         pos.Left = pos.Top = 0;
323         pos.Right = x - 1; pos.Bottom = y - 1;
324         SetConsoleWindowInfo(data->hConOut, FALSE, &pos);
325         return; /* we'll get called again upon event for new window size */
326     }
327     if (PRIVATE(data)->pad) WCCURSES_PosCursor(data);
328 }
329
330 /******************************************************************
331  *              WCCURSES_SetTitle
332  *
333  * Sets the title to the wine console
334  */
335 static void     WCCURSES_SetTitle(const struct inner_data* data)
336 {
337     WCHAR   wbuf[256];
338
339     if (WINECON_GetConsoleTitle(data->hConIn, wbuf, sizeof(wbuf)/sizeof(WCHAR)))
340     {
341         char        buffer[256];
342
343         WideCharToMultiByte(CP_UNIXCP, 0, wbuf, -1, buffer, sizeof(buffer),
344                             NULL, NULL);
345         fputs("\033]2;", stdout);
346         fputs(buffer, stdout);
347         fputc('\a', stdout);
348         fflush(stdout);
349     }
350 }
351
352 /******************************************************************
353  *              WCCURSES_Refresh
354  *
355  *
356  */
357 static void WCCURSES_Refresh(const struct inner_data* data, int tp, int bm)
358 {
359     unsigned int x;
360     int         y;
361     CHAR_INFO*  cell;
362     DWORD       attr;
363
364     for (y = tp; y <= bm; y++)
365     {
366         cell = &data->cells[y * data->curcfg.sb_width];
367         for (x = 0; x < data->curcfg.sb_width; x++)
368         {
369             /* check for some mapping to ACS characters (drawing boxes, arrows) */
370             if ((cell[x].Char.UnicodeChar >= 0x2500 && cell[x].Char.UnicodeChar <= 0x257F) ||
371                 (cell[x].Char.UnicodeChar >= 0x2190 && cell[x].Char.UnicodeChar <= 0x21FF))
372             {
373                 /* FIXME: we're also mapping heavy and lines item to single lines
374                  * (that's ugly, but that's better than crap)
375                  * Moreover, as the ACS_ macros refer to values in array acs_map[], we
376                  * cannot simply build static tables for the mapping (FIXME: this could be done
377                  * at load time)
378                  */
379                 switch (cell[x].Char.UnicodeChar)
380                 {
381                 case 0x2190: case 0x219E: case 0x21A2: case 0x21A4:
382                 case 0x21BC: case 0x21BD: case 0x21D0: case 0x21E6: attr = ACS_LARROW;   break;
383                 case 0x2191: case 0x219F: case 0x21A3: case 0x21A5:
384                 case 0x21BE: case 0x21BF: case 0x21D1: case 0x21E7: attr = ACS_UARROW;   break;
385                 case 0x2192: case 0x21A0: case 0x21A6: case 0x21C0:
386                 case 0x21C1: case 0x21D2: case 0x21E8:              attr = ACS_RARROW;   break;
387                 case 0x2193: case 0x21A1: case 0x21A7: case 0x21C2:
388                 case 0x21C3: case 0x21D3: case 0x21E9:              attr = ACS_DARROW;   break;
389
390                 case 0x2500: case 0x2501: case 0x257C: case 0x257E: attr = ACS_HLINE;    break;
391                 case 0x2502: case 0x2503: case 0x257D: case 0x257F: attr = ACS_VLINE;    break;
392                 case 0x250C: case 0x250D: case 0x250E: case 0x250F: attr = ACS_ULCORNER; break;
393                 case 0x2510: case 0x2511: case 0x2512: case 0x2513: attr = ACS_URCORNER; break;
394                 case 0x2514: case 0x2515: case 0x2516: case 0x2517: attr = ACS_LLCORNER; break;
395                 case 0x2518: case 0x2519: case 0x251A: case 0x251B: attr = ACS_LRCORNER; break;
396                 case 0x251C: case 0x251D: case 0x251E: case 0x251F:
397                 case 0x2520: case 0x2521: case 0x2522: case 0x2523: attr = ACS_LTEE;     break;
398                 case 0x2524: case 0x2525: case 0x2526: case 0x2527:
399                 case 0x2528: case 0x2529: case 0x252A: case 0x252B: attr = ACS_RTEE;     break;
400
401                 case 0x252C: case 0x252D: case 0x252E: case 0x252F:
402                 case 0x2530: case 0x2531: case 0x2532: case 0x2533: attr = ACS_TTEE;     break;
403                 case 0x2534: case 0x2535: case 0x2536: case 0x2537:
404                 case 0x2538: case 0x2539: case 0x253A: case 0x253B: attr = ACS_BTEE;     break;
405
406                 case 0x253C: case 0x253D: case 0x253E: case 0x253F:
407                 case 0x2540: case 0x2541: case 0x2542: case 0x2543:
408                 case 0x2544: case 0x2545: case 0x2546: case 0x2547:
409                 case 0x2548: case 0x2549: case 0x254A: case 0x254B: attr = ACS_PLUS;     break;
410
411                 case 0x2550:                                        attr = ACS_HLINE;    break;
412                 case 0x2551:                                        attr = ACS_VLINE;    break;
413                 case 0x2552: case 0x2553: case 0x2554:              attr = ACS_ULCORNER; break;
414                 case 0x2555: case 0x2556: case 0x2557:              attr = ACS_URCORNER; break;
415                 case 0x2558: case 0x2559: case 0x255A:              attr = ACS_LLCORNER; break;
416                 case 0x255B: case 0x255C: case 0x255D:              attr = ACS_LRCORNER; break;
417                 case 0x255E: case 0x255F: case 0x2560:              attr = ACS_LTEE;     break;
418                 case 0x2561: case 0x2562: case 0x2563:              attr = ACS_RTEE;     break;
419                 case 0x2564: case 0x2565: case 0x2566:              attr = ACS_TTEE;     break;
420                 case 0x2567: case 0x2568: case 0x2569:              attr = ACS_BTEE;     break;
421                 case 0x256A: case 0x256B: case 0x256C:              attr = ACS_PLUS;     break;
422                 default:
423                     WINE_FIXME("Unmapped special character (%x)\n", cell[x].Char.UnicodeChar);
424                     attr = ' ';
425                 }
426             }
427             else
428             {
429                 char     ch[2];
430
431                 if (WideCharToMultiByte(CP_UNIXCP, 0, &cell[x].Char.UnicodeChar, 1,
432                                         ch, sizeof(ch), NULL, NULL) == 1)
433                     attr = ((BYTE)ch[0] < 32) ? 32 : (BYTE)ch[0];
434                 else
435                     attr = 32;
436             }
437
438             if (cell[x].Attributes & FOREGROUND_RED)       attr |= COLOR_PAIR(COLOR_RED);
439             if (cell[x].Attributes & FOREGROUND_BLUE)      attr |= COLOR_PAIR(COLOR_BLUE);
440             if (cell[x].Attributes & FOREGROUND_GREEN)     attr |= COLOR_PAIR(COLOR_GREEN);
441             if (cell[x].Attributes & BACKGROUND_RED)       attr |= COLOR_PAIR(COLOR_RED << 3);
442             if (cell[x].Attributes & BACKGROUND_BLUE)      attr |= COLOR_PAIR(COLOR_BLUE << 3);
443             if (cell[x].Attributes & BACKGROUND_GREEN)     attr |= COLOR_PAIR(COLOR_GREEN << 3);
444
445             if (cell[x].Attributes & FOREGROUND_INTENSITY) attr |= A_BOLD;
446             PRIVATE(data)->line[x] = attr;
447         }
448         mvwaddchnstr(PRIVATE(data)->pad, y, 0, PRIVATE(data)->line, data->curcfg.sb_width);
449     }
450
451     WCCURSES_PosCursor(data);
452 }
453
454 /******************************************************************
455  *              WCCURSES_Scroll
456  *
457  *
458  */
459 static void WCCURSES_Scroll(struct inner_data* data, int pos, BOOL horz)
460 {
461     if (horz)
462     {
463         data->curcfg.win_pos.X = pos;
464     }
465     else
466     {
467         data->curcfg.win_pos.Y = pos;
468     }
469     WCCURSES_PosCursor(data);
470 }
471
472 /******************************************************************
473  *              WCCURSES_SetFont
474  *
475  *
476  */
477 static void WCCURSES_SetFont(struct inner_data* data, const WCHAR* font, 
478                             unsigned height, unsigned weight)
479 {
480     /* FIXME: really not much to do ? */
481 }
482
483 /******************************************************************
484  *              WCCURSES_Resize
485  *
486  */
487 static void WCCURSES_Resize(struct inner_data* data)
488 {
489     int width, height;
490
491     getmaxyx(stdscr, height, width);
492     WINECON_ResizeWithContainer(data, width, height);
493 }
494
495 /******************************************************************
496  *              WCCURSES_ScrollV
497  *
498  *
499  */
500 static void WCCURSES_ScrollV(struct inner_data* data, int delta)
501 {
502     struct config_data  cfg = data->curcfg;
503
504     cfg.win_pos.Y += delta;
505     if (cfg.win_pos.Y < 0) cfg.win_pos.Y = 0;
506     if (cfg.win_pos.Y > data->curcfg.sb_height - data->curcfg.win_height)
507         cfg.win_pos.Y = data->curcfg.sb_height - data->curcfg.win_height;
508     if (cfg.win_pos.Y != data->curcfg.win_pos.Y)
509     {
510         WCCURSES_PosCursor(data);
511         WINECON_SetConfig(data, &cfg);
512     }
513 }
514
515 /* Ascii -> VK, generated by calling VkKeyScanA(i) */
516 static const int vkkeyscan_table[256] =
517 {
518      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,
519      0,32,305,478,307,308,309,311,222,313,304,312,443,188,189,190,191,48,
520      49,50,51,52,53,54,55,56,57,442,186,444,187,446,447,306,321,322,323,
521      324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,
522      341,342,343,344,345,346,219,220,221,310,445,192,65,66,67,68,69,70,71,
523      72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,475,476,477,
524      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,
525      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
526      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
527      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
528 };
529
530 static const int mapvkey_0[256] =
531 {
532      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,
533      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,
534      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,
535      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,
536      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,
537      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,
538      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,
539      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,
540      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
541 }; 
542
543 /******************************************************************
544  *              WCCURSES_InitComplexChar
545  *
546  *
547  */
548 static inline void WCCURSES_InitComplexChar(INPUT_RECORD* ir, BOOL down, WORD vk, WORD kc, DWORD cks)
549 {
550     ir->EventType                        = KEY_EVENT;
551     ir->Event.KeyEvent.bKeyDown          = down;
552     ir->Event.KeyEvent.wRepeatCount      = 1;
553     
554     ir->Event.KeyEvent.wVirtualScanCode  = vk;
555     ir->Event.KeyEvent.wVirtualKeyCode   = kc;
556     ir->Event.KeyEvent.dwControlKeyState = cks;
557     ir->Event.KeyEvent.uChar.UnicodeChar = 0;
558 }
559
560 /******************************************************************
561  *              WCCURSES_FillSimpleChar
562  *
563  *
564  */
565 static unsigned WCCURSES_FillSimpleChar(INPUT_RECORD* ir, unsigned real_inchar)
566 {
567     unsigned vk;
568     unsigned inchar;
569     char ch;
570     unsigned numEvent = 0;
571     DWORD    cks = 0;
572
573     switch (real_inchar)
574     {
575     case   9: inchar = real_inchar;
576               real_inchar = 27; /* so that we don't think key is ctrl- something */     
577               break;
578     case  10: inchar = '\r'; 
579               real_inchar = 27; /* Fixme: so that we don't think key is ctrl- something */ 
580               break;
581     case 127: inchar = '\b'; 
582               break;
583     case  27:
584         /* we assume that ESC & and the second character are atomically
585          * generated otherwise, we'll have a race here. FIXME: This gives 1 sec. delay
586          * because curses looks for a second character.
587          */
588         if ((inchar = wgetch(stdscr)) != ERR)
589         {
590             /* we got a alt-something key... */
591             cks = LEFT_ALT_PRESSED;
592         }
593         else
594             inchar = 27;
595         break;
596     default:
597         inchar = real_inchar;
598         break;
599     }
600     if ((inchar & ~0xFF) != 0) WINE_FIXME("What a char (%u)\n", inchar);
601     vk = vkkeyscan_table[inchar];
602     if (vk & 0x0100)
603         WCCURSES_InitComplexChar(&ir[numEvent++], 1, 0x2a, 0x10, SHIFT_PRESSED);
604     if ((vk & 0x0200) || (unsigned char)real_inchar <= 26)
605         WCCURSES_InitComplexChar(&ir[numEvent++], 1, 0x1d, 0x11, LEFT_CTRL_PRESSED);
606     if (vk & 0x0400)
607         WCCURSES_InitComplexChar(&ir[numEvent++], 1, 0x38, 0x12, LEFT_ALT_PRESSED);
608
609     ir[numEvent].EventType                        = KEY_EVENT;
610     ir[numEvent].Event.KeyEvent.bKeyDown          = 1;
611     ir[numEvent].Event.KeyEvent.wRepeatCount      = 1;
612     ir[numEvent].Event.KeyEvent.dwControlKeyState = cks;
613     if (vk & 0x0100)
614         ir[numEvent].Event.KeyEvent.dwControlKeyState |= SHIFT_PRESSED;
615     if ((vk & 0x0200) || (unsigned char)real_inchar <= 26)
616         ir[numEvent].Event.KeyEvent.dwControlKeyState |= LEFT_CTRL_PRESSED;
617     if (vk & 0x0400)
618         ir[numEvent].Event.KeyEvent.dwControlKeyState |= LEFT_ALT_PRESSED;
619     ir[numEvent].Event.KeyEvent.wVirtualKeyCode = vk;
620     ir[numEvent].Event.KeyEvent.wVirtualScanCode = mapvkey_0[vk & 0x00ff]; /* VirtualKeyCodes to ScanCode */
621
622     ch = inchar;
623     MultiByteToWideChar(CP_UNIXCP, 0,&ch,1,&ir[numEvent].Event.KeyEvent.uChar.UnicodeChar, 1);
624     ir[numEvent + 1] = ir[numEvent];
625     ir[numEvent + 1].Event.KeyEvent.bKeyDown      = 0;
626
627     numEvent += 2;
628
629     if (vk & 0x0400)
630         WCCURSES_InitComplexChar(&ir[numEvent++], 0, 0x38, 0x12, LEFT_ALT_PRESSED);
631     if ((vk & 0x0200) || (unsigned char)real_inchar <= 26)
632         WCCURSES_InitComplexChar(&ir[numEvent++], 0, 0x1d, 0x11, 0);
633     if (vk & 0x0100)
634         WCCURSES_InitComplexChar(&ir[numEvent++], 0, 0x2a, 0x10, 0);
635
636     return numEvent;
637 }
638
639 /******************************************************************
640  *              WCCURSES_FillComplexChar
641  *
642  *
643  */
644 static unsigned WCCURSES_FillComplexChar(INPUT_RECORD* ir, WORD vk, WORD kc, DWORD cks)
645 {
646     WCCURSES_InitComplexChar(&ir[0], 1, vk, kc, ENHANCED_KEY | cks);
647     WCCURSES_InitComplexChar(&ir[1], 0, vk, kc, ENHANCED_KEY | cks);
648
649     return 2;
650 }
651
652 /******************************************************************
653  *              WCCURSES_FillMouse
654  *
655  *
656  */
657 static unsigned WCCURSES_FillMouse(INPUT_RECORD* ir)
658 {
659 #ifdef HAVE_MOUSEMASK
660     static      unsigned        bstate /* = 0 */;
661     static      COORD           pos /* = {0, 0} */;
662
663     MEVENT      mevt;
664
665     if (getmouse(&mevt) == ERR)
666         return 0;
667
668     WINE_TRACE("[%u]: (%d, %d) %08lx\n", 
669                mevt.id, mevt.x, mevt.y, (unsigned long)mevt.bstate);
670
671     /* macros to ease mapping ncurse button numbering to windows's one */
672 #define BTN1_BIT        FROM_LEFT_1ST_BUTTON_PRESSED
673 #define BTN2_BIT        RIGHTMOST_BUTTON_PRESSED
674 #define BTN3_BIT        FROM_LEFT_2ND_BUTTON_PRESSED
675 #define BTN4_BIT        0 /* not done yet */
676
677     if (mevt.bstate & BUTTON1_PRESSED)   bstate |= BTN1_BIT;
678     if (mevt.bstate & BUTTON1_RELEASED)  bstate &= ~BTN1_BIT;
679     if (mevt.bstate & BUTTON2_PRESSED)   bstate |= BTN2_BIT;
680     if (mevt.bstate & BUTTON2_RELEASED)  bstate &= ~BTN2_BIT;
681     if (mevt.bstate & BUTTON3_PRESSED)   bstate |= BTN3_BIT;
682     if (mevt.bstate & BUTTON3_RELEASED)  bstate &= ~BTN3_BIT;
683
684     ir->EventType = MOUSE_EVENT;
685     ir->Event.MouseEvent.dwMousePosition.X = mevt.x;
686     ir->Event.MouseEvent.dwMousePosition.Y = mevt.y;
687
688     ir->Event.MouseEvent.dwButtonState = bstate;
689
690     /* partial conversion */
691     ir->Event.MouseEvent.dwControlKeyState = 0;
692     if (mevt.bstate & BUTTON_SHIFT)     ir->Event.MouseEvent.dwControlKeyState |= SHIFT_PRESSED;
693     /* choose to map to left ctrl... could use both ? */
694     if (mevt.bstate & BUTTON_CTRL)      ir->Event.MouseEvent.dwControlKeyState |= LEFT_CTRL_PRESSED;
695     /* choose to map to left alt... could use both ? */
696     if (mevt.bstate & BUTTON_ALT)       ir->Event.MouseEvent.dwControlKeyState |= LEFT_ALT_PRESSED;
697     /* FIXME: unsupported yet flags: CAPSLOCK_ON, ENHANCED_KEY (??), NUMLOCK_ON, SCROLLLOCK_ON 
698      * could be reported from the key events...
699      */
700
701     ir->Event.MouseEvent.dwEventFlags = 0;
702     /* FIXME: we no longer generate double click events */
703
704     if (!(mevt.bstate & (BUTTON1_PRESSED|BUTTON1_RELEASED|BUTTON2_PRESSED|BUTTON2_RELEASED|BUTTON3_PRESSED|BUTTON3_RELEASED)) &&
705         (mevt.x != pos.X || mevt.y != pos.Y))
706     {
707         ir->Event.MouseEvent.dwEventFlags |= MOUSE_MOVED;
708     }
709     pos.X = mevt.x; pos.Y = mevt.y;
710
711     return 1;
712 #else
713     return 0;
714 #endif
715 }
716
717 /******************************************************************
718  *              WCCURSES_FillCode
719  *
720  *
721  */
722 static unsigned WCCURSES_FillCode(struct inner_data* data, INPUT_RECORD* ir, int inchar)
723 {
724     unsigned numEvent = 0;
725     
726     switch (inchar)
727     {
728     case KEY_BREAK:
729         goto notFound;
730     case KEY_DOWN:
731         numEvent = WCCURSES_FillComplexChar(ir, 0x50, 0x28, 0);
732         break;
733     case KEY_UP:
734         numEvent = WCCURSES_FillComplexChar(ir, 0x48, 0x26, 0);
735         break;
736     case KEY_LEFT:
737         numEvent = WCCURSES_FillComplexChar(ir, 0x4b, 0x25, 0);
738         break;
739     case KEY_RIGHT:
740         numEvent = WCCURSES_FillComplexChar(ir, 0x4d, 0x27, 0);
741         break;
742     case KEY_HOME:
743         numEvent = WCCURSES_FillComplexChar(ir, 0x47, 0x24, 0);
744         break;
745     case KEY_BACKSPACE:
746         numEvent = WCCURSES_FillSimpleChar(ir, 127);
747         break;
748         
749     case KEY_F0: /* up to F63 */
750         goto notFound;
751                     
752     case KEY_F( 1):
753     case KEY_F( 2):
754     case KEY_F( 3):
755     case KEY_F( 4):
756     case KEY_F( 5):
757     case KEY_F( 6):
758     case KEY_F( 7):
759     case KEY_F( 8):
760     case KEY_F( 9):
761     case KEY_F(10):
762         numEvent = WCCURSES_FillComplexChar(ir, 0x3b + inchar - KEY_F(1),
763                                             0x70 + inchar - KEY_F(1), 0);
764         break;
765     case KEY_F(11):
766     case KEY_F(12):
767         if (PRIVATE(data)->allow_scroll)
768         {
769             WCCURSES_ScrollV(data, inchar == KEY_F(11) ? 8 : -8);
770         }
771         else
772         {
773             numEvent = WCCURSES_FillComplexChar(ir, 0xd9 + inchar - KEY_F(11),
774                                                 0x7a + inchar - KEY_F(11), 0);
775         }
776         break;
777                     
778     case KEY_DL:
779     case KEY_IL:
780         goto notFound;
781
782     case KEY_DC:
783         numEvent = WCCURSES_FillComplexChar(ir, 0x53, 0x2e, 0);
784         break;
785     case KEY_IC:
786         numEvent = WCCURSES_FillComplexChar(ir, 0x52, 0x2d, 0);
787         break;
788
789     case KEY_EIC:
790     case KEY_CLEAR:
791     case KEY_EOS:
792     case KEY_EOL:
793     case KEY_SF:
794     case KEY_SR:
795         goto notFound;
796                     
797     case KEY_NPAGE:
798         numEvent = WCCURSES_FillComplexChar(ir, 0x51, 0x22, 0);
799         break;
800     case KEY_PPAGE:
801         numEvent = WCCURSES_FillComplexChar(ir, 0x49, 0x21, 0);
802         break;
803         
804     case KEY_STAB:
805     case KEY_CTAB:
806     case KEY_CATAB:
807     case KEY_ENTER:
808     case KEY_SRESET:
809     case KEY_RESET:
810     case KEY_PRINT:
811     case KEY_LL:
812     case KEY_A1:
813     case KEY_A3:
814     case KEY_B2:
815     case KEY_C1:
816     case KEY_C3:
817         goto notFound;
818     case KEY_BTAB:      /* shift tab */
819         numEvent = WCCURSES_FillSimpleChar(ir, 0x9);
820         ir[0].Event.KeyEvent.dwControlKeyState |= SHIFT_PRESSED;
821         ir[1].Event.KeyEvent.dwControlKeyState |= SHIFT_PRESSED;        
822         if (numEvent != 2) WINE_ERR("FillsimpleChar has changed\n");
823         break;
824
825     case KEY_BEG:
826     case KEY_CANCEL:
827     case KEY_CLOSE:
828     case KEY_COMMAND:
829     case KEY_COPY:
830     case KEY_CREATE:
831         goto notFound;
832
833     case KEY_END:
834         numEvent = WCCURSES_FillComplexChar(ir, 0x4f, 0x23, 0);
835         break;
836         
837     case KEY_EXIT:
838     case KEY_FIND:
839     case KEY_HELP:
840     case KEY_MARK:
841     case KEY_MESSAGE:
842         goto notFound;
843                     
844     case KEY_MOUSE:
845         numEvent = WCCURSES_FillMouse(ir);
846         break;
847 #ifdef KEY_RESIZE
848     case KEY_RESIZE:
849         WCCURSES_Resize(data);
850         break;
851 #endif
852
853     case KEY_MOVE:
854     case KEY_NEXT:
855     case KEY_OPEN:
856     case KEY_OPTIONS:
857     case KEY_PREVIOUS:
858     case KEY_REDO:
859     case KEY_REFERENCE:
860     case KEY_REFRESH:
861     case KEY_REPLACE:
862     case KEY_RESTART:
863     case KEY_RESUME:
864     case KEY_SAVE:
865     case KEY_SBEG:
866     case KEY_SCANCEL:
867     case KEY_SCOMMAND:
868     case KEY_SCOPY:
869     case KEY_SCREATE:
870         goto notFound;
871
872     case KEY_SDC:
873         numEvent = WCCURSES_FillComplexChar(ir, 0x53, 0x2e, SHIFT_PRESSED);
874         break;
875     case KEY_SDL:
876     case KEY_SELECT:
877         goto notFound;
878
879     case KEY_SEND:
880         numEvent = WCCURSES_FillComplexChar(ir, 0x4f, 0x23, SHIFT_PRESSED);
881         break;
882
883     case KEY_SEOL:
884     case KEY_SEXIT:
885     case KEY_SFIND:
886     case KEY_SHELP:
887         goto notFound;
888
889     case KEY_SHOME:
890         numEvent = WCCURSES_FillComplexChar(ir, 0x47, 0x24, SHIFT_PRESSED);
891         break;
892     case KEY_SIC:
893         numEvent = WCCURSES_FillComplexChar(ir, 0x52, 0x2d, SHIFT_PRESSED);
894         break;
895     case KEY_SLEFT:
896         numEvent = WCCURSES_FillComplexChar(ir, 0x4b, 0x25, SHIFT_PRESSED);
897         break;
898
899     case KEY_SMESSAGE:
900     case KEY_SMOVE:
901     case KEY_SNEXT:
902     case KEY_SOPTIONS:
903     case KEY_SPREVIOUS:
904     case KEY_SPRINT:
905     case KEY_SREDO:
906     case KEY_SREPLACE:
907         goto notFound;
908
909     case KEY_SRIGHT:
910         numEvent = WCCURSES_FillComplexChar(ir, 0x4d, 0x27, SHIFT_PRESSED);
911         break;
912
913     case KEY_SRSUME:
914     case KEY_SSAVE:
915     case KEY_SSUSPEND:
916     case KEY_SUNDO:
917     case KEY_SUSPEND:
918     case KEY_UNDO:
919     notFound:
920         WINE_FIXME("Not done yet (%o)\n", inchar);
921         break;
922     default:
923         WINE_ERR("Unknown val (%o)\n", inchar);
924         break;
925     }
926     return numEvent;
927 }
928
929 /******************************************************************
930  *              input_thread
931  */
932 static DWORD CALLBACK input_thread( void *arg )
933 {
934     struct inner_data* data = arg;
935     int                 inchar;
936     INPUT_RECORD        ir[8];
937     unsigned            numEvent;
938     DWORD               n;
939     struct pollfd pfd[2];
940
941     pfd[0].fd = 0;
942     pfd[0].events = POLLIN;
943     pfd[1].fd = PRIVATE(data)->sync_pipe[0];
944     pfd[1].events = POLLHUP;
945
946     for (;;)
947     {
948         pfd[0].revents = pfd[1].revents = 0;
949         if (poll( pfd, 2, -1 ) == -1) break;
950         if (pfd[0].revents & (POLLHUP|POLLERR)) break;
951         if (pfd[1].revents & (POLLHUP|POLLERR)) break;
952         if (!(pfd[0].revents & POLLIN)) continue;
953
954         /* we're called from input thread (not main thread), so force unique access */
955         EnterCriticalSection(&PRIVATE(data)->lock);
956         if ((inchar = wgetch(stdscr)) != ERR)
957         {
958             WINE_TRACE("Got o%o (0x%x)\n", inchar,inchar);
959
960             if (inchar >= KEY_MIN && inchar <= KEY_MAX)
961                 numEvent = WCCURSES_FillCode(data, ir, inchar);
962             else
963                 numEvent = WCCURSES_FillSimpleChar(ir, inchar);
964
965             if (numEvent) WriteConsoleInputW(data->hConIn, ir, numEvent, &n);
966         }
967         LeaveCriticalSection(&PRIVATE(data)->lock);
968     }
969     close( PRIVATE(data)->sync_pipe[0] );
970     return 0;
971 }
972
973 /******************************************************************
974  *              WCCURSES_DeleteBackend
975  *
976  *
977  */
978 static void WCCURSES_DeleteBackend(struct inner_data* data)
979 {
980     if (!PRIVATE(data)) return;
981
982     if (PRIVATE(data)->input_thread)
983     {
984         close( PRIVATE(data)->sync_pipe[1] );
985         WaitForSingleObject( PRIVATE(data)->input_thread, INFINITE );
986         CloseHandle( PRIVATE(data)->input_thread );
987     }
988     DeleteCriticalSection(&PRIVATE(data)->lock);
989
990     delwin(PRIVATE(data)->pad);
991 #ifdef HAVE_MOUSEMASK
992     {
993         mmask_t mm;
994         mousemask(PRIVATE(data)->initial_mouse_mask, &mm);
995     }
996 #endif
997     endwin();
998
999     HeapFree(GetProcessHeap(), 0, PRIVATE(data)->line);
1000     HeapFree(GetProcessHeap(), 0, PRIVATE(data));
1001     data->private = NULL;
1002 }
1003
1004 /******************************************************************
1005  *              WCCURSES_MainLoop
1006  *
1007  *
1008  */
1009 static int WCCURSES_MainLoop(struct inner_data* data)
1010 {
1011     DWORD       id;
1012     BOOL        cont = TRUE;
1013
1014     WCCURSES_Resize(data);
1015
1016     if (pipe( PRIVATE(data)->sync_pipe ) == -1) return 0;
1017     PRIVATE(data)->input_thread = CreateThread( NULL, 0, input_thread, data, 0, &id );
1018
1019     while (cont && WaitForSingleObject(data->hSynchro, INFINITE) == WAIT_OBJECT_0)
1020     {
1021         EnterCriticalSection(&PRIVATE(data)->lock);
1022         cont = WINECON_GrabChanges(data);
1023         LeaveCriticalSection(&PRIVATE(data)->lock);
1024     }
1025
1026     close( PRIVATE(data)->sync_pipe[1] );
1027     WaitForSingleObject( PRIVATE(data)->input_thread, INFINITE );
1028     CloseHandle( PRIVATE(data)->input_thread );
1029     PRIVATE(data)->input_thread = 0;
1030     return 0;
1031 }
1032
1033 /******************************************************************
1034  *              WCCURSES_InitBackend
1035  *
1036  * Initialisation part II: creation of window.
1037  *
1038  */
1039 enum init_return WCCURSES_InitBackend(struct inner_data* data)
1040 {
1041     if( !WCCURSES_bind_libcurses() )
1042         return init_failed;
1043
1044     data->private = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct inner_data_curse));
1045     if (!data->private) return init_failed;
1046
1047     data->fnMainLoop           = WCCURSES_MainLoop;
1048     data->fnPosCursor          = WCCURSES_PosCursor;
1049     data->fnShapeCursor        = WCCURSES_ShapeCursor;
1050     data->fnComputePositions   = WCCURSES_ComputePositions;
1051     data->fnRefresh            = WCCURSES_Refresh;
1052     data->fnResizeScreenBuffer = WCCURSES_ResizeScreenBuffer;
1053     data->fnSetTitle           = WCCURSES_SetTitle;
1054     data->fnScroll             = WCCURSES_Scroll;
1055     data->fnSetFont            = WCCURSES_SetFont;
1056     data->fnDeleteBackend      = WCCURSES_DeleteBackend;
1057     data->hWnd                 = NULL;
1058
1059     /* FIXME: should find a good way to enable buffer scrolling
1060      * For the time being, setting this to 1 will allow scrolling up/down 
1061      * on buffer with F11/F12.
1062      */
1063     /* PRIVATE(data)->allow_scroll = 1; */
1064
1065     initscr();
1066
1067     /* creating the basic colors - FIXME intensity not handled yet */
1068     if (has_colors())
1069     {
1070         int i, j;
1071
1072         start_color();
1073         for (i = 0; i < 8; i++)
1074             for (j = 0; j < 8; j++)
1075                 init_pair(i | (j << 3), i, j);
1076     }
1077
1078     raw();
1079     noecho();
1080     intrflush(stdscr, FALSE);
1081     nodelay(stdscr, TRUE);
1082     keypad(stdscr, TRUE);
1083 #ifdef HAVE_MOUSEMASK
1084     if (data->curcfg.quick_edit)
1085     {
1086         mmask_t mm;
1087         mousemask(BUTTON1_PRESSED|BUTTON1_RELEASED|
1088                   BUTTON2_PRESSED|BUTTON2_RELEASED|
1089                   BUTTON3_PRESSED|BUTTON3_RELEASED|
1090                   BUTTON_SHIFT|BUTTON_CTRL|BUTTON_ALT|REPORT_MOUSE_POSITION,
1091                   &mm);
1092         /* no click event generation... we just need button up/down events
1093          * it doesn't seem that mouseinterval(-1) behaves as documented... 
1094          * 0 seems to be better value to disable click event generation
1095          */
1096         mouseinterval(0);
1097         PRIVATE(data)->initial_mouse_mask = mm;
1098     }
1099     else
1100     {
1101         mmask_t mm;
1102         mousemask(0, &mm);
1103         PRIVATE(data)->initial_mouse_mask = mm;
1104     }
1105 #endif
1106     InitializeCriticalSection(&PRIVATE(data)->lock);
1107
1108     return init_success;
1109 }
1110
1111 #else
1112 enum init_return WCCURSES_InitBackend(struct inner_data* data)
1113 {
1114     WINE_ERR("(n)curses was not found at configuration time.\n"
1115              "If you want (n)curses support, please install relevant packages.\n");
1116     return init_not_supported;
1117 }
1118 #endif