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