Update for rapps database.
[reactos.git] / reactos / win32ss / user / winsrv / consrv / frontends / gui / guiterm.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: win32ss/user/winsrv/consrv/frontends/gui/guiterm.c
5 * PURPOSE: GUI Terminal Front-End
6 * PROGRAMMERS: Gé van Geldorp
7 * Johannes Anderwald
8 * Jeffrey Morlan
9 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
10 */
11
12 /* INCLUDES *******************************************************************/
13
14 #define COBJMACROS
15 #define NONAMELESSUNION
16
17 #include "consrv.h"
18 #include "include/conio.h"
19 #include "include/console.h"
20 #include "include/settings.h"
21 #include "conoutput.h"
22 #include "guiterm.h"
23 #include "guisettings.h"
24 #include "resource.h"
25
26 #include <windowsx.h>
27
28 #include <shlwapi.h>
29 #include <shlobj.h>
30
31 #define NDEBUG
32 #include <debug.h>
33
34 /* GUI Console Window Class name */
35 #define GUI_CONSOLE_WINDOW_CLASS L"ConsoleWindowClass"
36
37 #ifndef WM_APP
38 #define WM_APP 0x8000
39 #endif
40 #define PM_CREATE_CONSOLE (WM_APP + 1)
41 #define PM_DESTROY_CONSOLE (WM_APP + 2)
42 #define PM_RESIZE_TERMINAL (WM_APP + 3)
43 #define PM_CONSOLE_BEEP (WM_APP + 4)
44 #define PM_CONSOLE_SET_TITLE (WM_APP + 5)
45
46
47 /* Not defined in any header file */
48 extern VOID NTAPI PrivateCsrssManualGuiCheck(LONG Check);
49 // See winsrv/usersrv/init.c line 234
50
51
52 /* GLOBALS ********************************************************************/
53
54 typedef struct _GUI_INIT_INFO
55 {
56 PCONSOLE_INFO ConsoleInfo;
57 PCONSOLE_START_INFO ConsoleStartInfo;
58 ULONG ProcessId;
59 } GUI_INIT_INFO, *PGUI_INIT_INFO;
60
61 /**************************************************************\
62 \** Define the Console Leader Process for the console window **/
63 #define GWLP_CONSOLEWND_ALLOC (2 * sizeof(LONG_PTR))
64 #define GWLP_CONSOLE_LEADER_PID 0
65 #define GWLP_CONSOLE_LEADER_TID 4
66
67 #define SetConsoleWndConsoleLeaderCID(GuiData) \
68 do { \
69 PCONSOLE_PROCESS_DATA ProcessData; \
70 CLIENT_ID ConsoleLeaderCID; \
71 ProcessData = CONTAINING_RECORD((GuiData)->Console->ProcessList.Blink, \
72 CONSOLE_PROCESS_DATA, \
73 ConsoleLink); \
74 ConsoleLeaderCID = ProcessData->Process->ClientId; \
75 SetWindowLongPtrW((GuiData)->hWindow, GWLP_CONSOLE_LEADER_PID, \
76 (LONG_PTR)(ConsoleLeaderCID.UniqueProcess)); \
77 SetWindowLongPtrW((GuiData)->hWindow, GWLP_CONSOLE_LEADER_TID, \
78 (LONG_PTR)(ConsoleLeaderCID.UniqueThread )); \
79 } while (0)
80 /**************************************************************/
81
82 static BOOL ConsInitialized = FALSE;
83 static HICON ghDefaultIcon = NULL;
84 static HICON ghDefaultIconSm = NULL;
85 static HCURSOR ghDefaultCursor = NULL;
86 static HWND NotifyWnd = NULL;
87
88 typedef struct _GUICONSOLE_MENUITEM
89 {
90 UINT uID;
91 const struct _GUICONSOLE_MENUITEM *SubMenu;
92 WORD wCmdID;
93 } GUICONSOLE_MENUITEM, *PGUICONSOLE_MENUITEM;
94
95 static const GUICONSOLE_MENUITEM GuiConsoleEditMenuItems[] =
96 {
97 { IDS_MARK, NULL, ID_SYSTEM_EDIT_MARK },
98 { IDS_COPY, NULL, ID_SYSTEM_EDIT_COPY },
99 { IDS_PASTE, NULL, ID_SYSTEM_EDIT_PASTE },
100 { IDS_SELECTALL, NULL, ID_SYSTEM_EDIT_SELECTALL },
101 { IDS_SCROLL, NULL, ID_SYSTEM_EDIT_SCROLL },
102 { IDS_FIND, NULL, ID_SYSTEM_EDIT_FIND },
103
104 { 0, NULL, 0 } /* End of list */
105 };
106
107 static const GUICONSOLE_MENUITEM GuiConsoleMainMenuItems[] =
108 {
109 { IDS_EDIT, GuiConsoleEditMenuItems, 0 },
110 { IDS_DEFAULTS, NULL, ID_SYSTEM_DEFAULTS },
111 { IDS_PROPERTIES, NULL, ID_SYSTEM_PROPERTIES },
112
113 { 0, NULL, 0 } /* End of list */
114 };
115
116 /*
117 * Default 16-color palette for foreground and background
118 * (corresponding flags in comments).
119 */
120 const COLORREF s_Colors[16] =
121 {
122 RGB(0, 0, 0), // (Black)
123 RGB(0, 0, 128), // BLUE
124 RGB(0, 128, 0), // GREEN
125 RGB(0, 128, 128), // BLUE | GREEN
126 RGB(128, 0, 0), // RED
127 RGB(128, 0, 128), // BLUE | RED
128 RGB(128, 128, 0), // GREEN | RED
129 RGB(192, 192, 192), // BLUE | GREEN | RED
130
131 RGB(128, 128, 128), // (Grey) INTENSITY
132 RGB(0, 0, 255), // BLUE | INTENSITY
133 RGB(0, 255, 0), // GREEN | INTENSITY
134 RGB(0, 255, 255), // BLUE | GREEN | INTENSITY
135 RGB(255, 0, 0), // RED | INTENSITY
136 RGB(255, 0, 255), // BLUE | RED | INTENSITY
137 RGB(255, 255, 0), // GREEN | RED | INTENSITY
138 RGB(255, 255, 255) // BLUE | GREEN | RED | INTENSITY
139 };
140
141 /* FUNCTIONS ******************************************************************/
142
143 static VOID
144 GetScreenBufferSizeUnits(IN PCONSOLE_SCREEN_BUFFER Buffer,
145 IN PGUI_CONSOLE_DATA GuiData,
146 OUT PUINT WidthUnit,
147 OUT PUINT HeightUnit)
148 {
149 if (Buffer == NULL || GuiData == NULL ||
150 WidthUnit == NULL || HeightUnit == NULL)
151 {
152 return;
153 }
154
155 if (GetType(Buffer) == TEXTMODE_BUFFER)
156 {
157 *WidthUnit = GuiData->CharWidth ;
158 *HeightUnit = GuiData->CharHeight;
159 }
160 else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
161 {
162 *WidthUnit = 1;
163 *HeightUnit = 1;
164 }
165 }
166
167
168
169 static VOID
170 GuiConsoleAppendMenuItems(HMENU hMenu,
171 const GUICONSOLE_MENUITEM *Items)
172 {
173 UINT i = 0;
174 WCHAR szMenuString[255];
175 HMENU hSubMenu;
176
177 do
178 {
179 if (Items[i].uID != (UINT)-1)
180 {
181 if (LoadStringW(ConSrvDllInstance,
182 Items[i].uID,
183 szMenuString,
184 sizeof(szMenuString) / sizeof(szMenuString[0])) > 0)
185 {
186 if (Items[i].SubMenu != NULL)
187 {
188 hSubMenu = CreatePopupMenu();
189 if (hSubMenu != NULL)
190 {
191 GuiConsoleAppendMenuItems(hSubMenu,
192 Items[i].SubMenu);
193
194 if (!AppendMenuW(hMenu,
195 MF_STRING | MF_POPUP,
196 (UINT_PTR)hSubMenu,
197 szMenuString))
198 {
199 DestroyMenu(hSubMenu);
200 }
201 }
202 }
203 else
204 {
205 AppendMenuW(hMenu,
206 MF_STRING,
207 Items[i].wCmdID,
208 szMenuString);
209 }
210 }
211 }
212 else
213 {
214 AppendMenuW(hMenu,
215 MF_SEPARATOR,
216 0,
217 NULL);
218 }
219 i++;
220 } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 0));
221 }
222
223 static VOID
224 GuiConsoleCreateSysMenu(HWND hWnd)
225 {
226 HMENU hMenu = GetSystemMenu(hWnd, FALSE);
227 if (hMenu != NULL)
228 {
229 GuiConsoleAppendMenuItems(hMenu, GuiConsoleMainMenuItems);
230 DrawMenuBar(hWnd);
231 }
232 }
233
234 static VOID
235 GuiSendMenuEvent(PCONSOLE Console, UINT CmdId)
236 {
237 INPUT_RECORD er;
238
239 er.EventType = MENU_EVENT;
240 er.Event.MenuEvent.dwCommandId = CmdId;
241
242 ConioProcessInputEvent(Console, &er);
243 }
244
245 static VOID
246 GuiConsoleCopy(PGUI_CONSOLE_DATA GuiData);
247 static VOID
248 GuiConsolePaste(PGUI_CONSOLE_DATA GuiData);
249 static VOID
250 GuiConsoleUpdateSelection(PCONSOLE Console, PCOORD coord);
251 static VOID NTAPI
252 GuiDrawRegion(IN OUT PFRONTEND This, SMALL_RECT* Region);
253 static VOID
254 GuiConsoleResizeWindow(PGUI_CONSOLE_DATA GuiData);
255
256
257 static LRESULT
258 GuiConsoleHandleSysMenuCommand(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam)
259 {
260 LRESULT Ret = TRUE;
261 PCONSOLE Console = GuiData->Console;
262 PCONSOLE_SCREEN_BUFFER ActiveBuffer;
263
264 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
265 {
266 Ret = FALSE;
267 goto Quit;
268 }
269 // ActiveBuffer = ConDrvGetActiveScreenBuffer(Console);
270 ActiveBuffer = GuiData->ActiveBuffer;
271
272 /*
273 * In case the selected menu item belongs to the user-reserved menu id range,
274 * send to him a menu event and return directly. The user must handle those
275 * reserved menu commands...
276 */
277 if (GuiData->cmdIdLow <= (UINT)wParam && (UINT)wParam <= GuiData->cmdIdHigh)
278 {
279 GuiSendMenuEvent(Console, (UINT)wParam);
280 goto Unlock_Quit;
281 }
282
283 /* ... otherwise, perform actions. */
284 switch (wParam)
285 {
286 case ID_SYSTEM_EDIT_MARK:
287 {
288 LPWSTR WindowTitle = NULL;
289 SIZE_T Length = 0;
290
291 Console->dwSelectionCursor.X = ActiveBuffer->ViewOrigin.X;
292 Console->dwSelectionCursor.Y = ActiveBuffer->ViewOrigin.Y;
293 Console->Selection.dwSelectionAnchor = Console->dwSelectionCursor;
294 Console->Selection.dwFlags |= CONSOLE_SELECTION_IN_PROGRESS;
295 GuiConsoleUpdateSelection(Console, &Console->Selection.dwSelectionAnchor);
296
297 Length = Console->Title.Length + sizeof(L"Mark - ")/sizeof(WCHAR) + 1;
298 WindowTitle = ConsoleAllocHeap(0, Length * sizeof(WCHAR));
299 wcscpy(WindowTitle, L"Mark - ");
300 wcscat(WindowTitle, Console->Title.Buffer);
301 SetWindowText(GuiData->hWindow, WindowTitle);
302 ConsoleFreeHeap(WindowTitle);
303
304 break;
305 }
306
307 case ID_SYSTEM_EDIT_COPY:
308 GuiConsoleCopy(GuiData);
309 break;
310
311 case ID_SYSTEM_EDIT_PASTE:
312 GuiConsolePaste(GuiData);
313 break;
314
315 case ID_SYSTEM_EDIT_SELECTALL:
316 {
317 LPWSTR WindowTitle = NULL;
318 SIZE_T Length = 0;
319
320 /*
321 * The selection area extends to the whole screen buffer's width.
322 */
323 Console->Selection.dwSelectionAnchor.X = 0;
324 Console->Selection.dwSelectionAnchor.Y = 0;
325 Console->dwSelectionCursor.X = ActiveBuffer->ScreenBufferSize.X - 1;
326
327 /*
328 * Determine whether the selection must extend to just some part
329 * (for text-mode screen buffers) or to all of the screen buffer's
330 * height (for graphics ones).
331 */
332 if (GetType(ActiveBuffer) == TEXTMODE_BUFFER)
333 {
334 /*
335 * We select all the characters from the first line
336 * to the line where the cursor is positioned.
337 */
338 Console->dwSelectionCursor.Y = ActiveBuffer->CursorPosition.Y;
339 }
340 else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */
341 {
342 /*
343 * We select all the screen buffer area.
344 */
345 Console->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1;
346 }
347
348 Console->Selection.dwFlags |= CONSOLE_SELECTION_IN_PROGRESS | CONSOLE_MOUSE_SELECTION;
349 GuiConsoleUpdateSelection(Console, &Console->dwSelectionCursor);
350
351 Length = Console->Title.Length + sizeof(L"Selection - ")/sizeof(WCHAR) + 1;
352 WindowTitle = ConsoleAllocHeap(0, Length * sizeof(WCHAR));
353 wcscpy(WindowTitle, L"Selection - ");
354 wcscat(WindowTitle, Console->Title.Buffer);
355 SetWindowText(GuiData->hWindow, WindowTitle);
356 ConsoleFreeHeap(WindowTitle);
357
358 break;
359 }
360
361 case ID_SYSTEM_EDIT_SCROLL:
362 DPRINT1("Scrolling is not handled yet\n");
363 break;
364
365 case ID_SYSTEM_EDIT_FIND:
366 DPRINT1("Finding is not handled yet\n");
367 break;
368
369 case ID_SYSTEM_DEFAULTS:
370 GuiConsoleShowConsoleProperties(GuiData, TRUE);
371 break;
372
373 case ID_SYSTEM_PROPERTIES:
374 GuiConsoleShowConsoleProperties(GuiData, FALSE);
375 break;
376
377 default:
378 Ret = FALSE;
379 break;
380 }
381
382 Unlock_Quit:
383 LeaveCriticalSection(&Console->Lock);
384 Quit:
385 if (!Ret)
386 Ret = DefWindowProcW(GuiData->hWindow, WM_SYSCOMMAND, wParam, lParam);
387
388 return Ret;
389 }
390
391 static PGUI_CONSOLE_DATA
392 GuiGetGuiData(HWND hWnd)
393 {
394 /* This function ensures that the console pointer is not NULL */
395 PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
396 return ( ((GuiData == NULL) || (GuiData->hWindow == hWnd && GuiData->Console != NULL)) ? GuiData : NULL );
397 }
398
399 VOID
400 GuiConsoleMoveWindow(PGUI_CONSOLE_DATA GuiData)
401 {
402 /* Move the window if needed (not positioned by the system) */
403 if (!GuiData->GuiInfo.AutoPosition)
404 {
405 SetWindowPos(GuiData->hWindow,
406 NULL,
407 GuiData->GuiInfo.WindowOrigin.x,
408 GuiData->GuiInfo.WindowOrigin.y,
409 0,
410 0,
411 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
412 }
413 }
414
415 static VOID
416 GuiConsoleResizeWindow(PGUI_CONSOLE_DATA GuiData)
417 {
418 // PCONSOLE Console = GuiData->Console;
419 PCONSOLE_SCREEN_BUFFER Buff = GuiData->ActiveBuffer; // ConDrvGetActiveScreenBuffer(Console);
420 SCROLLINFO sInfo;
421
422 DWORD Width, Height;
423 UINT WidthUnit, HeightUnit;
424
425 GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
426
427 Width = Buff->ViewSize.X * WidthUnit +
428 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
429 Height = Buff->ViewSize.Y * HeightUnit +
430 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
431
432 /* Set scrollbar sizes */
433 sInfo.cbSize = sizeof(SCROLLINFO);
434 sInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
435 sInfo.nMin = 0;
436 if (Buff->ScreenBufferSize.Y > Buff->ViewSize.Y)
437 {
438 sInfo.nMax = Buff->ScreenBufferSize.Y - 1;
439 sInfo.nPage = Buff->ViewSize.Y;
440 sInfo.nPos = Buff->ViewOrigin.Y;
441 SetScrollInfo(GuiData->hWindow, SB_VERT, &sInfo, TRUE);
442 Width += GetSystemMetrics(SM_CXVSCROLL);
443 ShowScrollBar(GuiData->hWindow, SB_VERT, TRUE);
444 }
445 else
446 {
447 ShowScrollBar(GuiData->hWindow, SB_VERT, FALSE);
448 }
449
450 if (Buff->ScreenBufferSize.X > Buff->ViewSize.X)
451 {
452 sInfo.nMax = Buff->ScreenBufferSize.X - 1;
453 sInfo.nPage = Buff->ViewSize.X;
454 sInfo.nPos = Buff->ViewOrigin.X;
455 SetScrollInfo(GuiData->hWindow, SB_HORZ, &sInfo, TRUE);
456 Height += GetSystemMetrics(SM_CYHSCROLL);
457 ShowScrollBar(GuiData->hWindow, SB_HORZ, TRUE);
458 }
459 else
460 {
461 ShowScrollBar(GuiData->hWindow, SB_HORZ, FALSE);
462 }
463
464 /* Resize the window */
465 SetWindowPos(GuiData->hWindow, NULL, 0, 0, Width, Height,
466 SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOCOPYBITS);
467 // NOTE: The SWP_NOCOPYBITS flag can be replaced by a subsequent call
468 // to: InvalidateRect(GuiData->hWindow, NULL, TRUE);
469 }
470
471 static VOID
472 GuiConsoleSwitchFullScreen(PGUI_CONSOLE_DATA GuiData)
473 {
474 PCONSOLE Console = GuiData->Console;
475 // DEVMODE dmScreenSettings;
476
477 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
478
479 /* Switch to full-screen or to windowed mode */
480 GuiData->GuiInfo.FullScreen = !GuiData->GuiInfo.FullScreen;
481 DPRINT1("GuiConsoleSwitchFullScreen - Switch to %s ...\n",
482 (GuiData->GuiInfo.FullScreen ? "full-screen" : "windowed mode"));
483
484 // TODO: Change window appearance.
485 // See:
486 // http://stackoverflow.com/questions/2382464/win32-full-screen-and-hiding-taskbar
487 // http://stackoverflow.com/questions/3549148/fullscreen-management-with-NTAPI
488 // http://blogs.msdn.com/b/oldnewthing/archive/2010/04/12/9994016.aspx
489 // http://stackoverflow.com/questions/1400654/how-do-i-put-my-opengl-app-into-fullscreen-mode
490 // http://nehe.gamedev.net/tutorial/creating_an_opengl_window_win32/13001/
491 #if 0
492 if (GuiData->GuiInfo.FullScreen)
493 {
494 memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
495 dmScreenSettings.dmSize = sizeof(dmScreenSettings);
496 dmScreenSettings.dmDisplayFixedOutput = DMDFO_CENTER; // DMDFO_STRETCH // DMDFO_DEFAULT
497 dmScreenSettings.dmPelsWidth = 640; // GuiData->ActiveBuffer->ViewSize.X * GuiData->CharWidth;
498 dmScreenSettings.dmPelsHeight = 480; // GuiData->ActiveBuffer->ViewSize.Y * GuiData->CharHeight;
499 dmScreenSettings.dmBitsPerPel = 32;
500 dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
501 ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);
502 }
503 else
504 {
505 }
506 #endif
507
508 LeaveCriticalSection(&Console->Lock);
509 }
510
511 static BOOL
512 GuiConsoleHandleNcCreate(HWND hWnd, LPCREATESTRUCTW Create)
513 {
514 PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)Create->lpCreateParams;
515 PCONSOLE Console;
516 HDC hDC;
517 HFONT OldFont;
518 TEXTMETRICW Metrics;
519 SIZE CharSize;
520
521 DPRINT("GuiConsoleHandleNcCreate\n");
522
523 if (NULL == GuiData)
524 {
525 DPRINT1("GuiConsoleNcCreate: No GUI data\n");
526 return FALSE;
527 }
528
529 Console = GuiData->Console;
530
531 GuiData->hWindow = hWnd;
532
533 GuiData->Font = CreateFontW(LOWORD(GuiData->GuiInfo.FontSize),
534 0, // HIWORD(GuiData->GuiInfo.FontSize),
535 0,
536 TA_BASELINE,
537 GuiData->GuiInfo.FontWeight,
538 FALSE,
539 FALSE,
540 FALSE,
541 OEM_CHARSET,
542 OUT_DEFAULT_PRECIS,
543 CLIP_DEFAULT_PRECIS,
544 NONANTIALIASED_QUALITY,
545 FIXED_PITCH | GuiData->GuiInfo.FontFamily /* FF_DONTCARE */,
546 GuiData->GuiInfo.FaceName);
547
548 if (NULL == GuiData->Font)
549 {
550 DPRINT1("GuiConsoleNcCreate: CreateFont failed\n");
551 GuiData->hWindow = NULL;
552 SetEvent(GuiData->hGuiInitEvent);
553 return FALSE;
554 }
555 hDC = GetDC(GuiData->hWindow);
556 if (NULL == hDC)
557 {
558 DPRINT1("GuiConsoleNcCreate: GetDC failed\n");
559 DeleteObject(GuiData->Font);
560 GuiData->hWindow = NULL;
561 SetEvent(GuiData->hGuiInitEvent);
562 return FALSE;
563 }
564 OldFont = SelectObject(hDC, GuiData->Font);
565 if (NULL == OldFont)
566 {
567 DPRINT1("GuiConsoleNcCreate: SelectObject failed\n");
568 ReleaseDC(GuiData->hWindow, hDC);
569 DeleteObject(GuiData->Font);
570 GuiData->hWindow = NULL;
571 SetEvent(GuiData->hGuiInitEvent);
572 return FALSE;
573 }
574 if (!GetTextMetricsW(hDC, &Metrics))
575 {
576 DPRINT1("GuiConsoleNcCreate: GetTextMetrics failed\n");
577 SelectObject(hDC, OldFont);
578 ReleaseDC(GuiData->hWindow, hDC);
579 DeleteObject(GuiData->Font);
580 GuiData->hWindow = NULL;
581 SetEvent(GuiData->hGuiInitEvent);
582 return FALSE;
583 }
584 GuiData->CharWidth = Metrics.tmMaxCharWidth;
585 GuiData->CharHeight = Metrics.tmHeight + Metrics.tmExternalLeading;
586
587 /* Measure real char width more precisely if possible. */
588 if (GetTextExtentPoint32W(hDC, L"R", 1, &CharSize))
589 GuiData->CharWidth = CharSize.cx;
590
591 SelectObject(hDC, OldFont);
592
593 ReleaseDC(GuiData->hWindow, hDC);
594
595 // FIXME: Keep these instructions here ? ///////////////////////////////////
596 Console->ActiveBuffer->CursorBlinkOn = TRUE;
597 Console->ActiveBuffer->ForceCursorOff = FALSE;
598 ////////////////////////////////////////////////////////////////////////////
599
600 SetWindowLongPtrW(GuiData->hWindow, GWLP_USERDATA, (DWORD_PTR)GuiData);
601
602 SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL);
603 GuiConsoleCreateSysMenu(GuiData->hWindow);
604
605 DPRINT("GuiConsoleHandleNcCreate - setting start event\n");
606 SetEvent(GuiData->hGuiInitEvent);
607
608 return (BOOL)DefWindowProcW(GuiData->hWindow, WM_NCCREATE, 0, (LPARAM)Create);
609 }
610
611 static VOID
612 SmallRectToRect(PGUI_CONSOLE_DATA GuiData, PRECT Rect, PSMALL_RECT SmallRect)
613 {
614 // PCONSOLE Console = GuiData->Console;
615 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer; // ConDrvGetActiveScreenBuffer(Console);
616 UINT WidthUnit, HeightUnit;
617
618 GetScreenBufferSizeUnits(Buffer, GuiData, &WidthUnit, &HeightUnit);
619
620 Rect->left = (SmallRect->Left - Buffer->ViewOrigin.X) * WidthUnit ;
621 Rect->top = (SmallRect->Top - Buffer->ViewOrigin.Y) * HeightUnit;
622 Rect->right = (SmallRect->Right + 1 - Buffer->ViewOrigin.X) * WidthUnit ;
623 Rect->bottom = (SmallRect->Bottom + 1 - Buffer->ViewOrigin.Y) * HeightUnit;
624 }
625
626 static VOID
627 GuiConsoleUpdateSelection(PCONSOLE Console, PCOORD coord)
628 {
629 PGUI_CONSOLE_DATA GuiData = Console->TermIFace.Data;
630 RECT oldRect, newRect;
631
632 SmallRectToRect(GuiData, &oldRect, &Console->Selection.srSelection);
633
634 if (coord != NULL)
635 {
636 SMALL_RECT rc;
637 /* exchange left/top with right/bottom if required */
638 rc.Left = min(Console->Selection.dwSelectionAnchor.X, coord->X);
639 rc.Top = min(Console->Selection.dwSelectionAnchor.Y, coord->Y);
640 rc.Right = max(Console->Selection.dwSelectionAnchor.X, coord->X);
641 rc.Bottom = max(Console->Selection.dwSelectionAnchor.Y, coord->Y);
642
643 SmallRectToRect(GuiData, &newRect, &rc);
644
645 if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
646 {
647 if (memcmp(&rc, &Console->Selection.srSelection, sizeof(SMALL_RECT)) != 0)
648 {
649 HRGN rgn1, rgn2;
650
651 /* calculate the region that needs to be updated */
652 if ((rgn1 = CreateRectRgnIndirect(&oldRect)))
653 {
654 if ((rgn2 = CreateRectRgnIndirect(&newRect)))
655 {
656 if (CombineRgn(rgn1, rgn2, rgn1, RGN_XOR) != ERROR)
657 {
658 InvalidateRgn(GuiData->hWindow, rgn1, FALSE);
659 }
660 DeleteObject(rgn2);
661 }
662 DeleteObject(rgn1);
663 }
664 }
665 }
666 else
667 {
668 InvalidateRect(GuiData->hWindow, &newRect, FALSE);
669 }
670 Console->Selection.dwFlags |= CONSOLE_SELECTION_NOT_EMPTY;
671 Console->Selection.srSelection = rc;
672 Console->dwSelectionCursor = *coord;
673 ConioPause(Console, PAUSED_FROM_SELECTION);
674 }
675 else
676 {
677 /* clear the selection */
678 if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
679 {
680 InvalidateRect(GuiData->hWindow, &oldRect, FALSE);
681 }
682 Console->Selection.dwFlags = CONSOLE_NO_SELECTION;
683 ConioUnpause(Console, PAUSED_FROM_SELECTION);
684 }
685 }
686
687
688 VOID
689 GuiPaintTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer,
690 PGUI_CONSOLE_DATA GuiData,
691 HDC hDC,
692 PRECT rc);
693 VOID
694 GuiPaintGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer,
695 PGUI_CONSOLE_DATA GuiData,
696 HDC hDC,
697 PRECT rc);
698
699 static VOID
700 GuiConsoleHandlePaint(PGUI_CONSOLE_DATA GuiData)
701 {
702 BOOL Success = TRUE;
703 PCONSOLE Console = GuiData->Console;
704 PCONSOLE_SCREEN_BUFFER ActiveBuffer;
705 HDC hDC;
706 PAINTSTRUCT ps;
707
708 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
709 {
710 Success = FALSE;
711 goto Quit;
712 }
713 // ActiveBuffer = ConDrvGetActiveScreenBuffer(Console);
714 ActiveBuffer = GuiData->ActiveBuffer;
715
716 hDC = BeginPaint(GuiData->hWindow, &ps);
717 if (hDC != NULL &&
718 ps.rcPaint.left < ps.rcPaint.right &&
719 ps.rcPaint.top < ps.rcPaint.bottom)
720 {
721 EnterCriticalSection(&GuiData->Lock);
722
723 if (GetType(ActiveBuffer) == TEXTMODE_BUFFER)
724 {
725 GuiPaintTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)ActiveBuffer,
726 GuiData, hDC, &ps.rcPaint);
727 }
728 else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */
729 {
730 GuiPaintGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)ActiveBuffer,
731 GuiData, hDC, &ps.rcPaint);
732 }
733
734 if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
735 {
736 RECT rc;
737 SmallRectToRect(GuiData, &rc, &Console->Selection.srSelection);
738
739 /* invert the selection */
740 if (IntersectRect(&rc, &ps.rcPaint, &rc))
741 {
742 PatBlt(hDC,
743 rc.left,
744 rc.top,
745 rc.right - rc.left,
746 rc.bottom - rc.top,
747 DSTINVERT);
748 }
749 }
750
751 LeaveCriticalSection(&GuiData->Lock);
752 }
753 EndPaint(GuiData->hWindow, &ps);
754
755 Quit:
756 if (Success)
757 LeaveCriticalSection(&Console->Lock);
758 else
759 DefWindowProcW(GuiData->hWindow, WM_PAINT, 0, 0);
760
761 return;
762 }
763
764 static BOOL
765 IsSystemKey(WORD VirtualKeyCode)
766 {
767 switch (VirtualKeyCode)
768 {
769 /* From MSDN, "Virtual-Key Codes" */
770 case VK_RETURN:
771 case VK_SHIFT:
772 case VK_CONTROL:
773 case VK_MENU:
774 case VK_PAUSE:
775 case VK_CAPITAL:
776 case VK_ESCAPE:
777 case VK_LWIN:
778 case VK_RWIN:
779 case VK_NUMLOCK:
780 case VK_SCROLL:
781 return TRUE;
782 default:
783 return FALSE;
784 }
785 }
786
787 static VOID
788 GuiConsoleHandleKey(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam)
789 {
790 PCONSOLE Console = GuiData->Console;
791 PCONSOLE_SCREEN_BUFFER ActiveBuffer;
792
793 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
794
795 // ActiveBuffer = ConDrvGetActiveScreenBuffer(Console);
796 ActiveBuffer = GuiData->ActiveBuffer;
797
798 if (Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS)
799 {
800 WORD VirtualKeyCode = LOWORD(wParam);
801
802 if (msg != WM_KEYDOWN) goto Quit;
803
804 if (VirtualKeyCode == VK_RETURN)
805 {
806 /* Copy (and clear) selection if ENTER is pressed */
807 GuiConsoleCopy(GuiData);
808 goto Quit;
809 }
810 else if ( VirtualKeyCode == VK_ESCAPE ||
811 (VirtualKeyCode == 'C' && GetKeyState(VK_CONTROL) & 0x8000) )
812 {
813 /* Cancel selection if ESC or Ctrl-C are pressed */
814 GuiConsoleUpdateSelection(Console, NULL);
815 SetWindowText(GuiData->hWindow, Console->Title.Buffer);
816
817 goto Quit;
818 }
819
820 if ((Console->Selection.dwFlags & CONSOLE_MOUSE_SELECTION) == 0)
821 {
822 /* Keyboard selection mode */
823 BOOL Interpreted = FALSE;
824 BOOL MajPressed = (GetKeyState(VK_SHIFT) & 0x8000);
825
826 switch (VirtualKeyCode)
827 {
828 case VK_LEFT:
829 {
830 Interpreted = TRUE;
831 if (Console->dwSelectionCursor.X > 0)
832 Console->dwSelectionCursor.X--;
833
834 break;
835 }
836
837 case VK_RIGHT:
838 {
839 Interpreted = TRUE;
840 if (Console->dwSelectionCursor.X < ActiveBuffer->ScreenBufferSize.X - 1)
841 Console->dwSelectionCursor.X++;
842
843 break;
844 }
845
846 case VK_UP:
847 {
848 Interpreted = TRUE;
849 if (Console->dwSelectionCursor.Y > 0)
850 Console->dwSelectionCursor.Y--;
851
852 break;
853 }
854
855 case VK_DOWN:
856 {
857 Interpreted = TRUE;
858 if (Console->dwSelectionCursor.Y < ActiveBuffer->ScreenBufferSize.Y - 1)
859 Console->dwSelectionCursor.Y++;
860
861 break;
862 }
863
864 case VK_HOME:
865 {
866 Interpreted = TRUE;
867 Console->dwSelectionCursor.X = 0;
868 Console->dwSelectionCursor.Y = 0;
869 break;
870 }
871
872 case VK_END:
873 {
874 Interpreted = TRUE;
875 Console->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1;
876 break;
877 }
878
879 case VK_PRIOR:
880 {
881 Interpreted = TRUE;
882 Console->dwSelectionCursor.Y -= ActiveBuffer->ViewSize.Y;
883 if (Console->dwSelectionCursor.Y < 0)
884 Console->dwSelectionCursor.Y = 0;
885
886 break;
887 }
888
889 case VK_NEXT:
890 {
891 Interpreted = TRUE;
892 Console->dwSelectionCursor.Y += ActiveBuffer->ViewSize.Y;
893 if (Console->dwSelectionCursor.Y >= ActiveBuffer->ScreenBufferSize.Y)
894 Console->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1;
895
896 break;
897 }
898
899 default:
900 break;
901 }
902
903 if (Interpreted)
904 {
905 if (!MajPressed)
906 Console->Selection.dwSelectionAnchor = Console->dwSelectionCursor;
907
908 GuiConsoleUpdateSelection(Console, &Console->dwSelectionCursor);
909 }
910 else if (!IsSystemKey(VirtualKeyCode))
911 {
912 /* Emit an error beep sound */
913 SendNotifyMessage(GuiData->hWindow, PM_CONSOLE_BEEP, 0, 0);
914 }
915
916 goto Quit;
917 }
918 else
919 {
920 /* Mouse selection mode */
921
922 if (!IsSystemKey(VirtualKeyCode))
923 {
924 /* Clear the selection and send the key into the input buffer */
925 GuiConsoleUpdateSelection(Console, NULL);
926 SetWindowText(GuiData->hWindow, Console->Title.Buffer);
927 }
928 else
929 {
930 goto Quit;
931 }
932 }
933 }
934
935 if ((Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) == 0)
936 {
937 MSG Message;
938
939 Message.hwnd = GuiData->hWindow;
940 Message.message = msg;
941 Message.wParam = wParam;
942 Message.lParam = lParam;
943
944 ConioProcessKey(Console, &Message);
945 }
946
947 Quit:
948 LeaveCriticalSection(&Console->Lock);
949 }
950
951 static VOID
952 GuiInvalidateCell(IN OUT PFRONTEND This, SHORT x, SHORT y)
953 {
954 SMALL_RECT CellRect = { x, y, x, y };
955 GuiDrawRegion(This, &CellRect);
956 }
957
958 static VOID
959 GuiConsoleHandleTimer(PGUI_CONSOLE_DATA GuiData)
960 {
961 PCONSOLE Console = GuiData->Console;
962 PCONSOLE_SCREEN_BUFFER Buff;
963
964 SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CURSOR_BLINK_TIME, NULL);
965
966 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
967
968 Buff = GuiData->ActiveBuffer; // ConDrvGetActiveScreenBuffer(Console);
969
970 if (GetType(Buff) == TEXTMODE_BUFFER)
971 {
972 GuiInvalidateCell(&Console->TermIFace, Buff->CursorPosition.X, Buff->CursorPosition.Y);
973 Buff->CursorBlinkOn = !Buff->CursorBlinkOn;
974
975 if ((GuiData->OldCursor.x != Buff->CursorPosition.X) || (GuiData->OldCursor.y != Buff->CursorPosition.Y))
976 {
977 SCROLLINFO xScroll;
978 int OldScrollX = -1, OldScrollY = -1;
979 int NewScrollX = -1, NewScrollY = -1;
980
981 xScroll.cbSize = sizeof(SCROLLINFO);
982 xScroll.fMask = SIF_POS;
983 // Capture the original position of the scroll bars and save them.
984 if (GetScrollInfo(GuiData->hWindow, SB_HORZ, &xScroll)) OldScrollX = xScroll.nPos;
985 if (GetScrollInfo(GuiData->hWindow, SB_VERT, &xScroll)) OldScrollY = xScroll.nPos;
986
987 // If we successfully got the info for the horizontal scrollbar
988 if (OldScrollX >= 0)
989 {
990 if ((Buff->CursorPosition.X < Buff->ViewOrigin.X) || (Buff->CursorPosition.X >= (Buff->ViewOrigin.X + Buff->ViewSize.X)))
991 {
992 // Handle the horizontal scroll bar
993 if (Buff->CursorPosition.X >= Buff->ViewSize.X) NewScrollX = Buff->CursorPosition.X - Buff->ViewSize.X + 1;
994 else NewScrollX = 0;
995 }
996 else
997 {
998 NewScrollX = OldScrollX;
999 }
1000 }
1001 // If we successfully got the info for the vertical scrollbar
1002 if (OldScrollY >= 0)
1003 {
1004 if ((Buff->CursorPosition.Y < Buff->ViewOrigin.Y) || (Buff->CursorPosition.Y >= (Buff->ViewOrigin.Y + Buff->ViewSize.Y)))
1005 {
1006 // Handle the vertical scroll bar
1007 if (Buff->CursorPosition.Y >= Buff->ViewSize.Y) NewScrollY = Buff->CursorPosition.Y - Buff->ViewSize.Y + 1;
1008 else NewScrollY = 0;
1009 }
1010 else
1011 {
1012 NewScrollY = OldScrollY;
1013 }
1014 }
1015
1016 // Adjust scroll bars and refresh the window if the cursor has moved outside the visible area
1017 // NOTE: OldScroll# and NewScroll# will both be -1 (initial value) if the info for the respective scrollbar
1018 // was not obtained successfully in the previous steps. This means their difference is 0 (no scrolling)
1019 // and their associated scrollbar is left alone.
1020 if ((OldScrollX != NewScrollX) || (OldScrollY != NewScrollY))
1021 {
1022 Buff->ViewOrigin.X = NewScrollX;
1023 Buff->ViewOrigin.Y = NewScrollY;
1024 ScrollWindowEx(GuiData->hWindow,
1025 (OldScrollX - NewScrollX) * GuiData->CharWidth,
1026 (OldScrollY - NewScrollY) * GuiData->CharHeight,
1027 NULL,
1028 NULL,
1029 NULL,
1030 NULL,
1031 SW_INVALIDATE);
1032 if (NewScrollX >= 0)
1033 {
1034 xScroll.nPos = NewScrollX;
1035 SetScrollInfo(GuiData->hWindow, SB_HORZ, &xScroll, TRUE);
1036 }
1037 if (NewScrollY >= 0)
1038 {
1039 xScroll.nPos = NewScrollY;
1040 SetScrollInfo(GuiData->hWindow, SB_VERT, &xScroll, TRUE);
1041 }
1042 UpdateWindow(GuiData->hWindow);
1043 GuiData->OldCursor.x = Buff->CursorPosition.X;
1044 GuiData->OldCursor.y = Buff->CursorPosition.Y;
1045 }
1046 }
1047 }
1048 else /* if (GetType(Buff) == GRAPHICS_BUFFER) */
1049 {
1050 }
1051
1052 LeaveCriticalSection(&Console->Lock);
1053 }
1054
1055 static BOOL
1056 GuiConsoleHandleClose(PGUI_CONSOLE_DATA GuiData)
1057 {
1058 PCONSOLE Console = GuiData->Console;
1059
1060 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
1061 return TRUE;
1062
1063 // TODO: Prompt for termination ? (Warn the user about possible apps running in this console)
1064
1065 /*
1066 * FIXME: Windows will wait up to 5 seconds for the thread to exit.
1067 * We shouldn't wait here, though, since the console lock is entered.
1068 * A copy of the thread list probably needs to be made.
1069 */
1070 ConDrvConsoleProcessCtrlEvent(Console, 0, CTRL_CLOSE_EVENT);
1071
1072 LeaveCriticalSection(&Console->Lock);
1073 return FALSE;
1074 }
1075
1076 static LRESULT
1077 GuiConsoleHandleNcDestroy(HWND hWnd)
1078 {
1079 KillTimer(hWnd, CONGUI_UPDATE_TIMER);
1080 GetSystemMenu(hWnd, TRUE);
1081
1082 /* Free the GuiData registration */
1083 SetWindowLongPtrW(hWnd, GWLP_USERDATA, (DWORD_PTR)NULL);
1084
1085 return DefWindowProcW(hWnd, WM_NCDESTROY, 0, 0);
1086 }
1087
1088 static COORD
1089 PointToCoord(PGUI_CONSOLE_DATA GuiData, LPARAM lParam)
1090 {
1091 // PCONSOLE Console = GuiData->Console;
1092 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer; // ConDrvGetActiveScreenBuffer(Console);
1093 COORD Coord;
1094 UINT WidthUnit, HeightUnit;
1095
1096 GetScreenBufferSizeUnits(Buffer, GuiData, &WidthUnit, &HeightUnit);
1097
1098 Coord.X = Buffer->ViewOrigin.X + ((SHORT)LOWORD(lParam) / (int)WidthUnit );
1099 Coord.Y = Buffer->ViewOrigin.Y + ((SHORT)HIWORD(lParam) / (int)HeightUnit);
1100
1101 /* Clip coordinate to ensure it's inside buffer */
1102 if (Coord.X < 0)
1103 Coord.X = 0;
1104 else if (Coord.X >= Buffer->ScreenBufferSize.X)
1105 Coord.X = Buffer->ScreenBufferSize.X - 1;
1106
1107 if (Coord.Y < 0)
1108 Coord.Y = 0;
1109 else if (Coord.Y >= Buffer->ScreenBufferSize.Y)
1110 Coord.Y = Buffer->ScreenBufferSize.Y - 1;
1111
1112 return Coord;
1113 }
1114
1115 static LRESULT
1116 GuiConsoleHandleMouse(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam)
1117 {
1118 BOOL Err = FALSE;
1119 PCONSOLE Console = GuiData->Console;
1120
1121 if (GuiData->IgnoreNextMouseSignal)
1122 {
1123 if (msg != WM_LBUTTONDOWN &&
1124 msg != WM_MBUTTONDOWN &&
1125 msg != WM_RBUTTONDOWN)
1126 {
1127 /*
1128 * If this mouse signal is not a button-down action,
1129 * then it is the last signal being ignored.
1130 */
1131 GuiData->IgnoreNextMouseSignal = FALSE;
1132 }
1133 else
1134 {
1135 /*
1136 * This mouse signal is a button-down action.
1137 * Ignore it and perform default action.
1138 */
1139 Err = TRUE;
1140 }
1141 goto Quit;
1142 }
1143
1144 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
1145 {
1146 Err = TRUE;
1147 goto Quit;
1148 }
1149
1150 if ( (Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) ||
1151 (Console->QuickEdit) )
1152 {
1153 switch (msg)
1154 {
1155 case WM_LBUTTONDOWN:
1156 {
1157 LPWSTR WindowTitle = NULL;
1158 SIZE_T Length = 0;
1159
1160 Console->Selection.dwSelectionAnchor = PointToCoord(GuiData, lParam);
1161 SetCapture(GuiData->hWindow);
1162 Console->Selection.dwFlags |= CONSOLE_SELECTION_IN_PROGRESS | CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN;
1163 GuiConsoleUpdateSelection(Console, &Console->Selection.dwSelectionAnchor);
1164
1165 Length = Console->Title.Length + sizeof(L"Selection - ")/sizeof(WCHAR) + 1;
1166 WindowTitle = ConsoleAllocHeap(0, Length * sizeof(WCHAR));
1167 wcscpy(WindowTitle, L"Selection - ");
1168 wcscat(WindowTitle, Console->Title.Buffer);
1169 SetWindowText(GuiData->hWindow, WindowTitle);
1170 ConsoleFreeHeap(WindowTitle);
1171
1172 break;
1173 }
1174
1175 case WM_LBUTTONUP:
1176 {
1177 COORD c;
1178
1179 if (!(Console->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break;
1180
1181 c = PointToCoord(GuiData, lParam);
1182 Console->Selection.dwFlags &= ~CONSOLE_MOUSE_DOWN;
1183 GuiConsoleUpdateSelection(Console, &c);
1184 ReleaseCapture();
1185
1186 break;
1187 }
1188
1189 case WM_LBUTTONDBLCLK:
1190 {
1191 DPRINT1("Handle left-double-click for selecting a word\n");
1192 break;
1193 }
1194
1195 case WM_RBUTTONDOWN:
1196 case WM_RBUTTONDBLCLK:
1197 {
1198 if (!(Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY))
1199 {
1200 GuiConsolePaste(GuiData);
1201 }
1202 else
1203 {
1204 GuiConsoleCopy(GuiData);
1205 }
1206
1207 GuiData->IgnoreNextMouseSignal = TRUE;
1208 break;
1209 }
1210
1211 case WM_MOUSEMOVE:
1212 {
1213 COORD c;
1214
1215 if (!(wParam & MK_LBUTTON)) break;
1216 if (!(Console->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break;
1217
1218 c = PointToCoord(GuiData, lParam); /* TODO: Scroll buffer to bring c into view */
1219 GuiConsoleUpdateSelection(Console, &c);
1220
1221 break;
1222 }
1223
1224 default:
1225 Err = FALSE; // TRUE;
1226 break;
1227 }
1228 }
1229 else if (Console->InputBuffer.Mode & ENABLE_MOUSE_INPUT)
1230 {
1231 INPUT_RECORD er;
1232 WORD wKeyState = GET_KEYSTATE_WPARAM(wParam);
1233 DWORD dwButtonState = 0;
1234 DWORD dwControlKeyState = 0;
1235 DWORD dwEventFlags = 0;
1236
1237 switch (msg)
1238 {
1239 case WM_LBUTTONDOWN:
1240 SetCapture(GuiData->hWindow);
1241 dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
1242 dwEventFlags = 0;
1243 break;
1244
1245 case WM_MBUTTONDOWN:
1246 SetCapture(GuiData->hWindow);
1247 dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
1248 dwEventFlags = 0;
1249 break;
1250
1251 case WM_RBUTTONDOWN:
1252 SetCapture(GuiData->hWindow);
1253 dwButtonState = RIGHTMOST_BUTTON_PRESSED;
1254 dwEventFlags = 0;
1255 break;
1256
1257 case WM_LBUTTONUP:
1258 ReleaseCapture();
1259 dwButtonState = 0;
1260 dwEventFlags = 0;
1261 break;
1262
1263 case WM_MBUTTONUP:
1264 ReleaseCapture();
1265 dwButtonState = 0;
1266 dwEventFlags = 0;
1267 break;
1268
1269 case WM_RBUTTONUP:
1270 ReleaseCapture();
1271 dwButtonState = 0;
1272 dwEventFlags = 0;
1273 break;
1274
1275 case WM_LBUTTONDBLCLK:
1276 dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
1277 dwEventFlags = DOUBLE_CLICK;
1278 break;
1279
1280 case WM_MBUTTONDBLCLK:
1281 dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
1282 dwEventFlags = DOUBLE_CLICK;
1283 break;
1284
1285 case WM_RBUTTONDBLCLK:
1286 dwButtonState = RIGHTMOST_BUTTON_PRESSED;
1287 dwEventFlags = DOUBLE_CLICK;
1288 break;
1289
1290 case WM_MOUSEMOVE:
1291 dwButtonState = 0;
1292 dwEventFlags = MOUSE_MOVED;
1293 break;
1294
1295 case WM_MOUSEWHEEL:
1296 dwButtonState = GET_WHEEL_DELTA_WPARAM(wParam) << 16;
1297 dwEventFlags = MOUSE_WHEELED;
1298 break;
1299
1300 case WM_MOUSEHWHEEL:
1301 dwButtonState = GET_WHEEL_DELTA_WPARAM(wParam) << 16;
1302 dwEventFlags = MOUSE_HWHEELED;
1303 break;
1304
1305 default:
1306 Err = TRUE;
1307 break;
1308 }
1309
1310 if (!Err)
1311 {
1312 if (wKeyState & MK_LBUTTON)
1313 dwButtonState |= FROM_LEFT_1ST_BUTTON_PRESSED;
1314 if (wKeyState & MK_MBUTTON)
1315 dwButtonState |= FROM_LEFT_2ND_BUTTON_PRESSED;
1316 if (wKeyState & MK_RBUTTON)
1317 dwButtonState |= RIGHTMOST_BUTTON_PRESSED;
1318
1319 if (GetKeyState(VK_RMENU) & 0x8000)
1320 dwControlKeyState |= RIGHT_ALT_PRESSED;
1321 if (GetKeyState(VK_LMENU) & 0x8000)
1322 dwControlKeyState |= LEFT_ALT_PRESSED;
1323 if (GetKeyState(VK_RCONTROL) & 0x8000)
1324 dwControlKeyState |= RIGHT_CTRL_PRESSED;
1325 if (GetKeyState(VK_LCONTROL) & 0x8000)
1326 dwControlKeyState |= LEFT_CTRL_PRESSED;
1327 if (GetKeyState(VK_SHIFT) & 0x8000)
1328 dwControlKeyState |= SHIFT_PRESSED;
1329 if (GetKeyState(VK_NUMLOCK) & 0x0001)
1330 dwControlKeyState |= NUMLOCK_ON;
1331 if (GetKeyState(VK_SCROLL) & 0x0001)
1332 dwControlKeyState |= SCROLLLOCK_ON;
1333 if (GetKeyState(VK_CAPITAL) & 0x0001)
1334 dwControlKeyState |= CAPSLOCK_ON;
1335 /* See WM_CHAR MSDN documentation for instance */
1336 if (lParam & 0x01000000)
1337 dwControlKeyState |= ENHANCED_KEY;
1338
1339 er.EventType = MOUSE_EVENT;
1340 er.Event.MouseEvent.dwMousePosition = PointToCoord(GuiData, lParam);
1341 er.Event.MouseEvent.dwButtonState = dwButtonState;
1342 er.Event.MouseEvent.dwControlKeyState = dwControlKeyState;
1343 er.Event.MouseEvent.dwEventFlags = dwEventFlags;
1344
1345 ConioProcessInputEvent(Console, &er);
1346 }
1347 }
1348 else
1349 {
1350 Err = TRUE;
1351 }
1352
1353 LeaveCriticalSection(&Console->Lock);
1354
1355 Quit:
1356 if (Err)
1357 return DefWindowProcW(GuiData->hWindow, msg, wParam, lParam);
1358 else
1359 return 0;
1360 }
1361
1362 VOID GuiCopyFromTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer);
1363 VOID GuiCopyFromGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer);
1364
1365 static VOID
1366 GuiConsoleCopy(PGUI_CONSOLE_DATA GuiData)
1367 {
1368 if (OpenClipboard(GuiData->hWindow) == TRUE)
1369 {
1370 PCONSOLE Console = GuiData->Console;
1371 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer; // ConDrvGetActiveScreenBuffer(Console);
1372
1373 if (GetType(Buffer) == TEXTMODE_BUFFER)
1374 {
1375 GuiCopyFromTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)Buffer);
1376 }
1377 else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
1378 {
1379 GuiCopyFromGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)Buffer);
1380 }
1381
1382 CloseClipboard();
1383
1384 /* Clear the selection */
1385 GuiConsoleUpdateSelection(Console, NULL);
1386 SetWindowText(GuiData->hWindow, Console->Title.Buffer);
1387 }
1388 }
1389
1390 VOID GuiPasteToTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer);
1391 VOID GuiPasteToGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer);
1392
1393 static VOID
1394 GuiConsolePaste(PGUI_CONSOLE_DATA GuiData)
1395 {
1396 if (OpenClipboard(GuiData->hWindow) == TRUE)
1397 {
1398 // PCONSOLE Console = GuiData->Console;
1399 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer; // ConDrvGetActiveScreenBuffer(Console);
1400
1401 if (GetType(Buffer) == TEXTMODE_BUFFER)
1402 {
1403 GuiPasteToTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)Buffer);
1404 }
1405 else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
1406 {
1407 GuiPasteToGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)Buffer);
1408 }
1409
1410 CloseClipboard();
1411 }
1412 }
1413
1414 static VOID
1415 GuiConsoleGetMinMaxInfo(PGUI_CONSOLE_DATA GuiData, PMINMAXINFO minMaxInfo)
1416 {
1417 PCONSOLE Console = GuiData->Console;
1418 PCONSOLE_SCREEN_BUFFER ActiveBuffer;
1419 DWORD windx, windy;
1420 UINT WidthUnit, HeightUnit;
1421
1422 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
1423
1424 // ActiveBuffer = ConDrvGetActiveScreenBuffer(Console);
1425 ActiveBuffer = GuiData->ActiveBuffer;
1426
1427 GetScreenBufferSizeUnits(ActiveBuffer, GuiData, &WidthUnit, &HeightUnit);
1428
1429 windx = CONGUI_MIN_WIDTH * WidthUnit + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
1430 windy = CONGUI_MIN_HEIGHT * HeightUnit + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
1431
1432 minMaxInfo->ptMinTrackSize.x = windx;
1433 minMaxInfo->ptMinTrackSize.y = windy;
1434
1435 windx = (ActiveBuffer->ScreenBufferSize.X) * WidthUnit + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
1436 windy = (ActiveBuffer->ScreenBufferSize.Y) * HeightUnit + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
1437
1438 if (ActiveBuffer->ViewSize.X < ActiveBuffer->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar
1439 if (ActiveBuffer->ViewSize.Y < ActiveBuffer->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar
1440
1441 minMaxInfo->ptMaxTrackSize.x = windx;
1442 minMaxInfo->ptMaxTrackSize.y = windy;
1443
1444 LeaveCriticalSection(&Console->Lock);
1445 }
1446
1447 static VOID
1448 GuiConsoleResize(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam)
1449 {
1450 PCONSOLE Console = GuiData->Console;
1451
1452 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
1453
1454 if ((GuiData->WindowSizeLock == FALSE) &&
1455 (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED || wParam == SIZE_MINIMIZED))
1456 {
1457 PCONSOLE_SCREEN_BUFFER Buff = GuiData->ActiveBuffer; // ConDrvGetActiveScreenBuffer(Console);
1458 DWORD windx, windy, charx, chary;
1459 UINT WidthUnit, HeightUnit;
1460
1461 GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
1462
1463 GuiData->WindowSizeLock = TRUE;
1464
1465 windx = LOWORD(lParam);
1466 windy = HIWORD(lParam);
1467
1468 // Compensate for existing scroll bars (because lParam values do not accommodate scroll bar)
1469 if (Buff->ViewSize.X < Buff->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar
1470 if (Buff->ViewSize.Y < Buff->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar
1471
1472 charx = windx / (int)WidthUnit ;
1473 chary = windy / (int)HeightUnit;
1474
1475 // Character alignment (round size up or down)
1476 if ((windx % WidthUnit ) >= (WidthUnit / 2)) ++charx;
1477 if ((windy % HeightUnit) >= (HeightUnit / 2)) ++chary;
1478
1479 // Compensate for added scroll bars in new window
1480 if (charx < Buff->ScreenBufferSize.X) windy -= GetSystemMetrics(SM_CYHSCROLL); // new window will have a horizontal scroll bar
1481 if (chary < Buff->ScreenBufferSize.Y) windx -= GetSystemMetrics(SM_CXVSCROLL); // new window will have a vertical scroll bar
1482
1483 charx = windx / (int)WidthUnit ;
1484 chary = windy / (int)HeightUnit;
1485
1486 // Character alignment (round size up or down)
1487 if ((windx % WidthUnit ) >= (WidthUnit / 2)) ++charx;
1488 if ((windy % HeightUnit) >= (HeightUnit / 2)) ++chary;
1489
1490 // Resize window
1491 if ((charx != Buff->ViewSize.X) || (chary != Buff->ViewSize.Y))
1492 {
1493 Buff->ViewSize.X = (charx <= Buff->ScreenBufferSize.X) ? charx : Buff->ScreenBufferSize.X;
1494 Buff->ViewSize.Y = (chary <= Buff->ScreenBufferSize.Y) ? chary : Buff->ScreenBufferSize.Y;
1495 }
1496
1497 GuiConsoleResizeWindow(GuiData);
1498
1499 // Adjust the start of the visible area if we are attempting to show nonexistent areas
1500 if ((Buff->ScreenBufferSize.X - Buff->ViewOrigin.X) < Buff->ViewSize.X) Buff->ViewOrigin.X = Buff->ScreenBufferSize.X - Buff->ViewSize.X;
1501 if ((Buff->ScreenBufferSize.Y - Buff->ViewOrigin.Y) < Buff->ViewSize.Y) Buff->ViewOrigin.Y = Buff->ScreenBufferSize.Y - Buff->ViewSize.Y;
1502 InvalidateRect(GuiData->hWindow, NULL, TRUE);
1503
1504 GuiData->WindowSizeLock = FALSE;
1505 }
1506
1507 LeaveCriticalSection(&Console->Lock);
1508 }
1509
1510 /*
1511 // HACK: This functionality is standard for general scrollbars. Don't add it by hand.
1512
1513 VOID
1514 FASTCALL
1515 GuiConsoleHandleScrollbarMenu(VOID)
1516 {
1517 HMENU hMenu;
1518
1519 hMenu = CreatePopupMenu();
1520 if (hMenu == NULL)
1521 {
1522 DPRINT("CreatePopupMenu failed\n");
1523 return;
1524 }
1525
1526 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLHERE);
1527 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
1528 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLTOP);
1529 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLBOTTOM);
1530 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
1531 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_UP);
1532 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_DOWN);
1533 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
1534 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLUP);
1535 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLDOWN);
1536 }
1537 */
1538
1539 static LRESULT
1540 GuiConsoleHandleScroll(PGUI_CONSOLE_DATA GuiData, UINT uMsg, WPARAM wParam)
1541 {
1542 PCONSOLE Console = GuiData->Console;
1543 PCONSOLE_SCREEN_BUFFER Buff;
1544 SCROLLINFO sInfo;
1545 int fnBar;
1546 int old_pos, Maximum;
1547 PSHORT pShowXY;
1548
1549 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return 0;
1550
1551 Buff = GuiData->ActiveBuffer; // ConDrvGetActiveScreenBuffer(Console);
1552
1553 if (uMsg == WM_HSCROLL)
1554 {
1555 fnBar = SB_HORZ;
1556 Maximum = Buff->ScreenBufferSize.X - Buff->ViewSize.X;
1557 pShowXY = &Buff->ViewOrigin.X;
1558 }
1559 else
1560 {
1561 fnBar = SB_VERT;
1562 Maximum = Buff->ScreenBufferSize.Y - Buff->ViewSize.Y;
1563 pShowXY = &Buff->ViewOrigin.Y;
1564 }
1565
1566 /* set scrollbar sizes */
1567 sInfo.cbSize = sizeof(SCROLLINFO);
1568 sInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE | SIF_TRACKPOS;
1569
1570 if (!GetScrollInfo(GuiData->hWindow, fnBar, &sInfo)) goto Quit;
1571
1572 old_pos = sInfo.nPos;
1573
1574 switch (LOWORD(wParam))
1575 {
1576 case SB_LINELEFT:
1577 sInfo.nPos -= 1;
1578 break;
1579
1580 case SB_LINERIGHT:
1581 sInfo.nPos += 1;
1582 break;
1583
1584 case SB_PAGELEFT:
1585 sInfo.nPos -= sInfo.nPage;
1586 break;
1587
1588 case SB_PAGERIGHT:
1589 sInfo.nPos += sInfo.nPage;
1590 break;
1591
1592 case SB_THUMBTRACK:
1593 sInfo.nPos = sInfo.nTrackPos;
1594 ConioPause(Console, PAUSED_FROM_SCROLLBAR);
1595 break;
1596
1597 case SB_THUMBPOSITION:
1598 ConioUnpause(Console, PAUSED_FROM_SCROLLBAR);
1599 break;
1600
1601 case SB_TOP:
1602 sInfo.nPos = sInfo.nMin;
1603 break;
1604
1605 case SB_BOTTOM:
1606 sInfo.nPos = sInfo.nMax;
1607 break;
1608
1609 default:
1610 break;
1611 }
1612
1613 sInfo.nPos = max(sInfo.nPos, 0);
1614 sInfo.nPos = min(sInfo.nPos, Maximum);
1615
1616 if (old_pos != sInfo.nPos)
1617 {
1618 USHORT OldX = Buff->ViewOrigin.X;
1619 USHORT OldY = Buff->ViewOrigin.Y;
1620 UINT WidthUnit, HeightUnit;
1621
1622 *pShowXY = sInfo.nPos;
1623
1624 GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
1625
1626 ScrollWindowEx(GuiData->hWindow,
1627 (OldX - Buff->ViewOrigin.X) * WidthUnit ,
1628 (OldY - Buff->ViewOrigin.Y) * HeightUnit,
1629 NULL,
1630 NULL,
1631 NULL,
1632 NULL,
1633 SW_INVALIDATE);
1634
1635 sInfo.fMask = SIF_POS;
1636 SetScrollInfo(GuiData->hWindow, fnBar, &sInfo, TRUE);
1637
1638 UpdateWindow(GuiData->hWindow);
1639 }
1640
1641 Quit:
1642 LeaveCriticalSection(&Console->Lock);
1643 return 0;
1644 }
1645
1646 static LRESULT CALLBACK
1647 GuiConsoleWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1648 {
1649 LRESULT Result = 0;
1650 PGUI_CONSOLE_DATA GuiData = NULL;
1651 PCONSOLE Console = NULL;
1652
1653 /*
1654 * - If it's the first time we create a window for the terminal,
1655 * just initialize it and return.
1656 *
1657 * - If we are destroying the window, just do it and return.
1658 */
1659 if (msg == WM_NCCREATE)
1660 {
1661 return (LRESULT)GuiConsoleHandleNcCreate(hWnd, (LPCREATESTRUCTW)lParam);
1662 }
1663 else if (msg == WM_NCDESTROY)
1664 {
1665 return GuiConsoleHandleNcDestroy(hWnd);
1666 }
1667
1668 /*
1669 * Now the terminal window is initialized.
1670 * Get the terminal data via the window's data.
1671 * If there is no data, just go away.
1672 */
1673 GuiData = GuiGetGuiData(hWnd);
1674 if (GuiData == NULL) return DefWindowProcW(hWnd, msg, wParam, lParam);
1675
1676 // TEMPORARY HACK until all of the functions can deal with a NULL GuiData->ActiveBuffer ...
1677 if (GuiData->ActiveBuffer == NULL) return DefWindowProcW(hWnd, msg, wParam, lParam);
1678
1679 /*
1680 * Just retrieve a pointer to the console in case somebody needs it.
1681 * It is not NULL because it was checked in GuiGetGuiData.
1682 * Each helper function which needs the console has to validate and lock it.
1683 */
1684 Console = GuiData->Console;
1685
1686 /* We have a console, start message dispatching */
1687 switch (msg)
1688 {
1689 case WM_ACTIVATE:
1690 {
1691 if (LOWORD(wParam) == WA_CLICKACTIVE) GuiData->IgnoreNextMouseSignal = TRUE;
1692 break;
1693 }
1694
1695 case WM_CLOSE:
1696 if (GuiConsoleHandleClose(GuiData)) goto Default;
1697 break;
1698
1699 case WM_PAINT:
1700 GuiConsoleHandlePaint(GuiData);
1701 break;
1702
1703 case WM_PALETTECHANGED:
1704 {
1705 PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer; // ConDrvGetActiveScreenBuffer(GuiData->Console);
1706
1707 DPRINT1("WM_PALETTECHANGED called\n");
1708
1709 /*
1710 * Protects against infinite loops:
1711 * "... A window that receives this message must not realize
1712 * its palette, unless it determines that wParam does not contain
1713 * its own window handle." (WM_PALETTECHANGED description - MSDN)
1714 *
1715 * This message is sent to all windows, including the one that
1716 * changed the system palette and caused this message to be sent.
1717 * The wParam of this message contains the handle of the window
1718 * that caused the system palette to change. To avoid an infinite
1719 * loop, care must be taken to check that the wParam of this message
1720 * does not match the window's handle.
1721 */
1722 if ((HWND)wParam == hWnd) break;
1723
1724 DPRINT1("WM_PALETTECHANGED ok\n");
1725
1726 // if (GetType(ActiveBuffer) == GRAPHICS_BUFFER)
1727 if (ActiveBuffer->PaletteHandle)
1728 {
1729 /* Get the Device Context of the console window */
1730 HDC hDC = GetDC(GuiData->hWindow);
1731
1732 DPRINT1("WM_PALETTECHANGED changing palette\n");
1733
1734 /* Specify the use of the system palette */
1735 SetSystemPaletteUse(hDC, ActiveBuffer->PaletteUsage);
1736
1737 /* Realize the (logical) palette */
1738 RealizePalette(hDC);
1739
1740 /* Release the Device Context and return */
1741 ReleaseDC(GuiData->hWindow, hDC);
1742 }
1743
1744 DPRINT1("WM_PALETTECHANGED quit\n");
1745
1746 break;
1747 }
1748
1749 case WM_KEYDOWN:
1750 case WM_KEYUP:
1751 case WM_CHAR:
1752 case WM_DEADCHAR:
1753 case WM_SYSKEYDOWN:
1754 case WM_SYSKEYUP:
1755 case WM_SYSCHAR:
1756 case WM_SYSDEADCHAR:
1757 {
1758 /* Detect Alt-Enter presses and switch back and forth to fullscreen mode */
1759 if (msg == WM_SYSKEYDOWN && (HIWORD(lParam) & KF_ALTDOWN) && wParam == VK_RETURN)
1760 {
1761 /* Switch only at first Alt-Enter press, and ignore subsequent key repetitions */
1762 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT) GuiConsoleSwitchFullScreen(GuiData);
1763 break;
1764 }
1765
1766 GuiConsoleHandleKey(GuiData, msg, wParam, lParam);
1767 break;
1768 }
1769
1770 case WM_TIMER:
1771 GuiConsoleHandleTimer(GuiData);
1772 break;
1773
1774 case WM_SETCURSOR:
1775 {
1776 /*
1777 * The message was sent because we are manually triggering a change.
1778 * Check whether the mouse is indeed present on this console window
1779 * and take appropriate decisions.
1780 */
1781 if (wParam == -1 && lParam == -1)
1782 {
1783 POINT mouseCoords;
1784 HWND hWndHit;
1785
1786 /* Get the placement of the mouse */
1787 GetCursorPos(&mouseCoords);
1788
1789 /* On which window is placed the mouse ? */
1790 hWndHit = WindowFromPoint(mouseCoords);
1791
1792 /* It's our window. Perform the hit-test to be used later on. */
1793 if (hWndHit == hWnd)
1794 {
1795 wParam = (WPARAM)hWnd;
1796 lParam = DefWindowProcW(hWndHit, WM_NCHITTEST, 0,
1797 MAKELPARAM(mouseCoords.x, mouseCoords.y));
1798 }
1799 }
1800
1801 /* Set the mouse cursor only when we are in the client area */
1802 if ((HWND)wParam == hWnd && LOWORD(lParam) == HTCLIENT)
1803 {
1804 if (GuiData->MouseCursorRefCount >= 0)
1805 {
1806 /* Show the cursor */
1807 SetCursor(GuiData->hCursor);
1808 }
1809 else
1810 {
1811 /* Hide the cursor if the reference count is negative */
1812 SetCursor(NULL);
1813 }
1814 return TRUE;
1815 }
1816 else
1817 {
1818 goto Default;
1819 }
1820 }
1821
1822 case WM_LBUTTONDOWN:
1823 case WM_MBUTTONDOWN:
1824 case WM_RBUTTONDOWN:
1825 case WM_LBUTTONUP:
1826 case WM_MBUTTONUP:
1827 case WM_RBUTTONUP:
1828 case WM_LBUTTONDBLCLK:
1829 case WM_MBUTTONDBLCLK:
1830 case WM_RBUTTONDBLCLK:
1831 case WM_MOUSEMOVE:
1832 case WM_MOUSEWHEEL:
1833 case WM_MOUSEHWHEEL:
1834 {
1835 Result = GuiConsoleHandleMouse(GuiData, msg, wParam, lParam);
1836 break;
1837 }
1838
1839 case WM_HSCROLL:
1840 case WM_VSCROLL:
1841 {
1842 Result = GuiConsoleHandleScroll(GuiData, msg, wParam);
1843 break;
1844 }
1845
1846 case WM_NCRBUTTONDOWN:
1847 {
1848 DPRINT1("WM_NCRBUTTONDOWN\n");
1849 /*
1850 * HACK: !! Because, when we deal with WM_RBUTTON* and we do not
1851 * call after that DefWindowProc, on ReactOS, right-clicks on the
1852 * (non-client) application title-bar does not display the system
1853 * menu and does not trigger a WM_NCRBUTTONUP message too.
1854 * See: http://git.reactos.org/?p=reactos.git;a=blob;f=reactos/win32ss/user/user32/windows/defwnd.c;hb=332bc8f482f40fd05ab510f78276576719fbfba8#l1103
1855 * and line 1135 too.
1856 */
1857 #if 0
1858 if (DefWindowProcW(hWnd, WM_NCHITTEST, 0, lParam) == HTCAPTION)
1859 {
1860 /* Call DefWindowProcW with the WM_CONTEXTMENU message */
1861 msg = WM_CONTEXTMENU;
1862 }
1863 #endif
1864 goto Default;
1865 }
1866 // case WM_NCRBUTTONUP:
1867 // DPRINT1("WM_NCRBUTTONUP\n");
1868 // goto Default;
1869
1870 case WM_CONTEXTMENU:
1871 {
1872 if (DefWindowProcW(hWnd /*GuiData->hWindow*/, WM_NCHITTEST, 0, lParam) == HTCLIENT)
1873 {
1874 HMENU hMenu = CreatePopupMenu();
1875 if (hMenu != NULL)
1876 {
1877 GuiConsoleAppendMenuItems(hMenu, GuiConsoleEditMenuItems);
1878 TrackPopupMenuEx(hMenu,
1879 TPM_RIGHTBUTTON,
1880 GET_X_LPARAM(lParam),
1881 GET_Y_LPARAM(lParam),
1882 hWnd,
1883 NULL);
1884 DestroyMenu(hMenu);
1885 }
1886 break;
1887 }
1888 else
1889 {
1890 goto Default;
1891 }
1892 }
1893
1894 case WM_INITMENU:
1895 {
1896 HMENU hMenu = (HMENU)wParam;
1897 if (hMenu != NULL)
1898 {
1899 /* Enable or disable the Close menu item */
1900 EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND |
1901 (GuiData->IsCloseButtonEnabled ? MF_ENABLED : MF_GRAYED));
1902
1903 /* Enable or disable the Copy and Paste items */
1904 EnableMenuItem(hMenu, ID_SYSTEM_EDIT_COPY , MF_BYCOMMAND |
1905 ((Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) &&
1906 (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) ? MF_ENABLED : MF_GRAYED));
1907 EnableMenuItem(hMenu, ID_SYSTEM_EDIT_PASTE, MF_BYCOMMAND |
1908 (!(Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) &&
1909 IsClipboardFormatAvailable(CF_UNICODETEXT) ? MF_ENABLED : MF_GRAYED));
1910 }
1911
1912 if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
1913 {
1914 GuiSendMenuEvent(Console, WM_INITMENU);
1915 LeaveCriticalSection(&Console->Lock);
1916 }
1917 break;
1918 }
1919
1920 case WM_MENUSELECT:
1921 {
1922 if (HIWORD(wParam) == 0xFFFF) // Allow all the menu flags
1923 {
1924 if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
1925 {
1926 GuiSendMenuEvent(Console, WM_MENUSELECT);
1927 LeaveCriticalSection(&Console->Lock);
1928 }
1929 }
1930 break;
1931 }
1932
1933 case WM_COMMAND:
1934 case WM_SYSCOMMAND:
1935 {
1936 Result = GuiConsoleHandleSysMenuCommand(GuiData, wParam, lParam);
1937 break;
1938 }
1939
1940 case WM_SETFOCUS:
1941 case WM_KILLFOCUS:
1942 {
1943 if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
1944 {
1945 BOOL SetFocus = (msg == WM_SETFOCUS);
1946 INPUT_RECORD er;
1947
1948 er.EventType = FOCUS_EVENT;
1949 er.Event.FocusEvent.bSetFocus = SetFocus;
1950 ConioProcessInputEvent(Console, &er);
1951
1952 if (SetFocus)
1953 DPRINT1("TODO: Create console caret\n");
1954 else
1955 DPRINT1("TODO: Destroy console caret\n");
1956
1957 LeaveCriticalSection(&Console->Lock);
1958 }
1959 break;
1960 }
1961
1962 case WM_GETMINMAXINFO:
1963 GuiConsoleGetMinMaxInfo(GuiData, (PMINMAXINFO)lParam);
1964 break;
1965
1966 case WM_SIZE:
1967 GuiConsoleResize(GuiData, wParam, lParam);
1968 break;
1969
1970 case PM_RESIZE_TERMINAL:
1971 {
1972 /* Resize the window to the user's values */
1973 GuiData->WindowSizeLock = TRUE;
1974 GuiConsoleResizeWindow(GuiData);
1975 GuiData->WindowSizeLock = FALSE;
1976 break;
1977 }
1978
1979 case PM_APPLY_CONSOLE_INFO:
1980 {
1981 if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
1982 {
1983 GuiApplyUserSettings(GuiData, (HANDLE)wParam, (BOOL)lParam);
1984 LeaveCriticalSection(&Console->Lock);
1985 }
1986 break;
1987 }
1988
1989 case PM_CONSOLE_BEEP:
1990 DPRINT1("Beep !!\n");
1991 Beep(800, 200);
1992 break;
1993
1994 // case PM_CONSOLE_SET_TITLE:
1995 // SetWindowText(GuiData->hWindow, GuiData->Console->Title.Buffer);
1996 // break;
1997
1998 default: Default:
1999 Result = DefWindowProcW(hWnd, msg, wParam, lParam);
2000 break;
2001 }
2002
2003 return Result;
2004 }
2005
2006
2007
2008 /******************************************************************************
2009 * GUI Terminal Initialization *
2010 ******************************************************************************/
2011
2012 static LRESULT CALLBACK
2013 GuiConsoleNotifyWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
2014 {
2015 HWND NewWindow;
2016 LONG WindowCount;
2017 MSG Msg;
2018
2019 switch (msg)
2020 {
2021 case WM_CREATE:
2022 {
2023 SetWindowLongW(hWnd, GWL_USERDATA, 0);
2024 return 0;
2025 }
2026
2027 case PM_CREATE_CONSOLE:
2028 {
2029 PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)lParam;
2030 PCONSOLE Console = GuiData->Console;
2031
2032 NewWindow = CreateWindowExW(WS_EX_CLIENTEDGE,
2033 GUI_CONSOLE_WINDOW_CLASS,
2034 Console->Title.Buffer,
2035 WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
2036 CW_USEDEFAULT,
2037 CW_USEDEFAULT,
2038 CW_USEDEFAULT,
2039 CW_USEDEFAULT,
2040 NULL,
2041 NULL,
2042 ConSrvDllInstance,
2043 (PVOID)GuiData);
2044 if (NULL != NewWindow)
2045 {
2046 WindowCount = GetWindowLongW(hWnd, GWL_USERDATA);
2047 WindowCount++;
2048 SetWindowLongW(hWnd, GWL_USERDATA, WindowCount);
2049
2050 DPRINT("Set icons via PM_CREATE_CONSOLE\n");
2051 if (GuiData->hIcon == NULL)
2052 {
2053 DPRINT("Not really /o\\...\n");
2054 GuiData->hIcon = ghDefaultIcon;
2055 GuiData->hIconSm = ghDefaultIconSm;
2056 }
2057 else if (GuiData->hIcon != ghDefaultIcon)
2058 {
2059 DPRINT("Yes \\o/\n");
2060 SendMessageW(GuiData->hWindow, WM_SETICON, ICON_BIG, (LPARAM)GuiData->hIcon);
2061 SendMessageW(GuiData->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)GuiData->hIconSm);
2062 }
2063
2064 /* Move and resize the window to the user's values */
2065 /* CAN WE DEADLOCK ?? */
2066 GuiConsoleMoveWindow(GuiData);
2067 GuiData->WindowSizeLock = TRUE;
2068 GuiConsoleResizeWindow(GuiData);
2069 GuiData->WindowSizeLock = FALSE;
2070
2071 // ShowWindow(NewWindow, (int)wParam);
2072 ShowWindowAsync(NewWindow, (int)wParam);
2073 DPRINT("Window showed\n");
2074 }
2075
2076 return (LRESULT)NewWindow;
2077 }
2078
2079 case PM_DESTROY_CONSOLE:
2080 {
2081 PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)lParam;
2082
2083 /*
2084 * Window creation is done using a PostMessage(), so it's possible
2085 * that the window that we want to destroy doesn't exist yet.
2086 * So first empty the message queue.
2087 */
2088 /*
2089 while (PeekMessageW(&Msg, NULL, 0, 0, PM_REMOVE))
2090 {
2091 TranslateMessage(&Msg);
2092 DispatchMessageW(&Msg);
2093 }*/
2094 while (PeekMessageW(&Msg, NULL, 0, 0, PM_REMOVE)) ;
2095
2096 if (GuiData->hWindow != NULL) /* && DestroyWindow(GuiData->hWindow) */
2097 {
2098 DestroyWindow(GuiData->hWindow);
2099
2100 WindowCount = GetWindowLongW(hWnd, GWL_USERDATA);
2101 WindowCount--;
2102 SetWindowLongW(hWnd, GWL_USERDATA, WindowCount);
2103 if (0 == WindowCount)
2104 {
2105 NotifyWnd = NULL;
2106 DestroyWindow(hWnd);
2107 DPRINT("CONSRV: Going to quit the Gui Thread!!\n");
2108 PostQuitMessage(0);
2109 }
2110 }
2111
2112 return 0;
2113 }
2114
2115 default:
2116 return DefWindowProcW(hWnd, msg, wParam, lParam);
2117 }
2118 }
2119
2120 static DWORD NTAPI
2121 GuiConsoleGuiThread(PVOID Data)
2122 {
2123 MSG msg;
2124 PHANDLE GraphicsStartupEvent = (PHANDLE)Data;
2125
2126 /*
2127 * This thread dispatches all the console notifications to the notify window.
2128 * It is common for all the console windows.
2129 */
2130
2131 PrivateCsrssManualGuiCheck(+1);
2132
2133 NotifyWnd = CreateWindowW(L"ConSrvCreateNotify",
2134 L"",
2135 WS_OVERLAPPEDWINDOW,
2136 CW_USEDEFAULT,
2137 CW_USEDEFAULT,
2138 CW_USEDEFAULT,
2139 CW_USEDEFAULT,
2140 NULL,
2141 NULL,
2142 ConSrvDllInstance,
2143 NULL);
2144 if (NULL == NotifyWnd)
2145 {
2146 PrivateCsrssManualGuiCheck(-1);
2147 SetEvent(*GraphicsStartupEvent);
2148 return 1;
2149 }
2150
2151 SetEvent(*GraphicsStartupEvent);
2152
2153 while (GetMessageW(&msg, NULL, 0, 0))
2154 {
2155 TranslateMessage(&msg);
2156 DispatchMessageW(&msg);
2157 }
2158
2159 DPRINT("CONSRV: Quit the Gui Thread!!\n");
2160 PrivateCsrssManualGuiCheck(-1);
2161
2162 return 1;
2163 }
2164
2165 static BOOL
2166 GuiInit(VOID)
2167 {
2168 WNDCLASSEXW wc;
2169 ATOM ConsoleClassAtom;
2170
2171 /* Exit if we were already initialized */
2172 // if (ConsInitialized) return TRUE;
2173
2174 /*
2175 * Initialize and register the different window classes, if needed.
2176 */
2177 if (!ConsInitialized)
2178 {
2179 /* Initialize the notification window class */
2180 wc.cbSize = sizeof(WNDCLASSEXW);
2181 wc.lpszClassName = L"ConSrvCreateNotify";
2182 wc.lpfnWndProc = GuiConsoleNotifyWndProc;
2183 wc.style = 0;
2184 wc.hInstance = ConSrvDllInstance;
2185 wc.hIcon = NULL;
2186 wc.hIconSm = NULL;
2187 wc.hCursor = NULL;
2188 wc.hbrBackground = NULL;
2189 wc.lpszMenuName = NULL;
2190 wc.cbClsExtra = 0;
2191 wc.cbWndExtra = 0;
2192 if (RegisterClassExW(&wc) == 0)
2193 {
2194 DPRINT1("Failed to register GUI notify wndproc\n");
2195 return FALSE;
2196 }
2197
2198 /* Initialize the console window class */
2199 ghDefaultIcon = LoadImageW(ConSrvDllInstance,
2200 MAKEINTRESOURCEW(IDI_TERMINAL),
2201 IMAGE_ICON,
2202 GetSystemMetrics(SM_CXICON),
2203 GetSystemMetrics(SM_CYICON),
2204 LR_SHARED);
2205 ghDefaultIconSm = LoadImageW(ConSrvDllInstance,
2206 MAKEINTRESOURCEW(IDI_TERMINAL),
2207 IMAGE_ICON,
2208 GetSystemMetrics(SM_CXSMICON),
2209 GetSystemMetrics(SM_CYSMICON),
2210 LR_SHARED);
2211 ghDefaultCursor = LoadCursorW(NULL, IDC_ARROW);
2212 wc.cbSize = sizeof(WNDCLASSEXW);
2213 wc.lpszClassName = GUI_CONSOLE_WINDOW_CLASS;
2214 wc.lpfnWndProc = GuiConsoleWndProc;
2215 wc.style = CS_DBLCLKS /* | CS_HREDRAW | CS_VREDRAW */;
2216 wc.hInstance = ConSrvDllInstance;
2217 wc.hIcon = ghDefaultIcon;
2218 wc.hIconSm = ghDefaultIconSm;
2219 wc.hCursor = ghDefaultCursor;
2220 wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); // The color of a terminal when it is switch off.
2221 wc.lpszMenuName = NULL;
2222 wc.cbClsExtra = 0;
2223 wc.cbWndExtra = GWLP_CONSOLEWND_ALLOC;
2224
2225 ConsoleClassAtom = RegisterClassExW(&wc);
2226 if (ConsoleClassAtom == 0)
2227 {
2228 DPRINT1("Failed to register GUI console wndproc\n");
2229 return FALSE;
2230 }
2231 else
2232 {
2233 NtUserConsoleControl(GuiConsoleWndClassAtom, &ConsoleClassAtom, sizeof(ATOM));
2234 }
2235
2236 ConsInitialized = TRUE;
2237 }
2238
2239 /*
2240 * Set-up the notification window
2241 */
2242 if (NULL == NotifyWnd)
2243 {
2244 HANDLE ThreadHandle;
2245 HANDLE GraphicsStartupEvent;
2246
2247 GraphicsStartupEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
2248 if (NULL == GraphicsStartupEvent) return FALSE;
2249
2250 ThreadHandle = CreateThread(NULL,
2251 0,
2252 GuiConsoleGuiThread,
2253 (PVOID)&GraphicsStartupEvent,
2254 0,
2255 NULL);
2256 if (NULL == ThreadHandle)
2257 {
2258 CloseHandle(GraphicsStartupEvent);
2259 DPRINT1("CONSRV: Failed to create graphics console thread. Expect problems\n");
2260 return FALSE;
2261 }
2262 SetThreadPriority(ThreadHandle, THREAD_PRIORITY_HIGHEST);
2263 CloseHandle(ThreadHandle);
2264
2265 WaitForSingleObject(GraphicsStartupEvent, INFINITE);
2266 CloseHandle(GraphicsStartupEvent);
2267
2268 if (NULL == NotifyWnd)
2269 {
2270 DPRINT1("CONSRV: Failed to create notification window.\n");
2271 return FALSE;
2272 }
2273 }
2274
2275 // ConsInitialized = TRUE;
2276
2277 return TRUE;
2278 }
2279
2280
2281
2282 /******************************************************************************
2283 * GUI Console Driver *
2284 ******************************************************************************/
2285
2286 static VOID NTAPI
2287 GuiDeinitFrontEnd(IN OUT PFRONTEND This);
2288
2289 NTSTATUS NTAPI
2290 GuiInitFrontEnd(IN OUT PFRONTEND This,
2291 IN PCONSOLE Console)
2292 {
2293 PGUI_INIT_INFO GuiInitInfo;
2294 PCONSOLE_INFO ConsoleInfo;
2295 PCONSOLE_START_INFO ConsoleStartInfo;
2296
2297 PGUI_CONSOLE_DATA GuiData;
2298 GUI_CONSOLE_INFO TermInfo;
2299
2300 SIZE_T Length = 0;
2301 LPWSTR IconPath = NULL;
2302 INT IconIndex = 0;
2303
2304 if (This == NULL || Console == NULL || This->OldData == NULL)
2305 return STATUS_INVALID_PARAMETER;
2306
2307 ASSERT(This->Console == Console);
2308
2309 GuiInitInfo = This->OldData;
2310
2311 if (GuiInitInfo->ConsoleInfo == NULL || GuiInitInfo->ConsoleStartInfo == NULL)
2312 return STATUS_INVALID_PARAMETER;
2313
2314 ConsoleInfo = GuiInitInfo->ConsoleInfo;
2315 ConsoleStartInfo = GuiInitInfo->ConsoleStartInfo;
2316
2317 IconPath = ConsoleStartInfo->IconPath;
2318 IconIndex = ConsoleStartInfo->IconIndex;
2319
2320
2321 /* Terminal data allocation */
2322 GuiData = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(GUI_CONSOLE_DATA));
2323 if (!GuiData)
2324 {
2325 DPRINT1("CONSRV: Failed to create GUI_CONSOLE_DATA\n");
2326 return STATUS_UNSUCCESSFUL;
2327 }
2328 /* HACK */ Console->TermIFace.Data = (PVOID)GuiData; /* HACK */
2329 GuiData->Console = Console;
2330 GuiData->ActiveBuffer = Console->ActiveBuffer;
2331 GuiData->hWindow = NULL;
2332
2333 /* The console can be resized */
2334 Console->FixedSize = FALSE;
2335
2336 InitializeCriticalSection(&GuiData->Lock);
2337
2338
2339 /*
2340 * Load terminal settings
2341 */
2342
2343 /* 1. Load the default settings */
2344 GuiConsoleGetDefaultSettings(&TermInfo, GuiInitInfo->ProcessId);
2345
2346 /* 3. Load the remaining console settings via the registry. */
2347 if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
2348 {
2349 /* Load the terminal infos from the registry. */
2350 GuiConsoleReadUserSettings(&TermInfo,
2351 ConsoleInfo->ConsoleTitle,
2352 GuiInitInfo->ProcessId);
2353
2354 /*
2355 * Now, update them with the properties the user might gave to us
2356 * via the STARTUPINFO structure before calling CreateProcess
2357 * (and which was transmitted via the ConsoleStartInfo structure).
2358 * We therefore overwrite the values read in the registry.
2359 */
2360 if (ConsoleStartInfo->dwStartupFlags & STARTF_USESHOWWINDOW)
2361 {
2362 TermInfo.ShowWindow = ConsoleStartInfo->wShowWindow;
2363 }
2364 if (ConsoleStartInfo->dwStartupFlags & STARTF_USEPOSITION)
2365 {
2366 TermInfo.AutoPosition = FALSE;
2367 TermInfo.WindowOrigin.x = ConsoleStartInfo->dwWindowOrigin.X;
2368 TermInfo.WindowOrigin.y = ConsoleStartInfo->dwWindowOrigin.Y;
2369 }
2370 if (ConsoleStartInfo->dwStartupFlags & STARTF_RUNFULLSCREEN)
2371 {
2372 TermInfo.FullScreen = TRUE;
2373 }
2374 }
2375
2376
2377 /*
2378 * Set up GUI data
2379 */
2380
2381 Length = min(wcslen(TermInfo.FaceName) + 1, LF_FACESIZE); // wcsnlen
2382 wcsncpy(GuiData->GuiInfo.FaceName, TermInfo.FaceName, LF_FACESIZE);
2383 GuiData->GuiInfo.FaceName[Length] = L'\0';
2384 GuiData->GuiInfo.FontFamily = TermInfo.FontFamily;
2385 GuiData->GuiInfo.FontSize = TermInfo.FontSize;
2386 GuiData->GuiInfo.FontWeight = TermInfo.FontWeight;
2387 GuiData->GuiInfo.UseRasterFonts = TermInfo.UseRasterFonts;
2388 GuiData->GuiInfo.FullScreen = TermInfo.FullScreen;
2389 GuiData->GuiInfo.ShowWindow = TermInfo.ShowWindow;
2390 GuiData->GuiInfo.AutoPosition = TermInfo.AutoPosition;
2391 GuiData->GuiInfo.WindowOrigin = TermInfo.WindowOrigin;
2392
2393 /* Initialize the icon handles to their default values */
2394 GuiData->hIcon = ghDefaultIcon;
2395 GuiData->hIconSm = ghDefaultIconSm;
2396
2397 /* Get the associated icon, if any */
2398 if (IconPath == NULL || IconPath[0] == L'\0')
2399 {
2400 IconPath = ConsoleStartInfo->AppPath;
2401 IconIndex = 0;
2402 }
2403 DPRINT("IconPath = %S ; IconIndex = %lu\n", (IconPath ? IconPath : L"n/a"), IconIndex);
2404 if (IconPath && IconPath[0] != L'\0')
2405 {
2406 HICON hIcon = NULL, hIconSm = NULL;
2407 PrivateExtractIconExW(IconPath,
2408 IconIndex,
2409 &hIcon,
2410 &hIconSm,
2411 1);
2412 DPRINT("hIcon = 0x%p ; hIconSm = 0x%p\n", hIcon, hIconSm);
2413 if (hIcon != NULL)
2414 {
2415 DPRINT("Effectively set the icons\n");
2416 GuiData->hIcon = hIcon;
2417 GuiData->hIconSm = hIconSm;
2418 }
2419 }
2420
2421 /* Original system palette */
2422 GuiData->hSysPalette = NULL;
2423
2424 /* Mouse is shown by default with its default cursor shape */
2425 GuiData->hCursor = ghDefaultCursor;
2426 GuiData->MouseCursorRefCount = 0;
2427
2428 /* A priori don't ignore mouse signals */
2429 GuiData->IgnoreNextMouseSignal = FALSE;
2430
2431 /* Close button and the corresponding system menu item are enabled by default */
2432 GuiData->IsCloseButtonEnabled = TRUE;
2433
2434 /* There is no user-reserved menu id range by default */
2435 GuiData->cmdIdLow = GuiData->cmdIdHigh = 0;
2436
2437 /*
2438 * We need to wait until the GUI has been fully initialized
2439 * to retrieve custom settings i.e. WindowSize etc...
2440 * Ideally we could use SendNotifyMessage for this but its not
2441 * yet implemented.
2442 */
2443 GuiData->hGuiInitEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
2444
2445 DPRINT("GUI - Checkpoint\n");
2446
2447 /* Create the terminal window */
2448 PostMessageW(NotifyWnd, PM_CREATE_CONSOLE, GuiData->GuiInfo.ShowWindow, (LPARAM)GuiData);
2449
2450 /* Wait until initialization has finished */
2451 WaitForSingleObject(GuiData->hGuiInitEvent, INFINITE);
2452 DPRINT("OK we created the console window\n");
2453 CloseHandle(GuiData->hGuiInitEvent);
2454 GuiData->hGuiInitEvent = NULL;
2455
2456 /* Check whether we really succeeded in initializing the terminal window */
2457 if (GuiData->hWindow == NULL)
2458 {
2459 DPRINT("GuiInitConsole - We failed at creating a new terminal window\n");
2460 GuiDeinitFrontEnd(This);
2461 return STATUS_UNSUCCESSFUL;
2462 }
2463
2464 /* Finally, finish to initialize the frontend structure */
2465 This->Data = GuiData;
2466 if (This->OldData) ConsoleFreeHeap(This->OldData);
2467 This->OldData = NULL;
2468
2469 return STATUS_SUCCESS;
2470 }
2471
2472 static VOID NTAPI
2473 GuiDeinitFrontEnd(IN OUT PFRONTEND This)
2474 {
2475 PGUI_CONSOLE_DATA GuiData = This->Data;
2476
2477 SendMessageW(NotifyWnd, PM_DESTROY_CONSOLE, 0, (LPARAM)GuiData);
2478
2479 DPRINT("Destroying icons !! - GuiData->hIcon = 0x%p ; ghDefaultIcon = 0x%p ; GuiData->hIconSm = 0x%p ; ghDefaultIconSm = 0x%p\n",
2480 GuiData->hIcon, ghDefaultIcon, GuiData->hIconSm, ghDefaultIconSm);
2481 if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon)
2482 {
2483 DPRINT("Destroy hIcon\n");
2484 DestroyIcon(GuiData->hIcon);
2485 }
2486 if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm)
2487 {
2488 DPRINT("Destroy hIconSm\n");
2489 DestroyIcon(GuiData->hIconSm);
2490 }
2491
2492 This->Data = NULL;
2493 DeleteCriticalSection(&GuiData->Lock);
2494 ConsoleFreeHeap(GuiData);
2495
2496 DPRINT("Quit GuiDeinitFrontEnd\n");
2497 }
2498
2499 static VOID NTAPI
2500 GuiDrawRegion(IN OUT PFRONTEND This,
2501 SMALL_RECT* Region)
2502 {
2503 PGUI_CONSOLE_DATA GuiData = This->Data;
2504 RECT RegionRect;
2505
2506 SmallRectToRect(GuiData, &RegionRect, Region);
2507 /* Do not erase the background: it speeds up redrawing and reduce flickering */
2508 InvalidateRect(GuiData->hWindow, &RegionRect, FALSE);
2509 }
2510
2511 static VOID NTAPI
2512 GuiWriteStream(IN OUT PFRONTEND This,
2513 SMALL_RECT* Region,
2514 SHORT CursorStartX,
2515 SHORT CursorStartY,
2516 UINT ScrolledLines,
2517 PWCHAR Buffer,
2518 UINT Length)
2519 {
2520 PGUI_CONSOLE_DATA GuiData = This->Data;
2521 PCONSOLE_SCREEN_BUFFER Buff;
2522 SHORT CursorEndX, CursorEndY;
2523 RECT ScrollRect;
2524
2525 if (NULL == GuiData || NULL == GuiData->hWindow) return;
2526
2527 Buff = GuiData->ActiveBuffer; // ConDrvGetActiveScreenBuffer(GuiData->Console);
2528 if (GetType(Buff) != TEXTMODE_BUFFER) return;
2529
2530 if (0 != ScrolledLines)
2531 {
2532 ScrollRect.left = 0;
2533 ScrollRect.top = 0;
2534 ScrollRect.right = Buff->ViewSize.X * GuiData->CharWidth;
2535 ScrollRect.bottom = Region->Top * GuiData->CharHeight;
2536
2537 ScrollWindowEx(GuiData->hWindow,
2538 0,
2539 -(int)(ScrolledLines * GuiData->CharHeight),
2540 &ScrollRect,
2541 NULL,
2542 NULL,
2543 NULL,
2544 SW_INVALIDATE);
2545 }
2546
2547 GuiDrawRegion(This, Region);
2548
2549 if (CursorStartX < Region->Left || Region->Right < CursorStartX
2550 || CursorStartY < Region->Top || Region->Bottom < CursorStartY)
2551 {
2552 GuiInvalidateCell(This, CursorStartX, CursorStartY);
2553 }
2554
2555 CursorEndX = Buff->CursorPosition.X;
2556 CursorEndY = Buff->CursorPosition.Y;
2557 if ((CursorEndX < Region->Left || Region->Right < CursorEndX
2558 || CursorEndY < Region->Top || Region->Bottom < CursorEndY)
2559 && (CursorEndX != CursorStartX || CursorEndY != CursorStartY))
2560 {
2561 GuiInvalidateCell(This, CursorEndX, CursorEndY);
2562 }
2563
2564 // Set up the update timer (very short interval) - this is a "hack" for getting the OS to
2565 // repaint the window without having it just freeze up and stay on the screen permanently.
2566 Buff->CursorBlinkOn = TRUE;
2567 SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL);
2568 }
2569
2570 static BOOL NTAPI
2571 GuiSetCursorInfo(IN OUT PFRONTEND This,
2572 PCONSOLE_SCREEN_BUFFER Buff)
2573 {
2574 PGUI_CONSOLE_DATA GuiData = This->Data;
2575
2576 if (/*ConDrvGetActiveScreenBuffer(GuiData->Console)*/GuiData->ActiveBuffer == Buff)
2577 {
2578 GuiInvalidateCell(This, Buff->CursorPosition.X, Buff->CursorPosition.Y);
2579 }
2580
2581 return TRUE;
2582 }
2583
2584 static BOOL NTAPI
2585 GuiSetScreenInfo(IN OUT PFRONTEND This,
2586 PCONSOLE_SCREEN_BUFFER Buff,
2587 SHORT OldCursorX,
2588 SHORT OldCursorY)
2589 {
2590 PGUI_CONSOLE_DATA GuiData = This->Data;
2591
2592 if (/*ConDrvGetActiveScreenBuffer(GuiData->Console)*/GuiData->ActiveBuffer == Buff)
2593 {
2594 /* Redraw char at old position (remove cursor) */
2595 GuiInvalidateCell(This, OldCursorX, OldCursorY);
2596 /* Redraw char at new position (show cursor) */
2597 GuiInvalidateCell(This, Buff->CursorPosition.X, Buff->CursorPosition.Y);
2598 }
2599
2600 return TRUE;
2601 }
2602
2603 static VOID NTAPI
2604 GuiResizeTerminal(IN OUT PFRONTEND This)
2605 {
2606 PGUI_CONSOLE_DATA GuiData = This->Data;
2607
2608 /* Resize the window to the user's values */
2609 // GuiData->WindowSizeLock = TRUE;
2610 // GuiConsoleResizeWindow(GuiData);
2611 // GuiData->WindowSizeLock = FALSE;
2612 // NOTE: This code ^^ causes deadlocks...
2613
2614 PostMessageW(GuiData->hWindow, PM_RESIZE_TERMINAL, 0, 0);
2615 }
2616
2617 static VOID NTAPI
2618 GuiSetActiveScreenBuffer(IN OUT PFRONTEND This)
2619 {
2620 PGUI_CONSOLE_DATA GuiData = This->Data;
2621 PCONSOLE_SCREEN_BUFFER ActiveBuffer; // = GuiData->ActiveBuffer; // ConDrvGetActiveScreenBuffer(GuiData->Console);
2622 HDC hDC;
2623 HPALETTE hPalette;
2624
2625 EnterCriticalSection(&GuiData->Lock);
2626 GuiData->WindowSizeLock = TRUE;
2627
2628 InterlockedExchangePointer(&GuiData->ActiveBuffer,
2629 GuiData->Console->ActiveBuffer);
2630
2631 GuiData->WindowSizeLock = FALSE;
2632 LeaveCriticalSection(&GuiData->Lock);
2633
2634 ActiveBuffer = GuiData->ActiveBuffer;
2635
2636 /* Change the current palette */
2637 if (ActiveBuffer->PaletteHandle == NULL)
2638 {
2639 hPalette = GuiData->hSysPalette;
2640 }
2641 else
2642 {
2643 hPalette = ActiveBuffer->PaletteHandle;
2644 }
2645
2646 DPRINT1("GuiSetActiveScreenBuffer using palette 0x%p\n", hPalette);
2647
2648 /* Get the Device Context of the console window */
2649 hDC = GetDC(GuiData->hWindow);
2650
2651 /* Set the new palette */
2652 SelectPalette(hDC, hPalette, FALSE);
2653
2654 /* Specify the use of the system palette */
2655 SetSystemPaletteUse(hDC, ActiveBuffer->PaletteUsage);
2656
2657 /* Realize the (logical) palette */
2658 RealizePalette(hDC);
2659
2660 /* Release the Device Context */
2661 ReleaseDC(GuiData->hWindow, hDC);
2662
2663 GuiResizeTerminal(This);
2664 // ConioDrawConsole(Console);
2665 }
2666
2667 static VOID NTAPI
2668 GuiReleaseScreenBuffer(IN OUT PFRONTEND This,
2669 IN PCONSOLE_SCREEN_BUFFER ScreenBuffer)
2670 {
2671 PGUI_CONSOLE_DATA GuiData = This->Data;
2672 HDC hDC;
2673
2674 /*
2675 * If we were notified to release a screen buffer that is not actually
2676 * ours, then just ignore the notification...
2677 */
2678 if (ScreenBuffer != GuiData->ActiveBuffer) return;
2679
2680 /*
2681 * ... else, we must release our active buffer. Two cases are present:
2682 * - If ScreenBuffer (== GuiData->ActiveBuffer) IS NOT the console
2683 * active screen buffer, then we can safely switch to it.
2684 * - If ScreenBuffer IS the console active screen buffer, we must release
2685 * it ONLY.
2686 */
2687
2688 /* Get the Device Context of the console window */
2689 hDC = GetDC(GuiData->hWindow);
2690
2691 /* Release the old active palette and set the default one */
2692 if (GetCurrentObject(hDC, OBJ_PAL) == ScreenBuffer->PaletteHandle)
2693 {
2694 /* Set the new palette */
2695 SelectPalette(hDC, GuiData->hSysPalette, FALSE);
2696 }
2697
2698 /* Release the Device Context */
2699 ReleaseDC(GuiData->hWindow, hDC);
2700
2701 /* Set the adequate active screen buffer */
2702 if (ScreenBuffer != GuiData->Console->ActiveBuffer)
2703 {
2704 GuiSetActiveScreenBuffer(This);
2705 }
2706 else
2707 {
2708 EnterCriticalSection(&GuiData->Lock);
2709 GuiData->WindowSizeLock = TRUE;
2710
2711 InterlockedExchangePointer(&GuiData->ActiveBuffer, NULL);
2712
2713 GuiData->WindowSizeLock = FALSE;
2714 LeaveCriticalSection(&GuiData->Lock);
2715 }
2716 }
2717
2718 static BOOL NTAPI
2719 GuiProcessKeyCallback(IN OUT PFRONTEND This,
2720 MSG* msg,
2721 BYTE KeyStateMenu,
2722 DWORD ShiftState,
2723 UINT VirtualKeyCode,
2724 BOOL Down)
2725 {
2726 if ((ShiftState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED) || KeyStateMenu & 0x80) &&
2727 (VirtualKeyCode == VK_ESCAPE || VirtualKeyCode == VK_TAB || VirtualKeyCode == VK_SPACE))
2728 {
2729 DefWindowProcW(msg->hwnd, msg->message, msg->wParam, msg->lParam);
2730 return TRUE;
2731 }
2732
2733 return FALSE;
2734 }
2735
2736 static BOOL NTAPI
2737 GuiSetMouseCursor(IN OUT PFRONTEND This,
2738 HCURSOR hCursor);
2739
2740 static VOID NTAPI
2741 GuiRefreshInternalInfo(IN OUT PFRONTEND This)
2742 {
2743 PGUI_CONSOLE_DATA GuiData = This->Data;
2744
2745 /* Update the console leader information held by the window */
2746 SetConsoleWndConsoleLeaderCID(GuiData);
2747
2748 /*
2749 * HACK:
2750 * We reset the cursor here so that, when a console app quits, we reset
2751 * the cursor to the default one. It's quite a hack since it doesn't proceed
2752 * per - console process... This must be fixed.
2753 *
2754 * See GuiInitConsole(...) for more information.
2755 */
2756
2757 /* Mouse is shown by default with its default cursor shape */
2758 GuiData->MouseCursorRefCount = 0; // Reinitialize the reference counter
2759 GuiSetMouseCursor(This, NULL);
2760 }
2761
2762 static VOID NTAPI
2763 GuiChangeTitle(IN OUT PFRONTEND This)
2764 {
2765 PGUI_CONSOLE_DATA GuiData = This->Data;
2766 // PostMessageW(GuiData->hWindow, PM_CONSOLE_SET_TITLE, 0, 0);
2767 SetWindowText(GuiData->hWindow, GuiData->Console->Title.Buffer);
2768 }
2769
2770 static BOOL NTAPI
2771 GuiChangeIcon(IN OUT PFRONTEND This,
2772 HICON hWindowIcon)
2773 {
2774 PGUI_CONSOLE_DATA GuiData = This->Data;
2775 HICON hIcon, hIconSm;
2776
2777 if (hWindowIcon == NULL)
2778 {
2779 hIcon = ghDefaultIcon;
2780 hIconSm = ghDefaultIconSm;
2781 }
2782 else
2783 {
2784 hIcon = CopyIcon(hWindowIcon);
2785 hIconSm = CopyIcon(hWindowIcon);
2786 }
2787
2788 if (hIcon == NULL)
2789 {
2790 return FALSE;
2791 }
2792
2793 if (hIcon != GuiData->hIcon)
2794 {
2795 if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon)
2796 {
2797 DestroyIcon(GuiData->hIcon);
2798 }
2799 if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm)
2800 {
2801 DestroyIcon(GuiData->hIconSm);
2802 }
2803
2804 GuiData->hIcon = hIcon;
2805 GuiData->hIconSm = hIconSm;
2806
2807 DPRINT("Set icons in GuiChangeIcon\n");
2808 PostMessageW(GuiData->hWindow, WM_SETICON, ICON_BIG, (LPARAM)GuiData->hIcon);
2809 PostMessageW(GuiData->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)GuiData->hIconSm);
2810 }
2811
2812 return TRUE;
2813 }
2814
2815 static HWND NTAPI
2816 GuiGetConsoleWindowHandle(IN OUT PFRONTEND This)
2817 {
2818 PGUI_CONSOLE_DATA GuiData = This->Data;
2819 return GuiData->hWindow;
2820 }
2821
2822 static VOID NTAPI
2823 GuiGetLargestConsoleWindowSize(IN OUT PFRONTEND This,
2824 PCOORD pSize)
2825 {
2826 PGUI_CONSOLE_DATA GuiData = This->Data;
2827 PCONSOLE_SCREEN_BUFFER ActiveBuffer;
2828 RECT WorkArea;
2829 LONG width, height;
2830 UINT WidthUnit, HeightUnit;
2831
2832 if (!pSize) return;
2833
2834 if (!SystemParametersInfoW(SPI_GETWORKAREA, 0, &WorkArea, 0))
2835 {
2836 DPRINT1("SystemParametersInfoW failed - What to do ??\n");
2837 return;
2838 }
2839
2840 // ActiveBuffer = ConDrvGetActiveScreenBuffer(GuiData->Console);
2841 ActiveBuffer = GuiData->ActiveBuffer;
2842 if (ActiveBuffer)
2843 {
2844 GetScreenBufferSizeUnits(ActiveBuffer, GuiData, &WidthUnit, &HeightUnit);
2845 }
2846 else
2847 {
2848 /* Default: text mode */
2849 WidthUnit = GuiData->CharWidth ;
2850 HeightUnit = GuiData->CharHeight;
2851 }
2852
2853 width = WorkArea.right;
2854 height = WorkArea.bottom;
2855
2856 width -= (2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE)));
2857 height -= (2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION));
2858
2859 if (width < 0) width = 0;
2860 if (height < 0) height = 0;
2861
2862 pSize->X = (SHORT)(width / (int)WidthUnit ) /* HACK */ + 2;
2863 pSize->Y = (SHORT)(height / (int)HeightUnit) /* HACK */ + 1;
2864 }
2865
2866 static BOOL NTAPI
2867 GuiSetPalette(IN OUT PFRONTEND This,
2868 HPALETTE PaletteHandle,
2869 UINT PaletteUsage)
2870 {
2871 BOOL Success = TRUE;
2872 PGUI_CONSOLE_DATA GuiData = This->Data;
2873 // PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer; // ConDrvGetActiveScreenBuffer(GuiData->Console);
2874 HDC hDC;
2875 HPALETTE OldPalette;
2876
2877 DPRINT1("GuiSetPalette checkpt 0\n");
2878
2879 // if (GetType(ActiveBuffer) != GRAPHICS_BUFFER) return FALSE;
2880 if (PaletteHandle == NULL) return FALSE;
2881
2882 DPRINT1("GuiSetPalette checkpt 1\n");
2883
2884 /* Get the Device Context of the console window */
2885 hDC = GetDC(GuiData->hWindow);
2886
2887 DPRINT1("GuiSetPalette calling SelectPalette(0x%p, 0x%p, FALSE)\n", hDC, PaletteHandle);
2888
2889 /* Set the new palette */
2890 OldPalette = SelectPalette(hDC, PaletteHandle, FALSE);
2891 DPRINT1("OldPalette = 0x%p\n", OldPalette);
2892 if (OldPalette == NULL)
2893 {
2894 DPRINT1("SelectPalette failed\n");
2895 Success = FALSE;
2896 goto Quit;
2897 }
2898
2899 DPRINT1("GuiSetPalette checkpt 2\n");
2900
2901 /* Specify the use of the system palette */
2902 SetSystemPaletteUse(hDC, PaletteUsage);
2903
2904 /* Realize the (logical) palette */
2905 RealizePalette(hDC);
2906
2907 DPRINT1("GuiData->hSysPalette before == 0x%p\n", GuiData->hSysPalette);
2908
2909 /* Save the original system palette handle */
2910 if (GuiData->hSysPalette == NULL) GuiData->hSysPalette = OldPalette;
2911
2912 DPRINT1("GuiData->hSysPalette after == 0x%p\n", GuiData->hSysPalette);
2913
2914 Quit:
2915 DPRINT1("GuiSetPalette Quit\n");
2916 /* Release the Device Context and return */
2917 ReleaseDC(GuiData->hWindow, hDC);
2918 return Success;
2919 }
2920
2921 static ULONG NTAPI
2922 GuiGetDisplayMode(IN OUT PFRONTEND This)
2923 {
2924 PGUI_CONSOLE_DATA GuiData = This->Data;
2925 ULONG DisplayMode = 0;
2926
2927 if (GuiData->GuiInfo.FullScreen)
2928 DisplayMode |= CONSOLE_FULLSCREEN_HARDWARE; // CONSOLE_FULLSCREEN
2929 else
2930 DisplayMode |= CONSOLE_WINDOWED;
2931
2932 return DisplayMode;
2933 }
2934
2935 static BOOL NTAPI
2936 GuiSetDisplayMode(IN OUT PFRONTEND This,
2937 ULONG NewMode)
2938 {
2939 PGUI_CONSOLE_DATA GuiData = This->Data;
2940
2941 if (NewMode & ~(CONSOLE_FULLSCREEN_MODE | CONSOLE_WINDOWED_MODE))
2942 return FALSE;
2943
2944 GuiData->GuiInfo.FullScreen = (NewMode & CONSOLE_FULLSCREEN_MODE);
2945 // TODO: Change the display mode
2946 return TRUE;
2947 }
2948
2949 static INT NTAPI
2950 GuiShowMouseCursor(IN OUT PFRONTEND This,
2951 BOOL Show)
2952 {
2953 PGUI_CONSOLE_DATA GuiData = This->Data;
2954
2955 /* Set the reference count */
2956 if (Show) ++GuiData->MouseCursorRefCount;
2957 else --GuiData->MouseCursorRefCount;
2958
2959 /* Effectively show (or hide) the cursor (use special values for (w|l)Param) */
2960 PostMessageW(GuiData->hWindow, WM_SETCURSOR, -1, -1);
2961
2962 return GuiData->MouseCursorRefCount;
2963 }
2964
2965 static BOOL NTAPI
2966 GuiSetMouseCursor(IN OUT PFRONTEND This,
2967 HCURSOR hCursor)
2968 {
2969 PGUI_CONSOLE_DATA GuiData = This->Data;
2970
2971 /*
2972 * Set the cursor's handle. If the given handle is NULL,
2973 * then restore the default cursor.
2974 */
2975 GuiData->hCursor = (hCursor ? hCursor : ghDefaultCursor);
2976
2977 /* Effectively modify the shape of the cursor (use special values for (w|l)Param) */
2978 PostMessageW(GuiData->hWindow, WM_SETCURSOR, -1, -1);
2979
2980 return TRUE;
2981 }
2982
2983 static HMENU NTAPI
2984 GuiMenuControl(IN OUT PFRONTEND This,
2985 UINT cmdIdLow,
2986 UINT cmdIdHigh)
2987 {
2988 PGUI_CONSOLE_DATA GuiData = This->Data;
2989
2990 GuiData->cmdIdLow = cmdIdLow ;
2991 GuiData->cmdIdHigh = cmdIdHigh;
2992
2993 return GetSystemMenu(GuiData->hWindow, FALSE);
2994 }
2995
2996 static BOOL NTAPI
2997 GuiSetMenuClose(IN OUT PFRONTEND This,
2998 BOOL Enable)
2999 {
3000 /*
3001 * NOTE: See http://www.mail-archive.com/harbour@harbour-project.org/msg27509.html
3002 * or http://harbour-devel.1590103.n2.nabble.com/Question-about-hb-gt-win-CtrlHandler-usage-td4670862i20.html
3003 * for more information.
3004 */
3005
3006 PGUI_CONSOLE_DATA GuiData = This->Data;
3007 HMENU hSysMenu = GetSystemMenu(GuiData->hWindow, FALSE);
3008
3009 if (hSysMenu == NULL) return FALSE;
3010
3011 GuiData->IsCloseButtonEnabled = Enable;
3012 EnableMenuItem(hSysMenu, SC_CLOSE, MF_BYCOMMAND | (Enable ? MF_ENABLED : MF_GRAYED));
3013
3014 return TRUE;
3015 }
3016
3017 static FRONTEND_VTBL GuiVtbl =
3018 {