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