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