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