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