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