da3a0f116141b18d2331b512f6ccd55fa3546b88
[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 HMENU hMenu = GetSystemMenu(hWnd, FALSE);
257 if (hMenu != NULL)
258 {
259 AppendMenuItems(hMenu, GuiConsoleMainMenuItems);
260 DrawMenuBar(hWnd);
261 }
262 }
263
264 static VOID
265 SendMenuEvent(PCONSOLE Console, UINT CmdId)
266 {
267 INPUT_RECORD er;
268
269 er.EventType = MENU_EVENT;
270 er.Event.MenuEvent.dwCommandId = CmdId;
271
272 DPRINT("Menu item ID: %d\n", CmdId);
273 ConioProcessInputEvent(Console, &er);
274 }
275
276 static VOID
277 Copy(PGUI_CONSOLE_DATA GuiData);
278 static VOID
279 Paste(PGUI_CONSOLE_DATA GuiData);
280 static VOID
281 UpdateSelection(PGUI_CONSOLE_DATA GuiData, PCOORD coord);
282
283 static VOID
284 Mark(PGUI_CONSOLE_DATA GuiData)
285 {
286 PCONSOLE Console = GuiData->Console;
287 PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer;
288
289 /* Clear the old selection */
290 // UpdateSelection(GuiData, NULL);
291 Console->Selection.dwFlags = CONSOLE_NO_SELECTION;
292
293 /* Restart a new selection */
294 Console->dwSelectionCursor.X = ActiveBuffer->ViewOrigin.X;
295 Console->dwSelectionCursor.Y = ActiveBuffer->ViewOrigin.Y;
296 Console->Selection.dwSelectionAnchor = Console->dwSelectionCursor;
297 UpdateSelection(GuiData, &Console->Selection.dwSelectionAnchor);
298 }
299
300 static VOID
301 SelectAll(PGUI_CONSOLE_DATA GuiData)
302 {
303 PCONSOLE Console = GuiData->Console;
304 PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer;
305
306 /* Clear the old selection */
307 // UpdateSelection(GuiData, NULL);
308 Console->Selection.dwFlags = CONSOLE_NO_SELECTION;
309
310 /*
311 * The selection area extends to the whole screen buffer's width.
312 */
313 Console->Selection.dwSelectionAnchor.X = 0;
314 Console->Selection.dwSelectionAnchor.Y = 0;
315 Console->dwSelectionCursor.X = ActiveBuffer->ScreenBufferSize.X - 1;
316
317 /*
318 * Determine whether the selection must extend to just some part
319 * (for text-mode screen buffers) or to all of the screen buffer's
320 * height (for graphics ones).
321 */
322 if (GetType(ActiveBuffer) == TEXTMODE_BUFFER)
323 {
324 /*
325 * We select all the characters from the first line
326 * to the line where the cursor is positioned.
327 */
328 Console->dwSelectionCursor.Y = ActiveBuffer->CursorPosition.Y;
329 }
330 else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */
331 {
332 /*
333 * We select all the screen buffer area.
334 */
335 Console->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1;
336 }
337
338 /* Restart a new selection */
339 Console->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION;
340 UpdateSelection(GuiData, &Console->dwSelectionCursor);
341 }
342
343 static LRESULT
344 OnCommand(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam)
345 {
346 LRESULT Ret = TRUE;
347 PCONSOLE Console = GuiData->Console;
348
349 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
350 {
351 Ret = FALSE;
352 goto Quit;
353 }
354
355 /*
356 * In case the selected menu item belongs to the user-reserved menu id range,
357 * send to him a menu event and return directly. The user must handle those
358 * reserved menu commands...
359 */
360 if (GuiData->CmdIdLow <= (UINT)wParam && (UINT)wParam <= GuiData->CmdIdHigh)
361 {
362 SendMenuEvent(Console, (UINT)wParam);
363 goto Unlock_Quit;
364 }
365
366 /* ... otherwise, perform actions. */
367 switch (wParam)
368 {
369 case ID_SYSTEM_EDIT_MARK:
370 Mark(GuiData);
371 break;
372
373 case ID_SYSTEM_EDIT_COPY:
374 Copy(GuiData);
375 break;
376
377 case ID_SYSTEM_EDIT_PASTE:
378 Paste(GuiData);
379 break;
380
381 case ID_SYSTEM_EDIT_SELECTALL:
382 SelectAll(GuiData);
383 break;
384
385 case ID_SYSTEM_EDIT_SCROLL:
386 DPRINT1("Scrolling is not handled yet\n");
387 break;
388
389 case ID_SYSTEM_EDIT_FIND:
390 DPRINT1("Finding is not handled yet\n");
391 break;
392
393 case ID_SYSTEM_DEFAULTS:
394 GuiConsoleShowConsoleProperties(GuiData, TRUE);
395 break;
396
397 case ID_SYSTEM_PROPERTIES:
398 GuiConsoleShowConsoleProperties(GuiData, FALSE);
399 break;
400
401 default:
402 Ret = FALSE;
403 break;
404 }
405
406 Unlock_Quit:
407 LeaveCriticalSection(&Console->Lock);
408 Quit:
409 if (!Ret)
410 Ret = DefWindowProcW(GuiData->hWindow, WM_SYSCOMMAND, wParam, lParam);
411
412 return Ret;
413 }
414
415 static PGUI_CONSOLE_DATA
416 GuiGetGuiData(HWND hWnd)
417 {
418 /* This function ensures that the console pointer is not NULL */
419 PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
420 return ( ((GuiData == NULL) || (GuiData->hWindow == hWnd && GuiData->Console != NULL)) ? GuiData : NULL );
421 }
422
423 static VOID
424 ResizeConWnd(PGUI_CONSOLE_DATA GuiData, DWORD WidthUnit, DWORD HeightUnit)
425 {
426 PCONSOLE_SCREEN_BUFFER Buff = GuiData->ActiveBuffer;
427 SCROLLINFO sInfo;
428
429 DWORD Width, Height;
430
431 Width = Buff->ViewSize.X * WidthUnit +
432 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
433 Height = Buff->ViewSize.Y * HeightUnit +
434 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
435
436 /* Set scrollbar sizes */
437 sInfo.cbSize = sizeof(SCROLLINFO);
438 sInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
439 sInfo.nMin = 0;
440 if (Buff->ScreenBufferSize.Y > Buff->ViewSize.Y)
441 {
442 sInfo.nMax = Buff->ScreenBufferSize.Y - 1;
443 sInfo.nPage = Buff->ViewSize.Y;
444 sInfo.nPos = Buff->ViewOrigin.Y;
445 SetScrollInfo(GuiData->hWindow, SB_VERT, &sInfo, TRUE);
446 Width += GetSystemMetrics(SM_CXVSCROLL);
447 ShowScrollBar(GuiData->hWindow, SB_VERT, TRUE);
448 }
449 else
450 {
451 ShowScrollBar(GuiData->hWindow, SB_VERT, FALSE);
452 }
453
454 if (Buff->ScreenBufferSize.X > Buff->ViewSize.X)
455 {
456 sInfo.nMax = Buff->ScreenBufferSize.X - 1;
457 sInfo.nPage = Buff->ViewSize.X;
458 sInfo.nPos = Buff->ViewOrigin.X;
459 SetScrollInfo(GuiData->hWindow, SB_HORZ, &sInfo, TRUE);
460 Height += GetSystemMetrics(SM_CYHSCROLL);
461 ShowScrollBar(GuiData->hWindow, SB_HORZ, TRUE);
462 }
463 else
464 {
465 ShowScrollBar(GuiData->hWindow, SB_HORZ, FALSE);
466 }
467
468 /* Resize the window */
469 SetWindowPos(GuiData->hWindow, NULL, 0, 0, Width, Height,
470 SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOCOPYBITS);
471 // NOTE: The SWP_NOCOPYBITS flag can be replaced by a subsequent call
472 // to: InvalidateRect(GuiData->hWindow, NULL, TRUE);
473 }
474
475 static BOOL
476 OnNcCreate(HWND hWnd, LPCREATESTRUCTW Create)
477 {
478 PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)Create->lpCreateParams;
479 PCONSOLE Console;
480 HDC hDC;
481 HFONT OldFont;
482 TEXTMETRICW Metrics;
483 SIZE CharSize;
484
485 if (NULL == GuiData)
486 {
487 DPRINT1("GuiConsoleNcCreate: No GUI data\n");
488 return FALSE;
489 }
490
491 Console = GuiData->Console;
492
493 GuiData->hWindow = hWnd;
494
495 GuiData->Font = CreateFontW(LOWORD(GuiData->GuiInfo.FontSize),
496 0, // HIWORD(GuiData->GuiInfo.FontSize),
497 0,
498 TA_BASELINE,
499 GuiData->GuiInfo.FontWeight,
500 FALSE,
501 FALSE,
502 FALSE,
503 OEM_CHARSET,
504 OUT_DEFAULT_PRECIS,
505 CLIP_DEFAULT_PRECIS,
506 NONANTIALIASED_QUALITY,
507 FIXED_PITCH | GuiData->GuiInfo.FontFamily /* FF_DONTCARE */,
508 GuiData->GuiInfo.FaceName);
509
510 if (NULL == GuiData->Font)
511 {
512 DPRINT1("GuiConsoleNcCreate: CreateFont failed\n");
513 GuiData->hWindow = NULL;
514 SetEvent(GuiData->hGuiInitEvent);
515 return FALSE;
516 }
517 hDC = GetDC(GuiData->hWindow);
518 if (NULL == hDC)
519 {
520 DPRINT1("GuiConsoleNcCreate: GetDC failed\n");
521 DeleteObject(GuiData->Font);
522 GuiData->hWindow = NULL;
523 SetEvent(GuiData->hGuiInitEvent);
524 return FALSE;
525 }
526 OldFont = SelectObject(hDC, GuiData->Font);
527 if (NULL == OldFont)
528 {
529 DPRINT1("GuiConsoleNcCreate: SelectObject failed\n");
530 ReleaseDC(GuiData->hWindow, hDC);
531 DeleteObject(GuiData->Font);
532 GuiData->hWindow = NULL;
533 SetEvent(GuiData->hGuiInitEvent);
534 return FALSE;
535 }
536 if (!GetTextMetricsW(hDC, &Metrics))
537 {
538 DPRINT1("GuiConsoleNcCreate: GetTextMetrics failed\n");
539 SelectObject(hDC, OldFont);
540 ReleaseDC(GuiData->hWindow, hDC);
541 DeleteObject(GuiData->Font);
542 GuiData->hWindow = NULL;
543 SetEvent(GuiData->hGuiInitEvent);
544 return FALSE;
545 }
546 GuiData->CharWidth = Metrics.tmMaxCharWidth;
547 GuiData->CharHeight = Metrics.tmHeight + Metrics.tmExternalLeading;
548
549 /* Measure real char width more precisely if possible. */
550 if (GetTextExtentPoint32W(hDC, L"R", 1, &CharSize))
551 GuiData->CharWidth = CharSize.cx;
552
553 SelectObject(hDC, OldFont);
554
555 ReleaseDC(GuiData->hWindow, hDC);
556
557 /* Initialize the terminal framebuffer */
558 GuiData->hMemDC = CreateCompatibleDC(NULL);
559 GuiData->hBitmap = NULL;
560 GuiData->hSysPalette = NULL; /* Original system palette */
561
562 /* Update the icons of the window */
563 if (GuiData->hIcon != ghDefaultIcon)
564 {
565 DefWindowProcW(GuiData->hWindow, WM_SETICON, ICON_BIG , (LPARAM)GuiData->hIcon );
566 DefWindowProcW(GuiData->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)GuiData->hIconSm);
567 }
568
569 // FIXME: Keep these instructions here ? ///////////////////////////////////
570 Console->ActiveBuffer->CursorBlinkOn = TRUE;
571 Console->ActiveBuffer->ForceCursorOff = FALSE;
572 ////////////////////////////////////////////////////////////////////////////
573
574 SetWindowLongPtrW(GuiData->hWindow, GWLP_USERDATA, (DWORD_PTR)GuiData);
575
576 SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL);
577 CreateSysMenu(GuiData->hWindow);
578
579 DPRINT("OnNcCreate - setting start event\n");
580 SetEvent(GuiData->hGuiInitEvent);
581
582 return (BOOL)DefWindowProcW(GuiData->hWindow, WM_NCCREATE, 0, (LPARAM)Create);
583 }
584
585
586 BOOL
587 EnterFullScreen(PGUI_CONSOLE_DATA GuiData);
588 VOID
589 LeaveFullScreen(PGUI_CONSOLE_DATA GuiData);
590 VOID
591 SwitchFullScreen(PGUI_CONSOLE_DATA GuiData, BOOL FullScreen);
592 VOID
593 GuiConsoleSwitchFullScreen(PGUI_CONSOLE_DATA GuiData);
594
595 static VOID
596 OnActivate(PGUI_CONSOLE_DATA GuiData, WPARAM wParam)
597 {
598 PCONSOLE Console = GuiData->Console;
599 WORD ActivationState = LOWORD(wParam);
600
601 DPRINT1("WM_ACTIVATE - ActivationState = %d\n");
602
603 if ( ActivationState == WA_ACTIVE ||
604 ActivationState == WA_CLICKACTIVE )
605 {
606 if (GuiData->GuiInfo.FullScreen)
607 {
608 EnterFullScreen(GuiData);
609 // // PostMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_RESTORE, 0);
610 // SendMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_RESTORE, 0);
611 }
612 }
613 else // if (ActivationState == WA_INACTIVE)
614 {
615 if (GuiData->GuiInfo.FullScreen)
616 {
617 SendMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_MINIMIZE, 0);
618 LeaveFullScreen(GuiData);
619 // // PostMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_MINIMIZE, 0);
620 // SendMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_MINIMIZE, 0);
621 }
622 }
623
624 /*
625 * When we are in QuickEdit mode, ignore the next mouse signal
626 * when we are going to be enabled again via the mouse, in order
627 * to prevent e.g. an erroneous right-click from the user which
628 * would have as an effect to paste some unwanted text...
629 */
630 if (Console->QuickEdit && (ActivationState == WA_CLICKACTIVE))
631 GuiData->IgnoreNextMouseSignal = TRUE;
632 }
633
634 static VOID
635 OnFocus(PGUI_CONSOLE_DATA GuiData, BOOL SetFocus)
636 {
637 PCONSOLE Console = GuiData->Console;
638 INPUT_RECORD er;
639
640 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
641
642 er.EventType = FOCUS_EVENT;
643 er.Event.FocusEvent.bSetFocus = SetFocus;
644 ConioProcessInputEvent(Console, &er);
645
646 if (SetFocus)
647 DPRINT1("TODO: Create console caret\n");
648 else
649 DPRINT1("TODO: Destroy console caret\n");
650
651 LeaveCriticalSection(&Console->Lock);
652 }
653
654 static VOID
655 SmallRectToRect(PGUI_CONSOLE_DATA GuiData, PRECT Rect, PSMALL_RECT SmallRect)
656 {
657 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
658 UINT WidthUnit, HeightUnit;
659
660 GetScreenBufferSizeUnits(Buffer, GuiData, &WidthUnit, &HeightUnit);
661
662 Rect->left = (SmallRect->Left - Buffer->ViewOrigin.X) * WidthUnit ;
663 Rect->top = (SmallRect->Top - Buffer->ViewOrigin.Y) * HeightUnit;
664 Rect->right = (SmallRect->Right + 1 - Buffer->ViewOrigin.X) * WidthUnit ;
665 Rect->bottom = (SmallRect->Bottom + 1 - Buffer->ViewOrigin.Y) * HeightUnit;
666 }
667
668 static VOID
669 UpdateSelection(PGUI_CONSOLE_DATA GuiData, PCOORD coord)
670 {
671 PCONSOLE Console = GuiData->Console;
672 RECT oldRect;
673
674 SmallRectToRect(GuiData, &oldRect, &Console->Selection.srSelection);
675
676 if (coord != NULL)
677 {
678 RECT newRect;
679 SMALL_RECT rc;
680
681 /* Exchange left/top with right/bottom if required */
682 rc.Left = min(Console->Selection.dwSelectionAnchor.X, coord->X);
683 rc.Top = min(Console->Selection.dwSelectionAnchor.Y, coord->Y);
684 rc.Right = max(Console->Selection.dwSelectionAnchor.X, coord->X);
685 rc.Bottom = max(Console->Selection.dwSelectionAnchor.Y, coord->Y);
686
687 SmallRectToRect(GuiData, &newRect, &rc);
688
689 if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
690 {
691 if (memcmp(&rc, &Console->Selection.srSelection, sizeof(SMALL_RECT)) != 0)
692 {
693 HRGN rgn1, rgn2;
694
695 /* Calculate the region that needs to be updated */
696 if ((rgn1 = CreateRectRgnIndirect(&oldRect)))
697 {
698 if ((rgn2 = CreateRectRgnIndirect(&newRect)))
699 {
700 if (CombineRgn(rgn1, rgn2, rgn1, RGN_XOR) != ERROR)
701 {
702 InvalidateRgn(GuiData->hWindow, rgn1, FALSE);
703 }
704 DeleteObject(rgn2);
705 }
706 DeleteObject(rgn1);
707 }
708 }
709 }
710 else
711 {
712 InvalidateRect(GuiData->hWindow, &newRect, FALSE);
713 }
714
715 Console->Selection.dwFlags |= CONSOLE_SELECTION_NOT_EMPTY;
716 Console->Selection.srSelection = rc;
717 Console->dwSelectionCursor = *coord;
718
719 if ((Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) == 0)
720 {
721 LPWSTR SelectionType, WindowTitle = NULL;
722 SIZE_T Length = 0;
723
724 /* Clear the old selection */
725 if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
726 {
727 InvalidateRect(GuiData->hWindow, &oldRect, FALSE);
728 }
729
730 if (Console->Selection.dwFlags & CONSOLE_MOUSE_SELECTION)
731 {
732 SelectionType = L"Selection - ";
733 }
734 else
735 {
736 SelectionType = L"Mark - ";
737 }
738
739 Length = Console->Title.Length + wcslen(SelectionType) + 1;
740 WindowTitle = ConsoleAllocHeap(0, Length * sizeof(WCHAR));
741 wcscpy(WindowTitle, SelectionType);
742 wcscat(WindowTitle, Console->Title.Buffer);
743 SetWindowText(GuiData->hWindow, WindowTitle);
744 ConsoleFreeHeap(WindowTitle);
745
746 Console->Selection.dwFlags |= CONSOLE_SELECTION_IN_PROGRESS;
747 ConioPause(Console, PAUSED_FROM_SELECTION);
748 }
749 }
750 else
751 {
752 /* Clear the selection */
753 if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
754 {
755 InvalidateRect(GuiData->hWindow, &oldRect, FALSE);
756 }
757
758 Console->Selection.dwFlags = CONSOLE_NO_SELECTION;
759 ConioUnpause(Console, PAUSED_FROM_SELECTION);
760
761 SetWindowText(GuiData->hWindow, Console->Title.Buffer);
762 }
763 }
764
765
766 VOID
767 GuiPaintTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer,
768 PGUI_CONSOLE_DATA GuiData,
769 PRECT rcView,
770 PRECT rcFramebuffer);
771 VOID
772 GuiPaintGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer,
773 PGUI_CONSOLE_DATA GuiData,
774 PRECT rcView,
775 PRECT rcFramebuffer);
776
777 static VOID
778 OnPaint(PGUI_CONSOLE_DATA GuiData)
779 {
780 BOOL Success = TRUE;
781 PCONSOLE Console = GuiData->Console;
782 PCONSOLE_SCREEN_BUFFER ActiveBuffer;
783 PAINTSTRUCT ps;
784 RECT rcPaint;
785
786 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
787 {
788 Success = FALSE;
789 goto Quit;
790 }
791 ActiveBuffer = GuiData->ActiveBuffer;
792
793 BeginPaint(GuiData->hWindow, &ps);
794 if (ps.hdc != NULL &&
795 ps.rcPaint.left < ps.rcPaint.right &&
796 ps.rcPaint.top < ps.rcPaint.bottom)
797 {
798 EnterCriticalSection(&GuiData->Lock);
799
800 /* Compose the current screen-buffer on-memory */
801 if (GetType(ActiveBuffer) == TEXTMODE_BUFFER)
802 {
803 GuiPaintTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)ActiveBuffer,
804 GuiData, &ps.rcPaint, &rcPaint);
805 }
806 else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */
807 {
808 GuiPaintGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)ActiveBuffer,
809 GuiData, &ps.rcPaint, &rcPaint);
810 }
811
812 /* Send it to screen */
813 BitBlt(ps.hdc,
814 ps.rcPaint.left,
815 ps.rcPaint.top,
816 rcPaint.right - rcPaint.left,
817 rcPaint.bottom - rcPaint.top,
818 GuiData->hMemDC,
819 rcPaint.left,
820 rcPaint.top,
821 SRCCOPY);
822
823 if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
824 {
825 SmallRectToRect(GuiData, &rcPaint, &Console->Selection.srSelection);
826
827 /* Invert the selection */
828 if (IntersectRect(&rcPaint, &ps.rcPaint, &rcPaint))
829 {
830 InvertRect(ps.hdc, &rcPaint);
831 }
832 }
833
834 LeaveCriticalSection(&GuiData->Lock);
835 }
836 EndPaint(GuiData->hWindow, &ps);
837
838 Quit:
839 if (Success)
840 LeaveCriticalSection(&Console->Lock);
841 else
842 DefWindowProcW(GuiData->hWindow, WM_PAINT, 0, 0);
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 (Console->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 ((Console->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 (Console->dwSelectionCursor.X > 0)
933 Console->dwSelectionCursor.X--;
934
935 break;
936 }
937
938 case VK_RIGHT:
939 {
940 Interpreted = TRUE;
941 if (Console->dwSelectionCursor.X < ActiveBuffer->ScreenBufferSize.X - 1)
942 Console->dwSelectionCursor.X++;
943
944 break;
945 }
946
947 case VK_UP:
948 {
949 Interpreted = TRUE;
950 if (Console->dwSelectionCursor.Y > 0)
951 Console->dwSelectionCursor.Y--;
952
953 break;
954 }
955
956 case VK_DOWN:
957 {
958 Interpreted = TRUE;
959 if (Console->dwSelectionCursor.Y < ActiveBuffer->ScreenBufferSize.Y - 1)
960 Console->dwSelectionCursor.Y++;
961
962 break;
963 }
964
965 case VK_HOME:
966 {
967 Interpreted = TRUE;
968 Console->dwSelectionCursor.X = 0;
969 Console->dwSelectionCursor.Y = 0;
970 break;
971 }
972
973 case VK_END:
974 {
975 Interpreted = TRUE;
976 Console->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1;
977 break;
978 }
979
980 case VK_PRIOR:
981 {
982 Interpreted = TRUE;
983 Console->dwSelectionCursor.Y -= ActiveBuffer->ViewSize.Y;
984 if (Console->dwSelectionCursor.Y < 0)
985 Console->dwSelectionCursor.Y = 0;
986
987 break;
988 }
989
990 case VK_NEXT:
991 {
992 Interpreted = TRUE;
993 Console->dwSelectionCursor.Y += ActiveBuffer->ViewSize.Y;
994 if (Console->dwSelectionCursor.Y >= ActiveBuffer->ScreenBufferSize.Y)
995 Console->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 Console->Selection.dwSelectionAnchor = Console->dwSelectionCursor;
1008
1009 UpdateSelection(GuiData, &Console->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 ((Console->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 ConDrvConsoleProcessCtrlEvent(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 ( (Console->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 Console->Selection.dwFlags = CONSOLE_NO_SELECTION;
1278
1279 /* Restart a new selection */
1280 Console->Selection.dwSelectionAnchor = PointToCoord(GuiData, lParam);
1281 SetCapture(GuiData->hWindow);
1282 Console->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN;
1283 UpdateSelection(GuiData, &Console->Selection.dwSelectionAnchor);
1284
1285 break;
1286 }
1287
1288 case WM_LBUTTONUP:
1289 {
1290 // COORD c;
1291
1292 if (!(Console->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break;
1293
1294 // c = PointToCoord(GuiData, lParam);
1295 Console->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 Console->Selection.dwSelectionAnchor = cL;
1342 Console->dwSelectionCursor = cR;
1343
1344 Console->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN;
1345 UpdateSelection(GuiData, &Console->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 (!(Console->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 (!(Console->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 PCONSOLE Console = GuiData->Console;
1678 RECT rcWnd;
1679
1680 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
1681
1682 // TODO: Simplify the code.
1683 // See: GuiConsoleNotifyWndProc() PM_CREATE_CONSOLE.
1684
1685 /* Retrieve our real position */
1686 GetWindowRect(GuiData->hWindow, &rcWnd);
1687 GuiData->GuiInfo.WindowOrigin.x = rcWnd.left;
1688 GuiData->GuiInfo.WindowOrigin.y = rcWnd.top;
1689
1690 LeaveCriticalSection(&Console->Lock);
1691 }
1692
1693 /*
1694 // HACK: This functionality is standard for general scrollbars. Don't add it by hand.
1695
1696 VOID
1697 FASTCALL
1698 GuiConsoleHandleScrollbarMenu(VOID)
1699 {
1700 HMENU hMenu;
1701
1702 hMenu = CreatePopupMenu();
1703 if (hMenu == NULL)
1704 {
1705 DPRINT("CreatePopupMenu failed\n");
1706 return;
1707 }
1708
1709 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLHERE);
1710 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
1711 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLTOP);
1712 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLBOTTOM);
1713 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
1714 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_UP);
1715 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_DOWN);
1716 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
1717 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLUP);
1718 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLDOWN);
1719 }
1720 */
1721
1722 static LRESULT
1723 OnScroll(PGUI_CONSOLE_DATA GuiData, UINT uMsg, WPARAM wParam)
1724 {
1725 PCONSOLE Console = GuiData->Console;
1726 PCONSOLE_SCREEN_BUFFER Buff;
1727 SCROLLINFO sInfo;
1728 int fnBar;
1729 int old_pos, Maximum;
1730 PSHORT pShowXY;
1731
1732 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return 0;
1733
1734 Buff = GuiData->ActiveBuffer;
1735
1736 if (uMsg == WM_HSCROLL)
1737 {
1738 fnBar = SB_HORZ;
1739 Maximum = Buff->ScreenBufferSize.X - Buff->ViewSize.X;
1740 pShowXY = &Buff->ViewOrigin.X;
1741 }
1742 else
1743 {
1744 fnBar = SB_VERT;
1745 Maximum = Buff->ScreenBufferSize.Y - Buff->ViewSize.Y;
1746 pShowXY = &Buff->ViewOrigin.Y;
1747 }
1748
1749 /* set scrollbar sizes */
1750 sInfo.cbSize = sizeof(SCROLLINFO);
1751 sInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE | SIF_TRACKPOS;
1752
1753 if (!GetScrollInfo(GuiData->hWindow, fnBar, &sInfo)) goto Quit;
1754
1755 old_pos = sInfo.nPos;
1756
1757 switch (LOWORD(wParam))
1758 {
1759 case SB_LINELEFT:
1760 sInfo.nPos -= 1;
1761 break;
1762
1763 case SB_LINERIGHT:
1764 sInfo.nPos += 1;
1765 break;
1766
1767 case SB_PAGELEFT:
1768 sInfo.nPos -= sInfo.nPage;
1769 break;
1770
1771 case SB_PAGERIGHT:
1772 sInfo.nPos += sInfo.nPage;
1773 break;
1774
1775 case SB_THUMBTRACK:
1776 sInfo.nPos = sInfo.nTrackPos;
1777 ConioPause(Console, PAUSED_FROM_SCROLLBAR);
1778 break;
1779
1780 case SB_THUMBPOSITION:
1781 ConioUnpause(Console, PAUSED_FROM_SCROLLBAR);
1782 break;
1783
1784 case SB_TOP:
1785 sInfo.nPos = sInfo.nMin;
1786 break;
1787
1788 case SB_BOTTOM:
1789 sInfo.nPos = sInfo.nMax;
1790 break;
1791
1792 default:
1793 break;
1794 }
1795
1796 sInfo.nPos = max(sInfo.nPos, 0);
1797 sInfo.nPos = min(sInfo.nPos, Maximum);
1798
1799 if (old_pos != sInfo.nPos)
1800 {
1801 USHORT OldX = Buff->ViewOrigin.X;
1802 USHORT OldY = Buff->ViewOrigin.Y;
1803 UINT WidthUnit, HeightUnit;
1804
1805 *pShowXY = sInfo.nPos;
1806
1807 GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
1808
1809 ScrollWindowEx(GuiData->hWindow,
1810 (OldX - Buff->ViewOrigin.X) * WidthUnit ,
1811 (OldY - Buff->ViewOrigin.Y) * HeightUnit,
1812 NULL,
1813 NULL,
1814 NULL,
1815 NULL,
1816 SW_INVALIDATE);
1817
1818 sInfo.fMask = SIF_POS;
1819 SetScrollInfo(GuiData->hWindow, fnBar, &sInfo, TRUE);
1820
1821 UpdateWindow(GuiData->hWindow);
1822 // InvalidateRect(GuiData->hWindow, NULL, FALSE);
1823 }
1824
1825 Quit:
1826 LeaveCriticalSection(&Console->Lock);
1827 return 0;
1828 }
1829
1830
1831 static LRESULT CALLBACK
1832 ConWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1833 {
1834 LRESULT Result = 0;
1835 PGUI_CONSOLE_DATA GuiData = NULL;
1836 PCONSOLE Console = NULL;
1837
1838 /*
1839 * - If it's the first time we create a window for the terminal,
1840 * just initialize it and return.
1841 *
1842 * - If we are destroying the window, just do it and return.
1843 */
1844 if (msg == WM_NCCREATE)
1845 {
1846 return (LRESULT)OnNcCreate(hWnd, (LPCREATESTRUCTW)lParam);
1847 }
1848 else if (msg == WM_NCDESTROY)
1849 {
1850 return OnNcDestroy(hWnd);
1851 }
1852
1853 /*
1854 * Now the terminal window is initialized.
1855 * Get the terminal data via the window's data.
1856 * If there is no data, just go away.
1857 */
1858 GuiData = GuiGetGuiData(hWnd);
1859 if (GuiData == NULL) return DefWindowProcW(hWnd, msg, wParam, lParam);
1860
1861 // TEMPORARY HACK until all of the functions can deal with a NULL GuiData->ActiveBuffer ...
1862 if (GuiData->ActiveBuffer == NULL) return DefWindowProcW(hWnd, msg, wParam, lParam);
1863
1864 /*
1865 * Just retrieve a pointer to the console in case somebody needs it.
1866 * It is not NULL because it was checked in GuiGetGuiData.
1867 * Each helper function which needs the console has to validate and lock it.
1868 */
1869 Console = GuiData->Console;
1870
1871 /* We have a console, start message dispatching */
1872 switch (msg)
1873 {
1874 case WM_ACTIVATE:
1875 OnActivate(GuiData, wParam);
1876 break;
1877
1878 case WM_CLOSE:
1879 if (OnClose(GuiData)) goto Default;
1880 break;
1881
1882 case WM_PAINT:
1883 OnPaint(GuiData);
1884 break;
1885
1886 case WM_TIMER:
1887 OnTimer(GuiData);
1888 break;
1889
1890 case WM_PALETTECHANGED:
1891 {
1892 DPRINT("WM_PALETTECHANGED called\n");
1893
1894 /*
1895 * Protects against infinite loops:
1896 * "... A window that receives this message must not realize
1897 * its palette, unless it determines that wParam does not contain
1898 * its own window handle." (WM_PALETTECHANGED description - MSDN)
1899 *
1900 * This message is sent to all windows, including the one that
1901 * changed the system palette and caused this message to be sent.
1902 * The wParam of this message contains the handle of the window
1903 * that caused the system palette to change. To avoid an infinite
1904 * loop, care must be taken to check that the wParam of this message
1905 * does not match the window's handle.
1906 */
1907 if ((HWND)wParam == hWnd) break;
1908
1909 DPRINT("WM_PALETTECHANGED ok\n");
1910 OnPaletteChanged(GuiData);
1911 DPRINT("WM_PALETTECHANGED quit\n");
1912 break;
1913 }
1914
1915 case WM_KEYDOWN:
1916 case WM_KEYUP:
1917 case WM_CHAR:
1918 case WM_DEADCHAR:
1919 case WM_SYSKEYDOWN:
1920 case WM_SYSKEYUP:
1921 case WM_SYSCHAR:
1922 case WM_SYSDEADCHAR:
1923 {
1924 /* Detect Alt-Enter presses and switch back and forth to fullscreen mode */
1925 if (msg == WM_SYSKEYDOWN && (HIWORD(lParam) & KF_ALTDOWN) && wParam == VK_RETURN)
1926 {
1927 /* Switch only at first Alt-Enter press, and ignore subsequent key repetitions */
1928 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
1929 GuiConsoleSwitchFullScreen(GuiData);
1930
1931 break;
1932 }
1933
1934 OnKey(GuiData, msg, wParam, lParam);
1935 break;
1936 }
1937
1938 case WM_SETCURSOR:
1939 {
1940 /*
1941 * The message was sent because we are manually triggering a change.
1942 * Check whether the mouse is indeed present on this console window
1943 * and take appropriate decisions.
1944 */
1945 if (wParam == -1 && lParam == -1)
1946 {
1947 POINT mouseCoords;
1948 HWND hWndHit;
1949
1950 /* Get the placement of the mouse */
1951 GetCursorPos(&mouseCoords);
1952
1953 /* On which window is placed the mouse ? */
1954 hWndHit = WindowFromPoint(mouseCoords);
1955
1956 /* It's our window. Perform the hit-test to be used later on. */
1957 if (hWndHit == hWnd)
1958 {
1959 wParam = (WPARAM)hWnd;
1960 lParam = DefWindowProcW(hWndHit, WM_NCHITTEST, 0,
1961 MAKELPARAM(mouseCoords.x, mouseCoords.y));
1962 }
1963 }
1964
1965 /* Set the mouse cursor only when we are in the client area */
1966 if ((HWND)wParam == hWnd && LOWORD(lParam) == HTCLIENT)
1967 {
1968 if (GuiData->MouseCursorRefCount >= 0)
1969 {
1970 /* Show the cursor */
1971 SetCursor(GuiData->hCursor);
1972 }
1973 else
1974 {
1975 /* Hide the cursor if the reference count is negative */
1976 SetCursor(NULL);
1977 }
1978 return TRUE;
1979 }
1980 else
1981 {
1982 goto Default;
1983 }
1984 }
1985
1986 case WM_LBUTTONDOWN:
1987 case WM_MBUTTONDOWN:
1988 case WM_RBUTTONDOWN:
1989 case WM_LBUTTONUP:
1990 case WM_MBUTTONUP:
1991 case WM_RBUTTONUP:
1992 case WM_LBUTTONDBLCLK:
1993 case WM_MBUTTONDBLCLK:
1994 case WM_RBUTTONDBLCLK:
1995 case WM_MOUSEMOVE:
1996 case WM_MOUSEWHEEL:
1997 case WM_MOUSEHWHEEL:
1998 {
1999 Result = OnMouse(GuiData, msg, wParam, lParam);
2000 break;
2001 }
2002
2003 case WM_HSCROLL:
2004 case WM_VSCROLL:
2005 {
2006 Result = OnScroll(GuiData, msg, wParam);
2007 break;
2008 }
2009
2010 case WM_CONTEXTMENU:
2011 {
2012 if (DefWindowProcW(hWnd /*GuiData->hWindow*/, WM_NCHITTEST, 0, lParam) == HTCLIENT)
2013 {
2014 HMENU hMenu = CreatePopupMenu();
2015 if (hMenu != NULL)
2016 {
2017 AppendMenuItems(hMenu, GuiConsoleEditMenuItems);
2018 TrackPopupMenuEx(hMenu,
2019 TPM_RIGHTBUTTON,
2020 GET_X_LPARAM(lParam),
2021 GET_Y_LPARAM(lParam),
2022 hWnd,
2023 NULL);
2024 DestroyMenu(hMenu);
2025 }
2026 break;
2027 }
2028 else
2029 {
2030 goto Default;
2031 }
2032 }
2033
2034 case WM_INITMENU:
2035 {
2036 HMENU hMenu = (HMENU)wParam;
2037 if (hMenu != NULL)
2038 {
2039 /* Enable or disable the Close menu item */
2040 EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND |
2041 (GuiData->IsCloseButtonEnabled ? MF_ENABLED : MF_GRAYED));
2042
2043 /* Enable or disable the Copy and Paste items */
2044 EnableMenuItem(hMenu, ID_SYSTEM_EDIT_COPY , MF_BYCOMMAND |
2045 ((Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) &&
2046 (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) ? MF_ENABLED : MF_GRAYED));
2047 // FIXME: Following whether the active screen buffer is text-mode
2048 // or graphics-mode, search for CF_UNICODETEXT or CF_BITMAP formats.
2049 EnableMenuItem(hMenu, ID_SYSTEM_EDIT_PASTE, MF_BYCOMMAND |
2050 (!(Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) &&
2051 IsClipboardFormatAvailable(CF_UNICODETEXT) ? MF_ENABLED : MF_GRAYED));
2052 }
2053
2054 if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
2055 {
2056 SendMenuEvent(Console, WM_INITMENU);
2057 LeaveCriticalSection(&Console->Lock);
2058 }
2059 break;
2060 }
2061
2062 case WM_MENUSELECT:
2063 {
2064 if (HIWORD(wParam) == 0xFFFF) // Allow all the menu flags
2065 {
2066 if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
2067 {
2068 SendMenuEvent(Console, WM_MENUSELECT);
2069 LeaveCriticalSection(&Console->Lock);
2070 }
2071 }
2072 break;
2073 }
2074
2075 case WM_COMMAND:
2076 case WM_SYSCOMMAND:
2077 {
2078 Result = OnCommand(GuiData, wParam, lParam);
2079 break;
2080 }
2081
2082 case WM_SETFOCUS:
2083 case WM_KILLFOCUS:
2084 OnFocus(GuiData, (msg == WM_SETFOCUS));
2085 break;
2086
2087 case WM_GETMINMAXINFO:
2088 OnGetMinMaxInfo(GuiData, (PMINMAXINFO)lParam);
2089 break;
2090
2091 case WM_MOVE:
2092 OnMove(GuiData);
2093 break;
2094
2095 #if 0 // This code is here to prepare & control dynamic console SB resizing.
2096 case WM_SIZING:
2097 {
2098 PRECT dragRect = (PRECT)lParam;
2099 switch (wParam)
2100 {
2101 case WMSZ_LEFT:
2102 DPRINT1("WMSZ_LEFT\n");
2103 break;
2104 case WMSZ_RIGHT:
2105 DPRINT1("WMSZ_RIGHT\n");
2106 break;
2107 case WMSZ_TOP:
2108 DPRINT1("WMSZ_TOP\n");
2109 break;
2110 case WMSZ_TOPLEFT:
2111 DPRINT1("WMSZ_TOPLEFT\n");
2112 break;
2113 case WMSZ_TOPRIGHT:
2114 DPRINT1("WMSZ_TOPRIGHT\n");
2115 break;
2116 case WMSZ_BOTTOM:
2117 DPRINT1("WMSZ_BOTTOM\n");
2118 break;
2119 case WMSZ_BOTTOMLEFT:
2120 DPRINT1("WMSZ_BOTTOMLEFT\n");
2121 break;
2122 case WMSZ_BOTTOMRIGHT:
2123 DPRINT1("WMSZ_BOTTOMRIGHT\n");
2124 break;
2125 default:
2126 DPRINT1("wParam = %d\n", wParam);
2127 break;
2128 }
2129 DPRINT1("dragRect = {.left = %d ; .top = %d ; .right = %d ; .bottom = %d}\n",
2130 dragRect->left, dragRect->top, dragRect->right, dragRect->bottom);
2131 break;
2132 }
2133 #endif
2134
2135 case WM_SIZE:
2136 OnSize(GuiData, wParam, lParam);
2137 break;
2138
2139 case PM_RESIZE_TERMINAL:
2140 {
2141 PCONSOLE_SCREEN_BUFFER Buff = GuiData->ActiveBuffer;
2142 HDC hDC;
2143 HBITMAP hnew, hold;
2144
2145 DWORD Width, Height;
2146 UINT WidthUnit, HeightUnit;
2147
2148 GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
2149
2150 Width = Buff->ScreenBufferSize.X * WidthUnit ;
2151 Height = Buff->ScreenBufferSize.Y * HeightUnit;
2152
2153 /* Recreate the framebuffer */
2154 hDC = GetDC(GuiData->hWindow);
2155 hnew = CreateCompatibleBitmap(hDC, Width, Height);
2156 ReleaseDC(GuiData->hWindow, hDC);
2157 hold = SelectObject(GuiData->hMemDC, hnew);
2158 if (GuiData->hBitmap)
2159 {
2160 if (hold == GuiData->hBitmap) DeleteObject(GuiData->hBitmap);
2161 }
2162 GuiData->hBitmap = hnew;
2163
2164 /* Resize the window to the user's values */
2165 GuiData->WindowSizeLock = TRUE;
2166 ResizeConWnd(GuiData, WidthUnit, HeightUnit);
2167 GuiData->WindowSizeLock = FALSE;
2168 break;
2169 }
2170
2171 case PM_APPLY_CONSOLE_INFO:
2172 {
2173 if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
2174 {
2175 GuiApplyUserSettings(GuiData, (HANDLE)wParam, (BOOL)lParam);
2176 LeaveCriticalSection(&Console->Lock);
2177 }
2178 break;
2179 }
2180
2181 case PM_CONSOLE_BEEP:
2182 DPRINT1("Beep !!\n");
2183 Beep(800, 200);
2184 break;
2185
2186 // case PM_CONSOLE_SET_TITLE:
2187 // SetWindowText(GuiData->hWindow, GuiData->Console->Title.Buffer);
2188 // break;
2189
2190 default: Default:
2191 Result = DefWindowProcW(hWnd, msg, wParam, lParam);
2192 break;
2193 }
2194
2195 return Result;
2196 }
2197
2198 /* EOF */