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