4869f38d0308b5202107927e4a6c76423b228854
[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 WINAPI 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 WINAPI
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-winapi
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 WINAPI
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 WINAPI
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 WINAPI
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 WINAPI
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 WINAPI
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 WINAPI
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 WINAPI
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 WINAPI
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 WINAPI
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 WINAPI
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 WINAPI
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 WINAPI
2737 GuiSetMouseCursor(IN OUT PFRONTEND This,
2738 HCURSOR hCursor);
2739
2740 static VOID WINAPI
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 WINAPI
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 WINAPI
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 WINAPI
2816 GuiGetConsoleWindowHandle(IN OUT PFRONTEND This)
2817 {
2818 PGUI_CONSOLE_DATA GuiData = This->Data;
2819 return GuiData->hWindow;
2820 }
2821
2822 static VOID WINAPI
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 WINAPI
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 WINAPI
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 WINAPI
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 WINAPI
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 WINAPI
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 WINAPI
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 WINAPI
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 {
3019 GuiInitFrontEnd,
3020 GuiDeinitFrontEnd,
3021 GuiDrawRegion,
3022 GuiWriteStream,
3023 GuiSetCursorInfo,
3024 GuiSetScreenInfo,
3025 GuiResizeTerminal,
3026 GuiSetActiveScreenBuffer,
3027 GuiReleaseScreenBuffer,
3028 GuiProcessKeyCallback,
3029 GuiRefreshInternalInfo,
3030 GuiChangeTitle,
3031 GuiChangeIcon,
3032 GuiGetConsoleWindowHandle,
3033 GuiGetLargestConsoleWindowSize,
3034 GuiSetPalette,
3035 GuiGetDisplayMode,
3036 GuiSetDisplayMode,
3037 GuiShowMouseCursor,
3038 GuiSetMouseCursor,
3039 GuiMenuControl,
3040 GuiSetMenuClose,
3041 };
3042
3043
3044 static BOOL
3045 LoadShellLinkConsoleInfo(IN OUT PCONSOLE_START_INFO ConsoleStartInfo,
3046 IN OUT PCONSOLE_INFO ConsoleInfo)
3047 {
3048 #define PATH_SEPARATOR L'\\'
3049
3050 BOOL RetVal = FALSE;
3051 HRESULT hRes = S_OK;
3052 LPWSTR LinkName = NULL;
3053 SIZE_T Length = 0;
3054
3055 if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
3056 return FALSE;
3057
3058 ConsoleStartInfo->IconPath[0] = L'\0';
3059 ConsoleStartInfo->IconIndex = 0;
3060
3061 /* 1- Find the last path separator if any */
3062 LinkName = wcsrchr(ConsoleStartInfo->ConsoleTitle, PATH_SEPARATOR);
3063 if (LinkName == NULL)
3064 {
3065 LinkName = ConsoleStartInfo->ConsoleTitle;
3066 }
3067 else
3068 {
3069 /* Skip the path separator */
3070 ++LinkName;
3071 }
3072
3073 /* 2- Check for the link extension. The name ".lnk" is considered invalid. */
3074 Length = wcslen(LinkName);
3075 if ( (Length <= 4) || (wcsicmp(LinkName + (Length - 4), L".lnk") != 0) )
3076 return FALSE;
3077
3078 /* 3- It may be a link. Try to retrieve some properties */
3079 hRes = CoInitialize(NULL);
3080 if (SUCCEEDED(hRes))
3081 {
3082 /* Get a pointer to the IShellLink interface */
3083 IShellLinkW* pshl = NULL;
3084 hRes = CoCreateInstance(&CLSID_ShellLink,
3085 NULL,
3086 CLSCTX_INPROC_SERVER,
3087 &IID_IShellLinkW,
3088 (LPVOID*)&pshl);
3089 if (SUCCEEDED(hRes))
3090 {
3091 /* Get a pointer to the IPersistFile interface */
3092 IPersistFile* ppf = NULL;
3093 hRes = IPersistFile_QueryInterface(pshl, &IID_IPersistFile, (LPVOID*)&ppf);
3094 if (SUCCEEDED(hRes))
3095 {
3096 /* Load the shortcut */
3097 hRes = IPersistFile_Load(ppf, ConsoleStartInfo->ConsoleTitle, STGM_READ);
3098 if (SUCCEEDED(hRes))
3099 {
3100 /*
3101 * Finally we can get the properties !
3102 * Update the old ones if needed.
3103 */
3104 INT ShowCmd = 0;
3105 // WORD HotKey = 0;
3106
3107 /* Reset the name of the console with the name of the shortcut */
3108 Length = min(/*Length*/ Length - 4, // 4 == len(".lnk")
3109 sizeof(ConsoleInfo->ConsoleTitle) / sizeof(ConsoleInfo->ConsoleTitle[0]) - 1);
3110 wcsncpy(ConsoleInfo->ConsoleTitle, LinkName, Length);
3111 ConsoleInfo->ConsoleTitle[Length] = L'\0';
3112
3113 /* Get the window showing command */
3114 hRes = IShellLinkW_GetShowCmd(pshl, &ShowCmd);
3115 if (SUCCEEDED(hRes)) ConsoleStartInfo->wShowWindow = (WORD)ShowCmd;
3116
3117 /* Get the hotkey */
3118 // hRes = pshl->GetHotkey(&ShowCmd);
3119 // if (SUCCEEDED(hRes)) ConsoleStartInfo->HotKey = HotKey;
3120
3121 /* Get the icon location, if any */
3122
3123 hRes = IShellLinkW_GetIconLocation(pshl,
3124 ConsoleStartInfo->IconPath,
3125 sizeof(ConsoleStartInfo->IconPath)/sizeof(ConsoleStartInfo->IconPath[0]) - 1, // == MAX_PATH
3126 &ConsoleStartInfo->IconIndex);
3127 if (!SUCCEEDED(hRes))
3128 {
3129 ConsoleStartInfo->IconPath[0] = L'\0';
3130 ConsoleStartInfo->IconIndex = 0;
3131 }
3132
3133 // FIXME: Since we still don't load console properties from the shortcut,
3134 // return false. When this will be done, we will return true instead.
3135 RetVal = FALSE;
3136 }
3137 IPersistFile_Release(ppf);
3138 }
3139 IShellLinkW_Release(pshl);
3140 }
3141 }
3142 CoUninitialize();
3143
3144 return RetVal;
3145 }
3146
3147 NTSTATUS NTAPI
3148 GuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
3149 IN OUT PCONSOLE_INFO ConsoleInfo,
3150 IN OUT PVOID ExtraConsoleInfo,
3151 IN ULONG ProcessId)
3152 {
3153 PCONSOLE_START_INFO ConsoleStartInfo = ExtraConsoleInfo;
3154 PGUI_INIT_INFO GuiInitInfo;
3155
3156 if (FrontEnd == NULL || ConsoleInfo == NULL || ConsoleStartInfo == NULL)
3157 return STATUS_INVALID_PARAMETER;
3158
3159 /* Initialize GUI terminal emulator common functionalities */
3160 if (!GuiInit()) return STATUS_UNSUCCESSFUL;
3161
3162 /*
3163 * Load per-application terminal settings.
3164 *
3165 * Check whether the process creating the console was launched via
3166 * a shell-link. ConsoleInfo->ConsoleTitle may be updated with the
3167 * name of the shortcut, and ConsoleStartInfo->Icon[Path|Index] too.
3168 */
3169 if (ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME)
3170 {
3171 if (!LoadShellLinkConsoleInfo(ConsoleStartInfo, ConsoleInfo))
3172 {
3173 ConsoleStartInfo->dwStartupFlags &= ~STARTF_TITLEISLINKNAME;
3174 }
3175 }
3176
3177 /*
3178 * Initialize a private initialization info structure for later use.
3179 * It must be freed by a call to GuiUnloadFrontEnd or GuiInitFrontEnd.
3180 */
3181 GuiInitInfo = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(GUI_INIT_INFO));
3182 if (GuiInitInfo == NULL) return STATUS_NO_MEMORY;
3183
3184 // HACK: We suppose that the pointers will be valid in GuiInitFrontEnd...
3185 GuiInitInfo->ConsoleInfo = ConsoleInfo;
3186 GuiInitInfo->ConsoleStartInfo = ConsoleStartInfo;
3187 GuiInitInfo->ProcessId = ProcessId;
3188
3189 /* Finally, initialize the frontend structure */
3190 FrontEnd->Vtbl = &GuiVtbl;
3191 FrontEnd->Data = NULL;
3192 FrontEnd->OldData = GuiInitInfo;
3193
3194 return STATUS_SUCCESS;
3195 }
3196
3197 NTSTATUS NTAPI
3198 GuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd)
3199 {
3200 if (FrontEnd == NULL) return STATUS_INVALID_PARAMETER;
3201
3202 if (FrontEnd->Data) GuiDeinitFrontEnd(FrontEnd);
3203 if (FrontEnd->OldData) ConsoleFreeHeap(FrontEnd->OldData);
3204
3205 return STATUS_SUCCESS;
3206 }
3207
3208 /* EOF */