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