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