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