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