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