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