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