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