Fixed some issues found by winapi_check.
[wine] / programs / wineconsole / user.c
1 /*
2  * a GUI application for displaying a console
3  *      USER32 back end
4  * Copyright 2001 Eric Pouech
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include "winecon_user.h"
24
25 #include "wine/debug.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(wineconsole);
28 WINE_DECLARE_DEBUG_CHANNEL(wc_font);
29
30 /* mapping console colors to RGB values */
31 COLORREF        WCUSER_ColorMap[16] =
32 {
33     RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x80), RGB(0x00, 0x80, 0x00), RGB(0x00, 0x80, 0x80),
34     RGB(0x80, 0x00, 0x00), RGB(0x80, 0x00, 0x80), RGB(0x80, 0x80, 0x00), RGB(0x80, 0x80, 0x80),
35     RGB(0xC0, 0xC0, 0xC0), RGB(0x00, 0x00, 0xFF), RGB(0x00, 0xFF, 0x00), RGB(0x00, 0xFF, 0xFF),
36     RGB(0xFF, 0x00, 0x00), RGB(0xFF, 0x00, 0xFF), RGB(0xFF, 0xFF, 0x00), RGB(0xFF, 0xFF, 0xFF),
37 };
38
39 static BOOL WCUSER_SetFont(struct inner_data* data, const LOGFONT* font);
40
41 /******************************************************************
42  *              WCUSER_FillMemDC
43  *
44  * Fills the Mem DC with current cells values
45  */
46 static void WCUSER_FillMemDC(const struct inner_data* data, int upd_tp, int upd_bm)
47 {
48     unsigned            i, j, k;
49     CHAR_INFO*          cell;
50     HFONT               hOldFont;
51     WORD                attr;
52     WCHAR*              line;
53
54     /* no font has been set up yet, don't worry about filling the bitmap,
55      * we'll do it once a font is chosen
56      */
57     if (!PRIVATE(data)->hFont) return;
58
59     /* FIXME: could set up a mechanism to reuse the line between different
60      * calls to this function
61      */
62     if (!(line = HeapAlloc(GetProcessHeap(), 0, data->curcfg.sb_width * sizeof(WCHAR))))
63         WINECON_Fatal("OOM\n");
64
65     hOldFont = SelectObject(PRIVATE(data)->hMemDC, PRIVATE(data)->hFont);
66     for (j = upd_tp; j <= upd_bm; j++)
67     {
68         cell = &data->cells[j * data->curcfg.sb_width];
69         for (i = 0; i < data->curcfg.sb_width; i++)
70         {
71             attr = cell[i].Attributes;
72             SetBkColor(PRIVATE(data)->hMemDC, WCUSER_ColorMap[(attr>>4)&0x0F]);
73             SetTextColor(PRIVATE(data)->hMemDC, WCUSER_ColorMap[attr&0x0F]);
74             for (k = i; k < data->curcfg.sb_width && cell[k].Attributes == attr; k++)
75             {
76                 line[k - i] = cell[k].Char.UnicodeChar;
77             }
78             TextOut(PRIVATE(data)->hMemDC, i * data->curcfg.cell_width, j * data->curcfg.cell_height,
79                     line, k - i);
80             i = k - 1;
81         }
82     }
83     SelectObject(PRIVATE(data)->hMemDC, hOldFont);
84     HeapFree(GetProcessHeap(), 0, line);
85 }
86
87 /******************************************************************
88  *              WCUSER_NewBitmap
89  *
90  * Either the font geometry or the sb geometry has changed. we need
91  * to recreate the bitmap geometry.
92  */
93 static void WCUSER_NewBitmap(struct inner_data* data)
94 {
95     HDC         hDC;
96     HBITMAP     hnew, hold;
97
98     if (!data->curcfg.sb_width || !data->curcfg.sb_height ||
99         !PRIVATE(data)->hFont || !(hDC = GetDC(PRIVATE(data)->hWnd)))
100         return;
101     hnew = CreateCompatibleBitmap(hDC,
102                                   data->curcfg.sb_width  * data->curcfg.cell_width,
103                                   data->curcfg.sb_height * data->curcfg.cell_height);
104     ReleaseDC(PRIVATE(data)->hWnd, hDC);
105     hold = SelectObject(PRIVATE(data)->hMemDC, hnew);
106
107     if (PRIVATE(data)->hBitmap)
108     {
109         if (hold == PRIVATE(data)->hBitmap)
110             DeleteObject(PRIVATE(data)->hBitmap);
111         else
112             WINE_FIXME("leak\n");
113     }
114     PRIVATE(data)->hBitmap = hnew;
115     WCUSER_FillMemDC(data, 0, data->curcfg.sb_height - 1);
116 }
117
118 /******************************************************************
119  *              WCUSER_ResizeScreenBuffer
120  *
121  *
122  */
123 static void WCUSER_ResizeScreenBuffer(struct inner_data* data)
124 {
125     WCUSER_NewBitmap(data);
126 }
127
128 /******************************************************************
129  *              WCUSER_PosCursor
130  *
131  * Set a new position for the cursor
132  */
133 static void     WCUSER_PosCursor(const struct inner_data* data)
134 {
135     if (PRIVATE(data)->hWnd != GetFocus() || !data->curcfg.cursor_visible) return;
136
137     SetCaretPos((data->cursor.X - data->curcfg.win_pos.X) * data->curcfg.cell_width,
138                 (data->cursor.Y - data->curcfg.win_pos.Y) * data->curcfg.cell_height);
139     ShowCaret(PRIVATE(data)->hWnd);
140 }
141
142 /******************************************************************
143  *              WCUSER_ShapeCursor
144  *
145  * Sets a new shape for the cursor
146  */
147 static void     WCUSER_ShapeCursor(struct inner_data* data, int size, int vis, BOOL force)
148 {
149     if (force || size != data->curcfg.cursor_size)
150     {
151         if (data->curcfg.cursor_visible && PRIVATE(data)->hWnd == GetFocus()) DestroyCaret();
152         if (PRIVATE(data)->cursor_bitmap) DeleteObject(PRIVATE(data)->cursor_bitmap);
153         PRIVATE(data)->cursor_bitmap = (HBITMAP)0;
154         if (size != 100)
155         {
156             int         w16b; /* number of bytes per row, aligned on word size */
157             BYTE*       ptr;
158             int         i, j, nbl;
159
160             w16b = ((data->curcfg.cell_width + 15) & ~15) / 8;
161             ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, w16b * data->curcfg.cell_height);
162             if (!ptr) WINECON_Fatal("OOM");
163             nbl = max((data->curcfg.cell_height * size) / 100, 1);
164             for (j = data->curcfg.cell_height - nbl; j < data->curcfg.cell_height; j++)
165             {
166                 for (i = 0; i < data->curcfg.cell_width; i++)
167                 {
168                     ptr[w16b * j + (i / 8)] |= 0x80 >> (i & 7);
169                 }
170             }
171             PRIVATE(data)->cursor_bitmap = CreateBitmap(data->curcfg.cell_width,
172                                                data->curcfg.cell_height, 1, 1, ptr);
173             HeapFree(GetProcessHeap(), 0, ptr);
174         }
175         data->curcfg.cursor_size = size;
176         data->curcfg.cursor_visible = -1;
177     }
178
179     vis = (vis) ? TRUE : FALSE;
180     if (force || vis != data->curcfg.cursor_visible)
181     {
182         data->curcfg.cursor_visible = vis;
183         if (PRIVATE(data)->hWnd == GetFocus())
184         {
185             if (vis)
186             {
187                 CreateCaret(PRIVATE(data)->hWnd, PRIVATE(data)->cursor_bitmap,
188                             data->curcfg.cell_width, data->curcfg.cell_height);
189                 WCUSER_PosCursor(data);
190             }
191             else
192             {
193                 DestroyCaret();
194             }
195         }
196     }
197     WINECON_DumpConfig("crsr", &data->curcfg);
198 }
199
200 /******************************************************************
201  *              WCUSER_ComputePositions
202  *
203  * Recomputes all the components (mainly scroll bars) positions
204  */
205 void    WCUSER_ComputePositions(struct inner_data* data)
206 {
207     RECT                r;
208     int                 dx, dy;
209
210     /* compute window size from desired client size */
211     r.left = r.top = 0;
212     r.right = data->curcfg.win_width * data->curcfg.cell_width;
213     r.bottom = data->curcfg.win_height * data->curcfg.cell_height;
214
215     if (IsRectEmpty(&r)) return;
216
217     AdjustWindowRect(&r, GetWindowLong(PRIVATE(data)->hWnd, GWL_STYLE), FALSE);
218
219     dx = dy = 0;
220     if (data->curcfg.sb_width > data->curcfg.win_width)
221     {
222         dy = GetSystemMetrics(SM_CYHSCROLL);
223         SetScrollRange(PRIVATE(data)->hWnd, SB_HORZ, 0,
224                        data->curcfg.sb_width - data->curcfg.win_width, FALSE);
225         SetScrollPos(PRIVATE(data)->hWnd, SB_HORZ, 0, FALSE); /* FIXME */
226         ShowScrollBar(PRIVATE(data)->hWnd, SB_HORZ, TRUE);
227     }
228     else
229     {
230         ShowScrollBar(PRIVATE(data)->hWnd, SB_HORZ, FALSE);
231     }
232
233     if (data->curcfg.sb_height > data->curcfg.win_height)
234     {
235         dx = GetSystemMetrics(SM_CXVSCROLL);
236         SetScrollRange(PRIVATE(data)->hWnd, SB_VERT, 0,
237                        data->curcfg.sb_height - data->curcfg.win_height, FALSE);
238         SetScrollPos(PRIVATE(data)->hWnd, SB_VERT, 0, FALSE); /* FIXME */
239         ShowScrollBar(PRIVATE(data)->hWnd, SB_VERT, TRUE);
240     }
241     else
242     {
243         ShowScrollBar(PRIVATE(data)->hWnd, SB_VERT, FALSE);
244     }
245
246     SetWindowPos(PRIVATE(data)->hWnd, 0, 0, 0, r.right - r.left + dx, r.bottom - r.top + dy,
247                  SWP_NOMOVE|SWP_NOZORDER);
248     WCUSER_ShapeCursor(data, data->curcfg.cursor_size, data->curcfg.cursor_visible, TRUE);
249     WCUSER_PosCursor(data);
250 }
251
252 /******************************************************************
253  *              WCUSER_SetTitle
254  *
255  * Sets the title to the wine console
256  */
257 static void     WCUSER_SetTitle(const struct inner_data* data)
258 {
259     WCHAR       buffer[256];
260
261     if (WINECON_GetConsoleTitle(data->hConIn, buffer, sizeof(buffer)))
262         SetWindowText(PRIVATE(data)->hWnd, buffer);
263 }
264
265 void WCUSER_DumpLogFont(const char* pfx, const LOGFONT* lf, DWORD ft)
266 {
267     WINE_TRACE_(wc_font)("%s %s%s%s%s\n"
268                          "\tlf.lfHeight=%ld lf.lfWidth=%ld lf.lfEscapement=%ld lf.lfOrientation=%ld\n"
269                          "\tlf.lfWeight=%ld lf.lfItalic=%u lf.lfUnderline=%u lf.lfStrikeOut=%u\n"
270                          "\tlf.lfCharSet=%u lf.lfOutPrecision=%u lf.lfClipPrecision=%u lf.lfQuality=%u\n"
271                          "\tlf->lfPitchAndFamily=%u lf.lfFaceName=%s\n",
272                          pfx,
273                          (ft & RASTER_FONTTYPE) ? "raster" : "",
274                          (ft & TRUETYPE_FONTTYPE) ? "truetype" : "",
275                          ((ft & (RASTER_FONTTYPE|TRUETYPE_FONTTYPE)) == 0) ? "vector" : "",
276                          (ft & DEVICE_FONTTYPE) ? "|device" : "",
277                          lf->lfHeight, lf->lfWidth, lf->lfEscapement, lf->lfOrientation,
278                          lf->lfWeight, lf->lfItalic, lf->lfUnderline, lf->lfStrikeOut, lf->lfCharSet,
279                          lf->lfOutPrecision, lf->lfClipPrecision, lf->lfQuality, lf->lfPitchAndFamily,
280                          wine_dbgstr_w(lf->lfFaceName));
281 }
282
283 void WCUSER_DumpTextMetric(const TEXTMETRIC* tm, DWORD ft)
284 {
285     WINE_TRACE_(wc_font)("%s%s%s%s\n"
286                          "\ttmHeight=%ld tmAscent=%ld tmDescent=%ld tmInternalLeading=%ld tmExternalLeading=%ld\n"
287                          "\ttmAveCharWidth=%ld tmMaxCharWidth=%ld tmWeight=%ld tmOverhang=%ld\n"
288                          "\ttmDigitizedAspectX=%ld tmDigitizedAspectY=%ld\n"
289                          "\ttmFirstChar=%d tmLastChar=%d tmDefaultChar=%d tmBreakChar=%d\n"
290                          "\ttmItalic=%u tmUnderlined=%u tmStruckOut=%u tmPitchAndFamily=%u tmCharSet=%u\n",
291                          (ft & RASTER_FONTTYPE) ? "raster" : "",
292                          (ft & TRUETYPE_FONTTYPE) ? "truetype" : "",
293                          ((ft & (RASTER_FONTTYPE|TRUETYPE_FONTTYPE)) == 0) ? "vector" : "",
294                          (ft & DEVICE_FONTTYPE) ? "|device" : "",
295                          tm->tmHeight, tm->tmAscent, tm->tmDescent, tm->tmInternalLeading, tm->tmExternalLeading, tm->tmAveCharWidth,
296                          tm->tmMaxCharWidth, tm->tmWeight, tm->tmOverhang, tm->tmDigitizedAspectX, tm->tmDigitizedAspectY,
297                          tm->tmFirstChar, tm->tmLastChar, tm->tmDefaultChar, tm->tmBreakChar, tm->tmItalic, tm->tmUnderlined, tm->tmStruckOut,
298                          tm->tmPitchAndFamily, tm->tmCharSet);
299 }
300
301 /******************************************************************
302  *              WCUSER_AreFontsEqual
303  *
304  *
305  */
306 BOOL WCUSER_AreFontsEqual(const struct config_data* config, const LOGFONT* lf)
307 {
308     return lf->lfHeight == config->cell_height &&
309         lf->lfWeight == config->font_weight &&
310         !lf->lfItalic && !lf->lfUnderline && !lf->lfStrikeOut &&
311         !lstrcmp(lf->lfFaceName, config->face_name);
312 }
313
314 struct font_chooser
315 {
316     struct inner_data*  data;
317     int                 done;
318 };
319
320 /******************************************************************
321  *              WCUSER_ValidateFontMetric
322  *
323  * Returns true if the font described in tm is usable as a font for the renderer
324  */
325 BOOL    WCUSER_ValidateFontMetric(const struct inner_data* data, const TEXTMETRIC* tm, DWORD fontType)
326 {
327     BOOL        ret = TRUE;
328
329     if (fontType & RASTER_FONTTYPE)
330         ret = (tm->tmMaxCharWidth * data->curcfg.win_width < GetSystemMetrics(SM_CXSCREEN) &&
331                tm->tmHeight * data->curcfg.win_height < GetSystemMetrics(SM_CYSCREEN));
332     return ret && !tm->tmItalic && !tm->tmUnderlined && !tm->tmStruckOut &&
333         (tm->tmCharSet == DEFAULT_CHARSET || tm->tmCharSet == ANSI_CHARSET);
334 }
335
336 /******************************************************************
337  *              WCUSER_ValidateFont
338  *
339  * Returns true if the font family described in lf is usable as a font for the renderer
340  */
341 BOOL    WCUSER_ValidateFont(const struct inner_data* data, const LOGFONT* lf)
342 {
343     return (lf->lfPitchAndFamily & 3) == FIXED_PITCH &&
344         /* (lf->lfPitchAndFamily & 0xF0) == FF_MODERN && */
345         (lf->lfCharSet == DEFAULT_CHARSET || lf->lfCharSet == ANSI_CHARSET);
346 }
347
348 /******************************************************************
349  *              get_first_font_enum_2
350  *              get_first_font_enum
351  *
352  * Helper functions to get a decent font for the renderer
353  */
354 static int CALLBACK get_first_font_enum_2(const LOGFONT* lf, const TEXTMETRIC* tm,
355                                           DWORD FontType, LPARAM lParam)
356 {
357     struct font_chooser*        fc = (struct font_chooser*)lParam;
358
359     WCUSER_DumpTextMetric(tm, FontType);
360     if (WCUSER_ValidateFontMetric(fc->data, tm, FontType))
361     {
362         LOGFONT mlf = *lf;
363
364         /* Use the default sizes for the font (this is needed, especially for
365          * TrueType fonts, so that we get a decent size, not the max size)
366          */
367         mlf.lfWidth  = fc->data->curcfg.cell_width;
368         mlf.lfHeight = fc->data->curcfg.cell_height;
369         if (WCUSER_SetFont(fc->data, &mlf))
370         {
371             struct      config_data     defcfg;
372
373             WCUSER_DumpLogFont("InitChoosing: ", &mlf, FontType);
374             fc->done = 1;
375             /* since we've modified the current config with new font information,
376              * set this information as the new default.
377              */
378             WINECON_RegLoad(NULL, &defcfg);
379             defcfg.cell_width = fc->data->curcfg.cell_width;
380             defcfg.cell_height = fc->data->curcfg.cell_height;
381             lstrcpyW(defcfg.face_name, fc->data->curcfg.face_name);
382             /* Force also its writing back to the registry so that we can get it
383              * the next time.
384              */
385             WINECON_RegSave(&defcfg);
386             return 0;
387         }
388     }
389     return 1;
390 }
391
392 static int CALLBACK get_first_font_enum(const LOGFONT* lf, const TEXTMETRIC* tm,
393                                         DWORD FontType, LPARAM lParam)
394 {
395     struct font_chooser*        fc = (struct font_chooser*)lParam;
396
397     WCUSER_DumpLogFont("InitFamily: ", lf, FontType);
398     if (WCUSER_ValidateFont(fc->data, lf))
399     {
400         EnumFontFamilies(PRIVATE(fc->data)->hMemDC, lf->lfFaceName,
401                          get_first_font_enum_2, lParam);
402         return !fc->done; /* we just need the first matching one... */
403     }
404     return 1;
405 }
406
407 /******************************************************************
408  *              WCUSER_CopyFont
409  *
410  * get the relevant information from the font described in lf and store them
411  * in config
412  */
413 HFONT WCUSER_CopyFont(struct config_data* config, HWND hWnd, const LOGFONT* lf)
414 {
415     TEXTMETRIC  tm;
416     HDC         hDC;
417     HFONT       hFont, hOldFont;
418     int         w, i, buf[256];
419
420     if (!(hDC = GetDC(hWnd))) return (HFONT)0;
421     if (!(hFont = CreateFontIndirect(lf))) goto err1;
422
423     hOldFont = SelectObject(hDC, hFont);
424     GetTextMetrics(hDC, &tm);
425
426     /* FIXME:
427      * the current freetype engine (at least 2.0.x with x <= 8) and its implementation
428      * in Wine don't return adequate values for fixed fonts
429      * In Windows, those fonts are expected to return the same value for
430      *  - the average width
431      *  - the largest width
432      *  - the width of all characters in the font
433      * This isn't true in Wine. As a temporary workaround, we get as the width of the
434      * cell, the width of the first character in the font, after checking that all
435      * characters in the font have the same width (I hear paranoïa coming)
436      * when this gets fixed, the code should be using tm.tmAveCharWidth
437      * or tm.tmMaxCharWidth as the cell width.
438      */
439     GetCharWidth32(hDC, tm.tmFirstChar, tm.tmFirstChar, &w);
440     for (i = tm.tmFirstChar + 1; i <= tm.tmLastChar; i += sizeof(buf) / sizeof(buf[0]))
441     {
442         int j, l;
443
444         l = min(tm.tmLastChar - i, sizeof(buf) / sizeof(buf[0]) - 1);
445         GetCharWidth32(hDC, i, i + l, buf);
446         for (j = 0; j <= l; j++)
447         {
448             if (buf[j] != w)
449             {
450                 WINE_WARN("Non uniform cell width: [%d]=%d [%d]=%d\n"
451                           "This may be caused by old freetype libraries, >= 2.0.8 is recommended\n",
452                           i + j, buf[j], tm.tmFirstChar, w);
453                 goto err;
454             }
455         }
456     }
457     SelectObject(hDC, hOldFont);
458     ReleaseDC(hWnd, hDC);
459
460     config->cell_width  = w;
461     config->cell_height = tm.tmHeight + tm.tmExternalLeading;
462     config->font_weight = tm.tmWeight;
463     lstrcpy(config->face_name, lf->lfFaceName);
464
465     return hFont;
466  err:
467     if (hDC && hOldFont) SelectObject(hDC, hOldFont);
468     if (hFont) DeleteObject(hFont);
469  err1:
470     if (hDC) ReleaseDC(hWnd, hDC);
471
472     return (HFONT)0;
473 }
474
475 /******************************************************************
476  *              WCUSER_FillLogFont
477  *
478  *
479  */
480 void    WCUSER_FillLogFont(LOGFONT* lf, const WCHAR* name, UINT height, UINT weight)
481 {
482     lf->lfHeight        = height;
483     lf->lfWidth         = 0;
484     lf->lfEscapement    = 0;
485     lf->lfOrientation   = 0;
486     lf->lfWeight        = weight;
487     lf->lfItalic        = FALSE;
488     lf->lfUnderline     = FALSE;
489     lf->lfStrikeOut     = FALSE;
490     lf->lfCharSet       = DEFAULT_CHARSET;
491     lf->lfOutPrecision  = OUT_DEFAULT_PRECIS;
492     lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
493     lf->lfQuality       = DEFAULT_QUALITY;
494     lf->lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
495     lstrcpy(lf->lfFaceName, name);
496 }
497
498 /******************************************************************
499  *              WCUSER_SetFont
500  *
501  * sets logfont as the new font for the console
502  */
503 BOOL    WCUSER_SetFont(struct inner_data* data, const LOGFONT* logfont)
504 {
505     HFONT       hFont;
506
507     if (PRIVATE(data)->hFont != 0 && WCUSER_AreFontsEqual(&data->curcfg, logfont))
508         return TRUE;
509
510     hFont = WCUSER_CopyFont(&data->curcfg, PRIVATE(data)->hWnd, logfont);
511     if (!hFont) {WINE_ERR("wrong font\n"); return FALSE;}
512
513     if (PRIVATE(data)->hFont) DeleteObject(PRIVATE(data)->hFont);
514     PRIVATE(data)->hFont = hFont;
515
516     WCUSER_ComputePositions(data);
517     WCUSER_NewBitmap(data);
518     InvalidateRect(PRIVATE(data)->hWnd, NULL, FALSE);
519     UpdateWindow(PRIVATE(data)->hWnd);
520
521     return TRUE;
522 }
523
524 /******************************************************************
525  *              WCUSER_SetFontPmt
526  *
527  * Sets a new font for the console.
528  * In fact a wrapper for WCUSER_SetFont
529  */
530 static void     WCUSER_SetFontPmt(struct inner_data* data, const WCHAR* font,
531                                   unsigned height, unsigned weight)
532 {
533     LOGFONT             lf;
534     struct font_chooser fc;
535
536     WINE_TRACE_(wc_font)("=> %s h=%u w=%u\n",
537                          wine_dbgstr_wn(font, -1), height, weight);
538
539     if (font[0] != '\0' && height != 0 && weight != 0)
540     {
541         WCUSER_FillLogFont(&lf, font, height, weight);
542         if (WCUSER_SetFont(data, &lf))
543         {
544             WCUSER_DumpLogFont("InitReuses: ", &lf, 0);
545             return;
546         }
547     }
548
549     /* try to find an acceptable font */
550     WINE_WARN("Couldn't match the font from registry... trying to find one\n");
551     fc.data = data;
552     fc.done = 0;
553     EnumFontFamilies(PRIVATE(data)->hMemDC, NULL, get_first_font_enum, (LPARAM)&fc);
554     if (!fc.done) WINECON_Fatal("Couldn't find a decent font, aborting\n");
555 }
556
557 /******************************************************************
558  *              WCUSER_GetCell
559  *
560  * Get a cell from a relative coordinate in window (takes into
561  * account the scrolling)
562  */
563 static COORD    WCUSER_GetCell(const struct inner_data* data, LPARAM lParam)
564 {
565     COORD       c;
566
567     c.X = data->curcfg.win_pos.X + (short)LOWORD(lParam) / data->curcfg.cell_width;
568     c.Y = data->curcfg.win_pos.Y + (short)HIWORD(lParam) / data->curcfg.cell_height;
569
570     return c;
571 }
572
573 /******************************************************************
574  *              WCUSER_GetSelectionRect
575  *
576  * Get the selection rectangle
577  */
578 static void     WCUSER_GetSelectionRect(const struct inner_data* data, LPRECT r)
579 {
580     r->left   = (min(PRIVATE(data)->selectPt1.X, PRIVATE(data)->selectPt2.X)     - data->curcfg.win_pos.X) * data->curcfg.cell_width;
581     r->top    = (min(PRIVATE(data)->selectPt1.Y, PRIVATE(data)->selectPt2.Y)     - data->curcfg.win_pos.Y) * data->curcfg.cell_height;
582     r->right  = (max(PRIVATE(data)->selectPt1.X, PRIVATE(data)->selectPt2.X) + 1 - data->curcfg.win_pos.X) * data->curcfg.cell_width;
583     r->bottom = (max(PRIVATE(data)->selectPt1.Y, PRIVATE(data)->selectPt2.Y) + 1 - data->curcfg.win_pos.Y) * data->curcfg.cell_height;
584 }
585
586 /******************************************************************
587  *              WCUSER_SetSelection
588  *
589  *
590  */
591 static void     WCUSER_SetSelection(const struct inner_data* data, HDC hRefDC)
592 {
593     HDC         hDC;
594     RECT        r;
595
596     WCUSER_GetSelectionRect(data, &r);
597     hDC = hRefDC ? hRefDC : GetDC(PRIVATE(data)->hWnd);
598     if (hDC)
599     {
600         if (PRIVATE(data)->hWnd == GetFocus() && data->curcfg.cursor_visible)
601             HideCaret(PRIVATE(data)->hWnd);
602         InvertRect(hDC, &r);
603         if (hDC != hRefDC)
604             ReleaseDC(PRIVATE(data)->hWnd, hDC);
605         if (PRIVATE(data)->hWnd == GetFocus() && data->curcfg.cursor_visible)
606             ShowCaret(PRIVATE(data)->hWnd);
607     }
608 }
609
610 /******************************************************************
611  *              WCUSER_MoveSelection
612  *
613  *
614  */
615 static void     WCUSER_MoveSelection(struct inner_data* data, COORD c1, COORD c2, BOOL final)
616 {
617     RECT        r;
618     HDC         hDC;
619
620     WCUSER_GetSelectionRect(data, &r);
621     hDC = GetDC(PRIVATE(data)->hWnd);
622     if (hDC)
623     {
624         if (PRIVATE(data)->hWnd == GetFocus() && data->curcfg.cursor_visible)
625             HideCaret(PRIVATE(data)->hWnd);
626         InvertRect(hDC, &r);
627     }
628     PRIVATE(data)->selectPt1 = c1;
629     PRIVATE(data)->selectPt2 = c2;
630     if (hDC)
631     {
632         WCUSER_GetSelectionRect(data, &r);
633         InvertRect(hDC, &r);
634         ReleaseDC(PRIVATE(data)->hWnd, hDC);
635         if (PRIVATE(data)->hWnd == GetFocus() && data->curcfg.cursor_visible)
636             ShowCaret(PRIVATE(data)->hWnd);
637     }
638     if (final)
639     {
640         ReleaseCapture();
641         PRIVATE(data)->has_selection = TRUE;
642     }
643 }
644
645 /******************************************************************
646  *              WCUSER_CopySelectionToClipboard
647  *
648  * Copies the current selection into the clipboard
649  */
650 static void     WCUSER_CopySelectionToClipboard(const struct inner_data* data)
651 {
652     HANDLE      hMem;
653     LPWSTR      p;
654     unsigned    w, h;
655
656     w = abs(PRIVATE(data)->selectPt1.X - PRIVATE(data)->selectPt2.X) + 2;
657     h = abs(PRIVATE(data)->selectPt1.Y - PRIVATE(data)->selectPt2.Y) + 1;
658
659     if (!OpenClipboard(PRIVATE(data)->hWnd)) return;
660     EmptyClipboard();
661
662     hMem = GlobalAlloc(GMEM_MOVEABLE, (w * h) * sizeof(WCHAR));
663     if (hMem && (p = GlobalLock(hMem)))
664     {
665         COORD   c;
666         int     y;
667
668         c.X = min(PRIVATE(data)->selectPt1.X, PRIVATE(data)->selectPt2.X);
669         c.Y = min(PRIVATE(data)->selectPt1.Y, PRIVATE(data)->selectPt2.Y);
670
671         for (y = 0; y < h; y++, c.Y++)
672         {
673             ReadConsoleOutputCharacter(data->hConOut, &p[y * w], w - 1, c, NULL);
674             p[y * w + w - 1] = (y < h - 1) ? '\n' : '\0';
675         }
676         GlobalUnlock(hMem);
677         SetClipboardData(CF_UNICODETEXT, hMem);
678     }
679     CloseClipboard();
680 }
681
682 /******************************************************************
683  *              WCUSER_PasteFromClipboard
684  *
685  *
686  */
687 static void     WCUSER_PasteFromClipboard(struct inner_data* data)
688 {
689     HANDLE      h;
690     WCHAR*      ptr;
691
692     if (!OpenClipboard(PRIVATE(data)->hWnd)) return;
693     h = GetClipboardData(CF_UNICODETEXT);
694     if (h && (ptr = GlobalLock(h)))
695     {
696         int             i, len = GlobalSize(h) / sizeof(WCHAR);
697         INPUT_RECORD    ir[2];
698         DWORD           n;
699         SHORT           sh;
700
701         ir[0].EventType = KEY_EVENT;
702         ir[0].Event.KeyEvent.wRepeatCount = 0;
703         ir[0].Event.KeyEvent.dwControlKeyState = 0;
704         ir[0].Event.KeyEvent.bKeyDown = TRUE;
705
706         /* generate the corresponding input records */
707         for (i = 0; i < len; i++)
708         {
709             /* FIXME: the modifying keys are not generated (shift, ctrl...) */
710             sh = VkKeyScan(ptr[i]);
711             ir[0].Event.KeyEvent.wVirtualKeyCode = LOBYTE(sh);
712             ir[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(LOBYTE(sh), 0);
713             ir[0].Event.KeyEvent.uChar.UnicodeChar = ptr[i];
714
715             ir[1] = ir[0];
716             ir[1].Event.KeyEvent.bKeyDown = FALSE;
717
718             WriteConsoleInput(data->hConIn, ir, 2, &n);
719         }
720         GlobalUnlock(h);
721     }
722     CloseClipboard();
723 }
724
725 /******************************************************************
726  *              WCUSER_Refresh
727  *
728  *
729  */
730 static void WCUSER_Refresh(const struct inner_data* data, int tp, int bm)
731 {
732     WCUSER_FillMemDC(data, tp, bm);
733     if (data->curcfg.win_pos.Y <= bm && data->curcfg.win_pos.Y + data->curcfg.win_height >= tp)
734     {
735         RECT    r;
736
737         r.left   = 0;
738         r.right  = data->curcfg.win_width * data->curcfg.cell_width;
739         r.top    = (tp - data->curcfg.win_pos.Y) * data->curcfg.cell_height;
740         r.bottom = (bm - data->curcfg.win_pos.Y + 1) * data->curcfg.cell_height;
741         InvalidateRect(PRIVATE(data)->hWnd, &r, FALSE);
742         UpdateWindow(PRIVATE(data)->hWnd);
743     }
744 }
745
746 /******************************************************************
747  *              WCUSER_Paint
748  *
749  *
750  */
751 static void     WCUSER_Paint(const struct inner_data* data)
752 {
753     PAINTSTRUCT         ps;
754
755     BeginPaint(PRIVATE(data)->hWnd, &ps);
756     BitBlt(ps.hdc, 0, 0,
757            data->curcfg.win_width * data->curcfg.cell_width,
758            data->curcfg.win_height * data->curcfg.cell_height,
759            PRIVATE(data)->hMemDC,
760            data->curcfg.win_pos.X * data->curcfg.cell_width,
761            data->curcfg.win_pos.Y * data->curcfg.cell_height,
762            SRCCOPY);
763     if (PRIVATE(data)->has_selection)
764         WCUSER_SetSelection(data, ps.hdc);
765     EndPaint(PRIVATE(data)->hWnd, &ps);
766 }
767
768 /******************************************************************
769  *              WCUSER_Scroll
770  *
771  *
772  */
773 static void WCUSER_Scroll(struct inner_data* data, int pos, BOOL horz)
774 {
775     if (horz)
776     {
777         SetScrollPos(PRIVATE(data)->hWnd, SB_HORZ, pos, TRUE);
778         data->curcfg.win_pos.X = pos;
779     }
780     else
781     {
782         SetScrollPos(PRIVATE(data)->hWnd, SB_VERT, pos, TRUE);
783         data->curcfg.win_pos.Y = pos;
784     }
785     InvalidateRect(PRIVATE(data)->hWnd, NULL, FALSE);
786 }
787
788 /******************************************************************
789  *              WCUSER_FillMenu
790  *
791  *
792  */
793 static BOOL WCUSER_FillMenu(HMENU hMenu, BOOL sep)
794 {
795     HMENU               hSubMenu;
796     HINSTANCE           hInstance = GetModuleHandle(NULL);
797     WCHAR               buff[256];
798
799     if (!hMenu) return FALSE;
800
801     /* FIXME: error handling & memory cleanup */
802     hSubMenu = CreateMenu();
803     if (!hSubMenu) return FALSE;
804
805     LoadString(hInstance, IDS_MARK, buff, sizeof(buff) / sizeof(WCHAR));
806     InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_MARK, buff);
807     LoadString(hInstance, IDS_COPY, buff, sizeof(buff) / sizeof(WCHAR));
808     InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_COPY, buff);
809     LoadString(hInstance, IDS_PASTE, buff, sizeof(buff) / sizeof(WCHAR));
810     InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_PASTE, buff);
811     LoadString(hInstance, IDS_SELECTALL, buff, sizeof(buff) / sizeof(WCHAR));
812     InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_SELECTALL, buff);
813     LoadString(hInstance, IDS_SCROLL, buff, sizeof(buff) / sizeof(WCHAR));
814     InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_SCROLL, buff);
815     LoadString(hInstance, IDS_SEARCH, buff, sizeof(buff) / sizeof(WCHAR));
816     InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_SEARCH, buff);
817
818     if (sep) InsertMenu(hMenu, -1, MF_BYPOSITION|MF_SEPARATOR, 0, NULL);
819     LoadString(hInstance, IDS_EDIT, buff, sizeof(buff) / sizeof(WCHAR));
820     InsertMenu(hMenu, -1, MF_BYPOSITION|MF_STRING|MF_POPUP, (UINT_PTR)hSubMenu, buff);
821     LoadString(hInstance, IDS_DEFAULT, buff, sizeof(buff) / sizeof(WCHAR));
822     InsertMenu(hMenu, -1, MF_BYPOSITION|MF_STRING, IDS_DEFAULT, buff);
823     LoadString(hInstance, IDS_PROPERTIES, buff, sizeof(buff) / sizeof(WCHAR));
824     InsertMenu(hMenu, -1, MF_BYPOSITION|MF_STRING, IDS_PROPERTIES, buff);
825
826     return TRUE;
827 }
828
829 /******************************************************************
830  *              WCUSER_SetMenuDetails
831  *
832  * Grays / ungrays the menu items according to their state
833  */
834 static void     WCUSER_SetMenuDetails(const struct inner_data* data, HMENU hMenu)
835 {
836     if (!hMenu) {WINE_ERR("Issue in getting menu bits\n");return;}
837
838     EnableMenuItem(hMenu, IDS_COPY,
839                    MF_BYCOMMAND|(PRIVATE(data)->has_selection ? MF_ENABLED : MF_GRAYED));
840     EnableMenuItem(hMenu, IDS_PASTE,
841                    MF_BYCOMMAND|(IsClipboardFormatAvailable(CF_UNICODETEXT)
842                                  ? MF_ENABLED : MF_GRAYED));
843     EnableMenuItem(hMenu, IDS_SCROLL, MF_BYCOMMAND|MF_GRAYED);
844     EnableMenuItem(hMenu, IDS_SEARCH, MF_BYCOMMAND|MF_GRAYED);
845 }
846
847 /******************************************************************
848  *              WCUSER_Create
849  *
850  * Creates the window for the rendering
851  */
852 static LRESULT WCUSER_Create(HWND hWnd, LPCREATESTRUCT lpcs)
853 {
854     struct inner_data*  data;
855     HMENU               hSysMenu;
856
857     data = lpcs->lpCreateParams;
858     SetWindowLong(hWnd, 0L, (DWORD)data);
859     PRIVATE(data)->hWnd = hWnd;
860
861     hSysMenu = GetSystemMenu(hWnd, FALSE);
862     if (!hSysMenu) return 0;
863     PRIVATE(data)->hPopMenu = CreatePopupMenu();
864     if (!PRIVATE(data)->hPopMenu) return 0;
865
866     WCUSER_FillMenu(hSysMenu, TRUE);
867     WCUSER_FillMenu(PRIVATE(data)->hPopMenu, FALSE);
868
869     PRIVATE(data)->hMemDC = CreateCompatibleDC(0);
870     if (!PRIVATE(data)->hMemDC) {WINE_ERR("no mem dc\n");return 0;}
871
872     data->curcfg.quick_edit = FALSE;
873     return 0;
874 }
875
876 /******************************************************************
877  *              CUSER_GetCtrlKeyState
878  *
879  * Get the console bit mask equivalent to the VK_ status in keyState
880  */
881 static DWORD    WCUSER_GetCtrlKeyState(BYTE* keyState)
882 {
883     DWORD               ret = 0;
884
885     GetKeyboardState(keyState);
886     if (keyState[VK_SHIFT]    & 0x80)   ret |= SHIFT_PRESSED;
887     if (keyState[VK_CONTROL]  & 0x80)   ret |= LEFT_CTRL_PRESSED; /* FIXME: gotta choose one */
888     if (keyState[VK_LCONTROL] & 0x80)   ret |= LEFT_CTRL_PRESSED;
889     if (keyState[VK_RCONTROL] & 0x80)   ret |= RIGHT_CTRL_PRESSED;
890     if (keyState[VK_LMENU]    & 0x80)   ret |= LEFT_ALT_PRESSED;
891     if (keyState[VK_RMENU]    & 0x80)   ret |= RIGHT_ALT_PRESSED;
892     if (keyState[VK_CAPITAL]  & 0x01)   ret |= CAPSLOCK_ON;
893     if (keyState[VK_NUMLOCK]  & 0x01)   ret |= NUMLOCK_ON;
894     if (keyState[VK_SCROLL]   & 0x01)   ret |= SCROLLLOCK_ON;
895
896     return ret;
897 }
898
899 /******************************************************************
900  *              WCUSER_HandleSelectionKey
901  *
902  * Handles keys while selecting an area
903  */
904 static void WCUSER_HandleSelectionKey(struct inner_data* data, BOOL down,
905                                       WPARAM wParam, LPARAM lParam)
906 {
907     BYTE        keyState[256];
908     DWORD       state = WCUSER_GetCtrlKeyState(keyState) & ~(CAPSLOCK_ON|NUMLOCK_ON|SCROLLLOCK_ON);
909     COORD       c1, c2;
910
911     if (down) return;
912
913     switch (state)
914     {
915     case 0:
916         switch (wParam)
917         {
918         case VK_RETURN:
919             PRIVATE(data)->has_selection = FALSE;
920             WCUSER_SetSelection(data, 0);
921             WCUSER_CopySelectionToClipboard(data);
922             break;
923         case VK_RIGHT:
924             c1 = PRIVATE(data)->selectPt1;
925             c2 = PRIVATE(data)->selectPt2;
926             c1.X++; c2.X++;
927             if (c1.X < data->curcfg.sb_width && c2.X < data->curcfg.sb_width)
928             {
929                 WCUSER_MoveSelection(data, c1, c2, FALSE);
930             }
931             break;
932         case VK_LEFT:
933             c1 = PRIVATE(data)->selectPt1;
934             c2 = PRIVATE(data)->selectPt2;
935             c1.X--; c2.X--;
936             if (c1.X >= 0 && c2.X >= 0)
937             {
938                 WCUSER_MoveSelection(data, c1, c2, FALSE);
939             }
940             break;
941         case VK_UP:
942             c1 = PRIVATE(data)->selectPt1;
943             c2 = PRIVATE(data)->selectPt2;
944             c1.Y--; c2.Y--;
945             if (c1.Y >= 0 && c2.Y >= 0)
946             {
947                 WCUSER_MoveSelection(data, c1, c2, FALSE);
948             }
949             break;
950         case VK_DOWN:
951             c1 = PRIVATE(data)->selectPt1;
952             c2 = PRIVATE(data)->selectPt2;
953             c1.Y++; c2.Y++;
954             if (c1.X < data->curcfg.sb_height && c2.X < data->curcfg.sb_height)
955             {
956                 WCUSER_MoveSelection(data, c1, c2, FALSE);
957             }
958             break;
959         }
960         break;
961     case SHIFT_PRESSED:
962         switch (wParam)
963         {
964         case VK_RIGHT:
965             c1 = PRIVATE(data)->selectPt1;
966             c2 = PRIVATE(data)->selectPt2;
967             c2.X++;
968             if (c2.X < data->curcfg.sb_width)
969             {
970                 WCUSER_MoveSelection(data, c1, c2, FALSE);
971             }
972             break;
973         case VK_LEFT:
974             c1 = PRIVATE(data)->selectPt1;
975             c2 = PRIVATE(data)->selectPt2;
976             c2.X--;
977             if (c2.X >= c1.X)
978             {
979                 WCUSER_MoveSelection(data, c1, c2, FALSE);
980             }
981             break;
982         case VK_UP:
983             c1 = PRIVATE(data)->selectPt1;
984             c2 = PRIVATE(data)->selectPt2;
985             c2.Y--;
986             if (c2.Y >= c1.Y)
987             {
988                 WCUSER_MoveSelection(data, c1, c2, FALSE);
989             }
990             break;
991         case VK_DOWN:
992             c1 = PRIVATE(data)->selectPt1;
993             c2 = PRIVATE(data)->selectPt2;
994             c2.Y++;
995             if (c2.X < data->curcfg.sb_height)
996             {
997                 WCUSER_MoveSelection(data, c1, c2, FALSE);
998             }
999             break;
1000         }
1001         break;
1002     }
1003 }
1004
1005 /******************************************************************
1006  *              WCUSER_GenerateKeyInputRecord
1007  *
1008  * generates input_record from windows WM_KEYUP/WM_KEYDOWN messages
1009  */
1010 static void    WCUSER_GenerateKeyInputRecord(struct inner_data* data, BOOL down,
1011                                              WPARAM wParam, LPARAM lParam, BOOL sys)
1012 {
1013     INPUT_RECORD        ir;
1014     DWORD               n;
1015     WCHAR               buf[2];
1016     static      WCHAR   last; /* keep last char seen as feed for key up message */
1017     BYTE                keyState[256];
1018
1019     ir.EventType = KEY_EVENT;
1020     ir.Event.KeyEvent.bKeyDown = down;
1021     ir.Event.KeyEvent.wRepeatCount = LOWORD(lParam);
1022     ir.Event.KeyEvent.wVirtualKeyCode = wParam;
1023
1024     ir.Event.KeyEvent.wVirtualScanCode = HIWORD(lParam) & 0xFF;
1025
1026     ir.Event.KeyEvent.uChar.UnicodeChar = 0;
1027     ir.Event.KeyEvent.dwControlKeyState = WCUSER_GetCtrlKeyState(keyState);
1028     if (lParam & (1L << 24))            ir.Event.KeyEvent.dwControlKeyState |= ENHANCED_KEY;
1029     if (sys)                            ir.Event.KeyEvent.dwControlKeyState |= LEFT_ALT_PRESSED; /* FIXME: gotta choose one */
1030
1031     if (!(ir.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY))
1032     {
1033         if (down)
1034         {
1035             switch (ToUnicode(wParam, HIWORD(lParam), keyState, buf, 2, 0))
1036             {
1037             case 2:
1038                 /* FIXME... should generate two events... */
1039                 /* fall thru */
1040             case 1:
1041                 last = buf[0];
1042                 break;
1043             default:
1044                 last = 0;
1045                 break;
1046             }
1047         }
1048         ir.Event.KeyEvent.uChar.UnicodeChar = last; /* FIXME HACKY... and buggy 'coz it should be a stack, not a single value */
1049         if (!down) last = 0;
1050     }
1051
1052     WriteConsoleInput(data->hConIn, &ir, 1, &n);
1053 }
1054
1055 /******************************************************************
1056  *              WCUSER_GenerateMouseInputRecord
1057  *
1058  *
1059  */
1060 static void    WCUSER_GenerateMouseInputRecord(struct inner_data* data, COORD c,
1061                                                WPARAM wParam, DWORD event)
1062 {
1063     INPUT_RECORD        ir;
1064     BYTE                keyState[256];
1065     DWORD               mode, n;
1066
1067     /* MOUSE_EVENTs shouldn't be sent unless ENABLE_MOUSE_INPUT is active */
1068     if (!GetConsoleMode(data->hConIn, &mode) || !(mode & ENABLE_MOUSE_INPUT))
1069         return;
1070
1071     ir.EventType = MOUSE_EVENT;
1072     ir.Event.MouseEvent.dwMousePosition = c;
1073     ir.Event.MouseEvent.dwButtonState = 0;
1074     if (wParam & MK_LBUTTON) ir.Event.MouseEvent.dwButtonState |= FROM_LEFT_1ST_BUTTON_PRESSED;
1075     if (wParam & MK_MBUTTON) ir.Event.MouseEvent.dwButtonState |= FROM_LEFT_2ND_BUTTON_PRESSED;
1076     if (wParam & MK_RBUTTON) ir.Event.MouseEvent.dwButtonState |= RIGHTMOST_BUTTON_PRESSED;
1077     ir.Event.MouseEvent.dwControlKeyState = WCUSER_GetCtrlKeyState(keyState);
1078     ir.Event.MouseEvent.dwEventFlags = event;
1079
1080     WriteConsoleInput(data->hConIn, &ir, 1, &n);
1081 }
1082
1083 /******************************************************************
1084  *              WCUSER_Proc
1085  *
1086  *
1087  */
1088 static LRESULT CALLBACK WCUSER_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1089 {
1090     struct inner_data*  data = (struct inner_data*)GetWindowLong(hWnd, 0);
1091
1092     switch (uMsg)
1093     {
1094     case WM_CREATE:
1095         return WCUSER_Create(hWnd, (LPCREATESTRUCT)lParam);
1096     case WM_DESTROY:
1097         PRIVATE(data)->hWnd = 0;
1098         PostQuitMessage(0);
1099         break;
1100     case WM_PAINT:
1101         WCUSER_Paint(data);
1102         break;
1103     case WM_KEYDOWN:
1104     case WM_KEYUP:
1105         if (PRIVATE(data)->has_selection)
1106             WCUSER_HandleSelectionKey(data, uMsg == WM_KEYDOWN, wParam, lParam);
1107         else
1108             WCUSER_GenerateKeyInputRecord(data, uMsg == WM_KEYDOWN, wParam, lParam, FALSE);
1109         break;
1110     case WM_SYSKEYDOWN:
1111     case WM_SYSKEYUP:
1112         WCUSER_GenerateKeyInputRecord(data, uMsg == WM_SYSKEYDOWN, wParam, lParam, TRUE);
1113         break;
1114     case WM_LBUTTONDOWN:
1115         if (data->curcfg.quick_edit)
1116         {
1117             if (PRIVATE(data)->has_selection)
1118             {
1119                 PRIVATE(data)->has_selection = FALSE;
1120                 WCUSER_SetSelection(data, 0);
1121             }
1122             else
1123             {
1124                 PRIVATE(data)->selectPt1 = PRIVATE(data)->selectPt2 = WCUSER_GetCell(data, lParam);
1125                 SetCapture(PRIVATE(data)->hWnd);
1126                 WCUSER_SetSelection(data, 0);
1127                 PRIVATE(data)->has_selection = TRUE;
1128             }
1129         }
1130         else
1131         {
1132             WCUSER_GenerateMouseInputRecord(data, WCUSER_GetCell(data, lParam), wParam, 0);
1133         }
1134         break;
1135     case WM_MOUSEMOVE:
1136         if (data->curcfg.quick_edit)
1137         {
1138             if (GetCapture() == PRIVATE(data)->hWnd && PRIVATE(data)->has_selection &&
1139                 (wParam & MK_LBUTTON))
1140             {
1141                 WCUSER_MoveSelection(data, PRIVATE(data)->selectPt1, WCUSER_GetCell(data, lParam), FALSE);
1142             }
1143         }
1144         else
1145         {
1146             WCUSER_GenerateMouseInputRecord(data, WCUSER_GetCell(data, lParam), wParam, MOUSE_MOVED);
1147         }
1148         break;
1149     case WM_LBUTTONUP:
1150         if (data->curcfg.quick_edit)
1151         {
1152             if (GetCapture() == PRIVATE(data)->hWnd && PRIVATE(data)->has_selection &&
1153                 (wParam& MK_LBUTTON))
1154             {
1155                 WCUSER_MoveSelection(data, PRIVATE(data)->selectPt1, WCUSER_GetCell(data, lParam), TRUE);
1156             }
1157         }
1158         else
1159         {
1160             WCUSER_GenerateMouseInputRecord(data, WCUSER_GetCell(data, lParam), wParam, 0);
1161         }
1162         break;
1163     case WM_RBUTTONDOWN:
1164         if ((wParam & (MK_CONTROL|MK_SHIFT)) == data->curcfg.menu_mask)
1165         {
1166             RECT        r;
1167
1168             GetWindowRect(hWnd, &r);
1169             WCUSER_SetMenuDetails(data, PRIVATE(data)->hPopMenu);
1170             TrackPopupMenu(PRIVATE(data)->hPopMenu, TPM_LEFTALIGN|TPM_TOPALIGN,
1171                            r.left + LOWORD(lParam), r.top + HIWORD(lParam), 0, hWnd, NULL);
1172         }
1173         else
1174         {
1175             WCUSER_GenerateMouseInputRecord(data, WCUSER_GetCell(data, lParam), wParam, 0);
1176         }
1177         break;
1178     case WM_RBUTTONUP:
1179         /* no need to track for rbutton up when opening the popup... the event will be
1180          * swallowed by TrackPopupMenu */
1181         WCUSER_GenerateMouseInputRecord(data, WCUSER_GetCell(data, lParam), wParam, 0);
1182         break;
1183     case WM_MOUSEWHEEL:
1184         /* FIXME: should we scroll too ? */
1185         WCUSER_GenerateMouseInputRecord(data, WCUSER_GetCell(data, lParam), wParam, MOUSE_WHEELED);
1186         break;
1187     case WM_SETFOCUS:
1188         if (data->curcfg.cursor_visible)
1189         {
1190             CreateCaret(PRIVATE(data)->hWnd, PRIVATE(data)->cursor_bitmap,
1191                         data->curcfg.cell_width, data->curcfg.cell_height);
1192             WCUSER_PosCursor(data);
1193         }
1194         break;
1195     case WM_KILLFOCUS:
1196         if (data->curcfg.cursor_visible)
1197             DestroyCaret();
1198         break;
1199     case WM_HSCROLL:
1200         {
1201             int pos = data->curcfg.win_pos.X;
1202
1203             switch (LOWORD(wParam))
1204             {
1205             case SB_PAGEUP:     pos -= 8;               break;
1206             case SB_PAGEDOWN:   pos += 8;               break;
1207             case SB_LINEUP:     pos--;                  break;
1208             case SB_LINEDOWN:   pos++;                  break;
1209             case SB_THUMBTRACK: pos = HIWORD(wParam);   break;
1210             default:                                    break;
1211             }
1212             if (pos < 0) pos = 0;
1213             if (pos > data->curcfg.sb_width - data->curcfg.win_width)
1214                 pos = data->curcfg.sb_width - data->curcfg.win_width;
1215             if (pos != data->curcfg.win_pos.X)
1216             {
1217                 ScrollWindow(hWnd, (data->curcfg.win_pos.X - pos) * data->curcfg.cell_width, 0,
1218                              NULL, NULL);
1219                 data->curcfg.win_pos.X = pos;
1220                 SetScrollPos(hWnd, SB_HORZ, pos, TRUE);
1221                 UpdateWindow(hWnd);
1222                 WCUSER_PosCursor(data);
1223                 WINECON_NotifyWindowChange(data);
1224             }
1225         }
1226         break;
1227     case WM_VSCROLL:
1228         {
1229             int pos = data->curcfg.win_pos.Y;
1230
1231             switch (LOWORD(wParam))
1232             {
1233             case SB_PAGEUP:     pos -= 8;               break;
1234             case SB_PAGEDOWN:   pos += 8;               break;
1235             case SB_LINEUP:     pos--;                  break;
1236             case SB_LINEDOWN:   pos++;                  break;
1237             case SB_THUMBTRACK: pos = HIWORD(wParam);   break;
1238             default:                                    break;
1239             }
1240             if (pos < 0) pos = 0;
1241             if (pos > data->curcfg.sb_height - data->curcfg.win_height)
1242                 pos = data->curcfg.sb_height - data->curcfg.win_height;
1243             if (pos != data->curcfg.win_pos.Y)
1244             {
1245                 ScrollWindow(hWnd, 0, (data->curcfg.win_pos.Y - pos) * data->curcfg.cell_height,
1246                              NULL, NULL);
1247                 data->curcfg.win_pos.Y = pos;
1248                 SetScrollPos(hWnd, SB_VERT, pos, TRUE);
1249                 UpdateWindow(hWnd);
1250                 WCUSER_PosCursor(data);
1251                 WINECON_NotifyWindowChange(data);
1252             }
1253
1254         }
1255     case WM_SYSCOMMAND:
1256         switch (wParam)
1257         {
1258         case IDS_DEFAULT:
1259             WCUSER_GetProperties(data, FALSE);
1260             break;
1261         case IDS_PROPERTIES:
1262             WCUSER_GetProperties(data, TRUE);
1263             break;
1264         default:
1265             return DefWindowProc(hWnd, uMsg, wParam, lParam);
1266         }
1267         break;
1268     case WM_COMMAND:
1269         switch (wParam)
1270         {
1271         case IDS_DEFAULT:
1272             WCUSER_GetProperties(data, FALSE);
1273             break;
1274         case IDS_PROPERTIES:
1275             WCUSER_GetProperties(data, TRUE);
1276             break;
1277         case IDS_MARK:
1278             PRIVATE(data)->selectPt1.X = PRIVATE(data)->selectPt1.Y = 0;
1279             PRIVATE(data)->selectPt2.X = PRIVATE(data)->selectPt2.Y = 0;
1280             WCUSER_SetSelection(data, 0);
1281             PRIVATE(data)->has_selection = TRUE;
1282             break;
1283         case IDS_COPY:
1284             if (PRIVATE(data)->has_selection)
1285             {
1286                 PRIVATE(data)->has_selection = FALSE;
1287                 WCUSER_SetSelection(data, 0);
1288                 WCUSER_CopySelectionToClipboard(data);
1289             }
1290             break;
1291         case IDS_PASTE:
1292             WCUSER_PasteFromClipboard(data);
1293             break;
1294         case IDS_SELECTALL:
1295             PRIVATE(data)->selectPt1.X = PRIVATE(data)->selectPt1.Y = 0;
1296             PRIVATE(data)->selectPt2.X = (data->curcfg.sb_width - 1) * data->curcfg.cell_width;
1297             PRIVATE(data)->selectPt2.Y = (data->curcfg.sb_height - 1) * data->curcfg.cell_height;
1298             WCUSER_SetSelection(data, 0);
1299             PRIVATE(data)->has_selection = TRUE;
1300             break;
1301         case IDS_SCROLL:
1302         case IDS_SEARCH:
1303             WINE_FIXME("Unhandled yet command: %x\n", wParam);
1304             break;
1305         default:
1306             return DefWindowProc(hWnd, uMsg, wParam, lParam);
1307         }
1308         break;
1309     case WM_INITMENUPOPUP:
1310         if (!HIWORD(lParam))    return DefWindowProc(hWnd, uMsg, wParam, lParam);
1311         WCUSER_SetMenuDetails(data, GetSystemMenu(PRIVATE(data)->hWnd, FALSE));
1312         break;
1313     default:
1314         return DefWindowProc(hWnd, uMsg, wParam, lParam);
1315     }
1316     return 0;
1317 }
1318
1319 /******************************************************************
1320  *              WCUSER_DeleteBackend
1321  *
1322  *
1323  */
1324 void WCUSER_DeleteBackend(struct inner_data* data)
1325 {
1326     if (!PRIVATE(data)) return;
1327     if (PRIVATE(data)->hMemDC)          DeleteDC(PRIVATE(data)->hMemDC);
1328     if (PRIVATE(data)->hWnd)            DestroyWindow(PRIVATE(data)->hWnd);
1329     if (PRIVATE(data)->hFont)           DeleteObject(PRIVATE(data)->hFont);
1330     if (PRIVATE(data)->cursor_bitmap)   DeleteObject(PRIVATE(data)->cursor_bitmap);
1331     if (PRIVATE(data)->hBitmap)         DeleteObject(PRIVATE(data)->hBitmap);
1332     HeapFree(GetProcessHeap(), 0, PRIVATE(data));
1333 }
1334
1335 /******************************************************************
1336  *              WCUSER_MainLoop
1337  *
1338  *
1339  */
1340 static int WCUSER_MainLoop(struct inner_data* data)
1341 {
1342     MSG         msg;
1343
1344     ShowWindow(PRIVATE(data)->hWnd, SW_SHOW);
1345     for (;;)
1346     {
1347         switch (MsgWaitForMultipleObjects(1, &data->hSynchro, FALSE, INFINITE, QS_ALLINPUT))
1348         {
1349         case WAIT_OBJECT_0:
1350             if (!WINECON_GrabChanges(data) && data->curcfg.exit_on_die)
1351                 PostQuitMessage(0);
1352             break;
1353         case WAIT_OBJECT_0+1:
1354             /* need to use PeekMessage loop instead of simple GetMessage:
1355              * multiple messages might have arrived in between,
1356              * so GetMessage would lead to delayed processing */
1357             while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
1358             {
1359                 if (msg.message == WM_QUIT) return 0;
1360                 WINE_TRACE("dispatching msg %04x\n", msg.message);
1361                 DispatchMessage(&msg);
1362             }
1363             break;
1364         default:
1365             WINE_ERR("got pb\n");
1366             /* err */
1367             break;
1368         }
1369     }
1370 }
1371
1372 /******************************************************************
1373  *              WCUSER_InitBackend
1374  *
1375  * Initialisation part II: creation of window.
1376  *
1377  */
1378 BOOL WCUSER_InitBackend(struct inner_data* data)
1379 {
1380     static WCHAR wClassName[] = {'W','i','n','e','C','o','n','s','o','l','e','C','l','a','s','s',0};
1381
1382     WNDCLASS            wndclass;
1383
1384     data->private = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct inner_data_user));
1385     if (!data->private) return FALSE;
1386
1387     data->fnMainLoop = WCUSER_MainLoop;
1388     data->fnPosCursor = WCUSER_PosCursor;
1389     data->fnShapeCursor = WCUSER_ShapeCursor;
1390     data->fnComputePositions = WCUSER_ComputePositions;
1391     data->fnRefresh = WCUSER_Refresh;
1392     data->fnResizeScreenBuffer = WCUSER_ResizeScreenBuffer;
1393     data->fnSetTitle = WCUSER_SetTitle;
1394     data->fnSetFont = WCUSER_SetFontPmt;
1395     data->fnScroll = WCUSER_Scroll;
1396     data->fnDeleteBackend = WCUSER_DeleteBackend;
1397
1398     wndclass.style         = 0;
1399     wndclass.lpfnWndProc   = WCUSER_Proc;
1400     wndclass.cbClsExtra    = 0;
1401     wndclass.cbWndExtra    = sizeof(DWORD);
1402     wndclass.hInstance     = GetModuleHandle(NULL);
1403     wndclass.hIcon         = LoadIcon(0, IDI_WINLOGO);
1404     wndclass.hCursor       = LoadCursor(0, IDC_ARROW);
1405     wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1406     wndclass.lpszMenuName  = NULL;
1407     wndclass.lpszClassName = wClassName;
1408
1409     RegisterClass(&wndclass);
1410
1411     CreateWindow(wndclass.lpszClassName, NULL,
1412                  WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_MINIMIZEBOX|WS_HSCROLL|WS_VSCROLL,
1413                  CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, 0, 0, wndclass.hInstance, data);
1414     if (!PRIVATE(data)->hWnd) return FALSE;
1415
1416     return TRUE;
1417 }