Add x86_64 interlocked functions.
[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 = NULL;
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 static 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 NULL;
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, k;
443
444         k = min(tm.tmLastChar - i, sizeof(buf) / sizeof(buf[0]) - 1);
445         GetCharWidth32(hDC, i, i + k, buf);
446         for (j = 0; j <= k; 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 NULL;
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)
616 {
617     RECT        r;
618     HDC         hDC;
619
620     if (c1.X < 0 || c1.X >= data->curcfg.sb_width ||
621         c2.X < 0 || c2.X >= data->curcfg.sb_width ||
622         c1.Y < 0 || c1.Y >= data->curcfg.sb_height ||
623         c2.Y < 0 || c2.Y >= data->curcfg.sb_height)
624         return;
625
626     WCUSER_GetSelectionRect(data, &r);
627     hDC = GetDC(PRIVATE(data)->hWnd);
628     if (hDC)
629     {
630         if (PRIVATE(data)->hWnd == GetFocus() && data->curcfg.cursor_visible)
631             HideCaret(PRIVATE(data)->hWnd);
632         InvertRect(hDC, &r);
633     }
634     PRIVATE(data)->selectPt1 = c1;
635     PRIVATE(data)->selectPt2 = c2;
636     if (hDC)
637     {
638         WCUSER_GetSelectionRect(data, &r);
639         InvertRect(hDC, &r);
640         ReleaseDC(PRIVATE(data)->hWnd, hDC);
641         if (PRIVATE(data)->hWnd == GetFocus() && data->curcfg.cursor_visible)
642             ShowCaret(PRIVATE(data)->hWnd);
643     }
644 }
645
646 /******************************************************************
647  *              WCUSER_CopySelectionToClipboard
648  *
649  * Copies the current selection into the clipboard
650  */
651 static void     WCUSER_CopySelectionToClipboard(const struct inner_data* data)
652 {
653     HANDLE      hMem;
654     LPWSTR      p;
655     unsigned    w, h;
656
657     w = abs(PRIVATE(data)->selectPt1.X - PRIVATE(data)->selectPt2.X) + 2;
658     h = abs(PRIVATE(data)->selectPt1.Y - PRIVATE(data)->selectPt2.Y) + 1;
659
660     if (!OpenClipboard(PRIVATE(data)->hWnd)) return;
661     EmptyClipboard();
662
663     hMem = GlobalAlloc(GMEM_MOVEABLE, (w * h) * sizeof(WCHAR));
664     if (hMem && (p = GlobalLock(hMem)))
665     {
666         COORD   c;
667         int     y;
668
669         c.X = min(PRIVATE(data)->selectPt1.X, PRIVATE(data)->selectPt2.X);
670         c.Y = min(PRIVATE(data)->selectPt1.Y, PRIVATE(data)->selectPt2.Y);
671
672         for (y = 0; y < h; y++, c.Y++)
673         {
674             ReadConsoleOutputCharacter(data->hConOut, &p[y * w], w - 1, c, NULL);
675             p[y * w + w - 1] = (y < h - 1) ? '\n' : '\0';
676         }
677         GlobalUnlock(hMem);
678         SetClipboardData(CF_UNICODETEXT, hMem);
679     }
680     CloseClipboard();
681 }
682
683 /******************************************************************
684  *              WCUSER_PasteFromClipboard
685  *
686  *
687  */
688 static void     WCUSER_PasteFromClipboard(struct inner_data* data)
689 {
690     HANDLE      h;
691     WCHAR*      ptr;
692
693     if (!OpenClipboard(PRIVATE(data)->hWnd)) return;
694     h = GetClipboardData(CF_UNICODETEXT);
695     if (h && (ptr = GlobalLock(h)))
696     {
697         int             i, len = GlobalSize(h) / sizeof(WCHAR);
698         INPUT_RECORD    ir[2];
699         DWORD           n;
700         SHORT           sh;
701
702         ir[0].EventType = KEY_EVENT;
703         ir[0].Event.KeyEvent.wRepeatCount = 0;
704         ir[0].Event.KeyEvent.dwControlKeyState = 0;
705         ir[0].Event.KeyEvent.bKeyDown = TRUE;
706
707         /* generate the corresponding input records */
708         for (i = 0; i < len; i++)
709         {
710             /* FIXME: the modifying keys are not generated (shift, ctrl...) */
711             sh = VkKeyScan(ptr[i]);
712             ir[0].Event.KeyEvent.wVirtualKeyCode = LOBYTE(sh);
713             ir[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(LOBYTE(sh), 0);
714             ir[0].Event.KeyEvent.uChar.UnicodeChar = ptr[i];
715
716             ir[1] = ir[0];
717             ir[1].Event.KeyEvent.bKeyDown = FALSE;
718
719             WriteConsoleInput(data->hConIn, ir, 2, &n);
720         }
721         GlobalUnlock(h);
722     }
723     CloseClipboard();
724 }
725
726 /******************************************************************
727  *              WCUSER_Refresh
728  *
729  *
730  */
731 static void WCUSER_Refresh(const struct inner_data* data, int tp, int bm)
732 {
733     WCUSER_FillMemDC(data, tp, bm);
734     if (data->curcfg.win_pos.Y <= bm && data->curcfg.win_pos.Y + data->curcfg.win_height >= tp)
735     {
736         RECT    r;
737
738         r.left   = 0;
739         r.right  = data->curcfg.win_width * data->curcfg.cell_width;
740         r.top    = (tp - data->curcfg.win_pos.Y) * data->curcfg.cell_height;
741         r.bottom = (bm - data->curcfg.win_pos.Y + 1) * data->curcfg.cell_height;
742         InvalidateRect(PRIVATE(data)->hWnd, &r, FALSE);
743         UpdateWindow(PRIVATE(data)->hWnd);
744     }
745 }
746
747 /******************************************************************
748  *              WCUSER_Paint
749  *
750  *
751  */
752 static void     WCUSER_Paint(const struct inner_data* data)
753 {
754     PAINTSTRUCT         ps;
755
756     BeginPaint(PRIVATE(data)->hWnd, &ps);
757     BitBlt(ps.hdc, 0, 0,
758            data->curcfg.win_width * data->curcfg.cell_width,
759            data->curcfg.win_height * data->curcfg.cell_height,
760            PRIVATE(data)->hMemDC,
761            data->curcfg.win_pos.X * data->curcfg.cell_width,
762            data->curcfg.win_pos.Y * data->curcfg.cell_height,
763            SRCCOPY);
764     if (PRIVATE(data)->has_selection)
765         WCUSER_SetSelection(data, ps.hdc);
766     EndPaint(PRIVATE(data)->hWnd, &ps);
767 }
768
769 /******************************************************************
770  *              WCUSER_Scroll
771  *
772  *
773  */
774 static void WCUSER_Scroll(struct inner_data* data, int pos, BOOL horz)
775 {
776     if (horz)
777     {
778         SetScrollPos(PRIVATE(data)->hWnd, SB_HORZ, pos, TRUE);
779         data->curcfg.win_pos.X = pos;
780     }
781     else
782     {
783         SetScrollPos(PRIVATE(data)->hWnd, SB_VERT, pos, TRUE);
784         data->curcfg.win_pos.Y = pos;
785     }
786     InvalidateRect(PRIVATE(data)->hWnd, NULL, FALSE);
787 }
788
789 /******************************************************************
790  *              WCUSER_FillMenu
791  *
792  *
793  */
794 static BOOL WCUSER_FillMenu(HMENU hMenu, BOOL sep)
795 {
796     HMENU               hSubMenu;
797     HINSTANCE           hInstance = GetModuleHandle(NULL);
798     WCHAR               buff[256];
799
800     if (!hMenu) return FALSE;
801
802     /* FIXME: error handling & memory cleanup */
803     hSubMenu = CreateMenu();
804     if (!hSubMenu) return FALSE;
805
806     LoadString(hInstance, IDS_MARK, buff, sizeof(buff) / sizeof(WCHAR));
807     InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_MARK, buff);
808     LoadString(hInstance, IDS_COPY, buff, sizeof(buff) / sizeof(WCHAR));
809     InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_COPY, buff);
810     LoadString(hInstance, IDS_PASTE, buff, sizeof(buff) / sizeof(WCHAR));
811     InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_PASTE, buff);
812     LoadString(hInstance, IDS_SELECTALL, buff, sizeof(buff) / sizeof(WCHAR));
813     InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_SELECTALL, buff);
814     LoadString(hInstance, IDS_SCROLL, buff, sizeof(buff) / sizeof(WCHAR));
815     InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_SCROLL, buff);
816     LoadString(hInstance, IDS_SEARCH, buff, sizeof(buff) / sizeof(WCHAR));
817     InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_SEARCH, buff);
818
819     if (sep) InsertMenu(hMenu, -1, MF_BYPOSITION|MF_SEPARATOR, 0, NULL);
820     LoadString(hInstance, IDS_EDIT, buff, sizeof(buff) / sizeof(WCHAR));
821     InsertMenu(hMenu, -1, MF_BYPOSITION|MF_STRING|MF_POPUP, (UINT_PTR)hSubMenu, buff);
822     LoadString(hInstance, IDS_DEFAULT, buff, sizeof(buff) / sizeof(WCHAR));
823     InsertMenu(hMenu, -1, MF_BYPOSITION|MF_STRING, IDS_DEFAULT, buff);
824     LoadString(hInstance, IDS_PROPERTIES, buff, sizeof(buff) / sizeof(WCHAR));
825     InsertMenu(hMenu, -1, MF_BYPOSITION|MF_STRING, IDS_PROPERTIES, buff);
826
827     return TRUE;
828 }
829
830 /******************************************************************
831  *              WCUSER_SetMenuDetails
832  *
833  * Grays / ungrays the menu items according to their state
834  */
835 static void     WCUSER_SetMenuDetails(const struct inner_data* data, HMENU hMenu)
836 {
837     if (!hMenu) {WINE_ERR("Issue in getting menu bits\n");return;}
838
839     EnableMenuItem(hMenu, IDS_COPY,
840                    MF_BYCOMMAND|(PRIVATE(data)->has_selection ? MF_ENABLED : MF_GRAYED));
841     EnableMenuItem(hMenu, IDS_PASTE,
842                    MF_BYCOMMAND|(IsClipboardFormatAvailable(CF_UNICODETEXT)
843                                  ? MF_ENABLED : MF_GRAYED));
844     EnableMenuItem(hMenu, IDS_SCROLL, MF_BYCOMMAND|MF_GRAYED);
845     EnableMenuItem(hMenu, IDS_SEARCH, MF_BYCOMMAND|MF_GRAYED);
846 }
847
848 /******************************************************************
849  *              WCUSER_Create
850  *
851  * Creates the window for the rendering
852  */
853 static LRESULT WCUSER_Create(HWND hWnd, LPCREATESTRUCT lpcs)
854 {
855     struct inner_data*  data;
856     HMENU               hSysMenu;
857
858     data = lpcs->lpCreateParams;
859     SetWindowLong(hWnd, 0L, (DWORD)data);
860     PRIVATE(data)->hWnd = hWnd;
861
862     hSysMenu = GetSystemMenu(hWnd, FALSE);
863     if (!hSysMenu) return 0;
864     PRIVATE(data)->hPopMenu = CreatePopupMenu();
865     if (!PRIVATE(data)->hPopMenu) return 0;
866
867     WCUSER_FillMenu(hSysMenu, TRUE);
868     WCUSER_FillMenu(PRIVATE(data)->hPopMenu, FALSE);
869
870     PRIVATE(data)->hMemDC = CreateCompatibleDC(0);
871     if (!PRIVATE(data)->hMemDC) {WINE_ERR("no mem dc\n");return 0;}
872
873     data->curcfg.quick_edit = FALSE;
874     return 0;
875 }
876
877 /******************************************************************
878  *              WCUSER_GetCtrlKeyState
879  *
880  * Get the console bit mask equivalent to the VK_ status in keyState
881  */
882 static DWORD    WCUSER_GetCtrlKeyState(BYTE* keyState)
883 {
884     DWORD               ret = 0;
885
886     GetKeyboardState(keyState);
887     if (keyState[VK_SHIFT]    & 0x80)   ret |= SHIFT_PRESSED;
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             WCUSER_MoveSelection(data, c1, c2);
928             break;
929         case VK_LEFT:
930             c1 = PRIVATE(data)->selectPt1;
931             c2 = PRIVATE(data)->selectPt2;
932             c1.X--; c2.X--;
933             WCUSER_MoveSelection(data, c1, c2);
934             break;
935         case VK_UP:
936             c1 = PRIVATE(data)->selectPt1;
937             c2 = PRIVATE(data)->selectPt2;
938             c1.Y--; c2.Y--;
939             WCUSER_MoveSelection(data, c1, c2);
940             break;
941         case VK_DOWN:
942             c1 = PRIVATE(data)->selectPt1;
943             c2 = PRIVATE(data)->selectPt2;
944             c1.Y++; c2.Y++;
945             WCUSER_MoveSelection(data, c1, c2);
946             break;
947         }
948         break;
949     case SHIFT_PRESSED:
950         switch (wParam)
951         {
952         case VK_RIGHT:
953             c1 = PRIVATE(data)->selectPt1;
954             c2 = PRIVATE(data)->selectPt2;
955             c2.X++;
956             WCUSER_MoveSelection(data, c1, c2);
957             break;
958         case VK_LEFT:
959             c1 = PRIVATE(data)->selectPt1;
960             c2 = PRIVATE(data)->selectPt2;
961             c2.X--;
962             WCUSER_MoveSelection(data, c1, c2);
963             break;
964         case VK_UP:
965             c1 = PRIVATE(data)->selectPt1;
966             c2 = PRIVATE(data)->selectPt2;
967             c2.Y--;
968             WCUSER_MoveSelection(data, c1, c2);
969             break;
970         case VK_DOWN:
971             c1 = PRIVATE(data)->selectPt1;
972             c2 = PRIVATE(data)->selectPt2;
973             c2.Y++;
974             WCUSER_MoveSelection(data, c1, c2);
975             break;
976         }
977         break;
978     }
979 }
980
981 /******************************************************************
982  *              WCUSER_GenerateKeyInputRecord
983  *
984  * generates input_record from windows WM_KEYUP/WM_KEYDOWN messages
985  */
986 static void    WCUSER_GenerateKeyInputRecord(struct inner_data* data, BOOL down,
987                                              WPARAM wParam, LPARAM lParam, BOOL sys)
988 {
989     INPUT_RECORD        ir;
990     DWORD               n;
991     WCHAR               buf[2];
992     static      WCHAR   last; /* keep last char seen as feed for key up message */
993     BYTE                keyState[256];
994
995     ir.EventType = KEY_EVENT;
996     ir.Event.KeyEvent.bKeyDown = down;
997     ir.Event.KeyEvent.wRepeatCount = LOWORD(lParam);
998     ir.Event.KeyEvent.wVirtualKeyCode = wParam;
999
1000     ir.Event.KeyEvent.wVirtualScanCode = HIWORD(lParam) & 0xFF;
1001
1002     ir.Event.KeyEvent.uChar.UnicodeChar = 0;
1003     ir.Event.KeyEvent.dwControlKeyState = WCUSER_GetCtrlKeyState(keyState);
1004     if (lParam & (1L << 24))            ir.Event.KeyEvent.dwControlKeyState |= ENHANCED_KEY;
1005     if (sys)                            ir.Event.KeyEvent.dwControlKeyState |= LEFT_ALT_PRESSED; /* FIXME: gotta choose one */
1006
1007     if (!(ir.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY))
1008     {
1009         if (down)
1010         {
1011             switch (ToUnicode(wParam, HIWORD(lParam), keyState, buf, 2, 0))
1012             {
1013             case 2:
1014                 /* FIXME... should generate two events... */
1015                 /* fall thru */
1016             case 1:
1017                 last = buf[0];
1018                 break;
1019             default:
1020                 last = 0;
1021                 break;
1022             }
1023         }
1024         ir.Event.KeyEvent.uChar.UnicodeChar = last; /* FIXME HACKY... and buggy 'coz it should be a stack, not a single value */
1025         if (!down) last = 0;
1026     }
1027
1028     WriteConsoleInput(data->hConIn, &ir, 1, &n);
1029 }
1030
1031 /******************************************************************
1032  *              WCUSER_GenerateMouseInputRecord
1033  *
1034  *
1035  */
1036 static void    WCUSER_GenerateMouseInputRecord(struct inner_data* data, COORD c,
1037                                                WPARAM wParam, DWORD event)
1038 {
1039     INPUT_RECORD        ir;
1040     BYTE                keyState[256];
1041     DWORD               mode, n;
1042
1043     /* MOUSE_EVENTs shouldn't be sent unless ENABLE_MOUSE_INPUT is active */
1044     if (!GetConsoleMode(data->hConIn, &mode) || !(mode & ENABLE_MOUSE_INPUT))
1045         return;
1046
1047     ir.EventType = MOUSE_EVENT;
1048     ir.Event.MouseEvent.dwMousePosition = c;
1049     ir.Event.MouseEvent.dwButtonState = 0;
1050     if (wParam & MK_LBUTTON) ir.Event.MouseEvent.dwButtonState |= FROM_LEFT_1ST_BUTTON_PRESSED;
1051     if (wParam & MK_MBUTTON) ir.Event.MouseEvent.dwButtonState |= FROM_LEFT_2ND_BUTTON_PRESSED;
1052     if (wParam & MK_RBUTTON) ir.Event.MouseEvent.dwButtonState |= RIGHTMOST_BUTTON_PRESSED;
1053     ir.Event.MouseEvent.dwControlKeyState = WCUSER_GetCtrlKeyState(keyState);
1054     ir.Event.MouseEvent.dwEventFlags = event;
1055
1056     WriteConsoleInput(data->hConIn, &ir, 1, &n);
1057 }
1058
1059 /******************************************************************
1060  *              WCUSER_Proc
1061  *
1062  *
1063  */
1064 static LRESULT CALLBACK WCUSER_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1065 {
1066     struct inner_data*  data = (struct inner_data*)GetWindowLong(hWnd, 0);
1067
1068     switch (uMsg)
1069     {
1070     case WM_CREATE:
1071         return WCUSER_Create(hWnd, (LPCREATESTRUCT)lParam);
1072     case WM_DESTROY:
1073         PRIVATE(data)->hWnd = 0;
1074         PostQuitMessage(0);
1075         break;
1076     case WM_PAINT:
1077         WCUSER_Paint(data);
1078         break;
1079     case WM_KEYDOWN:
1080     case WM_KEYUP:
1081         if (PRIVATE(data)->has_selection)
1082             WCUSER_HandleSelectionKey(data, uMsg == WM_KEYDOWN, wParam, lParam);
1083         else
1084             WCUSER_GenerateKeyInputRecord(data, uMsg == WM_KEYDOWN, wParam, lParam, FALSE);
1085         break;
1086     case WM_SYSKEYDOWN:
1087     case WM_SYSKEYUP:
1088         WCUSER_GenerateKeyInputRecord(data, uMsg == WM_SYSKEYDOWN, wParam, lParam, TRUE);
1089         break;
1090     case WM_LBUTTONDOWN:
1091         if (data->curcfg.quick_edit)
1092         {
1093             if (PRIVATE(data)->has_selection)
1094             {
1095                 PRIVATE(data)->has_selection = FALSE;
1096                 WCUSER_SetSelection(data, 0);
1097             }
1098             else
1099             {
1100                 PRIVATE(data)->selectPt1 = PRIVATE(data)->selectPt2 = WCUSER_GetCell(data, lParam);
1101                 SetCapture(PRIVATE(data)->hWnd);
1102                 WCUSER_SetSelection(data, 0);
1103                 PRIVATE(data)->has_selection = TRUE;
1104             }
1105         }
1106         else
1107         {
1108             WCUSER_GenerateMouseInputRecord(data, WCUSER_GetCell(data, lParam), wParam, 0);
1109         }
1110         break;
1111     case WM_MOUSEMOVE:
1112         if (data->curcfg.quick_edit)
1113         {
1114             if (GetCapture() == PRIVATE(data)->hWnd && PRIVATE(data)->has_selection &&
1115                 (wParam & MK_LBUTTON))
1116             {
1117                 WCUSER_MoveSelection(data, PRIVATE(data)->selectPt1, WCUSER_GetCell(data, lParam));
1118             }
1119         }
1120         else
1121         {
1122             WCUSER_GenerateMouseInputRecord(data, WCUSER_GetCell(data, lParam), wParam, MOUSE_MOVED);
1123         }
1124         break;
1125     case WM_LBUTTONUP:
1126         if (data->curcfg.quick_edit)
1127         {
1128             if (GetCapture() == PRIVATE(data)->hWnd && PRIVATE(data)->has_selection &&
1129                 (wParam& MK_LBUTTON))
1130             {
1131                 WCUSER_MoveSelection(data, PRIVATE(data)->selectPt1, WCUSER_GetCell(data, lParam));
1132                 ReleaseCapture();
1133                 PRIVATE(data)->has_selection = FALSE;
1134             }
1135         }
1136         else
1137         {
1138             WCUSER_GenerateMouseInputRecord(data, WCUSER_GetCell(data, lParam), wParam, 0);
1139         }
1140         break;
1141     case WM_RBUTTONDOWN:
1142         if ((wParam & (MK_CONTROL|MK_SHIFT)) == data->curcfg.menu_mask)
1143         {
1144             RECT        r;
1145
1146             GetWindowRect(hWnd, &r);
1147             WCUSER_SetMenuDetails(data, PRIVATE(data)->hPopMenu);
1148             TrackPopupMenu(PRIVATE(data)->hPopMenu, TPM_LEFTALIGN|TPM_TOPALIGN,
1149                            r.left + LOWORD(lParam), r.top + HIWORD(lParam), 0, hWnd, NULL);
1150         }
1151         else
1152         {
1153             WCUSER_GenerateMouseInputRecord(data, WCUSER_GetCell(data, lParam), wParam, 0);
1154         }
1155         break;
1156     case WM_RBUTTONUP:
1157         /* no need to track for rbutton up when opening the popup... the event will be
1158          * swallowed by TrackPopupMenu */
1159         WCUSER_GenerateMouseInputRecord(data, WCUSER_GetCell(data, lParam), wParam, 0);
1160         break;
1161     case WM_MOUSEWHEEL:
1162         /* FIXME: should we scroll too ? */
1163         WCUSER_GenerateMouseInputRecord(data, WCUSER_GetCell(data, lParam), wParam, MOUSE_WHEELED);
1164         break;
1165     case WM_SETFOCUS:
1166         if (data->curcfg.cursor_visible)
1167         {
1168             CreateCaret(PRIVATE(data)->hWnd, PRIVATE(data)->cursor_bitmap,
1169                         data->curcfg.cell_width, data->curcfg.cell_height);
1170             WCUSER_PosCursor(data);
1171         }
1172         break;
1173     case WM_KILLFOCUS:
1174         if (data->curcfg.cursor_visible)
1175             DestroyCaret();
1176         break;
1177     case WM_HSCROLL:
1178         {
1179             int pos = data->curcfg.win_pos.X;
1180
1181             switch (LOWORD(wParam))
1182             {
1183             case SB_PAGEUP:     pos -= 8;               break;
1184             case SB_PAGEDOWN:   pos += 8;               break;
1185             case SB_LINEUP:     pos--;                  break;
1186             case SB_LINEDOWN:   pos++;                  break;
1187             case SB_THUMBTRACK: pos = HIWORD(wParam);   break;
1188             default:                                    break;
1189             }
1190             if (pos < 0) pos = 0;
1191             if (pos > data->curcfg.sb_width - data->curcfg.win_width)
1192                 pos = data->curcfg.sb_width - data->curcfg.win_width;
1193             if (pos != data->curcfg.win_pos.X)
1194             {
1195                 ScrollWindow(hWnd, (data->curcfg.win_pos.X - pos) * data->curcfg.cell_width, 0,
1196                              NULL, NULL);
1197                 data->curcfg.win_pos.X = pos;
1198                 SetScrollPos(hWnd, SB_HORZ, pos, TRUE);
1199                 UpdateWindow(hWnd);
1200                 WCUSER_PosCursor(data);
1201                 WINECON_NotifyWindowChange(data);
1202             }
1203         }
1204         break;
1205     case WM_VSCROLL:
1206         {
1207             int pos = data->curcfg.win_pos.Y;
1208
1209             switch (LOWORD(wParam))
1210             {
1211             case SB_PAGEUP:     pos -= 8;               break;
1212             case SB_PAGEDOWN:   pos += 8;               break;
1213             case SB_LINEUP:     pos--;                  break;
1214             case SB_LINEDOWN:   pos++;                  break;
1215             case SB_THUMBTRACK: pos = HIWORD(wParam);   break;
1216             default:                                    break;
1217             }
1218             if (pos < 0) pos = 0;
1219             if (pos > data->curcfg.sb_height - data->curcfg.win_height)
1220                 pos = data->curcfg.sb_height - data->curcfg.win_height;
1221             if (pos != data->curcfg.win_pos.Y)
1222             {
1223                 ScrollWindow(hWnd, 0, (data->curcfg.win_pos.Y - pos) * data->curcfg.cell_height,
1224                              NULL, NULL);
1225                 data->curcfg.win_pos.Y = pos;
1226                 SetScrollPos(hWnd, SB_VERT, pos, TRUE);
1227                 UpdateWindow(hWnd);
1228                 WCUSER_PosCursor(data);
1229                 WINECON_NotifyWindowChange(data);
1230             }
1231
1232         }
1233     case WM_SYSCOMMAND:
1234         switch (wParam)
1235         {
1236         case IDS_DEFAULT:
1237             WCUSER_GetProperties(data, FALSE);
1238             break;
1239         case IDS_PROPERTIES:
1240             WCUSER_GetProperties(data, TRUE);
1241             break;
1242         default:
1243             return DefWindowProc(hWnd, uMsg, wParam, lParam);
1244         }
1245         break;
1246     case WM_COMMAND:
1247         switch (wParam)
1248         {
1249         case IDS_DEFAULT:
1250             WCUSER_GetProperties(data, FALSE);
1251             break;
1252         case IDS_PROPERTIES:
1253             WCUSER_GetProperties(data, TRUE);
1254             break;
1255         case IDS_MARK:
1256             PRIVATE(data)->selectPt1.X = PRIVATE(data)->selectPt1.Y = 0;
1257             PRIVATE(data)->selectPt2.X = PRIVATE(data)->selectPt2.Y = 0;
1258             WCUSER_SetSelection(data, 0);
1259             PRIVATE(data)->has_selection = TRUE;
1260             break;
1261         case IDS_COPY:
1262             if (PRIVATE(data)->has_selection)
1263             {
1264                 PRIVATE(data)->has_selection = FALSE;
1265                 WCUSER_SetSelection(data, 0);
1266                 WCUSER_CopySelectionToClipboard(data);
1267             }
1268             break;
1269         case IDS_PASTE:
1270             WCUSER_PasteFromClipboard(data);
1271             break;
1272         case IDS_SELECTALL:
1273             PRIVATE(data)->selectPt1.X = PRIVATE(data)->selectPt1.Y = 0;
1274             PRIVATE(data)->selectPt2.X = (data->curcfg.sb_width - 1) * data->curcfg.cell_width;
1275             PRIVATE(data)->selectPt2.Y = (data->curcfg.sb_height - 1) * data->curcfg.cell_height;
1276             WCUSER_SetSelection(data, 0);
1277             PRIVATE(data)->has_selection = TRUE;
1278             break;
1279         case IDS_SCROLL:
1280         case IDS_SEARCH:
1281             WINE_FIXME("Unhandled yet command: %x\n", wParam);
1282             break;
1283         default:
1284             return DefWindowProc(hWnd, uMsg, wParam, lParam);
1285         }
1286         break;
1287     case WM_INITMENUPOPUP:
1288         if (!HIWORD(lParam))    return DefWindowProc(hWnd, uMsg, wParam, lParam);
1289         WCUSER_SetMenuDetails(data, GetSystemMenu(PRIVATE(data)->hWnd, FALSE));
1290         break;
1291     default:
1292         return DefWindowProc(hWnd, uMsg, wParam, lParam);
1293     }
1294     return 0;
1295 }
1296
1297 /******************************************************************
1298  *              WCUSER_DeleteBackend
1299  *
1300  *
1301  */
1302 static void WCUSER_DeleteBackend(struct inner_data* data)
1303 {
1304     if (!PRIVATE(data)) return;
1305     if (PRIVATE(data)->hMemDC)          DeleteDC(PRIVATE(data)->hMemDC);
1306     if (PRIVATE(data)->hWnd)            DestroyWindow(PRIVATE(data)->hWnd);
1307     if (PRIVATE(data)->hFont)           DeleteObject(PRIVATE(data)->hFont);
1308     if (PRIVATE(data)->cursor_bitmap)   DeleteObject(PRIVATE(data)->cursor_bitmap);
1309     if (PRIVATE(data)->hBitmap)         DeleteObject(PRIVATE(data)->hBitmap);
1310     HeapFree(GetProcessHeap(), 0, PRIVATE(data));
1311 }
1312
1313 /******************************************************************
1314  *              WCUSER_MainLoop
1315  *
1316  *
1317  */
1318 static int WCUSER_MainLoop(struct inner_data* data)
1319 {
1320     MSG         msg;
1321
1322     ShowWindow(PRIVATE(data)->hWnd, data->nCmdShow);
1323     for (;;)
1324     {
1325         switch (MsgWaitForMultipleObjects(1, &data->hSynchro, FALSE, INFINITE, QS_ALLINPUT))
1326         {
1327         case WAIT_OBJECT_0:
1328             if (!WINECON_GrabChanges(data) && data->curcfg.exit_on_die)
1329                 PostQuitMessage(0);
1330             break;
1331         case WAIT_OBJECT_0+1:
1332             /* need to use PeekMessage loop instead of simple GetMessage:
1333              * multiple messages might have arrived in between,
1334              * so GetMessage would lead to delayed processing */
1335             while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
1336             {
1337                 if (msg.message == WM_QUIT) return 0;
1338                 WINE_TRACE("dispatching msg %04x\n", msg.message);
1339                 DispatchMessage(&msg);
1340             }
1341             break;
1342         default:
1343             WINE_ERR("got pb\n");
1344             /* err */
1345             break;
1346         }
1347     }
1348 }
1349
1350 /******************************************************************
1351  *              WCUSER_InitBackend
1352  *
1353  * Initialisation part II: creation of window.
1354  *
1355  */
1356 enum init_return WCUSER_InitBackend(struct inner_data* data)
1357 {
1358     static const WCHAR wClassName[] = {'W','i','n','e','C','o','n','s','o','l','e','C','l','a','s','s',0};
1359
1360     WNDCLASS            wndclass;
1361
1362     data->private = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct inner_data_user));
1363     if (!data->private) return init_failed;
1364
1365     data->fnMainLoop = WCUSER_MainLoop;
1366     data->fnPosCursor = WCUSER_PosCursor;
1367     data->fnShapeCursor = WCUSER_ShapeCursor;
1368     data->fnComputePositions = WCUSER_ComputePositions;
1369     data->fnRefresh = WCUSER_Refresh;
1370     data->fnResizeScreenBuffer = WCUSER_ResizeScreenBuffer;
1371     data->fnSetTitle = WCUSER_SetTitle;
1372     data->fnSetFont = WCUSER_SetFontPmt;
1373     data->fnScroll = WCUSER_Scroll;
1374     data->fnDeleteBackend = WCUSER_DeleteBackend;
1375
1376     wndclass.style         = 0;
1377     wndclass.lpfnWndProc   = WCUSER_Proc;
1378     wndclass.cbClsExtra    = 0;
1379     wndclass.cbWndExtra    = sizeof(DWORD);
1380     wndclass.hInstance     = GetModuleHandle(NULL);
1381     wndclass.hIcon         = LoadIcon(0, IDI_WINLOGO);
1382     wndclass.hCursor       = LoadCursor(0, IDC_ARROW);
1383     wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1384     wndclass.lpszMenuName  = NULL;
1385     wndclass.lpszClassName = wClassName;
1386
1387     RegisterClass(&wndclass);
1388
1389     CreateWindow(wndclass.lpszClassName, NULL,
1390                  WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_MINIMIZEBOX|WS_HSCROLL|WS_VSCROLL,
1391                  CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, 0, 0, wndclass.hInstance, data);
1392     if (!PRIVATE(data)->hWnd) return init_failed;
1393
1394     return init_success;
1395 }