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