[CONSRV]: Reorganize/rename/add some fields to CONSRV_API_CONNECTINFO, partially...
[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->wShowWindow;
2283 }
2284 if (ConsoleStartInfo->dwStartupFlags & STARTF_USEPOSITION)
2285 {
2286 TermInfo.AutoPosition = FALSE;
2287 TermInfo.WindowOrigin.x = ConsoleStartInfo->dwWindowOrigin.X;
2288 TermInfo.WindowOrigin.y = ConsoleStartInfo->dwWindowOrigin.Y;
2289 }
2290 if (ConsoleStartInfo->dwStartupFlags & STARTF_RUNFULLSCREEN)
2291 {
2292 TermInfo.FullScreen = TRUE;
2293 }
2294 }
2295
2296
2297 /*
2298 * Set up GUI data
2299 */
2300
2301 Length = min(wcslen(TermInfo.FaceName) + 1, LF_FACESIZE); // wcsnlen
2302 wcsncpy(GuiData->GuiInfo.FaceName, TermInfo.FaceName, LF_FACESIZE);
2303 GuiData->GuiInfo.FaceName[Length] = L'\0';
2304 GuiData->GuiInfo.FontFamily = TermInfo.FontFamily;
2305 GuiData->GuiInfo.FontSize = TermInfo.FontSize;
2306 GuiData->GuiInfo.FontWeight = TermInfo.FontWeight;
2307 GuiData->GuiInfo.UseRasterFonts = TermInfo.UseRasterFonts;
2308 GuiData->GuiInfo.FullScreen = TermInfo.FullScreen;
2309 GuiData->GuiInfo.ShowWindow = TermInfo.ShowWindow;
2310 GuiData->GuiInfo.AutoPosition = TermInfo.AutoPosition;
2311 GuiData->GuiInfo.WindowOrigin = TermInfo.WindowOrigin;
2312
2313 /* Initialize the icon handles to their default values */
2314 GuiData->hIcon = ghDefaultIcon;
2315 GuiData->hIconSm = ghDefaultIconSm;
2316
2317 /* Get the associated icon, if any */
2318 if (IconPath == NULL || IconPath[0] == L'\0')
2319 {
2320 IconPath = ConsoleStartInfo->AppPath;
2321 IconIndex = 0;
2322 }
2323 DPRINT("IconPath = %S ; IconIndex = %lu\n", (IconPath ? IconPath : L"n/a"), IconIndex);
2324 if (IconPath && IconPath[0] != L'\0')
2325 {
2326 HICON hIcon = NULL, hIconSm = NULL;
2327 PrivateExtractIconExW(IconPath,
2328 IconIndex,
2329 &hIcon,
2330 &hIconSm,
2331 1);
2332 DPRINT("hIcon = 0x%p ; hIconSm = 0x%p\n", hIcon, hIconSm);
2333 if (hIcon != NULL)
2334 {
2335 DPRINT("Effectively set the icons\n");
2336 GuiData->hIcon = hIcon;
2337 GuiData->hIconSm = hIconSm;
2338 }
2339 }
2340
2341 /* Mouse is shown by default with its default cursor shape */
2342 GuiData->hCursor = ghDefaultCursor;
2343 GuiData->MouseCursorRefCount = 0;
2344
2345 /* A priori don't ignore mouse signals */
2346 GuiData->IgnoreNextMouseSignal = FALSE;
2347
2348 /* Close button and the corresponding system menu item are enabled by default */
2349 GuiData->IsCloseButtonEnabled = TRUE;
2350
2351 /* There is no user-reserved menu id range by default */
2352 GuiData->cmdIdLow = GuiData->cmdIdHigh = 0;
2353
2354 /*
2355 * We need to wait until the GUI has been fully initialized
2356 * to retrieve custom settings i.e. WindowSize etc...
2357 * Ideally we could use SendNotifyMessage for this but its not
2358 * yet implemented.
2359 */
2360 GuiData->hGuiInitEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
2361
2362 DPRINT("GUI - Checkpoint\n");
2363
2364 /* Create the terminal window */
2365 PostMessageW(NotifyWnd, PM_CREATE_CONSOLE, GuiData->GuiInfo.ShowWindow, (LPARAM)GuiData);
2366
2367 /* Wait until initialization has finished */
2368 WaitForSingleObject(GuiData->hGuiInitEvent, INFINITE);
2369 DPRINT("OK we created the console window\n");
2370 CloseHandle(GuiData->hGuiInitEvent);
2371 GuiData->hGuiInitEvent = NULL;
2372
2373 /* Check whether we really succeeded in initializing the terminal window */
2374 if (GuiData->hWindow == NULL)
2375 {
2376 DPRINT("GuiInitConsole - We failed at creating a new terminal window\n");
2377 GuiDeinitFrontEnd(This);
2378 return STATUS_UNSUCCESSFUL;
2379 }
2380
2381 /* Finally, finish to initialize the frontend structure */
2382 This->Data = GuiData;
2383 if (This->OldData) ConsoleFreeHeap(This->OldData);
2384 This->OldData = NULL;
2385
2386 return STATUS_SUCCESS;
2387 }
2388
2389 static VOID WINAPI
2390 GuiDeinitFrontEnd(IN OUT PFRONTEND This)
2391 {
2392 PGUI_CONSOLE_DATA GuiData = This->Data;
2393
2394 SendMessageW(NotifyWnd, PM_DESTROY_CONSOLE, 0, (LPARAM)GuiData);
2395
2396 DPRINT("Destroying icons !! - GuiData->hIcon = 0x%p ; ghDefaultIcon = 0x%p ; GuiData->hIconSm = 0x%p ; ghDefaultIconSm = 0x%p\n",
2397 GuiData->hIcon, ghDefaultIcon, GuiData->hIconSm, ghDefaultIconSm);
2398 if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon)
2399 {
2400 DPRINT("Destroy hIcon\n");
2401 DestroyIcon(GuiData->hIcon);
2402 }
2403 if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm)
2404 {
2405 DPRINT("Destroy hIconSm\n");
2406 DestroyIcon(GuiData->hIconSm);
2407 }
2408
2409 This->Data = NULL;
2410 DeleteCriticalSection(&GuiData->Lock);
2411 ConsoleFreeHeap(GuiData);
2412
2413 DPRINT("Quit GuiDeinitFrontEnd\n");
2414 }
2415
2416 static VOID WINAPI
2417 GuiDrawRegion(IN OUT PFRONTEND This,
2418 SMALL_RECT* Region)
2419 {
2420 PGUI_CONSOLE_DATA GuiData = This->Data;
2421 RECT RegionRect;
2422
2423 SmallRectToRect(GuiData, &RegionRect, Region);
2424 /* Do not erase the background: it speeds up redrawing and reduce flickering */
2425 InvalidateRect(GuiData->hWindow, &RegionRect, FALSE);
2426 }
2427
2428 static VOID WINAPI
2429 GuiWriteStream(IN OUT PFRONTEND This,
2430 SMALL_RECT* Region,
2431 SHORT CursorStartX,
2432 SHORT CursorStartY,
2433 UINT ScrolledLines,
2434 PWCHAR Buffer,
2435 UINT Length)
2436 {
2437 PGUI_CONSOLE_DATA GuiData = This->Data;
2438 PCONSOLE_SCREEN_BUFFER Buff;
2439 SHORT CursorEndX, CursorEndY;
2440 RECT ScrollRect;
2441
2442 if (NULL == GuiData || NULL == GuiData->hWindow) return;
2443
2444 Buff = ConDrvGetActiveScreenBuffer(GuiData->Console);
2445 if (GetType(Buff) != TEXTMODE_BUFFER) return;
2446
2447 if (0 != ScrolledLines)
2448 {
2449 ScrollRect.left = 0;
2450 ScrollRect.top = 0;
2451 ScrollRect.right = Buff->ViewSize.X * GuiData->CharWidth;
2452 ScrollRect.bottom = Region->Top * GuiData->CharHeight;
2453
2454 ScrollWindowEx(GuiData->hWindow,
2455 0,
2456 -(int)(ScrolledLines * GuiData->CharHeight),
2457 &ScrollRect,
2458 NULL,
2459 NULL,
2460 NULL,
2461 SW_INVALIDATE);
2462 }
2463
2464 GuiDrawRegion(This, Region);
2465
2466 if (CursorStartX < Region->Left || Region->Right < CursorStartX
2467 || CursorStartY < Region->Top || Region->Bottom < CursorStartY)
2468 {
2469 GuiInvalidateCell(This, CursorStartX, CursorStartY);
2470 }
2471
2472 CursorEndX = Buff->CursorPosition.X;
2473 CursorEndY = Buff->CursorPosition.Y;
2474 if ((CursorEndX < Region->Left || Region->Right < CursorEndX
2475 || CursorEndY < Region->Top || Region->Bottom < CursorEndY)
2476 && (CursorEndX != CursorStartX || CursorEndY != CursorStartY))
2477 {
2478 GuiInvalidateCell(This, CursorEndX, CursorEndY);
2479 }
2480
2481 // Set up the update timer (very short interval) - this is a "hack" for getting the OS to
2482 // repaint the window without having it just freeze up and stay on the screen permanently.
2483 Buff->CursorBlinkOn = TRUE;
2484 SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL);
2485 }
2486
2487 static BOOL WINAPI
2488 GuiSetCursorInfo(IN OUT PFRONTEND This,
2489 PCONSOLE_SCREEN_BUFFER Buff)
2490 {
2491 PGUI_CONSOLE_DATA GuiData = This->Data;
2492
2493 if (ConDrvGetActiveScreenBuffer(GuiData->Console) == Buff)
2494 {
2495 GuiInvalidateCell(This, Buff->CursorPosition.X, Buff->CursorPosition.Y);
2496 }
2497
2498 return TRUE;
2499 }
2500
2501 static BOOL WINAPI
2502 GuiSetScreenInfo(IN OUT PFRONTEND This,
2503 PCONSOLE_SCREEN_BUFFER Buff,
2504 SHORT OldCursorX,
2505 SHORT OldCursorY)
2506 {
2507 PGUI_CONSOLE_DATA GuiData = This->Data;
2508
2509 if (ConDrvGetActiveScreenBuffer(GuiData->Console) == Buff)
2510 {
2511 /* Redraw char at old position (remove cursor) */
2512 GuiInvalidateCell(This, OldCursorX, OldCursorY);
2513 /* Redraw char at new position (show cursor) */
2514 GuiInvalidateCell(This, Buff->CursorPosition.X, Buff->CursorPosition.Y);
2515 }
2516
2517 return TRUE;
2518 }
2519
2520 static VOID WINAPI
2521 GuiResizeTerminal(IN OUT PFRONTEND This)
2522 {
2523 PGUI_CONSOLE_DATA GuiData = This->Data;
2524
2525 /* Resize the window to the user's values */
2526 // GuiData->WindowSizeLock = TRUE;
2527 // GuiConsoleResizeWindow(GuiData);
2528 // GuiData->WindowSizeLock = FALSE;
2529 // NOTE: This code ^^ causes deadlocks...
2530
2531 PostMessageW(GuiData->hWindow, PM_RESIZE_TERMINAL, 0, 0);
2532 }
2533
2534 static BOOL WINAPI
2535 GuiProcessKeyCallback(IN OUT PFRONTEND This,
2536 MSG* msg,
2537 BYTE KeyStateMenu,
2538 DWORD ShiftState,
2539 UINT VirtualKeyCode,
2540 BOOL Down)
2541 {
2542 if ((ShiftState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED) || KeyStateMenu & 0x80) &&
2543 (VirtualKeyCode == VK_ESCAPE || VirtualKeyCode == VK_TAB || VirtualKeyCode == VK_SPACE))
2544 {
2545 DefWindowProcW(msg->hwnd, msg->message, msg->wParam, msg->lParam);
2546 return TRUE;
2547 }
2548
2549 return FALSE;
2550 }
2551
2552 static BOOL WINAPI
2553 GuiSetMouseCursor(IN OUT PFRONTEND This,
2554 HCURSOR hCursor);
2555
2556 static VOID WINAPI
2557 GuiRefreshInternalInfo(IN OUT PFRONTEND This)
2558 {
2559 PGUI_CONSOLE_DATA GuiData = This->Data;
2560
2561 /* Update the console leader information held by the window */
2562 SetConsoleWndConsoleLeaderCID(GuiData);
2563
2564 /*
2565 * HACK:
2566 * We reset the cursor here so that, when a console app quits, we reset
2567 * the cursor to the default one. It's quite a hack since it doesn't proceed
2568 * per - console process... This must be fixed.
2569 *
2570 * See GuiInitConsole(...) for more information.
2571 */
2572
2573 /* Mouse is shown by default with its default cursor shape */
2574 GuiData->MouseCursorRefCount = 0; // Reinitialize the reference counter
2575 GuiSetMouseCursor(This, NULL);
2576 }
2577
2578 static VOID WINAPI
2579 GuiChangeTitle(IN OUT PFRONTEND This)
2580 {
2581 PGUI_CONSOLE_DATA GuiData = This->Data;
2582 // PostMessageW(GuiData->hWindow, PM_CONSOLE_SET_TITLE, 0, 0);
2583 SetWindowText(GuiData->hWindow, GuiData->Console->Title.Buffer);
2584 }
2585
2586 static BOOL WINAPI
2587 GuiChangeIcon(IN OUT PFRONTEND This,
2588 HICON hWindowIcon)
2589 {
2590 PGUI_CONSOLE_DATA GuiData = This->Data;
2591 HICON hIcon, hIconSm;
2592
2593 if (hWindowIcon == NULL)
2594 {
2595 hIcon = ghDefaultIcon;
2596 hIconSm = ghDefaultIconSm;
2597 }
2598 else
2599 {
2600 hIcon = CopyIcon(hWindowIcon);
2601 hIconSm = CopyIcon(hWindowIcon);
2602 }
2603
2604 if (hIcon == NULL)
2605 {
2606 return FALSE;
2607 }
2608
2609 if (hIcon != GuiData->hIcon)
2610 {
2611 if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon)
2612 {
2613 DestroyIcon(GuiData->hIcon);
2614 }
2615 if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm)
2616 {
2617 DestroyIcon(GuiData->hIconSm);
2618 }
2619
2620 GuiData->hIcon = hIcon;
2621 GuiData->hIconSm = hIconSm;
2622
2623 DPRINT("Set icons in GuiChangeIcon\n");
2624 PostMessageW(GuiData->hWindow, WM_SETICON, ICON_BIG, (LPARAM)GuiData->hIcon);
2625 PostMessageW(GuiData->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)GuiData->hIconSm);
2626 }
2627
2628 return TRUE;
2629 }
2630
2631 static HWND WINAPI
2632 GuiGetConsoleWindowHandle(IN OUT PFRONTEND This)
2633 {
2634 PGUI_CONSOLE_DATA GuiData = This->Data;
2635 return GuiData->hWindow;
2636 }
2637
2638 static VOID WINAPI
2639 GuiGetLargestConsoleWindowSize(IN OUT PFRONTEND This,
2640 PCOORD pSize)
2641 {
2642 PGUI_CONSOLE_DATA GuiData = This->Data;
2643 PCONSOLE_SCREEN_BUFFER ActiveBuffer;
2644 RECT WorkArea;
2645 LONG width, height;
2646 UINT WidthUnit, HeightUnit;
2647
2648 if (!pSize) return;
2649
2650 if (!SystemParametersInfoW(SPI_GETWORKAREA, 0, &WorkArea, 0))
2651 {
2652 DPRINT1("SystemParametersInfoW failed - What to do ??\n");
2653 return;
2654 }
2655
2656 ActiveBuffer = ConDrvGetActiveScreenBuffer(GuiData->Console);
2657 if (ActiveBuffer)
2658 {
2659 GetScreenBufferSizeUnits(ActiveBuffer, GuiData, &WidthUnit, &HeightUnit);
2660 }
2661 else
2662 {
2663 /* Default: text mode */
2664 WidthUnit = GuiData->CharWidth ;
2665 HeightUnit = GuiData->CharHeight;
2666 }
2667
2668 width = WorkArea.right;
2669 height = WorkArea.bottom;
2670
2671 width -= (2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE)));
2672 height -= (2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION));
2673
2674 if (width < 0) width = 0;
2675 if (height < 0) height = 0;
2676
2677 pSize->X = (SHORT)(width / (int)WidthUnit ) /* HACK */ + 2;
2678 pSize->Y = (SHORT)(height / (int)HeightUnit) /* HACK */ + 1;
2679 }
2680
2681 static ULONG WINAPI
2682 GuiGetDisplayMode(IN OUT PFRONTEND This)
2683 {
2684 PGUI_CONSOLE_DATA GuiData = This->Data;
2685 ULONG DisplayMode = 0;
2686
2687 if (GuiData->GuiInfo.FullScreen)
2688 DisplayMode |= CONSOLE_FULLSCREEN_HARDWARE; // CONSOLE_FULLSCREEN
2689 else
2690 DisplayMode |= CONSOLE_WINDOWED;
2691
2692 return DisplayMode;
2693 }
2694
2695 static BOOL WINAPI
2696 GuiSetDisplayMode(IN OUT PFRONTEND This,
2697 ULONG NewMode)
2698 {
2699 PGUI_CONSOLE_DATA GuiData = This->Data;
2700
2701 if (NewMode & ~(CONSOLE_FULLSCREEN_MODE | CONSOLE_WINDOWED_MODE))
2702 return FALSE;
2703
2704 GuiData->GuiInfo.FullScreen = (NewMode & CONSOLE_FULLSCREEN_MODE);
2705 // TODO: Change the display mode
2706 return TRUE;
2707 }
2708
2709 static INT WINAPI
2710 GuiShowMouseCursor(IN OUT PFRONTEND This,
2711 BOOL Show)
2712 {
2713 PGUI_CONSOLE_DATA GuiData = This->Data;
2714
2715 /* Set the reference count */
2716 if (Show) ++GuiData->MouseCursorRefCount;
2717 else --GuiData->MouseCursorRefCount;
2718
2719 /* Effectively show (or hide) the cursor (use special values for (w|l)Param) */
2720 PostMessageW(GuiData->hWindow, WM_SETCURSOR, -1, -1);
2721
2722 return GuiData->MouseCursorRefCount;
2723 }
2724
2725 static BOOL WINAPI
2726 GuiSetMouseCursor(IN OUT PFRONTEND This,
2727 HCURSOR hCursor)
2728 {
2729 PGUI_CONSOLE_DATA GuiData = This->Data;
2730
2731 /*
2732 * Set the cursor's handle. If the given handle is NULL,
2733 * then restore the default cursor.
2734 */
2735 GuiData->hCursor = (hCursor ? hCursor : ghDefaultCursor);
2736
2737 /* Effectively modify the shape of the cursor (use special values for (w|l)Param) */
2738 PostMessageW(GuiData->hWindow, WM_SETCURSOR, -1, -1);
2739
2740 return TRUE;
2741 }
2742
2743 static HMENU WINAPI
2744 GuiMenuControl(IN OUT PFRONTEND This,
2745 UINT cmdIdLow,
2746 UINT cmdIdHigh)
2747 {
2748 PGUI_CONSOLE_DATA GuiData = This->Data;
2749
2750 GuiData->cmdIdLow = cmdIdLow ;
2751 GuiData->cmdIdHigh = cmdIdHigh;
2752
2753 return GetSystemMenu(GuiData->hWindow, FALSE);
2754 }
2755
2756 static BOOL WINAPI
2757 GuiSetMenuClose(IN OUT PFRONTEND This,
2758 BOOL Enable)
2759 {
2760 /*
2761 * NOTE: See http://www.mail-archive.com/harbour@harbour-project.org/msg27509.html
2762 * or http://harbour-devel.1590103.n2.nabble.com/Question-about-hb-gt-win-CtrlHandler-usage-td4670862i20.html
2763 * for more information.
2764 */
2765
2766 PGUI_CONSOLE_DATA GuiData = This->Data;
2767 HMENU hSysMenu = GetSystemMenu(GuiData->hWindow, FALSE);
2768
2769 if (hSysMenu == NULL) return FALSE;
2770
2771 GuiData->IsCloseButtonEnabled = Enable;
2772 EnableMenuItem(hSysMenu, SC_CLOSE, MF_BYCOMMAND | (Enable ? MF_ENABLED : MF_GRAYED));
2773
2774 return TRUE;
2775 }
2776
2777 static FRONTEND_VTBL GuiVtbl =
2778 {
2779 GuiInitFrontEnd,
2780 GuiDeinitFrontEnd,
2781 GuiDrawRegion,
2782 GuiWriteStream,
2783 GuiSetCursorInfo,
2784 GuiSetScreenInfo,
2785 GuiResizeTerminal,
2786 GuiProcessKeyCallback,
2787 GuiRefreshInternalInfo,
2788 GuiChangeTitle,
2789 GuiChangeIcon,
2790 GuiGetConsoleWindowHandle,
2791 GuiGetLargestConsoleWindowSize,
2792 GuiGetDisplayMode,
2793 GuiSetDisplayMode,
2794 GuiShowMouseCursor,
2795 GuiSetMouseCursor,
2796 GuiMenuControl,
2797 GuiSetMenuClose,
2798 };
2799
2800
2801 static BOOL
2802 LoadShellLinkConsoleInfo(IN OUT PCONSOLE_START_INFO ConsoleStartInfo,
2803 IN OUT PCONSOLE_INFO ConsoleInfo)
2804 {
2805 #define PATH_SEPARATOR L'\\'
2806
2807 BOOL RetVal = FALSE;
2808 HRESULT hRes = S_OK;
2809 LPWSTR LinkName = NULL;
2810 SIZE_T Length = 0;
2811
2812 if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
2813 return FALSE;
2814
2815 ConsoleStartInfo->IconPath[0] = L'\0';
2816 ConsoleStartInfo->IconIndex = 0;
2817
2818 /* 1- Find the last path separator if any */
2819 LinkName = wcsrchr(ConsoleStartInfo->ConsoleTitle, PATH_SEPARATOR);
2820 if (LinkName == NULL)
2821 {
2822 LinkName = ConsoleStartInfo->ConsoleTitle;
2823 }
2824 else
2825 {
2826 /* Skip the path separator */
2827 ++LinkName;
2828 }
2829
2830 /* 2- Check for the link extension. The name ".lnk" is considered invalid. */
2831 Length = wcslen(LinkName);
2832 if ( (Length <= 4) || (wcsicmp(LinkName + (Length - 4), L".lnk") != 0) )
2833 return FALSE;
2834
2835 /* 3- It may be a link. Try to retrieve some properties */
2836 hRes = CoInitialize(NULL);
2837 if (SUCCEEDED(hRes))
2838 {
2839 /* Get a pointer to the IShellLink interface */
2840 IShellLinkW* pshl = NULL;
2841 hRes = CoCreateInstance(&CLSID_ShellLink,
2842 NULL,
2843 CLSCTX_INPROC_SERVER,
2844 &IID_IShellLinkW,
2845 (LPVOID*)&pshl);
2846 if (SUCCEEDED(hRes))
2847 {
2848 /* Get a pointer to the IPersistFile interface */
2849 IPersistFile* ppf = NULL;
2850 hRes = IPersistFile_QueryInterface(pshl, &IID_IPersistFile, (LPVOID*)&ppf);
2851 if (SUCCEEDED(hRes))
2852 {
2853 /* Load the shortcut */
2854 hRes = IPersistFile_Load(ppf, ConsoleStartInfo->ConsoleTitle, STGM_READ);
2855 if (SUCCEEDED(hRes))
2856 {
2857 /*
2858 * Finally we can get the properties !
2859 * Update the old ones if needed.
2860 */
2861 INT ShowCmd = 0;
2862 // WORD HotKey = 0;
2863
2864 /* Reset the name of the console with the name of the shortcut */
2865 Length = min(/*Length*/ Length - 4, // 4 == len(".lnk")
2866 sizeof(ConsoleInfo->ConsoleTitle) / sizeof(ConsoleInfo->ConsoleTitle[0]) - 1);
2867 wcsncpy(ConsoleInfo->ConsoleTitle, LinkName, Length);
2868 ConsoleInfo->ConsoleTitle[Length] = L'\0';
2869
2870 /* Get the window showing command */
2871 hRes = IShellLinkW_GetShowCmd(pshl, &ShowCmd);
2872 if (SUCCEEDED(hRes)) ConsoleStartInfo->wShowWindow = (WORD)ShowCmd;
2873
2874 /* Get the hotkey */
2875 // hRes = pshl->GetHotkey(&ShowCmd);
2876 // if (SUCCEEDED(hRes)) ConsoleStartInfo->HotKey = HotKey;
2877
2878 /* Get the icon location, if any */
2879
2880 hRes = IShellLinkW_GetIconLocation(pshl,
2881 ConsoleStartInfo->IconPath,
2882 sizeof(ConsoleStartInfo->IconPath)/sizeof(ConsoleStartInfo->IconPath[0]) - 1, // == MAX_PATH
2883 &ConsoleStartInfo->IconIndex);
2884 if (!SUCCEEDED(hRes))
2885 {
2886 ConsoleStartInfo->IconPath[0] = L'\0';
2887 ConsoleStartInfo->IconIndex = 0;
2888 }
2889
2890 // FIXME: Since we still don't load console properties from the shortcut,
2891 // return false. When this will be done, we will return true instead.
2892 RetVal = FALSE;
2893 }
2894 IPersistFile_Release(ppf);
2895 }
2896 IShellLinkW_Release(pshl);
2897 }
2898 }
2899 CoUninitialize();
2900
2901 return RetVal;
2902 }
2903
2904 NTSTATUS NTAPI
2905 GuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
2906 IN OUT PCONSOLE_INFO ConsoleInfo,
2907 IN OUT PVOID ExtraConsoleInfo,
2908 IN ULONG ProcessId)
2909 {
2910 PCONSOLE_START_INFO ConsoleStartInfo = ExtraConsoleInfo;
2911 PGUI_INIT_INFO GuiInitInfo;
2912
2913 if (FrontEnd == NULL || ConsoleInfo == NULL || ConsoleStartInfo == NULL)
2914 return STATUS_INVALID_PARAMETER;
2915
2916 /* Initialize GUI terminal emulator common functionalities */
2917 if (!GuiInit()) return STATUS_UNSUCCESSFUL;
2918
2919 /*
2920 * Load per-application terminal settings.
2921 *
2922 * Check whether the process creating the console was launched via
2923 * a shell-link. ConsoleInfo->ConsoleTitle may be updated with the
2924 * name of the shortcut, and ConsoleStartInfo->Icon[Path|Index] too.
2925 */
2926 if (ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME)
2927 {
2928 if (!LoadShellLinkConsoleInfo(ConsoleStartInfo, ConsoleInfo))
2929 {
2930 ConsoleStartInfo->dwStartupFlags &= ~STARTF_TITLEISLINKNAME;
2931 }
2932 }
2933
2934 /*
2935 * Initialize a private initialization info structure for later use.
2936 * It must be freed by a call to GuiUnloadFrontEnd or GuiInitFrontEnd.
2937 */
2938 GuiInitInfo = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(GUI_INIT_INFO));
2939 if (GuiInitInfo == NULL) return STATUS_NO_MEMORY;
2940
2941 // HACK: We suppose that the pointers will be valid in GuiInitFrontEnd...
2942 GuiInitInfo->ConsoleInfo = ConsoleInfo;
2943 GuiInitInfo->ConsoleStartInfo = ConsoleStartInfo;
2944 GuiInitInfo->ProcessId = ProcessId;
2945
2946 /* Finally, initialize the frontend structure */
2947 FrontEnd->Vtbl = &GuiVtbl;
2948 FrontEnd->Data = NULL;
2949 FrontEnd->OldData = GuiInitInfo;
2950
2951 return STATUS_SUCCESS;
2952 }
2953
2954 NTSTATUS NTAPI
2955 GuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd)
2956 {
2957 if (FrontEnd == NULL) return STATUS_INVALID_PARAMETER;
2958
2959 if (FrontEnd->Data) GuiDeinitFrontEnd(FrontEnd);
2960 if (FrontEnd->OldData) ConsoleFreeHeap(FrontEnd->OldData);
2961
2962 return STATUS_SUCCESS;
2963 }
2964
2965 /* EOF */