e83c024df979c5fa370f421861b57e5bc6ac4be0
[reactos.git] / reactos / win32ss / user / winsrv / consrv / frontends / gui / conwnd.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: frontends/gui/conwnd.c
5 * PURPOSE: GUI Console Window Class
6 * PROGRAMMERS: Gé van Geldorp
7 * Johannes Anderwald
8 * Jeffrey Morlan
9 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
10 */
11
12 /* INCLUDES *******************************************************************/
13
14 #include <consrv.h>
15
16 #include <windowsx.h>
17
18 #define NDEBUG
19 #include <debug.h>
20
21 #include "guiterm.h"
22 #include "conwnd.h"
23 #include "resource.h"
24
25 /* GLOBALS ********************************************************************/
26
27 // #define PM_CREATE_CONSOLE (WM_APP + 1)
28 // #define PM_DESTROY_CONSOLE (WM_APP + 2)
29
30 // See guiterm.c
31 #define CONGUI_MIN_WIDTH 10
32 #define CONGUI_MIN_HEIGHT 10
33 #define CONGUI_UPDATE_TIME 0
34 #define CONGUI_UPDATE_TIMER 1
35
36 #define CURSOR_BLINK_TIME 500
37
38
39 /**************************************************************\
40 \** Define the Console Leader Process for the console window **/
41 #define GWLP_CONWND_ALLOC (2 * sizeof(LONG_PTR))
42 #define GWLP_CONSOLE_LEADER_PID 0
43 #define GWLP_CONSOLE_LEADER_TID 4
44
45 VOID
46 SetConWndConsoleLeaderCID(IN PGUI_CONSOLE_DATA GuiData)
47 {
48 PCONSOLE_PROCESS_DATA ProcessData;
49 CLIENT_ID ConsoleLeaderCID;
50
51 ProcessData = CONTAINING_RECORD(GuiData->Console->ProcessList.Blink,
52 CONSOLE_PROCESS_DATA,
53 ConsoleLink);
54 ConsoleLeaderCID = ProcessData->Process->ClientId;
55 SetWindowLongPtrW(GuiData->hWindow, GWLP_CONSOLE_LEADER_PID,
56 (LONG_PTR)(ConsoleLeaderCID.UniqueProcess));
57 SetWindowLongPtrW(GuiData->hWindow, GWLP_CONSOLE_LEADER_TID,
58 (LONG_PTR)(ConsoleLeaderCID.UniqueThread));
59 }
60 /**************************************************************/
61
62 HICON ghDefaultIcon = NULL;
63 HICON ghDefaultIconSm = NULL;
64 HCURSOR ghDefaultCursor = NULL;
65
66 typedef struct _GUICONSOLE_MENUITEM
67 {
68 UINT uID;
69 const struct _GUICONSOLE_MENUITEM *SubMenu;
70 WORD wCmdID;
71 } GUICONSOLE_MENUITEM, *PGUICONSOLE_MENUITEM;
72
73 static const GUICONSOLE_MENUITEM GuiConsoleEditMenuItems[] =
74 {
75 { IDS_MARK, NULL, ID_SYSTEM_EDIT_MARK },
76 { IDS_COPY, NULL, ID_SYSTEM_EDIT_COPY },
77 { IDS_PASTE, NULL, ID_SYSTEM_EDIT_PASTE },
78 { IDS_SELECTALL, NULL, ID_SYSTEM_EDIT_SELECTALL },
79 { IDS_SCROLL, NULL, ID_SYSTEM_EDIT_SCROLL },
80 { IDS_FIND, NULL, ID_SYSTEM_EDIT_FIND },
81
82 { 0, NULL, 0 } /* End of list */
83 };
84
85 static const GUICONSOLE_MENUITEM GuiConsoleMainMenuItems[] =
86 {
87 { IDS_EDIT, GuiConsoleEditMenuItems, 0 },
88 { IDS_DEFAULTS, NULL, ID_SYSTEM_DEFAULTS },
89 { IDS_PROPERTIES, NULL, ID_SYSTEM_PROPERTIES },
90
91 { 0, NULL, 0 } /* End of list */
92 };
93
94 /*
95 * Default 16-color palette for foreground and background
96 * (corresponding flags in comments).
97 */
98 const COLORREF s_Colors[16] =
99 {
100 RGB(0, 0, 0), // (Black)
101 RGB(0, 0, 128), // BLUE
102 RGB(0, 128, 0), // GREEN
103 RGB(0, 128, 128), // BLUE | GREEN
104 RGB(128, 0, 0), // RED
105 RGB(128, 0, 128), // BLUE | RED
106 RGB(128, 128, 0), // GREEN | RED
107 RGB(192, 192, 192), // BLUE | GREEN | RED
108
109 RGB(128, 128, 128), // (Grey) INTENSITY
110 RGB(0, 0, 255), // BLUE | INTENSITY
111 RGB(0, 255, 0), // GREEN | INTENSITY
112 RGB(0, 255, 255), // BLUE | GREEN | INTENSITY
113 RGB(255, 0, 0), // RED | INTENSITY
114 RGB(255, 0, 255), // BLUE | RED | INTENSITY
115 RGB(255, 255, 0), // GREEN | RED | INTENSITY
116 RGB(255, 255, 255) // BLUE | GREEN | RED | INTENSITY
117 };
118
119 /* FUNCTIONS ******************************************************************/
120
121 static LRESULT CALLBACK
122 ConWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
123
124 BOOLEAN
125 RegisterConWndClass(IN HINSTANCE hInstance)
126 {
127 WNDCLASSEXW WndClass;
128 ATOM WndClassAtom;
129
130 ghDefaultIcon = LoadImageW(hInstance,
131 MAKEINTRESOURCEW(IDI_TERMINAL),
132 IMAGE_ICON,
133 GetSystemMetrics(SM_CXICON),
134 GetSystemMetrics(SM_CYICON),
135 LR_SHARED);
136 ghDefaultIconSm = LoadImageW(hInstance,
137 MAKEINTRESOURCEW(IDI_TERMINAL),
138 IMAGE_ICON,
139 GetSystemMetrics(SM_CXSMICON),
140 GetSystemMetrics(SM_CYSMICON),
141 LR_SHARED);
142 ghDefaultCursor = LoadCursorW(NULL, IDC_ARROW);
143
144 WndClass.cbSize = sizeof(WNDCLASSEXW);
145 WndClass.lpszClassName = GUI_CONWND_CLASS;
146 WndClass.lpfnWndProc = ConWndProc;
147 WndClass.style = CS_DBLCLKS /* | CS_HREDRAW | CS_VREDRAW */;
148 WndClass.hInstance = hInstance;
149 WndClass.hIcon = ghDefaultIcon;
150 WndClass.hIconSm = ghDefaultIconSm;
151 WndClass.hCursor = ghDefaultCursor;
152 WndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); // The color of a terminal when it is switched off.
153 WndClass.lpszMenuName = NULL;
154 WndClass.cbClsExtra = 0;
155 WndClass.cbWndExtra = GWLP_CONWND_ALLOC;
156
157 WndClassAtom = RegisterClassExW(&WndClass);
158 if (WndClassAtom == 0)
159 {
160 DPRINT1("Failed to register GUI console class\n");
161 }
162 else
163 {
164 NtUserConsoleControl(GuiConsoleWndClassAtom, &WndClassAtom, sizeof(ATOM));
165 }
166
167 return (WndClassAtom != 0);
168 }
169
170 BOOLEAN
171 UnRegisterConWndClass(HINSTANCE hInstance)
172 {
173 return !!UnregisterClassW(GUI_CONWND_CLASS, hInstance);
174 }
175
176
177
178 static VOID
179 GetScreenBufferSizeUnits(IN PCONSOLE_SCREEN_BUFFER Buffer,
180 IN PGUI_CONSOLE_DATA GuiData,
181 OUT PUINT WidthUnit,
182 OUT PUINT HeightUnit)
183 {
184 if (Buffer == NULL || GuiData == NULL ||
185 WidthUnit == NULL || HeightUnit == NULL)
186 {
187 return;
188 }
189
190 if (GetType(Buffer) == TEXTMODE_BUFFER)
191 {
192 *WidthUnit = GuiData->CharWidth ;
193 *HeightUnit = GuiData->CharHeight;
194 }
195 else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
196 {
197 *WidthUnit = 1;
198 *HeightUnit = 1;
199 }
200 }
201
202 static VOID
203 GuiConsoleAppendMenuItems(HMENU hMenu,
204 const GUICONSOLE_MENUITEM *Items)
205 {
206 UINT i = 0;
207 WCHAR szMenuString[255];
208 HMENU hSubMenu;
209
210 do
211 {
212 if (Items[i].uID != (UINT)-1)
213 {
214 if (LoadStringW(ConSrvDllInstance,
215 Items[i].uID,
216 szMenuString,
217 sizeof(szMenuString) / sizeof(szMenuString[0])) > 0)
218 {
219 if (Items[i].SubMenu != NULL)
220 {
221 hSubMenu = CreatePopupMenu();
222 if (hSubMenu != NULL)
223 {
224 GuiConsoleAppendMenuItems(hSubMenu,
225 Items[i].SubMenu);
226
227 if (!AppendMenuW(hMenu,
228 MF_STRING | MF_POPUP,
229 (UINT_PTR)hSubMenu,
230 szMenuString))
231 {
232 DestroyMenu(hSubMenu);
233 }
234 }
235 }
236 else
237 {
238 AppendMenuW(hMenu,
239 MF_STRING,
240 Items[i].wCmdID,
241 szMenuString);
242 }
243 }
244 }
245 else
246 {
247 AppendMenuW(hMenu,
248 MF_SEPARATOR,
249 0,
250 NULL);
251 }
252 i++;
253 } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 0));
254 }
255
256 static VOID
257 GuiConsoleCreateSysMenu(HWND hWnd)
258 {
259 HMENU hMenu = GetSystemMenu(hWnd, FALSE);
260 if (hMenu != NULL)
261 {
262 GuiConsoleAppendMenuItems(hMenu, GuiConsoleMainMenuItems);
263 DrawMenuBar(hWnd);
264 }
265 }
266
267 static VOID
268 GuiSendMenuEvent(PCONSOLE Console, UINT CmdId)
269 {
270 INPUT_RECORD er;
271
272 er.EventType = MENU_EVENT;
273 er.Event.MenuEvent.dwCommandId = CmdId;
274
275 DPRINT("Menu item ID: %d\n", CmdId);
276 ConioProcessInputEvent(Console, &er);
277 }
278
279 static VOID
280 GuiConsoleCopy(PGUI_CONSOLE_DATA GuiData);
281 static VOID
282 GuiConsolePaste(PGUI_CONSOLE_DATA GuiData);
283 static VOID
284 GuiConsoleUpdateSelection(PCONSOLE Console, PCOORD coord);
285 static VOID
286 GuiConsoleResizeWindow(PGUI_CONSOLE_DATA GuiData, DWORD WidthUnit, DWORD HeightUnit);
287
288 static LRESULT
289 GuiConsoleHandleSysMenuCommand(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam)
290 {
291 LRESULT Ret = TRUE;
292 PCONSOLE Console = GuiData->Console;
293 PCONSOLE_SCREEN_BUFFER ActiveBuffer;
294
295 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
296 {
297 Ret = FALSE;
298 goto Quit;
299 }
300 ActiveBuffer = GuiData->ActiveBuffer;
301
302 /*
303 * In case the selected menu item belongs to the user-reserved menu id range,
304 * send to him a menu event and return directly. The user must handle those
305 * reserved menu commands...
306 */
307 if (GuiData->CmdIdLow <= (UINT)wParam && (UINT)wParam <= GuiData->CmdIdHigh)
308 {
309 GuiSendMenuEvent(Console, (UINT)wParam);
310 goto Unlock_Quit;
311 }
312
313 /* ... otherwise, perform actions. */
314 switch (wParam)
315 {
316 case ID_SYSTEM_EDIT_MARK:
317 {
318 /* Clear the old selection */
319 // GuiConsoleUpdateSelection(Console, NULL);
320 Console->Selection.dwFlags = CONSOLE_NO_SELECTION;
321
322 /* Restart a new selection */
323 Console->dwSelectionCursor.X = ActiveBuffer->ViewOrigin.X;
324 Console->dwSelectionCursor.Y = ActiveBuffer->ViewOrigin.Y;
325 Console->Selection.dwSelectionAnchor = Console->dwSelectionCursor;
326 GuiConsoleUpdateSelection(Console, &Console->Selection.dwSelectionAnchor);
327
328 break;
329 }
330
331 case ID_SYSTEM_EDIT_COPY:
332 GuiConsoleCopy(GuiData);
333 break;
334
335 case ID_SYSTEM_EDIT_PASTE:
336 GuiConsolePaste(GuiData);
337 break;
338
339 case ID_SYSTEM_EDIT_SELECTALL:
340 {
341 /* Clear the old selection */
342 // GuiConsoleUpdateSelection(Console, NULL);
343 Console->Selection.dwFlags = CONSOLE_NO_SELECTION;
344
345 /*
346 * The selection area extends to the whole screen buffer's width.
347 */
348 Console->Selection.dwSelectionAnchor.X = 0;
349 Console->Selection.dwSelectionAnchor.Y = 0;
350 Console->dwSelectionCursor.X = ActiveBuffer->ScreenBufferSize.X - 1;
351
352 /*
353 * Determine whether the selection must extend to just some part
354 * (for text-mode screen buffers) or to all of the screen buffer's
355 * height (for graphics ones).
356 */
357 if (GetType(ActiveBuffer) == TEXTMODE_BUFFER)
358 {
359 /*
360 * We select all the characters from the first line
361 * to the line where the cursor is positioned.
362 */
363 Console->dwSelectionCursor.Y = ActiveBuffer->CursorPosition.Y;
364 }
365 else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */
366 {
367 /*
368 * We select all the screen buffer area.
369 */
370 Console->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1;
371 }
372
373 /* Restart a new selection */
374 Console->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION;
375 GuiConsoleUpdateSelection(Console, &Console->dwSelectionCursor);
376
377 break;
378 }
379
380 case ID_SYSTEM_EDIT_SCROLL:
381 DPRINT1("Scrolling is not handled yet\n");
382 break;
383
384 case ID_SYSTEM_EDIT_FIND:
385 DPRINT1("Finding is not handled yet\n");
386 break;
387
388 case ID_SYSTEM_DEFAULTS:
389 GuiConsoleShowConsoleProperties(GuiData, TRUE);
390 break;
391
392 case ID_SYSTEM_PROPERTIES:
393 GuiConsoleShowConsoleProperties(GuiData, FALSE);
394 break;
395
396 default:
397 Ret = FALSE;
398 break;
399 }
400
401 Unlock_Quit:
402 LeaveCriticalSection(&Console->Lock);
403 Quit:
404 if (!Ret)
405 Ret = DefWindowProcW(GuiData->hWindow, WM_SYSCOMMAND, wParam, lParam);
406
407 return Ret;
408 }
409
410 static PGUI_CONSOLE_DATA
411 GuiGetGuiData(HWND hWnd)
412 {
413 /* This function ensures that the console pointer is not NULL */
414 PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
415 return ( ((GuiData == NULL) || (GuiData->hWindow == hWnd && GuiData->Console != NULL)) ? GuiData : NULL );
416 }
417
418 static VOID
419 GuiConsoleResizeWindow(PGUI_CONSOLE_DATA GuiData, DWORD WidthUnit, DWORD HeightUnit)
420 {
421 PCONSOLE_SCREEN_BUFFER Buff = GuiData->ActiveBuffer;
422 SCROLLINFO sInfo;
423
424 DWORD Width, Height;
425
426 Width = Buff->ViewSize.X * WidthUnit +
427 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
428 Height = Buff->ViewSize.Y * HeightUnit +
429 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
430
431 /* Set scrollbar sizes */
432 sInfo.cbSize = sizeof(SCROLLINFO);
433 sInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
434 sInfo.nMin = 0;
435 if (Buff->ScreenBufferSize.Y > Buff->ViewSize.Y)
436 {
437 sInfo.nMax = Buff->ScreenBufferSize.Y - 1;
438 sInfo.nPage = Buff->ViewSize.Y;
439 sInfo.nPos = Buff->ViewOrigin.Y;
440 SetScrollInfo(GuiData->hWindow, SB_VERT, &sInfo, TRUE);
441 Width += GetSystemMetrics(SM_CXVSCROLL);
442 ShowScrollBar(GuiData->hWindow, SB_VERT, TRUE);
443 }
444 else
445 {
446 ShowScrollBar(GuiData->hWindow, SB_VERT, FALSE);
447 }
448
449 if (Buff->ScreenBufferSize.X > Buff->ViewSize.X)
450 {
451 sInfo.nMax = Buff->ScreenBufferSize.X - 1;
452 sInfo.nPage = Buff->ViewSize.X;
453 sInfo.nPos = Buff->ViewOrigin.X;
454 SetScrollInfo(GuiData->hWindow, SB_HORZ, &sInfo, TRUE);
455 Height += GetSystemMetrics(SM_CYHSCROLL);
456 ShowScrollBar(GuiData->hWindow, SB_HORZ, TRUE);
457 }
458 else
459 {
460 ShowScrollBar(GuiData->hWindow, SB_HORZ, FALSE);
461 }
462
463 /* Resize the window */
464 SetWindowPos(GuiData->hWindow, NULL, 0, 0, Width, Height,
465 SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOCOPYBITS);
466 // NOTE: The SWP_NOCOPYBITS flag can be replaced by a subsequent call
467 // to: InvalidateRect(GuiData->hWindow, NULL, TRUE);
468 }
469
470 static BOOL
471 GuiConsoleHandleNcCreate(HWND hWnd, LPCREATESTRUCTW Create)
472 {
473 PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)Create->lpCreateParams;
474 PCONSOLE Console;
475 HDC hDC;
476 HFONT OldFont;
477 TEXTMETRICW Metrics;
478 SIZE CharSize;
479
480 DPRINT("GuiConsoleHandleNcCreate\n");
481
482 if (NULL == GuiData)
483 {
484 DPRINT1("GuiConsoleNcCreate: No GUI data\n");
485 return FALSE;
486 }
487
488 Console = GuiData->Console;
489
490 GuiData->hWindow = hWnd;
491
492 GuiData->Font = CreateFontW(LOWORD(GuiData->GuiInfo.FontSize),
493 0, // HIWORD(GuiData->GuiInfo.FontSize),
494 0,
495 TA_BASELINE,
496 GuiData->GuiInfo.FontWeight,
497 FALSE,
498 FALSE,
499 FALSE,
500 OEM_CHARSET,
501 OUT_DEFAULT_PRECIS,
502 CLIP_DEFAULT_PRECIS,
503 NONANTIALIASED_QUALITY,
504 FIXED_PITCH | GuiData->GuiInfo.FontFamily /* FF_DONTCARE */,
505 GuiData->GuiInfo.FaceName);
506
507 if (NULL == GuiData->Font)
508 {
509 DPRINT1("GuiConsoleNcCreate: CreateFont failed\n");
510 GuiData->hWindow = NULL;
511 SetEvent(GuiData->hGuiInitEvent);
512 return FALSE;
513 }
514 hDC = GetDC(GuiData->hWindow);
515 if (NULL == hDC)
516 {
517 DPRINT1("GuiConsoleNcCreate: GetDC failed\n");
518 DeleteObject(GuiData->Font);
519 GuiData->hWindow = NULL;
520 SetEvent(GuiData->hGuiInitEvent);
521 return FALSE;
522 }
523 OldFont = SelectObject(hDC, GuiData->Font);
524 if (NULL == OldFont)
525 {
526 DPRINT1("GuiConsoleNcCreate: SelectObject failed\n");
527 ReleaseDC(GuiData->hWindow, hDC);
528 DeleteObject(GuiData->Font);
529 GuiData->hWindow = NULL;
530 SetEvent(GuiData->hGuiInitEvent);
531 return FALSE;
532 }
533 if (!GetTextMetricsW(hDC, &Metrics))
534 {
535 DPRINT1("GuiConsoleNcCreate: GetTextMetrics failed\n");
536 SelectObject(hDC, OldFont);
537 ReleaseDC(GuiData->hWindow, hDC);
538 DeleteObject(GuiData->Font);
539 GuiData->hWindow = NULL;
540 SetEvent(GuiData->hGuiInitEvent);
541 return FALSE;
542 }
543 GuiData->CharWidth = Metrics.tmMaxCharWidth;
544 GuiData->CharHeight = Metrics.tmHeight + Metrics.tmExternalLeading;
545
546 /* Measure real char width more precisely if possible. */
547 if (GetTextExtentPoint32W(hDC, L"R", 1, &CharSize))
548 GuiData->CharWidth = CharSize.cx;
549
550 SelectObject(hDC, OldFont);
551
552 ReleaseDC(GuiData->hWindow, hDC);
553
554 /* Initialize the terminal framebuffer */
555 GuiData->hMemDC = CreateCompatibleDC(NULL);
556 GuiData->hBitmap = NULL;
557 GuiData->hSysPalette = NULL; /* Original system palette */
558
559 // FIXME: Keep these instructions here ? ///////////////////////////////////
560 Console->ActiveBuffer->CursorBlinkOn = TRUE;
561 Console->ActiveBuffer->ForceCursorOff = FALSE;
562 ////////////////////////////////////////////////////////////////////////////
563
564 SetWindowLongPtrW(GuiData->hWindow, GWLP_USERDATA, (DWORD_PTR)GuiData);
565
566 SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL);
567 GuiConsoleCreateSysMenu(GuiData->hWindow);
568
569 DPRINT("GuiConsoleHandleNcCreate - setting start event\n");
570 SetEvent(GuiData->hGuiInitEvent);
571
572 return (BOOL)DefWindowProcW(GuiData->hWindow, WM_NCCREATE, 0, (LPARAM)Create);
573 }
574
575 static VOID
576 SmallRectToRect(PGUI_CONSOLE_DATA GuiData, PRECT Rect, PSMALL_RECT SmallRect)
577 {
578 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
579 UINT WidthUnit, HeightUnit;
580
581 GetScreenBufferSizeUnits(Buffer, GuiData, &WidthUnit, &HeightUnit);
582
583 Rect->left = (SmallRect->Left - Buffer->ViewOrigin.X) * WidthUnit ;
584 Rect->top = (SmallRect->Top - Buffer->ViewOrigin.Y) * HeightUnit;
585 Rect->right = (SmallRect->Right + 1 - Buffer->ViewOrigin.X) * WidthUnit ;
586 Rect->bottom = (SmallRect->Bottom + 1 - Buffer->ViewOrigin.Y) * HeightUnit;
587 }
588
589 static VOID
590 GuiConsoleUpdateSelection(PCONSOLE Console, PCOORD coord)
591 {
592 PGUI_CONSOLE_DATA GuiData = Console->TermIFace.Data;
593 RECT oldRect;
594
595 SmallRectToRect(GuiData, &oldRect, &Console->Selection.srSelection);
596
597 if (coord != NULL)
598 {
599 RECT newRect;
600 SMALL_RECT rc;
601
602 /* Exchange left/top with right/bottom if required */
603 rc.Left = min(Console->Selection.dwSelectionAnchor.X, coord->X);
604 rc.Top = min(Console->Selection.dwSelectionAnchor.Y, coord->Y);
605 rc.Right = max(Console->Selection.dwSelectionAnchor.X, coord->X);
606 rc.Bottom = max(Console->Selection.dwSelectionAnchor.Y, coord->Y);
607
608 SmallRectToRect(GuiData, &newRect, &rc);
609
610 if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
611 {
612 if (memcmp(&rc, &Console->Selection.srSelection, sizeof(SMALL_RECT)) != 0)
613 {
614 HRGN rgn1, rgn2;
615
616 /* Calculate the region that needs to be updated */
617 if ((rgn1 = CreateRectRgnIndirect(&oldRect)))
618 {
619 if ((rgn2 = CreateRectRgnIndirect(&newRect)))
620 {
621 if (CombineRgn(rgn1, rgn2, rgn1, RGN_XOR) != ERROR)
622 {
623 InvalidateRgn(GuiData->hWindow, rgn1, FALSE);
624 }
625 DeleteObject(rgn2);
626 }
627 DeleteObject(rgn1);
628 }
629 }
630 }
631 else
632 {
633 InvalidateRect(GuiData->hWindow, &newRect, FALSE);
634 }
635
636 Console->Selection.dwFlags |= CONSOLE_SELECTION_NOT_EMPTY;
637 Console->Selection.srSelection = rc;
638 Console->dwSelectionCursor = *coord;
639
640 if ((Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) == 0)
641 {
642 LPWSTR SelectionType, WindowTitle = NULL;
643 SIZE_T Length = 0;
644
645 /* Clear the old selection */
646 if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
647 {
648 InvalidateRect(GuiData->hWindow, &oldRect, FALSE);
649 }
650
651 if (Console->Selection.dwFlags & CONSOLE_MOUSE_SELECTION)
652 {
653 SelectionType = L"Selection - ";
654 }
655 else
656 {
657 SelectionType = L"Mark - ";
658 }
659
660 Length = Console->Title.Length + wcslen(SelectionType) + 1;
661 WindowTitle = ConsoleAllocHeap(0, Length * sizeof(WCHAR));
662 wcscpy(WindowTitle, SelectionType);
663 wcscat(WindowTitle, Console->Title.Buffer);
664 SetWindowText(GuiData->hWindow, WindowTitle);
665 ConsoleFreeHeap(WindowTitle);
666
667 Console->Selection.dwFlags |= CONSOLE_SELECTION_IN_PROGRESS;
668 ConioPause(Console, PAUSED_FROM_SELECTION);
669 }
670 }
671 else
672 {
673 /* Clear the selection */
674 if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
675 {
676 InvalidateRect(GuiData->hWindow, &oldRect, FALSE);
677 }
678
679 Console->Selection.dwFlags = CONSOLE_NO_SELECTION;
680 ConioUnpause(Console, PAUSED_FROM_SELECTION);
681
682 SetWindowText(GuiData->hWindow, Console->Title.Buffer);
683 }
684 }
685
686
687 VOID
688 GuiPaintTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer,
689 PGUI_CONSOLE_DATA GuiData,
690 PRECT rcView,
691 PRECT rcFramebuffer);
692 VOID
693 GuiPaintGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer,
694 PGUI_CONSOLE_DATA GuiData,
695 PRECT rcView,
696 PRECT rcFramebuffer);
697
698 static VOID
699 GuiConsoleHandlePaint(PGUI_CONSOLE_DATA GuiData)
700 {
701 BOOL Success = TRUE;
702 PCONSOLE Console = GuiData->Console;
703 PCONSOLE_SCREEN_BUFFER ActiveBuffer;
704 PAINTSTRUCT ps;
705 RECT rcPaint;
706
707 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
708 {
709 Success = FALSE;
710 goto Quit;
711 }
712 ActiveBuffer = GuiData->ActiveBuffer;
713
714 BeginPaint(GuiData->hWindow, &ps);
715 if (ps.hdc != NULL &&
716 ps.rcPaint.left < ps.rcPaint.right &&
717 ps.rcPaint.top < ps.rcPaint.bottom)
718 {
719 EnterCriticalSection(&GuiData->Lock);
720
721 /* Compose the current screen-buffer on-memory */
722 if (GetType(ActiveBuffer) == TEXTMODE_BUFFER)
723 {
724 GuiPaintTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)ActiveBuffer,
725 GuiData, &ps.rcPaint, &rcPaint);
726 }
727 else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */
728 {
729 GuiPaintGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)ActiveBuffer,
730 GuiData, &ps.rcPaint, &rcPaint);
731 }
732
733 /* Send it to screen */
734 BitBlt(ps.hdc,
735 ps.rcPaint.left,
736 ps.rcPaint.top,
737 rcPaint.right - rcPaint.left,
738 rcPaint.bottom - rcPaint.top,
739 GuiData->hMemDC,
740 rcPaint.left,
741 rcPaint.top,
742 SRCCOPY);
743
744 if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
745 {
746 SmallRectToRect(GuiData, &rcPaint, &Console->Selection.srSelection);
747
748 /* Invert the selection */
749 if (IntersectRect(&rcPaint, &ps.rcPaint, &rcPaint))
750 {
751 InvertRect(ps.hdc, &rcPaint);
752 }
753 }
754
755 LeaveCriticalSection(&GuiData->Lock);
756 }
757 EndPaint(GuiData->hWindow, &ps);
758
759 Quit:
760 if (Success)
761 LeaveCriticalSection(&Console->Lock);
762 else
763 DefWindowProcW(GuiData->hWindow, WM_PAINT, 0, 0);
764
765 return;
766 }
767
768 static BOOL
769 IsSystemKey(WORD VirtualKeyCode)
770 {
771 switch (VirtualKeyCode)
772 {
773 /* From MSDN, "Virtual-Key Codes" */
774 case VK_RETURN:
775 case VK_SHIFT:
776 case VK_CONTROL:
777 case VK_MENU:
778 case VK_PAUSE:
779 case VK_CAPITAL:
780 case VK_ESCAPE:
781 case VK_LWIN:
782 case VK_RWIN:
783 case VK_NUMLOCK:
784 case VK_SCROLL:
785 return TRUE;
786 default:
787 return FALSE;
788 }
789 }
790
791 static VOID
792 GuiConsoleHandleKey(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam)
793 {
794 PCONSOLE Console = GuiData->Console;
795 PCONSOLE_SCREEN_BUFFER ActiveBuffer;
796
797 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
798
799 ActiveBuffer = GuiData->ActiveBuffer;
800
801 if (Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS)
802 {
803 WORD VirtualKeyCode = LOWORD(wParam);
804
805 if (msg != WM_KEYDOWN) goto Quit;
806
807 if (VirtualKeyCode == VK_RETURN)
808 {
809 /* Copy (and clear) selection if ENTER is pressed */
810 GuiConsoleCopy(GuiData);
811 goto Quit;
812 }
813 else if ( VirtualKeyCode == VK_ESCAPE ||
814 (VirtualKeyCode == 'C' && GetKeyState(VK_CONTROL) & 0x8000) )
815 {
816 /* Cancel selection if ESC or Ctrl-C are pressed */
817 GuiConsoleUpdateSelection(Console, NULL);
818 goto Quit;
819 }
820
821 if ((Console->Selection.dwFlags & CONSOLE_MOUSE_SELECTION) == 0)
822 {
823 /* Keyboard selection mode */
824 BOOL Interpreted = FALSE;
825 BOOL MajPressed = (GetKeyState(VK_SHIFT) & 0x8000);
826
827 switch (VirtualKeyCode)
828 {
829 case VK_LEFT:
830 {
831 Interpreted = TRUE;
832 if (Console->dwSelectionCursor.X > 0)
833 Console->dwSelectionCursor.X--;
834
835 break;
836 }
837
838 case VK_RIGHT:
839 {
840 Interpreted = TRUE;
841 if (Console->dwSelectionCursor.X < ActiveBuffer->ScreenBufferSize.X - 1)
842 Console->dwSelectionCursor.X++;
843
844 break;
845 }
846
847 case VK_UP:
848 {
849 Interpreted = TRUE;
850 if (Console->dwSelectionCursor.Y > 0)
851 Console->dwSelectionCursor.Y--;
852
853 break;
854 }
855
856 case VK_DOWN:
857 {
858 Interpreted = TRUE;
859 if (Console->dwSelectionCursor.Y < ActiveBuffer->ScreenBufferSize.Y - 1)
860 Console->dwSelectionCursor.Y++;
861
862 break;
863 }
864
865 case VK_HOME:
866 {
867 Interpreted = TRUE;
868 Console->dwSelectionCursor.X = 0;
869 Console->dwSelectionCursor.Y = 0;
870 break;
871 }
872
873 case VK_END:
874 {
875 Interpreted = TRUE;
876 Console->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1;
877 break;
878 }
879
880 case VK_PRIOR:
881 {
882 Interpreted = TRUE;
883 Console->dwSelectionCursor.Y -= ActiveBuffer->ViewSize.Y;
884 if (Console->dwSelectionCursor.Y < 0)
885 Console->dwSelectionCursor.Y = 0;
886
887 break;
888 }
889
890 case VK_NEXT:
891 {
892 Interpreted = TRUE;
893 Console->dwSelectionCursor.Y += ActiveBuffer->ViewSize.Y;
894 if (Console->dwSelectionCursor.Y >= ActiveBuffer->ScreenBufferSize.Y)
895 Console->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1;
896
897 break;
898 }
899
900 default:
901 break;
902 }
903
904 if (Interpreted)
905 {
906 if (!MajPressed)
907 Console->Selection.dwSelectionAnchor = Console->dwSelectionCursor;
908
909 GuiConsoleUpdateSelection(Console, &Console->dwSelectionCursor);
910 }
911 else if (!IsSystemKey(VirtualKeyCode))
912 {
913 /* Emit an error beep sound */
914 SendNotifyMessage(GuiData->hWindow, PM_CONSOLE_BEEP, 0, 0);
915 }
916
917 goto Quit;
918 }
919 else
920 {
921 /* Mouse selection mode */
922
923 if (!IsSystemKey(VirtualKeyCode))
924 {
925 /* Clear the selection and send the key into the input buffer */
926 GuiConsoleUpdateSelection(Console, NULL);
927 }
928 else
929 {
930 goto Quit;
931 }
932 }
933 }
934
935 if ((Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) == 0)
936 {
937 MSG Message;
938
939 Message.hwnd = GuiData->hWindow;
940 Message.message = msg;
941 Message.wParam = wParam;
942 Message.lParam = lParam;
943
944 ConioProcessKey(Console, &Message);
945 }
946
947 Quit:
948 LeaveCriticalSection(&Console->Lock);
949 }
950
951
952 // FIXME: Remove after fixing GuiConsoleHandleTimer
953 VOID
954 GuiInvalidateCell(IN OUT PFRONTEND This, SHORT x, SHORT y);
955
956 static VOID
957 GuiConsoleHandleTimer(PGUI_CONSOLE_DATA GuiData)
958 {
959 PCONSOLE Console = GuiData->Console;
960 PCONSOLE_SCREEN_BUFFER Buff;
961
962 SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CURSOR_BLINK_TIME, NULL);
963
964 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
965
966 Buff = GuiData->ActiveBuffer;
967
968 if (GetType(Buff) == TEXTMODE_BUFFER)
969 {
970 GuiInvalidateCell(&Console->TermIFace, Buff->CursorPosition.X, Buff->CursorPosition.Y);
971 Buff->CursorBlinkOn = !Buff->CursorBlinkOn;
972
973 if ((GuiData->OldCursor.x != Buff->CursorPosition.X) ||
974 (GuiData->OldCursor.y != Buff->CursorPosition.Y))
975 {
976 SCROLLINFO xScroll;
977 int OldScrollX = -1, OldScrollY = -1;
978 int NewScrollX = -1, NewScrollY = -1;
979
980 xScroll.cbSize = sizeof(SCROLLINFO);
981 xScroll.fMask = SIF_POS;
982 // Capture the original position of the scroll bars and save them.
983 if (GetScrollInfo(GuiData->hWindow, SB_HORZ, &xScroll)) OldScrollX = xScroll.nPos;
984 if (GetScrollInfo(GuiData->hWindow, SB_VERT, &xScroll)) OldScrollY = xScroll.nPos;
985
986 // If we successfully got the info for the horizontal scrollbar
987 if (OldScrollX >= 0)
988 {
989 if ((Buff->CursorPosition.X < Buff->ViewOrigin.X) ||
990 (Buff->CursorPosition.X >= (Buff->ViewOrigin.X + Buff->ViewSize.X)))
991 {
992 // Handle the horizontal scroll bar
993 if (Buff->CursorPosition.X >= Buff->ViewSize.X)
994 NewScrollX = Buff->CursorPosition.X - Buff->ViewSize.X + 1;
995 else
996 NewScrollX = 0;
997 }
998 else
999 {
1000 NewScrollX = OldScrollX;
1001 }
1002 }
1003 // If we successfully got the info for the vertical scrollbar
1004 if (OldScrollY >= 0)
1005 {
1006 if ((Buff->CursorPosition.Y < Buff->ViewOrigin.Y) ||
1007 (Buff->CursorPosition.Y >= (Buff->ViewOrigin.Y + Buff->ViewSize.Y)))
1008 {
1009 // Handle the vertical scroll bar
1010 if (Buff->CursorPosition.Y >= Buff->ViewSize.Y)
1011 NewScrollY = Buff->CursorPosition.Y - Buff->ViewSize.Y + 1;
1012 else
1013 NewScrollY = 0;
1014 }
1015 else
1016 {
1017 NewScrollY = OldScrollY;
1018 }
1019 }
1020
1021 // Adjust scroll bars and refresh the window if the cursor has moved outside the visible area
1022 // NOTE: OldScroll# and NewScroll# will both be -1 (initial value) if the info for the respective scrollbar
1023 // was not obtained successfully in the previous steps. This means their difference is 0 (no scrolling)
1024 // and their associated scrollbar is left alone.
1025 if ((OldScrollX != NewScrollX) || (OldScrollY != NewScrollY))
1026 {
1027 Buff->ViewOrigin.X = NewScrollX;
1028 Buff->ViewOrigin.Y = NewScrollY;
1029 ScrollWindowEx(GuiData->hWindow,
1030 (OldScrollX - NewScrollX) * GuiData->CharWidth,
1031 (OldScrollY - NewScrollY) * GuiData->CharHeight,
1032 NULL,
1033 NULL,
1034 NULL,
1035 NULL,
1036 SW_INVALIDATE);
1037 if (NewScrollX >= 0)
1038 {
1039 xScroll.nPos = NewScrollX;
1040 SetScrollInfo(GuiData->hWindow, SB_HORZ, &xScroll, TRUE);
1041 }
1042 if (NewScrollY >= 0)
1043 {
1044 xScroll.nPos = NewScrollY;
1045 SetScrollInfo(GuiData->hWindow, SB_VERT, &xScroll, TRUE);
1046 }
1047 UpdateWindow(GuiData->hWindow);
1048 // InvalidateRect(GuiData->hWindow, NULL, FALSE);
1049 GuiData->OldCursor.x = Buff->CursorPosition.X;
1050 GuiData->OldCursor.y = Buff->CursorPosition.Y;
1051 }
1052 }
1053 }
1054 else /* if (GetType(Buff) == GRAPHICS_BUFFER) */
1055 {
1056 }
1057
1058 LeaveCriticalSection(&Console->Lock);
1059 }
1060
1061 static BOOL
1062 GuiConsoleHandleClose(PGUI_CONSOLE_DATA GuiData)
1063 {
1064 PCONSOLE Console = GuiData->Console;
1065
1066 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
1067 return TRUE;
1068
1069 // TODO: Prompt for termination ? (Warn the user about possible apps running in this console)
1070
1071 /*
1072 * FIXME: Windows will wait up to 5 seconds for the thread to exit.
1073 * We shouldn't wait here, though, since the console lock is entered.
1074 * A copy of the thread list probably needs to be made.
1075 */
1076 ConDrvConsoleProcessCtrlEvent(Console, 0, CTRL_CLOSE_EVENT);
1077
1078 LeaveCriticalSection(&Console->Lock);
1079 return FALSE;
1080 }
1081
1082 static LRESULT
1083 GuiConsoleHandleNcDestroy(HWND hWnd)
1084 {
1085 PGUI_CONSOLE_DATA GuiData = GuiGetGuiData(hWnd);
1086
1087 KillTimer(hWnd, CONGUI_UPDATE_TIMER);
1088 GetSystemMenu(hWnd, TRUE);
1089
1090 if (GuiData)
1091 {
1092 /* Free the terminal framebuffer */
1093 if (GuiData->hMemDC ) DeleteDC(GuiData->hMemDC);
1094 if (GuiData->hBitmap) DeleteObject(GuiData->hBitmap);
1095 // if (GuiData->hSysPalette) DeleteObject(GuiData->hSysPalette);
1096 if (GuiData->Font) DeleteObject(GuiData->Font);
1097 }
1098
1099 /* Free the GuiData registration */
1100 SetWindowLongPtrW(hWnd, GWLP_USERDATA, (DWORD_PTR)NULL);
1101
1102 return DefWindowProcW(hWnd, WM_NCDESTROY, 0, 0);
1103 }
1104
1105 static COORD
1106 PointToCoord(PGUI_CONSOLE_DATA GuiData, LPARAM lParam)
1107 {
1108 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
1109 COORD Coord;
1110 UINT WidthUnit, HeightUnit;
1111
1112 GetScreenBufferSizeUnits(Buffer, GuiData, &WidthUnit, &HeightUnit);
1113
1114 Coord.X = Buffer->ViewOrigin.X + ((SHORT)LOWORD(lParam) / (int)WidthUnit );
1115 Coord.Y = Buffer->ViewOrigin.Y + ((SHORT)HIWORD(lParam) / (int)HeightUnit);
1116
1117 /* Clip coordinate to ensure it's inside buffer */
1118 if (Coord.X < 0)
1119 Coord.X = 0;
1120 else if (Coord.X >= Buffer->ScreenBufferSize.X)
1121 Coord.X = Buffer->ScreenBufferSize.X - 1;
1122
1123 if (Coord.Y < 0)
1124 Coord.Y = 0;
1125 else if (Coord.Y >= Buffer->ScreenBufferSize.Y)
1126 Coord.Y = Buffer->ScreenBufferSize.Y - 1;
1127
1128 return Coord;
1129 }
1130
1131 static LRESULT
1132 GuiConsoleHandleMouse(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam)
1133 {
1134 BOOL Err = FALSE;
1135 PCONSOLE Console = GuiData->Console;
1136
1137 if (GuiData->IgnoreNextMouseSignal)
1138 {
1139 if (msg != WM_LBUTTONDOWN &&
1140 msg != WM_MBUTTONDOWN &&
1141 msg != WM_RBUTTONDOWN &&
1142 msg != WM_MOUSEMOVE)
1143 {
1144 /*
1145 * If this mouse signal is not a button-down action or a move,
1146 * then it is the last signal being ignored.
1147 */
1148 GuiData->IgnoreNextMouseSignal = FALSE;
1149 }
1150 else
1151 {
1152 /*
1153 * This mouse signal is a button-down action or a move.
1154 * Ignore it and perform default action.
1155 */
1156 Err = TRUE;
1157 }
1158 goto Quit;
1159 }
1160
1161 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
1162 {
1163 Err = TRUE;
1164 goto Quit;
1165 }
1166
1167 if ( (Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) ||
1168 (Console->QuickEdit) )
1169 {
1170 switch (msg)
1171 {
1172 case WM_LBUTTONDOWN:
1173 {
1174 /* Clear the old selection */
1175 // GuiConsoleUpdateSelection(Console, NULL);
1176 Console->Selection.dwFlags = CONSOLE_NO_SELECTION;
1177
1178 /* Restart a new selection */
1179 Console->Selection.dwSelectionAnchor = PointToCoord(GuiData, lParam);
1180 SetCapture(GuiData->hWindow);
1181 Console->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN;
1182 GuiConsoleUpdateSelection(Console, &Console->Selection.dwSelectionAnchor);
1183
1184 break;
1185 }
1186
1187 case WM_LBUTTONUP:
1188 {
1189 // COORD c;
1190
1191 if (!(Console->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break;
1192
1193 // c = PointToCoord(GuiData, lParam);
1194 Console->Selection.dwFlags &= ~CONSOLE_MOUSE_DOWN;
1195 // GuiConsoleUpdateSelection(Console, &c);
1196 ReleaseCapture();
1197
1198 break;
1199 }
1200
1201 case WM_LBUTTONDBLCLK:
1202 {
1203 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
1204
1205 if (GetType(Buffer) == TEXTMODE_BUFFER)
1206 {
1207 #ifdef IS_WHITESPACE
1208 #undef IS_WHITESPACE
1209 #endif
1210 #define IS_WHITESPACE(c) \
1211 ((c) == L'\0' || (c) == L' ' || (c) == L'\t' || (c) == L'\r' || (c) == L'\n')
1212
1213 PTEXTMODE_SCREEN_BUFFER TextBuffer = (PTEXTMODE_SCREEN_BUFFER)Buffer;
1214 COORD cL, cR;
1215 PCHAR_INFO ptrL, ptrR;
1216
1217 /* Starting point */
1218 cL = cR = PointToCoord(GuiData, lParam);
1219 ptrL = ptrR = ConioCoordToPointer(TextBuffer, cL.X, cL.Y);
1220
1221 /* Enlarge the selection by checking for whitespace */
1222 while ((0 < cL.X) && !IS_WHITESPACE(ptrL->Char.UnicodeChar)
1223 && !IS_WHITESPACE((ptrL-1)->Char.UnicodeChar))
1224 {
1225 --cL.X;
1226 --ptrL;
1227 }
1228 while ((cR.X < TextBuffer->ScreenBufferSize.X - 1) &&
1229 !IS_WHITESPACE(ptrR->Char.UnicodeChar) &&
1230 !IS_WHITESPACE((ptrR+1)->Char.UnicodeChar))
1231 {
1232 ++cR.X;
1233 ++ptrR;
1234 }
1235
1236 /*
1237 * Update the selection started with the single
1238 * left-click that preceded this double-click.
1239 */
1240 Console->Selection.dwSelectionAnchor = cL;
1241 Console->dwSelectionCursor = cR;
1242
1243 Console->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN;
1244 GuiConsoleUpdateSelection(Console, &Console->dwSelectionCursor);
1245
1246 /* Ignore the next mouse move signal */
1247 GuiData->IgnoreNextMouseSignal = TRUE;
1248 }
1249
1250 break;
1251 }
1252
1253 case WM_RBUTTONDOWN:
1254 case WM_RBUTTONDBLCLK:
1255 {
1256 if (!(Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY))
1257 {
1258 GuiConsolePaste(GuiData);
1259 }
1260 else
1261 {
1262 GuiConsoleCopy(GuiData);
1263 }
1264
1265 /* Ignore the next mouse move signal */
1266 GuiData->IgnoreNextMouseSignal = TRUE;
1267 break;
1268 }
1269
1270 case WM_MOUSEMOVE:
1271 {
1272 COORD c;
1273
1274 if (!(wParam & MK_LBUTTON)) break;
1275 if (!(Console->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break;
1276
1277 c = PointToCoord(GuiData, lParam); /* TODO: Scroll buffer to bring c into view */
1278 GuiConsoleUpdateSelection(Console, &c);
1279
1280 break;
1281 }
1282
1283 default:
1284 Err = FALSE; // TRUE;
1285 break;
1286 }
1287 }
1288 else if (Console->InputBuffer.Mode & ENABLE_MOUSE_INPUT)
1289 {
1290 INPUT_RECORD er;
1291 WORD wKeyState = GET_KEYSTATE_WPARAM(wParam);
1292 DWORD dwButtonState = 0;
1293 DWORD dwControlKeyState = 0;
1294 DWORD dwEventFlags = 0;
1295
1296 switch (msg)
1297 {
1298 case WM_LBUTTONDOWN:
1299 SetCapture(GuiData->hWindow);
1300 dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
1301 dwEventFlags = 0;
1302 break;
1303
1304 case WM_MBUTTONDOWN:
1305 SetCapture(GuiData->hWindow);
1306 dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
1307 dwEventFlags = 0;
1308 break;
1309
1310 case WM_RBUTTONDOWN:
1311 SetCapture(GuiData->hWindow);
1312 dwButtonState = RIGHTMOST_BUTTON_PRESSED;
1313 dwEventFlags = 0;
1314 break;
1315
1316 case WM_LBUTTONUP:
1317 ReleaseCapture();
1318 dwButtonState = 0;
1319 dwEventFlags = 0;
1320 break;
1321
1322 case WM_MBUTTONUP:
1323 ReleaseCapture();
1324 dwButtonState = 0;
1325 dwEventFlags = 0;
1326 break;
1327
1328 case WM_RBUTTONUP:
1329 ReleaseCapture();
1330 dwButtonState = 0;
1331 dwEventFlags = 0;
1332 break;
1333
1334 case WM_LBUTTONDBLCLK:
1335 dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
1336 dwEventFlags = DOUBLE_CLICK;
1337 break;
1338
1339 case WM_MBUTTONDBLCLK:
1340 dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
1341 dwEventFlags = DOUBLE_CLICK;
1342 break;
1343
1344 case WM_RBUTTONDBLCLK:
1345 dwButtonState = RIGHTMOST_BUTTON_PRESSED;
1346 dwEventFlags = DOUBLE_CLICK;
1347 break;
1348
1349 case WM_MOUSEMOVE:
1350 dwButtonState = 0;
1351 dwEventFlags = MOUSE_MOVED;
1352 break;
1353
1354 case WM_MOUSEWHEEL:
1355 dwButtonState = GET_WHEEL_DELTA_WPARAM(wParam) << 16;
1356 dwEventFlags = MOUSE_WHEELED;
1357 break;
1358
1359 case WM_MOUSEHWHEEL:
1360 dwButtonState = GET_WHEEL_DELTA_WPARAM(wParam) << 16;
1361 dwEventFlags = MOUSE_HWHEELED;
1362 break;
1363
1364 default:
1365 Err = TRUE;
1366 break;
1367 }
1368
1369 if (!Err)
1370 {
1371 if (wKeyState & MK_LBUTTON)
1372 dwButtonState |= FROM_LEFT_1ST_BUTTON_PRESSED;
1373 if (wKeyState & MK_MBUTTON)
1374 dwButtonState |= FROM_LEFT_2ND_BUTTON_PRESSED;
1375 if (wKeyState & MK_RBUTTON)
1376 dwButtonState |= RIGHTMOST_BUTTON_PRESSED;
1377
1378 if (GetKeyState(VK_RMENU) & 0x8000)
1379 dwControlKeyState |= RIGHT_ALT_PRESSED;
1380 if (GetKeyState(VK_LMENU) & 0x8000)
1381 dwControlKeyState |= LEFT_ALT_PRESSED;
1382 if (GetKeyState(VK_RCONTROL) & 0x8000)
1383 dwControlKeyState |= RIGHT_CTRL_PRESSED;
1384 if (GetKeyState(VK_LCONTROL) & 0x8000)
1385 dwControlKeyState |= LEFT_CTRL_PRESSED;
1386 if (GetKeyState(VK_SHIFT) & 0x8000)
1387 dwControlKeyState |= SHIFT_PRESSED;
1388 if (GetKeyState(VK_NUMLOCK) & 0x0001)
1389 dwControlKeyState |= NUMLOCK_ON;
1390 if (GetKeyState(VK_SCROLL) & 0x0001)
1391 dwControlKeyState |= SCROLLLOCK_ON;
1392 if (GetKeyState(VK_CAPITAL) & 0x0001)
1393 dwControlKeyState |= CAPSLOCK_ON;
1394 /* See WM_CHAR MSDN documentation for instance */
1395 if (lParam & 0x01000000)
1396 dwControlKeyState |= ENHANCED_KEY;
1397
1398 er.EventType = MOUSE_EVENT;
1399 er.Event.MouseEvent.dwMousePosition = PointToCoord(GuiData, lParam);
1400 er.Event.MouseEvent.dwButtonState = dwButtonState;
1401 er.Event.MouseEvent.dwControlKeyState = dwControlKeyState;
1402 er.Event.MouseEvent.dwEventFlags = dwEventFlags;
1403
1404 ConioProcessInputEvent(Console, &er);
1405 }
1406 }
1407 else
1408 {
1409 Err = TRUE;
1410 }
1411
1412 LeaveCriticalSection(&Console->Lock);
1413
1414 Quit:
1415 if (Err)
1416 return DefWindowProcW(GuiData->hWindow, msg, wParam, lParam);
1417 else
1418 return 0;
1419 }
1420
1421 VOID
1422 GuiCopyFromTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer,
1423 PGUI_CONSOLE_DATA GuiData);
1424 VOID
1425 GuiCopyFromGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer,
1426 PGUI_CONSOLE_DATA GuiData);
1427
1428 static VOID
1429 GuiConsoleCopy(PGUI_CONSOLE_DATA GuiData)
1430 {
1431 PCONSOLE Console = GuiData->Console;
1432
1433 if (OpenClipboard(GuiData->hWindow) == TRUE)
1434 {
1435 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
1436
1437 if (GetType(Buffer) == TEXTMODE_BUFFER)
1438 {
1439 GuiCopyFromTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)Buffer, GuiData);
1440 }
1441 else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
1442 {
1443 GuiCopyFromGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)Buffer, GuiData);
1444 }
1445
1446 CloseClipboard();
1447 }
1448
1449 /* Clear the selection */
1450 GuiConsoleUpdateSelection(Console, NULL);
1451 }
1452
1453 VOID
1454 GuiPasteToTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer,
1455 PGUI_CONSOLE_DATA GuiData);
1456 VOID
1457 GuiPasteToGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer,
1458 PGUI_CONSOLE_DATA GuiData);
1459
1460 static VOID
1461 GuiConsolePaste(PGUI_CONSOLE_DATA GuiData)
1462 {
1463 if (OpenClipboard(GuiData->hWindow) == TRUE)
1464 {
1465 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
1466
1467 if (GetType(Buffer) == TEXTMODE_BUFFER)
1468 {
1469 GuiPasteToTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)Buffer, GuiData);
1470 }
1471 else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
1472 {
1473 GuiPasteToGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)Buffer, GuiData);
1474 }
1475
1476 CloseClipboard();
1477 }
1478 }
1479
1480 static VOID
1481 GuiConsoleGetMinMaxInfo(PGUI_CONSOLE_DATA GuiData, PMINMAXINFO minMaxInfo)
1482 {
1483 PCONSOLE Console = GuiData->Console;
1484 PCONSOLE_SCREEN_BUFFER ActiveBuffer;
1485 DWORD windx, windy;
1486 UINT WidthUnit, HeightUnit;
1487
1488 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
1489
1490 ActiveBuffer = GuiData->ActiveBuffer;
1491
1492 GetScreenBufferSizeUnits(ActiveBuffer, GuiData, &WidthUnit, &HeightUnit);
1493
1494 windx = CONGUI_MIN_WIDTH * WidthUnit + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
1495 windy = CONGUI_MIN_HEIGHT * HeightUnit + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
1496
1497 minMaxInfo->ptMinTrackSize.x = windx;
1498 minMaxInfo->ptMinTrackSize.y = windy;
1499
1500 windx = (ActiveBuffer->ScreenBufferSize.X) * WidthUnit + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
1501 windy = (ActiveBuffer->ScreenBufferSize.Y) * HeightUnit + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
1502
1503 if (ActiveBuffer->ViewSize.X < ActiveBuffer->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar
1504 if (ActiveBuffer->ViewSize.Y < ActiveBuffer->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar
1505
1506 minMaxInfo->ptMaxTrackSize.x = windx;
1507 minMaxInfo->ptMaxTrackSize.y = windy;
1508
1509 LeaveCriticalSection(&Console->Lock);
1510 }
1511
1512 static VOID
1513 GuiConsoleResize(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam)
1514 {
1515 PCONSOLE Console = GuiData->Console;
1516
1517 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
1518
1519 if ((GuiData->WindowSizeLock == FALSE) &&
1520 (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED || wParam == SIZE_MINIMIZED))
1521 {
1522 PCONSOLE_SCREEN_BUFFER Buff = GuiData->ActiveBuffer;
1523 DWORD windx, windy, charx, chary;
1524 UINT WidthUnit, HeightUnit;
1525
1526 GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
1527
1528 GuiData->WindowSizeLock = TRUE;
1529
1530 windx = LOWORD(lParam);
1531 windy = HIWORD(lParam);
1532
1533 // Compensate for existing scroll bars (because lParam values do not accommodate scroll bar)
1534 if (Buff->ViewSize.X < Buff->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar
1535 if (Buff->ViewSize.Y < Buff->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar
1536
1537 charx = windx / (int)WidthUnit ;
1538 chary = windy / (int)HeightUnit;
1539
1540 // Character alignment (round size up or down)
1541 if ((windx % WidthUnit ) >= (WidthUnit / 2)) ++charx;
1542 if ((windy % HeightUnit) >= (HeightUnit / 2)) ++chary;
1543
1544 // Compensate for added scroll bars in new window
1545 if (charx < Buff->ScreenBufferSize.X) windy -= GetSystemMetrics(SM_CYHSCROLL); // new window will have a horizontal scroll bar
1546 if (chary < Buff->ScreenBufferSize.Y) windx -= GetSystemMetrics(SM_CXVSCROLL); // new window will have a vertical scroll bar
1547
1548 charx = windx / (int)WidthUnit ;
1549 chary = windy / (int)HeightUnit;
1550
1551 // Character alignment (round size up or down)
1552 if ((windx % WidthUnit ) >= (WidthUnit / 2)) ++charx;
1553 if ((windy % HeightUnit) >= (HeightUnit / 2)) ++chary;
1554
1555 // Resize window
1556 if ((charx != Buff->ViewSize.X) || (chary != Buff->ViewSize.Y))
1557 {
1558 Buff->ViewSize.X = (charx <= Buff->ScreenBufferSize.X) ? charx : Buff->ScreenBufferSize.X;
1559 Buff->ViewSize.Y = (chary <= Buff->ScreenBufferSize.Y) ? chary : Buff->ScreenBufferSize.Y;
1560 }
1561
1562 GuiConsoleResizeWindow(GuiData, WidthUnit, HeightUnit);
1563
1564 // Adjust the start of the visible area if we are attempting to show nonexistent areas
1565 if ((Buff->ScreenBufferSize.X - Buff->ViewOrigin.X) < Buff->ViewSize.X) Buff->ViewOrigin.X = Buff->ScreenBufferSize.X - Buff->ViewSize.X;
1566 if ((Buff->ScreenBufferSize.Y - Buff->ViewOrigin.Y) < Buff->ViewSize.Y) Buff->ViewOrigin.Y = Buff->ScreenBufferSize.Y - Buff->ViewSize.Y;
1567 InvalidateRect(GuiData->hWindow, NULL, TRUE);
1568
1569 GuiData->WindowSizeLock = FALSE;
1570 }
1571
1572 LeaveCriticalSection(&Console->Lock);
1573 }
1574
1575 /*
1576 // HACK: This functionality is standard for general scrollbars. Don't add it by hand.
1577
1578 VOID
1579 FASTCALL
1580 GuiConsoleHandleScrollbarMenu(VOID)
1581 {
1582 HMENU hMenu;
1583
1584 hMenu = CreatePopupMenu();
1585 if (hMenu == NULL)
1586 {
1587 DPRINT("CreatePopupMenu failed\n");
1588 return;
1589 }
1590
1591 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLHERE);
1592 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
1593 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLTOP);
1594 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLBOTTOM);
1595 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
1596 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_UP);
1597 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_DOWN);
1598 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
1599 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLUP);
1600 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLDOWN);
1601 }
1602 */
1603
1604 static LRESULT
1605 GuiConsoleHandleScroll(PGUI_CONSOLE_DATA GuiData, UINT uMsg, WPARAM wParam)
1606 {
1607 PCONSOLE Console = GuiData->Console;
1608 PCONSOLE_SCREEN_BUFFER Buff;
1609 SCROLLINFO sInfo;
1610 int fnBar;
1611 int old_pos, Maximum;
1612 PSHORT pShowXY;
1613
1614 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return 0;
1615
1616 Buff = GuiData->ActiveBuffer;
1617
1618 if (uMsg == WM_HSCROLL)
1619 {
1620 fnBar = SB_HORZ;
1621 Maximum = Buff->ScreenBufferSize.X - Buff->ViewSize.X;
1622 pShowXY = &Buff->ViewOrigin.X;
1623 }
1624 else
1625 {
1626 fnBar = SB_VERT;
1627 Maximum = Buff->ScreenBufferSize.Y - Buff->ViewSize.Y;
1628 pShowXY = &Buff->ViewOrigin.Y;
1629 }
1630
1631 /* set scrollbar sizes */
1632 sInfo.cbSize = sizeof(SCROLLINFO);
1633 sInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE | SIF_TRACKPOS;
1634
1635 if (!GetScrollInfo(GuiData->hWindow, fnBar, &sInfo)) goto Quit;
1636
1637 old_pos = sInfo.nPos;
1638
1639 switch (LOWORD(wParam))
1640 {
1641 case SB_LINELEFT:
1642 sInfo.nPos -= 1;
1643 break;
1644
1645 case SB_LINERIGHT:
1646 sInfo.nPos += 1;
1647 break;
1648
1649 case SB_PAGELEFT:
1650 sInfo.nPos -= sInfo.nPage;
1651 break;
1652
1653 case SB_PAGERIGHT:
1654 sInfo.nPos += sInfo.nPage;
1655 break;
1656
1657 case SB_THUMBTRACK:
1658 sInfo.nPos = sInfo.nTrackPos;
1659 ConioPause(Console, PAUSED_FROM_SCROLLBAR);
1660 break;
1661
1662 case SB_THUMBPOSITION:
1663 ConioUnpause(Console, PAUSED_FROM_SCROLLBAR);
1664 break;
1665
1666 case SB_TOP:
1667 sInfo.nPos = sInfo.nMin;
1668 break;
1669
1670 case SB_BOTTOM:
1671 sInfo.nPos = sInfo.nMax;
1672 break;
1673
1674 default:
1675 break;
1676 }
1677
1678 sInfo.nPos = max(sInfo.nPos, 0);
1679 sInfo.nPos = min(sInfo.nPos, Maximum);
1680
1681 if (old_pos != sInfo.nPos)
1682 {
1683 USHORT OldX = Buff->ViewOrigin.X;
1684 USHORT OldY = Buff->ViewOrigin.Y;
1685 UINT WidthUnit, HeightUnit;
1686
1687 *pShowXY = sInfo.nPos;
1688
1689 GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
1690
1691 ScrollWindowEx(GuiData->hWindow,
1692 (OldX - Buff->ViewOrigin.X) * WidthUnit ,
1693 (OldY - Buff->ViewOrigin.Y) * HeightUnit,
1694 NULL,
1695 NULL,
1696 NULL,
1697 NULL,
1698 SW_INVALIDATE);
1699
1700 sInfo.fMask = SIF_POS;
1701 SetScrollInfo(GuiData->hWindow, fnBar, &sInfo, TRUE);
1702
1703 UpdateWindow(GuiData->hWindow);
1704 // InvalidateRect(GuiData->hWindow, NULL, FALSE);
1705 }
1706
1707 Quit:
1708 LeaveCriticalSection(&Console->Lock);
1709 return 0;
1710 }
1711
1712
1713 BOOL
1714 EnterFullScreen(PGUI_CONSOLE_DATA GuiData);
1715 VOID
1716 LeaveFullScreen(PGUI_CONSOLE_DATA GuiData);
1717 VOID
1718 SwitchFullScreen(PGUI_CONSOLE_DATA GuiData, BOOL FullScreen);
1719 VOID
1720 GuiConsoleSwitchFullScreen(PGUI_CONSOLE_DATA GuiData);
1721
1722 static LRESULT CALLBACK
1723 ConWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1724 {
1725 LRESULT Result = 0;
1726 PGUI_CONSOLE_DATA GuiData = NULL;
1727 PCONSOLE Console = NULL;
1728
1729 /*
1730 * - If it's the first time we create a window for the terminal,
1731 * just initialize it and return.
1732 *
1733 * - If we are destroying the window, just do it and return.
1734 */
1735 if (msg == WM_NCCREATE)
1736 {
1737 return (LRESULT)GuiConsoleHandleNcCreate(hWnd, (LPCREATESTRUCTW)lParam);
1738 }
1739 else if (msg == WM_NCDESTROY)
1740 {
1741 return GuiConsoleHandleNcDestroy(hWnd);
1742 }
1743
1744 /*
1745 * Now the terminal window is initialized.
1746 * Get the terminal data via the window's data.
1747 * If there is no data, just go away.
1748 */
1749 GuiData = GuiGetGuiData(hWnd);
1750 if (GuiData == NULL) return DefWindowProcW(hWnd, msg, wParam, lParam);
1751
1752 // TEMPORARY HACK until all of the functions can deal with a NULL GuiData->ActiveBuffer ...
1753 if (GuiData->ActiveBuffer == NULL) return DefWindowProcW(hWnd, msg, wParam, lParam);
1754
1755 /*
1756 * Just retrieve a pointer to the console in case somebody needs it.
1757 * It is not NULL because it was checked in GuiGetGuiData.
1758 * Each helper function which needs the console has to validate and lock it.
1759 */
1760 Console = GuiData->Console;
1761
1762 /* We have a console, start message dispatching */
1763 switch (msg)
1764 {
1765 case WM_ACTIVATE:
1766 {
1767 WORD ActivationState = LOWORD(wParam);
1768
1769 DPRINT1("WM_ACTIVATE - ActivationState = %d\n");
1770
1771 if ( ActivationState == WA_ACTIVE ||
1772 ActivationState == WA_CLICKACTIVE )
1773 {
1774 if (GuiData->GuiInfo.FullScreen)
1775 {
1776 EnterFullScreen(GuiData);
1777 // // PostMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_RESTORE, 0);
1778 // SendMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_RESTORE, 0);
1779 }
1780 }
1781 else // if (ActivationState == WA_INACTIVE)
1782 {
1783 if (GuiData->GuiInfo.FullScreen)
1784 {
1785 SendMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_MINIMIZE, 0);
1786 LeaveFullScreen(GuiData);
1787 // // PostMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_MINIMIZE, 0);
1788 // SendMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_MINIMIZE, 0);
1789 }
1790 }
1791
1792 /*
1793 * When we are in QuickEdit mode, ignore the next mouse signal
1794 * when we are going to be enabled again via the mouse, in order
1795 * to prevent e.g. an erroneous right-click from the user which
1796 * would have as an effect to paste some unwanted text...
1797 */
1798 if (Console->QuickEdit && (ActivationState == WA_CLICKACTIVE))
1799 GuiData->IgnoreNextMouseSignal = TRUE;
1800
1801 break;
1802 }
1803
1804 case WM_CLOSE:
1805 if (GuiConsoleHandleClose(GuiData)) goto Default;
1806 break;
1807
1808 case WM_PAINT:
1809 GuiConsoleHandlePaint(GuiData);
1810 break;
1811
1812 case WM_TIMER:
1813 GuiConsoleHandleTimer(GuiData);
1814 break;
1815
1816 case WM_PALETTECHANGED:
1817 {
1818 PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer;
1819
1820 DPRINT("WM_PALETTECHANGED called\n");
1821
1822 /*
1823 * Protects against infinite loops:
1824 * "... A window that receives this message must not realize
1825 * its palette, unless it determines that wParam does not contain
1826 * its own window handle." (WM_PALETTECHANGED description - MSDN)
1827 *
1828 * This message is sent to all windows, including the one that
1829 * changed the system palette and caused this message to be sent.
1830 * The wParam of this message contains the handle of the window
1831 * that caused the system palette to change. To avoid an infinite
1832 * loop, care must be taken to check that the wParam of this message
1833 * does not match the window's handle.
1834 */
1835 if ((HWND)wParam == hWnd) break;
1836
1837 DPRINT("WM_PALETTECHANGED ok\n");
1838
1839 // if (GetType(ActiveBuffer) == GRAPHICS_BUFFER)
1840 if (ActiveBuffer->PaletteHandle)
1841 {
1842 DPRINT("WM_PALETTECHANGED changing palette\n");
1843
1844 /* Specify the use of the system palette for the framebuffer */
1845 SetSystemPaletteUse(GuiData->hMemDC, ActiveBuffer->PaletteUsage);
1846
1847 /* Realize the (logical) palette */
1848 RealizePalette(GuiData->hMemDC);
1849 }
1850
1851 DPRINT("WM_PALETTECHANGED quit\n");
1852
1853 break;
1854 }
1855
1856 case WM_KEYDOWN:
1857 case WM_KEYUP:
1858 case WM_CHAR:
1859 case WM_DEADCHAR:
1860 case WM_SYSKEYDOWN:
1861 case WM_SYSKEYUP:
1862 case WM_SYSCHAR:
1863 case WM_SYSDEADCHAR:
1864 {
1865 /* Detect Alt-Enter presses and switch back and forth to fullscreen mode */
1866 if (msg == WM_SYSKEYDOWN && (HIWORD(lParam) & KF_ALTDOWN) && wParam == VK_RETURN)
1867 {
1868 /* Switch only at first Alt-Enter press, and ignore subsequent key repetitions */
1869 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
1870 GuiConsoleSwitchFullScreen(GuiData);
1871
1872 break;
1873 }
1874
1875 GuiConsoleHandleKey(GuiData, msg, wParam, lParam);
1876 break;
1877 }
1878
1879 case WM_SETCURSOR:
1880 {
1881 /*
1882 * The message was sent because we are manually triggering a change.
1883 * Check whether the mouse is indeed present on this console window
1884 * and take appropriate decisions.
1885 */
1886 if (wParam == -1 && lParam == -1)
1887 {
1888 POINT mouseCoords;
1889 HWND hWndHit;
1890
1891 /* Get the placement of the mouse */
1892 GetCursorPos(&mouseCoords);
1893
1894 /* On which window is placed the mouse ? */
1895 hWndHit = WindowFromPoint(mouseCoords);
1896
1897 /* It's our window. Perform the hit-test to be used later on. */
1898 if (hWndHit == hWnd)
1899 {
1900 wParam = (WPARAM)hWnd;
1901 lParam = DefWindowProcW(hWndHit, WM_NCHITTEST, 0,
1902 MAKELPARAM(mouseCoords.x, mouseCoords.y));
1903 }
1904 }
1905
1906 /* Set the mouse cursor only when we are in the client area */
1907 if ((HWND)wParam == hWnd && LOWORD(lParam) == HTCLIENT)
1908 {
1909 if (GuiData->MouseCursorRefCount >= 0)
1910 {
1911 /* Show the cursor */
1912 SetCursor(GuiData->hCursor);
1913 }
1914 else
1915 {
1916 /* Hide the cursor if the reference count is negative */
1917 SetCursor(NULL);
1918 }
1919 return TRUE;
1920 }
1921 else
1922 {
1923 goto Default;
1924 }
1925 }
1926
1927 case WM_LBUTTONDOWN:
1928 case WM_MBUTTONDOWN:
1929 case WM_RBUTTONDOWN:
1930 case WM_LBUTTONUP:
1931 case WM_MBUTTONUP:
1932 case WM_RBUTTONUP:
1933 case WM_LBUTTONDBLCLK:
1934 case WM_MBUTTONDBLCLK:
1935 case WM_RBUTTONDBLCLK:
1936 case WM_MOUSEMOVE:
1937 case WM_MOUSEWHEEL:
1938 case WM_MOUSEHWHEEL:
1939 {
1940 Result = GuiConsoleHandleMouse(GuiData, msg, wParam, lParam);
1941 break;
1942 }
1943
1944 case WM_HSCROLL:
1945 case WM_VSCROLL:
1946 {
1947 Result = GuiConsoleHandleScroll(GuiData, msg, wParam);
1948 break;
1949 }
1950
1951 case WM_NCRBUTTONDOWN:
1952 {
1953 DPRINT1("WM_NCRBUTTONDOWN\n");
1954 /*
1955 * HACK: !! Because, when we deal with WM_RBUTTON* and we do not
1956 * call after that DefWindowProc, on ReactOS, right-clicks on the
1957 * (non-client) application title-bar does not display the system
1958 * menu and does not trigger a WM_NCRBUTTONUP message too.
1959 * See: http://git.reactos.org/?p=reactos.git;a=blob;f=reactos/win32ss/user/user32/windows/defwnd.c;hb=332bc8f482f40fd05ab510f78276576719fbfba8#l1103
1960 * and line 1135 too.
1961 */
1962 #if 0
1963 if (DefWindowProcW(hWnd, WM_NCHITTEST, 0, lParam) == HTCAPTION)
1964 {
1965 /* Call DefWindowProcW with the WM_CONTEXTMENU message */
1966 msg = WM_CONTEXTMENU;
1967 }
1968 #endif
1969 goto Default;
1970 }
1971 // case WM_NCRBUTTONUP:
1972 // DPRINT1("WM_NCRBUTTONUP\n");
1973 // goto Default;
1974
1975 case WM_CONTEXTMENU:
1976 {
1977 if (DefWindowProcW(hWnd /*GuiData->hWindow*/, WM_NCHITTEST, 0, lParam) == HTCLIENT)
1978 {
1979 HMENU hMenu = CreatePopupMenu();
1980 if (hMenu != NULL)
1981 {
1982 GuiConsoleAppendMenuItems(hMenu, GuiConsoleEditMenuItems);
1983 TrackPopupMenuEx(hMenu,
1984 TPM_RIGHTBUTTON,
1985 GET_X_LPARAM(lParam),
1986 GET_Y_LPARAM(lParam),
1987 hWnd,
1988 NULL);
1989 DestroyMenu(hMenu);
1990 }
1991 break;
1992 }
1993 else
1994 {
1995 goto Default;
1996 }
1997 }
1998
1999 case WM_INITMENU:
2000 {
2001 HMENU hMenu = (HMENU)wParam;
2002 if (hMenu != NULL)
2003 {
2004 /* Enable or disable the Close menu item */
2005 EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND |
2006 (GuiData->IsCloseButtonEnabled ? MF_ENABLED : MF_GRAYED));
2007
2008 /* Enable or disable the Copy and Paste items */
2009 EnableMenuItem(hMenu, ID_SYSTEM_EDIT_COPY , MF_BYCOMMAND |
2010 ((Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) &&
2011 (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) ? MF_ENABLED : MF_GRAYED));
2012 // FIXME: Following whether the active screen buffer is text-mode
2013 // or graphics-mode, search for CF_UNICODETEXT or CF_BITMAP formats.
2014 EnableMenuItem(hMenu, ID_SYSTEM_EDIT_PASTE, MF_BYCOMMAND |
2015 (!(Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) &&
2016 IsClipboardFormatAvailable(CF_UNICODETEXT) ? MF_ENABLED : MF_GRAYED));
2017 }
2018
2019 if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
2020 {
2021 GuiSendMenuEvent(Console, WM_INITMENU);
2022 LeaveCriticalSection(&Console->Lock);
2023 }
2024 break;
2025 }
2026
2027 case WM_MENUSELECT:
2028 {
2029 if (HIWORD(wParam) == 0xFFFF) // Allow all the menu flags
2030 {
2031 if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
2032 {
2033 GuiSendMenuEvent(Console, WM_MENUSELECT);
2034 LeaveCriticalSection(&Console->Lock);
2035 }
2036 }
2037 break;
2038 }
2039
2040 case WM_COMMAND:
2041 case WM_SYSCOMMAND:
2042 {
2043 Result = GuiConsoleHandleSysMenuCommand(GuiData, wParam, lParam);
2044 break;
2045 }
2046
2047 case WM_SETFOCUS:
2048 case WM_KILLFOCUS:
2049 {
2050 if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
2051 {
2052 BOOL SetFocus = (msg == WM_SETFOCUS);
2053 INPUT_RECORD er;
2054
2055 er.EventType = FOCUS_EVENT;
2056 er.Event.FocusEvent.bSetFocus = SetFocus;
2057 ConioProcessInputEvent(Console, &er);
2058
2059 if (SetFocus)
2060 DPRINT1("TODO: Create console caret\n");
2061 else
2062 DPRINT1("TODO: Destroy console caret\n");
2063
2064 LeaveCriticalSection(&Console->Lock);
2065 }
2066 break;
2067 }
2068
2069 case WM_GETMINMAXINFO:
2070 GuiConsoleGetMinMaxInfo(GuiData, (PMINMAXINFO)lParam);
2071 break;
2072
2073 case WM_MOVE:
2074 {
2075 if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
2076 {
2077 // TODO: Simplify the code.
2078 // See: GuiConsoleNotifyWndProc() PM_CREATE_CONSOLE.
2079
2080 RECT rcWnd;
2081
2082 /* Retrieve our real position */
2083 GetWindowRect(GuiData->hWindow, &rcWnd);
2084 GuiData->GuiInfo.WindowOrigin.x = rcWnd.left;
2085 GuiData->GuiInfo.WindowOrigin.y = rcWnd.top;
2086
2087 LeaveCriticalSection(&Console->Lock);
2088 }
2089 break;
2090 }
2091
2092 case WM_SIZE:
2093 GuiConsoleResize(GuiData, wParam, lParam);
2094 break;
2095
2096 case PM_RESIZE_TERMINAL:
2097 {
2098 PCONSOLE_SCREEN_BUFFER Buff = GuiData->ActiveBuffer;
2099 HDC hDC;
2100 HBITMAP hnew, hold;
2101
2102 DWORD Width, Height;
2103 UINT WidthUnit, HeightUnit;
2104
2105 GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
2106
2107 Width = Buff->ScreenBufferSize.X * WidthUnit ;
2108 Height = Buff->ScreenBufferSize.Y * HeightUnit;
2109
2110 /* Recreate the framebuffer */
2111 hDC = GetDC(GuiData->hWindow);
2112 hnew = CreateCompatibleBitmap(hDC, Width, Height);
2113 ReleaseDC(GuiData->hWindow, hDC);
2114 hold = SelectObject(GuiData->hMemDC, hnew);
2115 if (GuiData->hBitmap)
2116 {
2117 if (hold == GuiData->hBitmap) DeleteObject(GuiData->hBitmap);
2118 }
2119 GuiData->hBitmap = hnew;
2120
2121 /* Resize the window to the user's values */
2122 GuiData->WindowSizeLock = TRUE;
2123 GuiConsoleResizeWindow(GuiData, WidthUnit, HeightUnit);
2124 GuiData->WindowSizeLock = FALSE;
2125 break;
2126 }
2127
2128 case PM_APPLY_CONSOLE_INFO:
2129 {
2130 if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
2131 {
2132 GuiApplyUserSettings(GuiData, (HANDLE)wParam, (BOOL)lParam);
2133 LeaveCriticalSection(&Console->Lock);
2134 }
2135 break;
2136 }
2137
2138 case PM_CONSOLE_BEEP:
2139 DPRINT1("Beep !!\n");
2140 Beep(800, 200);
2141 break;
2142
2143 // case PM_CONSOLE_SET_TITLE:
2144 // SetWindowText(GuiData->hWindow, GuiData->Console->Title.Buffer);
2145 // break;
2146
2147 default: Default:
2148 Result = DefWindowProcW(hWnd, msg, wParam, lParam);
2149 break;
2150 }
2151
2152 return Result;
2153 }
2154
2155 /* EOF */