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