[CONSRV]: Use InterlockedExchange16 to exchange Begin and End COORD structure members.
[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 #include <intrin.h>
16 #include <windowsx.h>
17
18 #define NDEBUG
19 #include <debug.h>
20
21 #include "guiterm.h"
22 #include "conwnd.h"
23 #include "resource.h"
24
25 /* GLOBALS ********************************************************************/
26
27 // #define PM_CREATE_CONSOLE (WM_APP + 1)
28 // #define PM_DESTROY_CONSOLE (WM_APP + 2)
29
30 // See guiterm.c
31 #define CONGUI_MIN_WIDTH 10
32 #define CONGUI_MIN_HEIGHT 10
33 #define CONGUI_UPDATE_TIME 0
34 #define CONGUI_UPDATE_TIMER 1
35
36 #define CURSOR_BLINK_TIME 500
37
38
39 /**************************************************************\
40 \** Define the Console Leader Process for the console window **/
41 #define GWLP_CONWND_ALLOC (2 * sizeof(LONG_PTR))
42 #define GWLP_CONSOLE_LEADER_PID 0
43 #define GWLP_CONSOLE_LEADER_TID 4
44
45 VOID
46 SetConWndConsoleLeaderCID(IN PGUI_CONSOLE_DATA GuiData)
47 {
48 PCONSOLE_PROCESS_DATA ProcessData;
49 CLIENT_ID ConsoleLeaderCID;
50
51 ProcessData = ConDrvGetConsoleLeaderProcess(GuiData->Console);
52 ConsoleLeaderCID = ProcessData->Process->ClientId;
53 SetWindowLongPtrW(GuiData->hWindow, GWLP_CONSOLE_LEADER_PID,
54 (LONG_PTR)(ConsoleLeaderCID.UniqueProcess));
55 SetWindowLongPtrW(GuiData->hWindow, GWLP_CONSOLE_LEADER_TID,
56 (LONG_PTR)(ConsoleLeaderCID.UniqueThread));
57 }
58 /**************************************************************/
59
60 HICON ghDefaultIcon = NULL;
61 HICON ghDefaultIconSm = NULL;
62 HCURSOR ghDefaultCursor = NULL;
63
64 typedef struct _GUICONSOLE_MENUITEM
65 {
66 UINT uID;
67 const struct _GUICONSOLE_MENUITEM *SubMenu;
68 WORD wCmdID;
69 } GUICONSOLE_MENUITEM, *PGUICONSOLE_MENUITEM;
70
71 static const GUICONSOLE_MENUITEM GuiConsoleEditMenuItems[] =
72 {
73 { IDS_MARK, NULL, ID_SYSTEM_EDIT_MARK },
74 { IDS_COPY, NULL, ID_SYSTEM_EDIT_COPY },
75 { IDS_PASTE, NULL, ID_SYSTEM_EDIT_PASTE },
76 { IDS_SELECTALL, NULL, ID_SYSTEM_EDIT_SELECTALL },
77 { IDS_SCROLL, NULL, ID_SYSTEM_EDIT_SCROLL },
78 { IDS_FIND, NULL, ID_SYSTEM_EDIT_FIND },
79
80 { 0, NULL, 0 } /* End of list */
81 };
82
83 static const GUICONSOLE_MENUITEM GuiConsoleMainMenuItems[] =
84 {
85 { IDS_EDIT, GuiConsoleEditMenuItems, 0 },
86 { IDS_DEFAULTS, NULL, ID_SYSTEM_DEFAULTS },
87 { IDS_PROPERTIES, NULL, ID_SYSTEM_PROPERTIES },
88
89 { 0, NULL, 0 } /* End of list */
90 };
91
92 /*
93 * Default 16-color palette for foreground and background
94 * (corresponding flags in comments).
95 */
96 const COLORREF s_Colors[16] =
97 {
98 RGB(0, 0, 0), // (Black)
99 RGB(0, 0, 128), // BLUE
100 RGB(0, 128, 0), // GREEN
101 RGB(0, 128, 128), // BLUE | GREEN
102 RGB(128, 0, 0), // RED
103 RGB(128, 0, 128), // BLUE | RED
104 RGB(128, 128, 0), // GREEN | RED
105 RGB(192, 192, 192), // BLUE | GREEN | RED
106
107 RGB(128, 128, 128), // (Grey) INTENSITY
108 RGB(0, 0, 255), // BLUE | INTENSITY
109 RGB(0, 255, 0), // GREEN | INTENSITY
110 RGB(0, 255, 255), // BLUE | GREEN | INTENSITY
111 RGB(255, 0, 0), // RED | INTENSITY
112 RGB(255, 0, 255), // BLUE | RED | INTENSITY
113 RGB(255, 255, 0), // GREEN | RED | INTENSITY
114 RGB(255, 255, 255) // BLUE | GREEN | RED | INTENSITY
115 };
116
117 /* FUNCTIONS ******************************************************************/
118
119 static LRESULT CALLBACK
120 ConWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
121
122 BOOLEAN
123 RegisterConWndClass(IN HINSTANCE hInstance)
124 {
125 WNDCLASSEXW WndClass;
126 ATOM WndClassAtom;
127
128 ghDefaultIcon = LoadImageW(hInstance,
129 MAKEINTRESOURCEW(IDI_TERMINAL),
130 IMAGE_ICON,
131 GetSystemMetrics(SM_CXICON),
132 GetSystemMetrics(SM_CYICON),
133 LR_SHARED);
134 ghDefaultIconSm = LoadImageW(hInstance,
135 MAKEINTRESOURCEW(IDI_TERMINAL),
136 IMAGE_ICON,
137 GetSystemMetrics(SM_CXSMICON),
138 GetSystemMetrics(SM_CYSMICON),
139 LR_SHARED);
140 ghDefaultCursor = LoadCursorW(NULL, IDC_ARROW);
141
142 WndClass.cbSize = sizeof(WNDCLASSEXW);
143 WndClass.lpszClassName = GUI_CONWND_CLASS;
144 WndClass.lpfnWndProc = ConWndProc;
145 WndClass.style = CS_DBLCLKS /* | CS_HREDRAW | CS_VREDRAW */;
146 WndClass.hInstance = hInstance;
147 WndClass.hIcon = ghDefaultIcon;
148 WndClass.hIconSm = ghDefaultIconSm;
149 WndClass.hCursor = ghDefaultCursor;
150 WndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); // The color of a terminal when it is switched off.
151 WndClass.lpszMenuName = NULL;
152 WndClass.cbClsExtra = 0;
153 WndClass.cbWndExtra = GWLP_CONWND_ALLOC;
154
155 WndClassAtom = RegisterClassExW(&WndClass);
156 if (WndClassAtom == 0)
157 {
158 DPRINT1("Failed to register GUI console class\n");
159 }
160 else
161 {
162 NtUserConsoleControl(GuiConsoleWndClassAtom, &WndClassAtom, sizeof(ATOM));
163 }
164
165 return (WndClassAtom != 0);
166 }
167
168 BOOLEAN
169 UnRegisterConWndClass(HINSTANCE hInstance)
170 {
171 return !!UnregisterClassW(GUI_CONWND_CLASS, hInstance);
172 }
173
174
175
176 static VOID
177 GetScreenBufferSizeUnits(IN PCONSOLE_SCREEN_BUFFER Buffer,
178 IN PGUI_CONSOLE_DATA GuiData,
179 OUT PUINT WidthUnit,
180 OUT PUINT HeightUnit)
181 {
182 if (Buffer == NULL || GuiData == NULL ||
183 WidthUnit == NULL || HeightUnit == NULL)
184 {
185 return;
186 }
187
188 if (GetType(Buffer) == TEXTMODE_BUFFER)
189 {
190 *WidthUnit = GuiData->CharWidth ;
191 *HeightUnit = GuiData->CharHeight;
192 }
193 else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
194 {
195 *WidthUnit = 1;
196 *HeightUnit = 1;
197 }
198 }
199
200 static VOID
201 AppendMenuItems(HMENU hMenu,
202 const GUICONSOLE_MENUITEM *Items)
203 {
204 UINT i = 0;
205 WCHAR szMenuString[255];
206 HMENU hSubMenu;
207
208 do
209 {
210 if (Items[i].uID != (UINT)-1)
211 {
212 if (LoadStringW(ConSrvDllInstance,
213 Items[i].uID,
214 szMenuString,
215 sizeof(szMenuString) / sizeof(szMenuString[0])) > 0)
216 {
217 if (Items[i].SubMenu != NULL)
218 {
219 hSubMenu = CreatePopupMenu();
220 if (hSubMenu != NULL)
221 {
222 AppendMenuItems(hSubMenu, Items[i].SubMenu);
223
224 if (!AppendMenuW(hMenu,
225 MF_STRING | MF_POPUP,
226 (UINT_PTR)hSubMenu,
227 szMenuString))
228 {
229 DestroyMenu(hSubMenu);
230 }
231 }
232 }
233 else
234 {
235 AppendMenuW(hMenu,
236 MF_STRING,
237 Items[i].wCmdID,
238 szMenuString);
239 }
240 }
241 }
242 else
243 {
244 AppendMenuW(hMenu,
245 MF_SEPARATOR,
246 0,
247 NULL);
248 }
249 i++;
250 } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 0));
251 }
252
253 static 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 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;
949 PAINTSTRUCT ps;
950 RECT rcPaint;
951
952 ActiveBuffer = GuiData->ActiveBuffer;
953
954 BeginPaint(GuiData->hWindow, &ps);
955 if (ps.hdc != NULL &&
956 ps.rcPaint.left < ps.rcPaint.right &&
957 ps.rcPaint.top < ps.rcPaint.bottom)
958 {
959 EnterCriticalSection(&GuiData->Lock);
960
961 /* Compose the current screen-buffer on-memory */
962 if (GetType(ActiveBuffer) == TEXTMODE_BUFFER)
963 {
964 GuiPaintTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)ActiveBuffer,
965 GuiData, &ps.rcPaint, &rcPaint);
966 }
967 else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */
968 {
969 GuiPaintGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)ActiveBuffer,
970 GuiData, &ps.rcPaint, &rcPaint);
971 }
972
973 /* Send it to screen */
974 BitBlt(ps.hdc,
975 ps.rcPaint.left,
976 ps.rcPaint.top,
977 rcPaint.right - rcPaint.left,
978 rcPaint.bottom - rcPaint.top,
979 GuiData->hMemDC,
980 rcPaint.left,
981 rcPaint.top,
982 SRCCOPY);
983
984 /* Draw the selection region if needed */
985 if (GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
986 {
987 PaintSelectionRect(GuiData, &ps);
988 }
989
990 LeaveCriticalSection(&GuiData->Lock);
991 }
992 EndPaint(GuiData->hWindow, &ps);
993
994 return;
995 }
996
997 static VOID
998 OnPaletteChanged(PGUI_CONSOLE_DATA GuiData)
999 {
1000 PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer;
1001
1002 // See WM_PALETTECHANGED message
1003 // if ((HWND)wParam == hWnd) break;
1004
1005 // if (GetType(ActiveBuffer) == GRAPHICS_BUFFER)
1006 if (ActiveBuffer->PaletteHandle)
1007 {
1008 DPRINT("WM_PALETTECHANGED changing palette\n");
1009
1010 /* Specify the use of the system palette for the framebuffer */
1011 SetSystemPaletteUse(GuiData->hMemDC, ActiveBuffer->PaletteUsage);
1012
1013 /* Realize the (logical) palette */
1014 RealizePalette(GuiData->hMemDC);
1015 }
1016 }
1017
1018 static BOOL
1019 IsSystemKey(WORD VirtualKeyCode)
1020 {
1021 switch (VirtualKeyCode)
1022 {
1023 /* From MSDN, "Virtual-Key Codes" */
1024 case VK_RETURN:
1025 case VK_SHIFT:
1026 case VK_CONTROL:
1027 case VK_MENU:
1028 case VK_PAUSE:
1029 case VK_CAPITAL:
1030 case VK_ESCAPE:
1031 case VK_LWIN:
1032 case VK_RWIN:
1033 case VK_NUMLOCK:
1034 case VK_SCROLL:
1035 return TRUE;
1036 default:
1037 return FALSE;
1038 }
1039 }
1040
1041 static VOID
1042 OnKey(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam)
1043 {
1044 PCONSOLE Console = GuiData->Console;
1045 PCONSOLE_SCREEN_BUFFER ActiveBuffer;
1046
1047 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
1048
1049 ActiveBuffer = GuiData->ActiveBuffer;
1050
1051 if (GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS)
1052 {
1053 WORD VirtualKeyCode = LOWORD(wParam);
1054
1055 if (msg != WM_KEYDOWN) goto Quit;
1056
1057 if (VirtualKeyCode == VK_RETURN)
1058 {
1059 /* Copy (and clear) selection if ENTER is pressed */
1060 Copy(GuiData);
1061 goto Quit;
1062 }
1063 else if ( VirtualKeyCode == VK_ESCAPE ||
1064 (VirtualKeyCode == 'C' && (GetKeyState(VK_CONTROL) & 0x8000)) )
1065 {
1066 /* Cancel selection if ESC or Ctrl-C are pressed */
1067 UpdateSelection(GuiData, NULL, NULL);
1068 goto Quit;
1069 }
1070
1071 if ((GuiData->Selection.dwFlags & CONSOLE_MOUSE_SELECTION) == 0)
1072 {
1073 /* Keyboard selection mode */
1074 BOOL Interpreted = FALSE;
1075 BOOL MajPressed = !!(GetKeyState(VK_SHIFT) & 0x8000);
1076
1077 switch (VirtualKeyCode)
1078 {
1079 case VK_LEFT:
1080 {
1081 Interpreted = TRUE;
1082 if (GuiData->dwSelectionCursor.X > 0)
1083 GuiData->dwSelectionCursor.X--;
1084
1085 break;
1086 }
1087
1088 case VK_RIGHT:
1089 {
1090 Interpreted = TRUE;
1091 if (GuiData->dwSelectionCursor.X < ActiveBuffer->ScreenBufferSize.X - 1)
1092 GuiData->dwSelectionCursor.X++;
1093
1094 break;
1095 }
1096
1097 case VK_UP:
1098 {
1099 Interpreted = TRUE;
1100 if (GuiData->dwSelectionCursor.Y > 0)
1101 GuiData->dwSelectionCursor.Y--;
1102
1103 break;
1104 }
1105
1106 case VK_DOWN:
1107 {
1108 Interpreted = TRUE;
1109 if (GuiData->dwSelectionCursor.Y < ActiveBuffer->ScreenBufferSize.Y - 1)
1110 GuiData->dwSelectionCursor.Y++;
1111
1112 break;
1113 }
1114
1115 case VK_HOME:
1116 {
1117 Interpreted = TRUE;
1118 GuiData->dwSelectionCursor.X = 0;
1119 GuiData->dwSelectionCursor.Y = 0;
1120 break;
1121 }
1122
1123 case VK_END:
1124 {
1125 Interpreted = TRUE;
1126 GuiData->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1;
1127 break;
1128 }
1129
1130 case VK_PRIOR:
1131 {
1132 Interpreted = TRUE;
1133 GuiData->dwSelectionCursor.Y -= ActiveBuffer->ViewSize.Y;
1134 if (GuiData->dwSelectionCursor.Y < 0)
1135 GuiData->dwSelectionCursor.Y = 0;
1136
1137 break;
1138 }
1139
1140 case VK_NEXT:
1141 {
1142 Interpreted = TRUE;
1143 GuiData->dwSelectionCursor.Y += ActiveBuffer->ViewSize.Y;
1144 if (GuiData->dwSelectionCursor.Y >= ActiveBuffer->ScreenBufferSize.Y)
1145 GuiData->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1;
1146
1147 break;
1148 }
1149
1150 default:
1151 break;
1152 }
1153
1154 if (Interpreted)
1155 {
1156 UpdateSelection(GuiData,
1157 !MajPressed ? &GuiData->dwSelectionCursor : NULL,
1158 &GuiData->dwSelectionCursor);
1159 }
1160 else if (!IsSystemKey(VirtualKeyCode))
1161 {
1162 /* Emit an error beep sound */
1163 SendNotifyMessage(GuiData->hWindow, PM_CONSOLE_BEEP, 0, 0);
1164 }
1165
1166 goto Quit;
1167 }
1168 else
1169 {
1170 /* Mouse selection mode */
1171
1172 if (!IsSystemKey(VirtualKeyCode))
1173 {
1174 /* Clear the selection and send the key into the input buffer */
1175 UpdateSelection(GuiData, NULL, NULL);
1176 }
1177 else
1178 {
1179 goto Quit;
1180 }
1181 }
1182 }
1183
1184 if ((GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) == 0)
1185 {
1186 MSG Message;
1187
1188 Message.hwnd = GuiData->hWindow;
1189 Message.message = msg;
1190 Message.wParam = wParam;
1191 Message.lParam = lParam;
1192
1193 ConioProcessKey(Console, &Message);
1194 }
1195
1196 Quit:
1197 LeaveCriticalSection(&Console->Lock);
1198 }
1199
1200
1201 // FIXME: Remove after fixing OnTimer
1202 VOID
1203 InvalidateCell(PGUI_CONSOLE_DATA GuiData,
1204 SHORT x, SHORT y);
1205
1206 static VOID
1207 OnTimer(PGUI_CONSOLE_DATA GuiData)
1208 {
1209 PCONSOLE Console = GuiData->Console;
1210 PCONSOLE_SCREEN_BUFFER Buff;
1211
1212 SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CURSOR_BLINK_TIME, NULL);
1213
1214 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
1215
1216 Buff = GuiData->ActiveBuffer;
1217
1218 if (GetType(Buff) == TEXTMODE_BUFFER)
1219 {
1220 InvalidateCell(GuiData, Buff->CursorPosition.X, Buff->CursorPosition.Y);
1221 Buff->CursorBlinkOn = !Buff->CursorBlinkOn;
1222
1223 if ((GuiData->OldCursor.x != Buff->CursorPosition.X) ||
1224 (GuiData->OldCursor.y != Buff->CursorPosition.Y))
1225 {
1226 SCROLLINFO xScroll;
1227 int OldScrollX = -1, OldScrollY = -1;
1228 int NewScrollX = -1, NewScrollY = -1;
1229
1230 xScroll.cbSize = sizeof(SCROLLINFO);
1231 xScroll.fMask = SIF_POS;
1232 // Capture the original position of the scroll bars and save them.
1233 if (GetScrollInfo(GuiData->hWindow, SB_HORZ, &xScroll)) OldScrollX = xScroll.nPos;
1234 if (GetScrollInfo(GuiData->hWindow, SB_VERT, &xScroll)) OldScrollY = xScroll.nPos;
1235
1236 // If we successfully got the info for the horizontal scrollbar
1237 if (OldScrollX >= 0)
1238 {
1239 if ((Buff->CursorPosition.X < Buff->ViewOrigin.X) ||
1240 (Buff->CursorPosition.X >= (Buff->ViewOrigin.X + Buff->ViewSize.X)))
1241 {
1242 // Handle the horizontal scroll bar
1243 if (Buff->CursorPosition.X >= Buff->ViewSize.X)
1244 NewScrollX = Buff->CursorPosition.X - Buff->ViewSize.X + 1;
1245 else
1246 NewScrollX = 0;
1247 }
1248 else
1249 {
1250 NewScrollX = OldScrollX;
1251 }
1252 }
1253 // If we successfully got the info for the vertical scrollbar
1254 if (OldScrollY >= 0)
1255 {
1256 if ((Buff->CursorPosition.Y < Buff->ViewOrigin.Y) ||
1257 (Buff->CursorPosition.Y >= (Buff->ViewOrigin.Y + Buff->ViewSize.Y)))
1258 {
1259 // Handle the vertical scroll bar
1260 if (Buff->CursorPosition.Y >= Buff->ViewSize.Y)
1261 NewScrollY = Buff->CursorPosition.Y - Buff->ViewSize.Y + 1;
1262 else
1263 NewScrollY = 0;
1264 }
1265 else
1266 {
1267 NewScrollY = OldScrollY;
1268 }
1269 }
1270
1271 // Adjust scroll bars and refresh the window if the cursor has moved outside the visible area
1272 // NOTE: OldScroll# and NewScroll# will both be -1 (initial value) if the info for the respective scrollbar
1273 // was not obtained successfully in the previous steps. This means their difference is 0 (no scrolling)
1274 // and their associated scrollbar is left alone.
1275 if ((OldScrollX != NewScrollX) || (OldScrollY != NewScrollY))
1276 {
1277 Buff->ViewOrigin.X = NewScrollX;
1278 Buff->ViewOrigin.Y = NewScrollY;
1279 ScrollWindowEx(GuiData->hWindow,
1280 (OldScrollX - NewScrollX) * GuiData->CharWidth,
1281 (OldScrollY - NewScrollY) * GuiData->CharHeight,
1282 NULL,
1283 NULL,
1284 NULL,
1285 NULL,
1286 SW_INVALIDATE);
1287 if (NewScrollX >= 0)
1288 {
1289 xScroll.nPos = NewScrollX;
1290 SetScrollInfo(GuiData->hWindow, SB_HORZ, &xScroll, TRUE);
1291 }
1292 if (NewScrollY >= 0)
1293 {
1294 xScroll.nPos = NewScrollY;
1295 SetScrollInfo(GuiData->hWindow, SB_VERT, &xScroll, TRUE);
1296 }
1297 UpdateWindow(GuiData->hWindow);
1298 // InvalidateRect(GuiData->hWindow, NULL, FALSE);
1299 GuiData->OldCursor.x = Buff->CursorPosition.X;
1300 GuiData->OldCursor.y = Buff->CursorPosition.Y;
1301 }
1302 }
1303 }
1304 else /* if (GetType(Buff) == GRAPHICS_BUFFER) */
1305 {
1306 }
1307
1308 LeaveCriticalSection(&Console->Lock);
1309 }
1310
1311 static BOOL
1312 OnClose(PGUI_CONSOLE_DATA GuiData)
1313 {
1314 PCONSOLE Console = GuiData->Console;
1315
1316 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
1317 return TRUE;
1318
1319 // TODO: Prompt for termination ? (Warn the user about possible apps running in this console)
1320
1321 /*
1322 * FIXME: Windows will wait up to 5 seconds for the thread to exit.
1323 * We shouldn't wait here, though, since the console lock is entered.
1324 * A copy of the thread list probably needs to be made.
1325 */
1326 ConDrvConsoleProcessCtrlEvent(Console, 0, CTRL_CLOSE_EVENT);
1327
1328 LeaveCriticalSection(&Console->Lock);
1329 return FALSE;
1330 }
1331
1332 static LRESULT
1333 OnNcDestroy(HWND hWnd)
1334 {
1335 PGUI_CONSOLE_DATA GuiData = GuiGetGuiData(hWnd);
1336
1337 KillTimer(hWnd, CONGUI_UPDATE_TIMER);
1338 GetSystemMenu(hWnd, TRUE);
1339
1340 if (GuiData)
1341 {
1342 /* Free the terminal framebuffer */
1343 if (GuiData->hMemDC ) DeleteDC(GuiData->hMemDC);
1344 if (GuiData->hBitmap) DeleteObject(GuiData->hBitmap);
1345 // if (GuiData->hSysPalette) DeleteObject(GuiData->hSysPalette);
1346 if (GuiData->Font) DeleteObject(GuiData->Font);
1347 }
1348
1349 /* Free the GuiData registration */
1350 SetWindowLongPtrW(hWnd, GWLP_USERDATA, (DWORD_PTR)NULL);
1351
1352 return DefWindowProcW(hWnd, WM_NCDESTROY, 0, 0);
1353 }
1354
1355 static COORD
1356 PointToCoord(PGUI_CONSOLE_DATA GuiData, LPARAM lParam)
1357 {
1358 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
1359 COORD Coord;
1360 UINT WidthUnit, HeightUnit;
1361
1362 GetScreenBufferSizeUnits(Buffer, GuiData, &WidthUnit, &HeightUnit);
1363
1364 Coord.X = Buffer->ViewOrigin.X + ((SHORT)LOWORD(lParam) / (int)WidthUnit );
1365 Coord.Y = Buffer->ViewOrigin.Y + ((SHORT)HIWORD(lParam) / (int)HeightUnit);
1366
1367 /* Clip coordinate to ensure it's inside buffer */
1368 if (Coord.X < 0)
1369 Coord.X = 0;
1370 else if (Coord.X >= Buffer->ScreenBufferSize.X)
1371 Coord.X = Buffer->ScreenBufferSize.X - 1;
1372
1373 if (Coord.Y < 0)
1374 Coord.Y = 0;
1375 else if (Coord.Y >= Buffer->ScreenBufferSize.Y)
1376 Coord.Y = Buffer->ScreenBufferSize.Y - 1;
1377
1378 return Coord;
1379 }
1380
1381 static LRESULT
1382 OnMouse(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam)
1383 {
1384 BOOL Err = FALSE;
1385 PCONSOLE Console = GuiData->Console;
1386
1387 if (GuiData->IgnoreNextMouseSignal)
1388 {
1389 if (msg != WM_LBUTTONDOWN &&
1390 msg != WM_MBUTTONDOWN &&
1391 msg != WM_RBUTTONDOWN &&
1392 msg != WM_MOUSEMOVE)
1393 {
1394 /*
1395 * If this mouse signal is not a button-down action or a move,
1396 * then it is the last signal being ignored.
1397 */
1398 GuiData->IgnoreNextMouseSignal = FALSE;
1399 }
1400 else
1401 {
1402 /*
1403 * This mouse signal is a button-down action or a move.
1404 * Ignore it and perform default action.
1405 */
1406 Err = TRUE;
1407 }
1408 goto Quit;
1409 }
1410
1411 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
1412 {
1413 Err = TRUE;
1414 goto Quit;
1415 }
1416
1417 if ( (GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) ||
1418 (Console->QuickEdit) )
1419 {
1420 switch (msg)
1421 {
1422 case WM_LBUTTONDOWN:
1423 {
1424 /* Clear the old selection */
1425 GuiData->Selection.dwFlags = CONSOLE_NO_SELECTION;
1426
1427 /* Restart a new selection */
1428 GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam);
1429 SetCapture(GuiData->hWindow);
1430 GuiData->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN;
1431 UpdateSelection(GuiData,
1432 &GuiData->dwSelectionCursor,
1433 &GuiData->dwSelectionCursor);
1434
1435 break;
1436 }
1437
1438 case WM_LBUTTONUP:
1439 {
1440 if (!(GuiData->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break;
1441
1442 // GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam);
1443 GuiData->Selection.dwFlags &= ~CONSOLE_MOUSE_DOWN;
1444 // UpdateSelection(GuiData, NULL, &GuiData->dwSelectionCursor);
1445 ReleaseCapture();
1446
1447 break;
1448 }
1449
1450 case WM_LBUTTONDBLCLK:
1451 {
1452 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
1453
1454 if (GetType(Buffer) == TEXTMODE_BUFFER)
1455 {
1456 #define IS_WORD_SEP(c) \
1457 ((c) == L'\0' || (c) == L' ' || (c) == L'\t' || (c) == L'\r' || (c) == L'\n')
1458
1459 PTEXTMODE_SCREEN_BUFFER TextBuffer = (PTEXTMODE_SCREEN_BUFFER)Buffer;
1460 COORD cL, cR;
1461 PCHAR_INFO ptrL, ptrR;
1462
1463 /* Starting point */
1464 cL = cR = PointToCoord(GuiData, lParam);
1465 ptrL = ptrR = ConioCoordToPointer(TextBuffer, cL.X, cL.Y);
1466
1467 /* Enlarge the selection by checking for whitespace */
1468 while ((0 < cL.X) && !IS_WORD_SEP(ptrL->Char.UnicodeChar)
1469 && !IS_WORD_SEP((ptrL-1)->Char.UnicodeChar))
1470 {
1471 --cL.X;
1472 --ptrL;
1473 }
1474 while ((cR.X < TextBuffer->ScreenBufferSize.X - 1) &&
1475 !IS_WORD_SEP(ptrR->Char.UnicodeChar) &&
1476 !IS_WORD_SEP((ptrR+1)->Char.UnicodeChar))
1477 {
1478 ++cR.X;
1479 ++ptrR;
1480 }
1481
1482 /*
1483 * Update the selection started with the single
1484 * left-click that preceded this double-click.
1485 */
1486 GuiData->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN;
1487 UpdateSelection(GuiData, &cL, &cR);
1488
1489 /* Ignore the next mouse move signal */
1490 GuiData->IgnoreNextMouseSignal = TRUE;
1491 }
1492
1493 break;
1494 }
1495
1496 case WM_RBUTTONDOWN:
1497 case WM_RBUTTONDBLCLK:
1498 {
1499 if (!(GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY))
1500 {
1501 Paste(GuiData);
1502 }
1503 else
1504 {
1505 Copy(GuiData);
1506 }
1507
1508 /* Ignore the next mouse move signal */
1509 GuiData->IgnoreNextMouseSignal = TRUE;
1510 break;
1511 }
1512
1513 case WM_MOUSEMOVE:
1514 {
1515 if (!(wParam & MK_LBUTTON)) break;
1516 if (!(GuiData->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break;
1517
1518 // TODO: Scroll buffer to bring SelectionCursor into view
1519 GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam);
1520 UpdateSelection(GuiData, NULL, &GuiData->dwSelectionCursor);
1521
1522 break;
1523 }
1524
1525 default:
1526 Err = FALSE; // TRUE;
1527 break;
1528 }
1529 }
1530 else if (Console->InputBuffer.Mode & ENABLE_MOUSE_INPUT)
1531 {
1532 INPUT_RECORD er;
1533 WORD wKeyState = GET_KEYSTATE_WPARAM(wParam);
1534 DWORD dwButtonState = 0;
1535 DWORD dwControlKeyState = 0;
1536 DWORD dwEventFlags = 0;
1537
1538 switch (msg)
1539 {
1540 case WM_LBUTTONDOWN:
1541 SetCapture(GuiData->hWindow);
1542 dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
1543 dwEventFlags = 0;
1544 break;
1545
1546 case WM_MBUTTONDOWN:
1547 SetCapture(GuiData->hWindow);
1548 dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
1549 dwEventFlags = 0;
1550 break;
1551
1552 case WM_RBUTTONDOWN:
1553 SetCapture(GuiData->hWindow);
1554 dwButtonState = RIGHTMOST_BUTTON_PRESSED;
1555 dwEventFlags = 0;
1556 break;
1557
1558 case WM_LBUTTONUP:
1559 ReleaseCapture();
1560 dwButtonState = 0;
1561 dwEventFlags = 0;
1562 break;
1563
1564 case WM_MBUTTONUP:
1565 ReleaseCapture();
1566 dwButtonState = 0;
1567 dwEventFlags = 0;
1568 break;
1569
1570 case WM_RBUTTONUP:
1571 ReleaseCapture();
1572 dwButtonState = 0;
1573 dwEventFlags = 0;
1574 break;
1575
1576 case WM_LBUTTONDBLCLK:
1577 dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
1578 dwEventFlags = DOUBLE_CLICK;
1579 break;
1580
1581 case WM_MBUTTONDBLCLK:
1582 dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
1583 dwEventFlags = DOUBLE_CLICK;
1584 break;
1585
1586 case WM_RBUTTONDBLCLK:
1587 dwButtonState = RIGHTMOST_BUTTON_PRESSED;
1588 dwEventFlags = DOUBLE_CLICK;
1589 break;
1590
1591 case WM_MOUSEMOVE:
1592 dwButtonState = 0;
1593 dwEventFlags = MOUSE_MOVED;
1594 break;
1595
1596 case WM_MOUSEWHEEL:
1597 dwButtonState = GET_WHEEL_DELTA_WPARAM(wParam) << 16;
1598 dwEventFlags = MOUSE_WHEELED;
1599 break;
1600
1601 case WM_MOUSEHWHEEL:
1602 dwButtonState = GET_WHEEL_DELTA_WPARAM(wParam) << 16;
1603 dwEventFlags = MOUSE_HWHEELED;
1604 break;
1605
1606 default:
1607 Err = TRUE;
1608 break;
1609 }
1610
1611 if (!Err)
1612 {
1613 if (wKeyState & MK_LBUTTON)
1614 dwButtonState |= FROM_LEFT_1ST_BUTTON_PRESSED;
1615 if (wKeyState & MK_MBUTTON)
1616 dwButtonState |= FROM_LEFT_2ND_BUTTON_PRESSED;
1617 if (wKeyState & MK_RBUTTON)
1618 dwButtonState |= RIGHTMOST_BUTTON_PRESSED;
1619
1620 if (GetKeyState(VK_RMENU) & 0x8000)
1621 dwControlKeyState |= RIGHT_ALT_PRESSED;
1622 if (GetKeyState(VK_LMENU) & 0x8000)
1623 dwControlKeyState |= LEFT_ALT_PRESSED;
1624 if (GetKeyState(VK_RCONTROL) & 0x8000)
1625 dwControlKeyState |= RIGHT_CTRL_PRESSED;
1626 if (GetKeyState(VK_LCONTROL) & 0x8000)
1627 dwControlKeyState |= LEFT_CTRL_PRESSED;
1628 if (GetKeyState(VK_SHIFT) & 0x8000)
1629 dwControlKeyState |= SHIFT_PRESSED;
1630 if (GetKeyState(VK_NUMLOCK) & 0x0001)
1631 dwControlKeyState |= NUMLOCK_ON;
1632 if (GetKeyState(VK_SCROLL) & 0x0001)
1633 dwControlKeyState |= SCROLLLOCK_ON;
1634 if (GetKeyState(VK_CAPITAL) & 0x0001)
1635 dwControlKeyState |= CAPSLOCK_ON;
1636 /* See WM_CHAR MSDN documentation for instance */
1637 if (lParam & 0x01000000)
1638 dwControlKeyState |= ENHANCED_KEY;
1639
1640 er.EventType = MOUSE_EVENT;
1641 er.Event.MouseEvent.dwMousePosition = PointToCoord(GuiData, lParam);
1642 er.Event.MouseEvent.dwButtonState = dwButtonState;
1643 er.Event.MouseEvent.dwControlKeyState = dwControlKeyState;
1644 er.Event.MouseEvent.dwEventFlags = dwEventFlags;
1645
1646 ConioProcessInputEvent(Console, &er);
1647 }
1648 }
1649 else
1650 {
1651 Err = TRUE;
1652 }
1653
1654 LeaveCriticalSection(&Console->Lock);
1655
1656 Quit:
1657 if (Err)
1658 return DefWindowProcW(GuiData->hWindow, msg, wParam, lParam);
1659 else
1660 return 0;
1661 }
1662
1663 VOID
1664 GuiCopyFromTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer,
1665 PGUI_CONSOLE_DATA GuiData);
1666 VOID
1667 GuiCopyFromGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer,
1668 PGUI_CONSOLE_DATA GuiData);
1669
1670 static VOID
1671 Copy(PGUI_CONSOLE_DATA GuiData)
1672 {
1673 if (OpenClipboard(GuiData->hWindow) == TRUE)
1674 {
1675 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
1676
1677 if (GetType(Buffer) == TEXTMODE_BUFFER)
1678 {
1679 GuiCopyFromTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)Buffer, GuiData);
1680 }
1681 else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
1682 {
1683 GuiCopyFromGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)Buffer, GuiData);
1684 }
1685
1686 CloseClipboard();
1687 }
1688
1689 /* Clear the selection */
1690 UpdateSelection(GuiData, NULL, NULL);
1691 }
1692
1693 VOID
1694 GuiPasteToTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer,
1695 PGUI_CONSOLE_DATA GuiData);
1696 VOID
1697 GuiPasteToGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer,
1698 PGUI_CONSOLE_DATA GuiData);
1699
1700 static VOID
1701 Paste(PGUI_CONSOLE_DATA GuiData)
1702 {
1703 if (OpenClipboard(GuiData->hWindow) == TRUE)
1704 {
1705 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
1706
1707 if (GetType(Buffer) == TEXTMODE_BUFFER)
1708 {
1709 GuiPasteToTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)Buffer, GuiData);
1710 }
1711 else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
1712 {
1713 GuiPasteToGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)Buffer, GuiData);
1714 }
1715
1716 CloseClipboard();
1717 }
1718 }
1719
1720 static VOID
1721 OnGetMinMaxInfo(PGUI_CONSOLE_DATA GuiData, PMINMAXINFO minMaxInfo)
1722 {
1723 PCONSOLE Console = GuiData->Console;
1724 PCONSOLE_SCREEN_BUFFER ActiveBuffer;
1725 DWORD windx, windy;
1726 UINT WidthUnit, HeightUnit;
1727
1728 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
1729
1730 ActiveBuffer = GuiData->ActiveBuffer;
1731
1732 GetScreenBufferSizeUnits(ActiveBuffer, GuiData, &WidthUnit, &HeightUnit);
1733
1734 windx = CONGUI_MIN_WIDTH * WidthUnit + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
1735 windy = CONGUI_MIN_HEIGHT * HeightUnit + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
1736
1737 minMaxInfo->ptMinTrackSize.x = windx;
1738 minMaxInfo->ptMinTrackSize.y = windy;
1739
1740 windx = (ActiveBuffer->ScreenBufferSize.X) * WidthUnit + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
1741 windy = (ActiveBuffer->ScreenBufferSize.Y) * HeightUnit + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
1742
1743 if (ActiveBuffer->ViewSize.X < ActiveBuffer->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar
1744 if (ActiveBuffer->ViewSize.Y < ActiveBuffer->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar
1745
1746 minMaxInfo->ptMaxTrackSize.x = windx;
1747 minMaxInfo->ptMaxTrackSize.y = windy;
1748
1749 LeaveCriticalSection(&Console->Lock);
1750 }
1751
1752 static VOID
1753 OnSize(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam)
1754 {
1755 PCONSOLE Console = GuiData->Console;
1756
1757 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
1758
1759 if ((GuiData->WindowSizeLock == FALSE) &&
1760 (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED || wParam == SIZE_MINIMIZED))
1761 {
1762 PCONSOLE_SCREEN_BUFFER Buff = GuiData->ActiveBuffer;
1763 DWORD windx, windy, charx, chary;
1764 UINT WidthUnit, HeightUnit;
1765
1766 GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
1767
1768 GuiData->WindowSizeLock = TRUE;
1769
1770 windx = LOWORD(lParam);
1771 windy = HIWORD(lParam);
1772
1773 // Compensate for existing scroll bars (because lParam values do not accommodate scroll bar)
1774 if (Buff->ViewSize.X < Buff->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar
1775 if (Buff->ViewSize.Y < Buff->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar
1776
1777 charx = windx / (int)WidthUnit ;
1778 chary = windy / (int)HeightUnit;
1779
1780 // Character alignment (round size up or down)
1781 if ((windx % WidthUnit ) >= (WidthUnit / 2)) ++charx;
1782 if ((windy % HeightUnit) >= (HeightUnit / 2)) ++chary;
1783
1784 // Compensate for added scroll bars in new window
1785 if (charx < Buff->ScreenBufferSize.X) windy -= GetSystemMetrics(SM_CYHSCROLL); // new window will have a horizontal scroll bar
1786 if (chary < Buff->ScreenBufferSize.Y) windx -= GetSystemMetrics(SM_CXVSCROLL); // new window will have a vertical scroll bar
1787
1788 charx = windx / (int)WidthUnit ;
1789 chary = windy / (int)HeightUnit;
1790
1791 // Character alignment (round size up or down)
1792 if ((windx % WidthUnit ) >= (WidthUnit / 2)) ++charx;
1793 if ((windy % HeightUnit) >= (HeightUnit / 2)) ++chary;
1794
1795 // Resize window
1796 if ((charx != Buff->ViewSize.X) || (chary != Buff->ViewSize.Y))
1797 {
1798 Buff->ViewSize.X = (charx <= Buff->ScreenBufferSize.X) ? charx : Buff->ScreenBufferSize.X;
1799 Buff->ViewSize.Y = (chary <= Buff->ScreenBufferSize.Y) ? chary : Buff->ScreenBufferSize.Y;
1800 }
1801
1802 ResizeConWnd(GuiData, WidthUnit, HeightUnit);
1803
1804 // Adjust the start of the visible area if we are attempting to show nonexistent areas
1805 if ((Buff->ScreenBufferSize.X - Buff->ViewOrigin.X) < Buff->ViewSize.X) Buff->ViewOrigin.X = Buff->ScreenBufferSize.X - Buff->ViewSize.X;
1806 if ((Buff->ScreenBufferSize.Y - Buff->ViewOrigin.Y) < Buff->ViewSize.Y) Buff->ViewOrigin.Y = Buff->ScreenBufferSize.Y - Buff->ViewSize.Y;
1807 InvalidateRect(GuiData->hWindow, NULL, TRUE);
1808
1809 GuiData->WindowSizeLock = FALSE;
1810 }
1811
1812 LeaveCriticalSection(&Console->Lock);
1813 }
1814
1815 static VOID
1816 OnMove(PGUI_CONSOLE_DATA GuiData)
1817 {
1818 RECT rcWnd;
1819
1820 // TODO: Simplify the code.
1821 // See: GuiConsoleNotifyWndProc() PM_CREATE_CONSOLE.
1822
1823 /* Retrieve our real position */
1824 GetWindowRect(GuiData->hWindow, &rcWnd);
1825 GuiData->GuiInfo.WindowOrigin.x = rcWnd.left;
1826 GuiData->GuiInfo.WindowOrigin.y = rcWnd.top;
1827 }
1828
1829 /*
1830 // HACK: This functionality is standard for general scrollbars. Don't add it by hand.
1831
1832 VOID
1833 FASTCALL
1834 GuiConsoleHandleScrollbarMenu(VOID)
1835 {
1836 HMENU hMenu;
1837
1838 hMenu = CreatePopupMenu();
1839 if (hMenu == NULL)
1840 {
1841 DPRINT("CreatePopupMenu failed\n");
1842 return;
1843 }
1844
1845 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLHERE);
1846 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
1847 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLTOP);
1848 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLBOTTOM);
1849 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
1850 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_UP);
1851 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_DOWN);
1852 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
1853 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLUP);
1854 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLDOWN);
1855 }
1856 */
1857
1858 static LRESULT
1859 OnScroll(PGUI_CONSOLE_DATA GuiData, UINT uMsg, WPARAM wParam)
1860 {
1861 PCONSOLE Console = GuiData->Console;
1862 PCONSOLE_SCREEN_BUFFER Buff;
1863 SCROLLINFO sInfo;
1864 int fnBar;
1865 int old_pos, Maximum;
1866 PSHORT pShowXY;
1867
1868 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return 0;
1869
1870 Buff = GuiData->ActiveBuffer;
1871
1872 if (uMsg == WM_HSCROLL)
1873 {
1874 fnBar = SB_HORZ;
1875 Maximum = Buff->ScreenBufferSize.X - Buff->ViewSize.X;
1876 pShowXY = &Buff->ViewOrigin.X;
1877 }
1878 else
1879 {
1880 fnBar = SB_VERT;
1881 Maximum = Buff->ScreenBufferSize.Y - Buff->ViewSize.Y;
1882 pShowXY = &Buff->ViewOrigin.Y;
1883 }
1884
1885 /* set scrollbar sizes */
1886 sInfo.cbSize = sizeof(SCROLLINFO);
1887 sInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE | SIF_TRACKPOS;
1888
1889 if (!GetScrollInfo(GuiData->hWindow, fnBar, &sInfo)) goto Quit;
1890
1891 old_pos = sInfo.nPos;
1892
1893 switch (LOWORD(wParam))
1894 {
1895 case SB_LINELEFT:
1896 sInfo.nPos -= 1;
1897 break;
1898
1899 case SB_LINERIGHT:
1900 sInfo.nPos += 1;
1901 break;
1902
1903 case SB_PAGELEFT:
1904 sInfo.nPos -= sInfo.nPage;
1905 break;
1906
1907 case SB_PAGERIGHT:
1908 sInfo.nPos += sInfo.nPage;
1909 break;
1910
1911 case SB_THUMBTRACK:
1912 sInfo.nPos = sInfo.nTrackPos;
1913 ConioPause(Console, PAUSED_FROM_SCROLLBAR);
1914 break;
1915
1916 case SB_THUMBPOSITION:
1917 ConioUnpause(Console, PAUSED_FROM_SCROLLBAR);
1918 break;
1919
1920 case SB_TOP:
1921 sInfo.nPos = sInfo.nMin;
1922 break;
1923
1924 case SB_BOTTOM:
1925 sInfo.nPos = sInfo.nMax;
1926 break;
1927
1928 default:
1929 break;
1930 }
1931
1932 sInfo.nPos = max(sInfo.nPos, 0);
1933 sInfo.nPos = min(sInfo.nPos, Maximum);
1934
1935 if (old_pos != sInfo.nPos)
1936 {
1937 USHORT OldX = Buff->ViewOrigin.X;
1938 USHORT OldY = Buff->ViewOrigin.Y;
1939 UINT WidthUnit, HeightUnit;
1940
1941 *pShowXY = sInfo.nPos;
1942
1943 GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
1944
1945 ScrollWindowEx(GuiData->hWindow,
1946 (OldX - Buff->ViewOrigin.X) * WidthUnit ,
1947 (OldY - Buff->ViewOrigin.Y) * HeightUnit,
1948 NULL,
1949 NULL,
1950 NULL,
1951 NULL,
1952 SW_INVALIDATE);
1953
1954 sInfo.fMask = SIF_POS;
1955 SetScrollInfo(GuiData->hWindow, fnBar, &sInfo, TRUE);
1956
1957 UpdateWindow(GuiData->hWindow);
1958 // InvalidateRect(GuiData->hWindow, NULL, FALSE);
1959 }
1960
1961 Quit:
1962 LeaveCriticalSection(&Console->Lock);
1963 return 0;
1964 }
1965
1966
1967 static LRESULT CALLBACK
1968 ConWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1969 {
1970 LRESULT Result = 0;
1971 PGUI_CONSOLE_DATA GuiData = NULL;
1972 PCONSOLE Console = NULL;
1973
1974 /*
1975 * - If it's the first time we create a window for the terminal,
1976 * just initialize it and return.
1977 *
1978 * - If we are destroying the window, just do it and return.
1979 */
1980 if (msg == WM_NCCREATE)
1981 {
1982 return (LRESULT)OnNcCreate(hWnd, (LPCREATESTRUCTW)lParam);
1983 }
1984 else if (msg == WM_NCDESTROY)
1985 {
1986 return OnNcDestroy(hWnd);
1987 }
1988
1989 /*
1990 * Now the terminal window is initialized.
1991 * Get the terminal data via the window's data.
1992 * If there is no data, just go away.
1993 */
1994 GuiData = GuiGetGuiData(hWnd);
1995 if (GuiData == NULL) return DefWindowProcW(hWnd, msg, wParam, lParam);
1996
1997 // TEMPORARY HACK until all of the functions can deal with a NULL GuiData->ActiveBuffer ...
1998 if (GuiData->ActiveBuffer == NULL) return DefWindowProcW(hWnd, msg, wParam, lParam);
1999
2000 /*
2001 * Just retrieve a pointer to the console in case somebody needs it.
2002 * It is not NULL because it was checked in GuiGetGuiData.
2003 * Each helper function which needs the console has to validate and lock it.
2004 */
2005 Console = GuiData->Console;
2006
2007 /* We have a console, start message dispatching */
2008 switch (msg)
2009 {
2010 case WM_ACTIVATE:
2011 OnActivate(GuiData, wParam);
2012 break;
2013
2014 case WM_CLOSE:
2015 if (OnClose(GuiData)) goto Default;
2016 break;
2017
2018 case WM_PAINT:
2019 OnPaint(GuiData);
2020 break;
2021
2022 case WM_TIMER:
2023 OnTimer(GuiData);
2024 break;
2025
2026 case WM_PALETTECHANGED:
2027 {
2028 DPRINT("WM_PALETTECHANGED called\n");
2029
2030 /*
2031 * Protects against infinite loops:
2032 * "... A window that receives this message must not realize
2033 * its palette, unless it determines that wParam does not contain
2034 * its own window handle." (WM_PALETTECHANGED description - MSDN)
2035 *
2036 * This message is sent to all windows, including the one that
2037 * changed the system palette and caused this message to be sent.
2038 * The wParam of this message contains the handle of the window
2039 * that caused the system palette to change. To avoid an infinite
2040 * loop, care must be taken to check that the wParam of this message
2041 * does not match the window's handle.
2042 */
2043 if ((HWND)wParam == hWnd) break;
2044
2045 DPRINT("WM_PALETTECHANGED ok\n");
2046 OnPaletteChanged(GuiData);
2047 DPRINT("WM_PALETTECHANGED quit\n");
2048 break;
2049 }
2050
2051 case WM_KEYDOWN:
2052 case WM_KEYUP:
2053 case WM_CHAR:
2054 case WM_DEADCHAR:
2055 case WM_SYSKEYDOWN:
2056 case WM_SYSKEYUP:
2057 case WM_SYSCHAR:
2058 case WM_SYSDEADCHAR:
2059 {
2060 /* Detect Alt-Enter presses and switch back and forth to fullscreen mode */
2061 if (msg == WM_SYSKEYDOWN && (HIWORD(lParam) & KF_ALTDOWN) && wParam == VK_RETURN)
2062 {
2063 /* Switch only at first Alt-Enter press, and ignore subsequent key repetitions */
2064 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
2065 GuiConsoleSwitchFullScreen(GuiData);
2066
2067 break;
2068 }
2069 /* Detect Alt-Esc/Space/Tab presses defer to DefWindowProc */
2070 if ( (HIWORD(lParam) & KF_ALTDOWN) && (wParam == VK_ESCAPE || wParam == VK_SPACE || wParam == VK_TAB))
2071 {
2072 return DefWindowProcW(hWnd, msg, wParam, lParam);
2073 }
2074
2075 OnKey(GuiData, msg, wParam, lParam);
2076 break;
2077 }
2078
2079 case WM_SETCURSOR:
2080 {
2081 /*
2082 * The message was sent because we are manually triggering a change.
2083 * Check whether the mouse is indeed present on this console window
2084 * and take appropriate decisions.
2085 */
2086 if (wParam == -1 && lParam == -1)
2087 {
2088 POINT mouseCoords;
2089 HWND hWndHit;
2090
2091 /* Get the placement of the mouse */
2092 GetCursorPos(&mouseCoords);
2093
2094 /* On which window is placed the mouse ? */
2095 hWndHit = WindowFromPoint(mouseCoords);
2096
2097 /* It's our window. Perform the hit-test to be used later on. */
2098 if (hWndHit == hWnd)
2099 {
2100 wParam = (WPARAM)hWnd;
2101 lParam = DefWindowProcW(hWndHit, WM_NCHITTEST, 0,
2102 MAKELPARAM(mouseCoords.x, mouseCoords.y));
2103 }
2104 }
2105
2106 /* Set the mouse cursor only when we are in the client area */
2107 if ((HWND)wParam == hWnd && LOWORD(lParam) == HTCLIENT)
2108 {
2109 if (GuiData->MouseCursorRefCount >= 0)
2110 {
2111 /* Show the cursor */
2112 SetCursor(GuiData->hCursor);
2113 }
2114 else
2115 {
2116 /* Hide the cursor if the reference count is negative */
2117 SetCursor(NULL);
2118 }
2119 return TRUE;
2120 }
2121 else
2122 {
2123 goto Default;
2124 }
2125 }
2126
2127 case WM_LBUTTONDOWN:
2128 case WM_MBUTTONDOWN:
2129 case WM_RBUTTONDOWN:
2130 case WM_LBUTTONUP:
2131 case WM_MBUTTONUP:
2132 case WM_RBUTTONUP:
2133 case WM_LBUTTONDBLCLK:
2134 case WM_MBUTTONDBLCLK:
2135 case WM_RBUTTONDBLCLK:
2136 case WM_MOUSEMOVE:
2137 case WM_MOUSEWHEEL:
2138 case WM_MOUSEHWHEEL:
2139 {
2140 Result = OnMouse(GuiData, msg, wParam, lParam);
2141 break;
2142 }
2143
2144 case WM_HSCROLL:
2145 case WM_VSCROLL:
2146 {
2147 Result = OnScroll(GuiData, msg, wParam);
2148 break;
2149 }
2150
2151 case WM_CONTEXTMENU:
2152 {
2153 if (DefWindowProcW(hWnd /*GuiData->hWindow*/, WM_NCHITTEST, 0, lParam) == HTCLIENT)
2154 {
2155 HMENU hMenu = CreatePopupMenu();
2156 if (hMenu != NULL)
2157 {
2158 AppendMenuItems(hMenu, GuiConsoleEditMenuItems);
2159 TrackPopupMenuEx(hMenu,
2160 TPM_RIGHTBUTTON,
2161 GET_X_LPARAM(lParam),
2162 GET_Y_LPARAM(lParam),
2163 hWnd,
2164 NULL);
2165 DestroyMenu(hMenu);
2166 }
2167 break;
2168 }
2169 else
2170 {
2171 goto Default;
2172 }
2173 }
2174
2175 case WM_INITMENU:
2176 {
2177 HMENU hMenu = (HMENU)wParam;
2178 if (hMenu != NULL)
2179 {
2180 /* Enable or disable the Close menu item */
2181 EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND |
2182 (GuiData->IsCloseButtonEnabled ? MF_ENABLED : MF_GRAYED));
2183
2184 /* Enable or disable the Copy and Paste items */
2185 EnableMenuItem(hMenu, ID_SYSTEM_EDIT_COPY , MF_BYCOMMAND |
2186 ((GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) &&
2187 (GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) ? MF_ENABLED : MF_GRAYED));
2188 // FIXME: Following whether the active screen buffer is text-mode
2189 // or graphics-mode, search for CF_UNICODETEXT or CF_BITMAP formats.
2190 EnableMenuItem(hMenu, ID_SYSTEM_EDIT_PASTE, MF_BYCOMMAND |
2191 (!(GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) &&
2192 IsClipboardFormatAvailable(CF_UNICODETEXT) ? MF_ENABLED : MF_GRAYED));
2193 }
2194
2195 SendMenuEvent(Console, WM_INITMENU);
2196 break;
2197 }
2198
2199 case WM_MENUSELECT:
2200 {
2201 if (HIWORD(wParam) == 0xFFFF) // Allow all the menu flags
2202 {
2203 SendMenuEvent(Console, WM_MENUSELECT);
2204 }
2205 break;
2206 }
2207
2208 case WM_COMMAND:
2209 case WM_SYSCOMMAND:
2210 {
2211 Result = OnCommand(GuiData, wParam, lParam);
2212 break;
2213 }
2214
2215 case WM_SETFOCUS:
2216 case WM_KILLFOCUS:
2217 OnFocus(GuiData, (msg == WM_SETFOCUS));
2218 break;
2219
2220 case WM_GETMINMAXINFO:
2221 OnGetMinMaxInfo(GuiData, (PMINMAXINFO)lParam);
2222 break;
2223
2224 case WM_MOVE:
2225 OnMove(GuiData);
2226 break;
2227
2228 #if 0 // This code is here to prepare & control dynamic console SB resizing.
2229 case WM_SIZING:
2230 {
2231 PRECT dragRect = (PRECT)lParam;
2232 switch (wParam)
2233 {
2234 case WMSZ_LEFT:
2235 DPRINT1("WMSZ_LEFT\n");
2236 break;
2237 case WMSZ_RIGHT:
2238 DPRINT1("WMSZ_RIGHT\n");
2239 break;
2240 case WMSZ_TOP:
2241 DPRINT1("WMSZ_TOP\n");
2242 break;
2243 case WMSZ_TOPLEFT:
2244 DPRINT1("WMSZ_TOPLEFT\n");
2245 break;
2246 case WMSZ_TOPRIGHT:
2247 DPRINT1("WMSZ_TOPRIGHT\n");
2248 break;
2249 case WMSZ_BOTTOM:
2250 DPRINT1("WMSZ_BOTTOM\n");
2251 break;
2252 case WMSZ_BOTTOMLEFT:
2253 DPRINT1("WMSZ_BOTTOMLEFT\n");
2254 break;
2255 case WMSZ_BOTTOMRIGHT:
2256 DPRINT1("WMSZ_BOTTOMRIGHT\n");
2257 break;
2258 default:
2259 DPRINT1("wParam = %d\n", wParam);
2260 break;
2261 }
2262 DPRINT1("dragRect = {.left = %d ; .top = %d ; .right = %d ; .bottom = %d}\n",
2263 dragRect->left, dragRect->top, dragRect->right, dragRect->bottom);
2264 break;
2265 }
2266 #endif
2267
2268 case WM_SIZE:
2269 OnSize(GuiData, wParam, lParam);
2270 break;
2271
2272 case PM_RESIZE_TERMINAL:
2273 {
2274 PCONSOLE_SCREEN_BUFFER Buff = GuiData->ActiveBuffer;
2275 HDC hDC;
2276 HBITMAP hnew, hold;
2277
2278 DWORD Width, Height;
2279 UINT WidthUnit, HeightUnit;
2280
2281 GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
2282
2283 Width = Buff->ScreenBufferSize.X * WidthUnit ;
2284 Height = Buff->ScreenBufferSize.Y * HeightUnit;
2285
2286 /* Recreate the framebuffer */
2287 hDC = GetDC(GuiData->hWindow);
2288 hnew = CreateCompatibleBitmap(hDC, Width, Height);
2289 ReleaseDC(GuiData->hWindow, hDC);
2290 hold = SelectObject(GuiData->hMemDC, hnew);
2291 if (GuiData->hBitmap)
2292 {
2293 if (hold == GuiData->hBitmap) DeleteObject(GuiData->hBitmap);
2294 }
2295 GuiData->hBitmap = hnew;
2296
2297 /* Resize the window to the user's values */
2298 GuiData->WindowSizeLock = TRUE;
2299 ResizeConWnd(GuiData, WidthUnit, HeightUnit);
2300 GuiData->WindowSizeLock = FALSE;
2301 break;
2302 }
2303
2304 case PM_APPLY_CONSOLE_INFO:
2305 {
2306 GuiApplyUserSettings(GuiData, (HANDLE)wParam, (BOOL)lParam);
2307 break;
2308 }
2309
2310 case PM_CONSOLE_BEEP:
2311 DPRINT1("Beep !!\n");
2312 Beep(800, 200);
2313 break;
2314
2315 // case PM_CONSOLE_SET_TITLE:
2316 // SetWindowText(GuiData->hWindow, GuiData->Console->Title.Buffer);
2317 // break;
2318
2319 default: Default:
2320 Result = DefWindowProcW(hWnd, msg, wParam, lParam);
2321 break;
2322 }
2323
2324 return Result;
2325 }
2326
2327 /* EOF */