[CSRSRV]
[reactos.git] / win32ss / user / consrv / guiconsole.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: win32ss/user/consrv/guiconsole.c
5 * PURPOSE: GUI terminal emulator
6 * PROGRAMMERS:
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "consrv.h"
12 #include "settings.h"
13 #include "guiconsole.h"
14
15 #define NDEBUG
16 #include <debug.h>
17
18 /*
19 // Define wmemset(...)
20 #include <wchar.h>
21 #define HAVE_WMEMSET
22 */
23
24 /* GUI Console Window Class name */
25 #define GUI_CONSOLE_WINDOW_CLASS L"ConsoleWindowClass"
26
27 #ifndef WM_APP
28 #define WM_APP 0x8000
29 #endif
30 #define PM_CREATE_CONSOLE (WM_APP + 1)
31 #define PM_DESTROY_CONSOLE (WM_APP + 2)
32 #define PM_CONSOLE_BEEP (WM_APP + 3)
33 #define PM_CONSOLE_SET_TITLE (WM_APP + 4)
34
35
36 /* Not defined in any header file */
37 // extern VOID WINAPI PrivateCsrssManualGuiCheck(LONG Check);
38 // From win32ss/user/win32csr/dllmain.c
39 VOID
40 WINAPI
41 PrivateCsrssManualGuiCheck(LONG Check)
42 {
43 NtUserCallOneParam(Check, ONEPARAM_ROUTINE_CSRSS_GUICHECK);
44 }
45
46 /* GLOBALS ********************************************************************/
47
48 typedef struct _GUI_CONSOLE_DATA
49 {
50 CRITICAL_SECTION Lock;
51 HANDLE hGuiInitEvent;
52 BOOL WindowSizeLock;
53 POINT OldCursor;
54
55 HWND hWindow; /* Handle to the console's window */
56 HICON hIcon; /* Handle to the console's icon (big) */
57 HICON hIconSm; /* Handle to the console's icon (small) */
58 // COLORREF Colors[16];
59
60 HFONT Font;
61 UINT CharWidth;
62 UINT CharHeight;
63
64 PCONSOLE Console; /* Pointer to the owned console */
65 GUI_CONSOLE_INFO GuiInfo; /* GUI terminal settings */
66 } GUI_CONSOLE_DATA, *PGUI_CONSOLE_DATA;
67
68 static BOOL ConsInitialized = FALSE;
69 static HICON ghDefaultIcon = NULL;
70 static HICON ghDefaultIconSm = NULL;
71 static HCURSOR ghDefaultCursor = NULL;
72 static HWND NotifyWnd = NULL;
73
74 typedef struct _GUICONSOLE_MENUITEM
75 {
76 UINT uID;
77 const struct _GUICONSOLE_MENUITEM *SubMenu;
78 WORD wCmdID;
79 } GUICONSOLE_MENUITEM, *PGUICONSOLE_MENUITEM;
80
81 static const GUICONSOLE_MENUITEM GuiConsoleEditMenuItems[] =
82 {
83 { IDS_MARK, NULL, ID_SYSTEM_EDIT_MARK },
84 { IDS_COPY, NULL, ID_SYSTEM_EDIT_COPY },
85 { IDS_PASTE, NULL, ID_SYSTEM_EDIT_PASTE },
86 { IDS_SELECTALL, NULL, ID_SYSTEM_EDIT_SELECTALL },
87 { IDS_SCROLL, NULL, ID_SYSTEM_EDIT_SCROLL },
88 { IDS_FIND, NULL, ID_SYSTEM_EDIT_FIND },
89
90 { 0, NULL, 0 } /* End of list */
91 };
92
93 static const GUICONSOLE_MENUITEM GuiConsoleMainMenuItems[] =
94 {
95 { IDS_EDIT, GuiConsoleEditMenuItems, 0 },
96 { IDS_DEFAULTS, NULL, ID_SYSTEM_DEFAULTS },
97 { IDS_PROPERTIES, NULL, ID_SYSTEM_PROPERTIES },
98
99 { 0, NULL, 0 } /* End of list */
100 };
101
102 /*
103 * Default 16-color palette for foreground and background
104 * (corresponding flags in comments).
105 */
106 const COLORREF s_Colors[16] =
107 {
108 RGB(0, 0, 0), // (Black)
109 RGB(0, 0, 128), // BLUE
110 RGB(0, 128, 0), // GREEN
111 RGB(0, 128, 128), // BLUE | GREEN
112 RGB(128, 0, 0), // RED
113 RGB(128, 0, 128), // BLUE | RED
114 RGB(128, 128, 0), // GREEN | RED
115 RGB(192, 192, 192), // BLUE | GREEN | RED
116
117 RGB(128, 128, 128), // (Grey) INTENSITY
118 RGB(0, 0, 255), // BLUE | INTENSITY
119 RGB(0, 255, 0), // GREEN | INTENSITY
120 RGB(0, 255, 255), // BLUE | GREEN | INTENSITY
121 RGB(255, 0, 0), // RED | INTENSITY
122 RGB(255, 0, 255), // BLUE | RED | INTENSITY
123 RGB(255, 255, 0), // GREEN | RED | INTENSITY
124 RGB(255, 255, 255) // BLUE | GREEN | RED | INTENSITY
125 };
126
127 /* FUNCTIONS ******************************************************************/
128
129 static VOID
130 GuiConsoleAppendMenuItems(HMENU hMenu,
131 const GUICONSOLE_MENUITEM *Items)
132 {
133 UINT i = 0;
134 WCHAR szMenuString[255];
135 HMENU hSubMenu;
136
137 do
138 {
139 if (Items[i].uID != (UINT)-1)
140 {
141 if (LoadStringW(ConSrvDllInstance,
142 Items[i].uID,
143 szMenuString,
144 sizeof(szMenuString) / sizeof(szMenuString[0])) > 0)
145 {
146 if (Items[i].SubMenu != NULL)
147 {
148 hSubMenu = CreatePopupMenu();
149 if (hSubMenu != NULL)
150 {
151 GuiConsoleAppendMenuItems(hSubMenu,
152 Items[i].SubMenu);
153
154 if (!AppendMenuW(hMenu,
155 MF_STRING | MF_POPUP,
156 (UINT_PTR)hSubMenu,
157 szMenuString))
158 {
159 DestroyMenu(hSubMenu);
160 }
161 }
162 }
163 else
164 {
165 AppendMenuW(hMenu,
166 MF_STRING,
167 Items[i].wCmdID,
168 szMenuString);
169 }
170 }
171 }
172 else
173 {
174 AppendMenuW(hMenu,
175 MF_SEPARATOR,
176 0,
177 NULL);
178 }
179 i++;
180 } while(!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 0));
181 }
182
183 static VOID
184 GuiConsoleCreateSysMenu(HWND hWnd)
185 {
186 HMENU hMenu;
187 hMenu = GetSystemMenu(hWnd, FALSE);
188 if (hMenu != NULL)
189 {
190 GuiConsoleAppendMenuItems(hMenu, GuiConsoleMainMenuItems);
191 DrawMenuBar(hWnd);
192 }
193 }
194
195
196 static VOID
197 GuiConsoleCopy(PGUI_CONSOLE_DATA GuiData);
198 static VOID
199 GuiConsolePaste(PGUI_CONSOLE_DATA GuiData);
200 static VOID
201 GuiConsoleUpdateSelection(PCONSOLE Console, PCOORD coord);
202 static VOID
203 GuiConsoleShowConsoleProperties(PGUI_CONSOLE_DATA GuiData, BOOL Defaults);
204 static VOID WINAPI
205 GuiDrawRegion(PCONSOLE Console, SMALL_RECT* Region);
206 static NTSTATUS WINAPI
207 GuiResizeBuffer(PCONSOLE Console, PCONSOLE_SCREEN_BUFFER ScreenBuffer, COORD Size);
208 static VOID
209 GuiConsoleMoveWindow(PGUI_CONSOLE_DATA GuiData);
210 static VOID
211 GuiConsoleResizeWindow(PGUI_CONSOLE_DATA GuiData);
212
213
214 static LRESULT
215 GuiConsoleHandleSysMenuCommand(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam)
216 {
217 LRESULT Ret = TRUE;
218
219 switch (wParam)
220 {
221 case ID_SYSTEM_EDIT_MARK:
222 DPRINT1("Marking not handled yet\n");
223 break;
224
225 case ID_SYSTEM_EDIT_COPY:
226 GuiConsoleCopy(GuiData);
227 break;
228
229 case ID_SYSTEM_EDIT_PASTE:
230 GuiConsolePaste(GuiData);
231 break;
232
233 case ID_SYSTEM_EDIT_SELECTALL:
234 {
235 PCONSOLE Console = GuiData->Console;
236 COORD bottomRight = { 0, 0 };
237
238 bottomRight.X = Console->Size.X - 1;
239 bottomRight.Y = Console->Size.Y - 1;
240 GuiConsoleUpdateSelection(Console, &bottomRight);
241 break;
242 }
243
244 case ID_SYSTEM_EDIT_SCROLL:
245 DPRINT1("Scrolling is not handled yet\n");
246 break;
247
248 case ID_SYSTEM_EDIT_FIND:
249 DPRINT1("Finding is not handled yet\n");
250 break;
251
252 case ID_SYSTEM_DEFAULTS:
253 GuiConsoleShowConsoleProperties(GuiData, TRUE);
254 break;
255
256 case ID_SYSTEM_PROPERTIES:
257 GuiConsoleShowConsoleProperties(GuiData, FALSE);
258 break;
259
260 default:
261 Ret = DefWindowProcW(GuiData->hWindow, WM_SYSCOMMAND, wParam, lParam);
262 break;
263 }
264 return Ret;
265 }
266
267 static VOID
268 GuiConsoleShowConsoleProperties(PGUI_CONSOLE_DATA GuiData, BOOL Defaults)
269 {
270 NTSTATUS Status;
271 PCONSOLE Console = GuiData->Console;
272 PCONSOLE_PROCESS_DATA ProcessData;
273 HANDLE hSection = NULL, hClientSection = NULL;
274 LARGE_INTEGER SectionSize;
275 ULONG ViewSize = 0;
276 SIZE_T Length = 0;
277 PCONSOLE_PROPS pSharedInfo = NULL;
278
279 DPRINT("GuiConsoleShowConsoleProperties entered\n");
280
281 /* Create a memory section to share with the applet, and map it */
282 SectionSize.QuadPart = sizeof(CONSOLE_PROPS);
283 Status = NtCreateSection(&hSection,
284 SECTION_ALL_ACCESS,
285 NULL,
286 &SectionSize,
287 PAGE_READWRITE,
288 SEC_COMMIT,
289 NULL);
290 if (!NT_SUCCESS(Status))
291 {
292 DPRINT1("Error: Impossible to create a shared section ; Status = %lu\n", Status);
293 return;
294 }
295
296 Status = NtMapViewOfSection(hSection,
297 NtCurrentProcess(),
298 (PVOID*)&pSharedInfo,
299 0,
300 0,
301 NULL,
302 &ViewSize,
303 ViewUnmap,
304 0,
305 PAGE_READWRITE);
306 if (!NT_SUCCESS(Status))
307 {
308 DPRINT1("Error: Impossible to map the shared section ; Status = %lu\n", Status);
309 NtClose(hSection);
310 return;
311 }
312
313 /*
314 * Setup the shared console properties structure.
315 */
316 /* Header */
317 pSharedInfo->hConsoleWindow = GuiData->hWindow;
318 pSharedInfo->ShowDefaultParams = Defaults;
319 /* Console information */
320 pSharedInfo->ci.HistoryBufferSize = Console->HistoryBufferSize;
321 pSharedInfo->ci.NumberOfHistoryBuffers = Console->NumberOfHistoryBuffers;
322 pSharedInfo->ci.HistoryNoDup = Console->HistoryNoDup;
323 pSharedInfo->ci.FullScreen = Console->FullScreen;
324 pSharedInfo->ci.QuickEdit = Console->QuickEdit;
325 pSharedInfo->ci.InsertMode = Console->InsertMode;
326 pSharedInfo->ci.InputBufferSize = 0;
327 pSharedInfo->ci.ScreenBufferSize = Console->ActiveBuffer->ScreenBufferSize;
328 pSharedInfo->ci.ConsoleSize = Console->Size;
329 pSharedInfo->ci.CursorBlinkOn;
330 pSharedInfo->ci.ForceCursorOff;
331 pSharedInfo->ci.CursorSize = Console->ActiveBuffer->CursorInfo.dwSize;
332 pSharedInfo->ci.ScreenAttrib = Console->ActiveBuffer->ScreenDefaultAttrib;
333 pSharedInfo->ci.PopupAttrib = Console->ActiveBuffer->PopupDefaultAttrib;
334 pSharedInfo->ci.CodePage;
335 /* GUI Information */
336 wcsncpy(pSharedInfo->ci.u.GuiInfo.FaceName, GuiData->GuiInfo.FaceName, LF_FACESIZE);
337 pSharedInfo->ci.u.GuiInfo.FontSize = (DWORD)GuiData->GuiInfo.FontSize;
338 pSharedInfo->ci.u.GuiInfo.FontWeight = GuiData->GuiInfo.FontWeight;
339 pSharedInfo->ci.u.GuiInfo.UseRasterFonts = GuiData->GuiInfo.UseRasterFonts;
340 /// pSharedInfo->ci.u.GuiInfo.WindowPosition = GuiData->GuiInfo.WindowPosition;
341 pSharedInfo->ci.u.GuiInfo.AutoPosition = GuiData->GuiInfo.AutoPosition;
342 pSharedInfo->ci.u.GuiInfo.WindowOrigin = GuiData->GuiInfo.WindowOrigin;
343 /* Palette */
344 memcpy(pSharedInfo->ci.Colors, Console->Colors, sizeof(s_Colors)); // FIXME: Possible buffer overflow if s_colors is bigger than pSharedInfo->Colors.
345 /* Title of the console, original one corresponding to the one set by the console leader */
346 Length = min(sizeof(pSharedInfo->ci.ConsoleTitle) / sizeof(pSharedInfo->ci.ConsoleTitle[0]) - 1,
347 Console->OriginalTitle.Length / sizeof(WCHAR));
348 wcsncpy(pSharedInfo->ci.ConsoleTitle, Console->OriginalTitle.Buffer, Length);
349 pSharedInfo->ci.ConsoleTitle[Length] = L'\0';
350
351 /* Unmap the view */
352 NtUnmapViewOfSection(NtCurrentProcess(), pSharedInfo);
353
354 /* Get the console leader process, our client */
355 ProcessData = CONTAINING_RECORD(Console->ProcessList.Blink,
356 CONSOLE_PROCESS_DATA,
357 ConsoleLink);
358
359 /* Duplicate the section handle for the client */
360 Status = NtDuplicateObject(NtCurrentProcess(),
361 hSection,
362 ProcessData->Process->ProcessHandle,
363 &hClientSection,
364 0, 0, DUPLICATE_SAME_ACCESS);
365 if (!NT_SUCCESS(Status))
366 {
367 DPRINT1("Error: Impossible to duplicate section handle for client ; Status = %lu\n", Status);
368 NtClose(hSection);
369 return;
370 }
371
372 /* Start the properties dialog */
373 if (ProcessData->PropDispatcher)
374 {
375 HANDLE Thread;
376
377 Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0,
378 ProcessData->PropDispatcher,
379 (PVOID)hClientSection, 0, NULL);
380 if (NULL == Thread)
381 {
382 DPRINT1("Failed thread creation (Error: 0x%x)\n", GetLastError());
383 return;
384 }
385
386 DPRINT1("We succeeded at creating ProcessData->PropDispatcher remote thread, ProcessId = %x, Process = 0x%p\n", ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process);
387 /// WaitForSingleObject(Thread, INFINITE);
388 CloseHandle(Thread);
389 }
390
391 /* We have finished, close the section handle */
392 NtClose(hSection);
393 return;
394 }
395
396 static VOID
397 GuiApplyUserSettings(PGUI_CONSOLE_DATA GuiData,
398 HANDLE hClientSection,
399 BOOL SaveSettings)
400 {
401 NTSTATUS Status;
402 PCONSOLE Console = GuiData->Console;
403 PCONSOLE_PROCESS_DATA ProcessData;
404 HANDLE hSection = NULL;
405 ULONG ViewSize = 0;
406 PCONSOLE_PROPS pConInfo = NULL;
407 PCONSOLE_SCREEN_BUFFER ActiveBuffer = Console->ActiveBuffer;
408 COORD BufSize;
409 BOOL SizeChanged = FALSE;
410
411 /// LOCK /// EnterCriticalSection(&Console->Lock);
412
413 /* Get the console leader process, our client */
414 ProcessData = CONTAINING_RECORD(Console->ProcessList.Blink,
415 CONSOLE_PROCESS_DATA,
416 ConsoleLink);
417
418 /* Duplicate the section handle for ourselves */
419 Status = NtDuplicateObject(ProcessData->Process->ProcessHandle,
420 hClientSection,
421 NtCurrentProcess(),
422 &hSection,
423 0, 0, DUPLICATE_SAME_ACCESS);
424 if (!NT_SUCCESS(Status))
425 {
426 DPRINT1("Error when mapping client handle, Status = %lu\n", Status);
427 return;
428 }
429
430 /* Get a view of the shared section */
431 Status = NtMapViewOfSection(hSection,
432 NtCurrentProcess(),
433 (PVOID*)&pConInfo,
434 0,
435 0,
436 NULL,
437 &ViewSize,
438 ViewUnmap,
439 0,
440 PAGE_READONLY);
441 if (!NT_SUCCESS(Status))
442 {
443 DPRINT1("Error when mapping view of file, Status = %lu\n", Status);
444 NtClose(hSection);
445 return;
446 }
447
448 /* Check that the section is well-sized */
449 if (ViewSize < sizeof(CONSOLE_PROPS))
450 {
451 DPRINT1("Error: section bad-sized: sizeof(Section) < sizeof(CONSOLE_PROPS)\n");
452 NtUnmapViewOfSection(NtCurrentProcess(), pConInfo);
453 NtClose(hSection);
454 return;
455 }
456
457 // TODO: Check that GuiData->hWindow == pConInfo->hConsoleWindow
458
459 /*
460 * Apply foreground and background colors for both screen and popup.
461 * Copy the new palette.
462 * TODO: Really update the screen attributes as FillConsoleOutputAttribute does.
463 */
464 ActiveBuffer->ScreenDefaultAttrib = pConInfo->ci.ScreenAttrib;
465 ActiveBuffer->PopupDefaultAttrib = pConInfo->ci.PopupAttrib;
466 memcpy(Console->Colors, pConInfo->ci.Colors, sizeof(s_Colors)); // FIXME: Possible buffer overflow if s_colors is bigger than pConInfo->Colors.
467
468 /* Apply cursor size */
469 ActiveBuffer->CursorInfo.bVisible = (pConInfo->ci.CursorSize != 0);
470 ActiveBuffer->CursorInfo.dwSize = min(max(pConInfo->ci.CursorSize, 0), 100);
471
472 if (pConInfo->ci.ConsoleSize.X != Console->Size.X ||
473 pConInfo->ci.ConsoleSize.Y != Console->Size.Y)
474 {
475 /* Resize window */
476 Console->Size = pConInfo->ci.ConsoleSize;
477 SizeChanged = TRUE;
478 }
479
480 BufSize = pConInfo->ci.ScreenBufferSize;
481 if (BufSize.X != ActiveBuffer->ScreenBufferSize.X || BufSize.Y != ActiveBuffer->ScreenBufferSize.Y)
482 {
483 if (NT_SUCCESS(GuiResizeBuffer(Console, ActiveBuffer, BufSize)))
484 SizeChanged = TRUE;
485 }
486
487 /* Move the window to the user's values */
488 GuiData->GuiInfo.AutoPosition = pConInfo->ci.u.GuiInfo.AutoPosition;
489 GuiData->GuiInfo.WindowOrigin = pConInfo->ci.u.GuiInfo.WindowOrigin;
490 GuiConsoleMoveWindow(GuiData);
491
492 if (SizeChanged)
493 {
494 /* Resize the window to the user's values */
495 GuiData->WindowSizeLock = TRUE;
496 GuiConsoleResizeWindow(GuiData);
497 GuiData->WindowSizeLock = FALSE;
498 }
499
500 /// LOCK /// LeaveCriticalSection(&Console->Lock);
501 InvalidateRect(pConInfo->hConsoleWindow, NULL, TRUE);
502
503 /* Save settings if needed */
504 // FIXME: Do it in the console properties applet ??
505 if (SaveSettings)
506 {
507 DWORD ProcessId = HandleToUlong(ProcessData->Process->ClientId.UniqueProcess);
508 ConSrvWriteUserSettings(&pConInfo->ci, ProcessId);
509 }
510
511 /* Finally, close the section */
512 NtUnmapViewOfSection(NtCurrentProcess(), pConInfo);
513 NtClose(hSection);
514 }
515
516 static PGUI_CONSOLE_DATA
517 GuiGetGuiData(HWND hWnd)
518 {
519 PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
520 return ( ((GuiData == NULL) || (GuiData->hWindow == hWnd && GuiData->Console != NULL)) ? GuiData : NULL );
521 }
522
523 static VOID
524 GuiConsoleMoveWindow(PGUI_CONSOLE_DATA GuiData)
525 {
526 /* Move the window if needed (not positioned by the system) */
527 if (!GuiData->GuiInfo.AutoPosition)
528 {
529 SetWindowPos(GuiData->hWindow,
530 NULL,
531 GuiData->GuiInfo.WindowOrigin.x,
532 GuiData->GuiInfo.WindowOrigin.y,
533 0,
534 0,
535 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
536 }
537 }
538
539 static VOID
540 GuiConsoleResizeWindow(PGUI_CONSOLE_DATA GuiData)
541 {
542 PCONSOLE Console = GuiData->Console;
543 SCROLLINFO sInfo;
544
545 DWORD Width = Console->Size.X * GuiData->CharWidth +
546 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
547 DWORD Height = Console->Size.Y * GuiData->CharHeight +
548 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
549
550 /* Set scrollbar sizes */
551 sInfo.cbSize = sizeof(SCROLLINFO);
552 sInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
553 sInfo.nMin = 0;
554 if (Console->ActiveBuffer->ScreenBufferSize.Y > Console->Size.Y)
555 {
556 sInfo.nMax = Console->ActiveBuffer->ScreenBufferSize.Y - 1;
557 sInfo.nPage = Console->Size.Y;
558 sInfo.nPos = Console->ActiveBuffer->ShowY;
559 SetScrollInfo(GuiData->hWindow, SB_VERT, &sInfo, TRUE);
560 Width += GetSystemMetrics(SM_CXVSCROLL);
561 ShowScrollBar(GuiData->hWindow, SB_VERT, TRUE);
562 }
563 else
564 {
565 ShowScrollBar(GuiData->hWindow, SB_VERT, FALSE);
566 }
567
568 if (Console->ActiveBuffer->ScreenBufferSize.X > Console->Size.X)
569 {
570 sInfo.nMax = Console->ActiveBuffer->ScreenBufferSize.X - 1;
571 sInfo.nPage = Console->Size.X;
572 sInfo.nPos = Console->ActiveBuffer->ShowX;
573 SetScrollInfo(GuiData->hWindow, SB_HORZ, &sInfo, TRUE);
574 Height += GetSystemMetrics(SM_CYHSCROLL);
575 ShowScrollBar(GuiData->hWindow, SB_HORZ, TRUE);
576 }
577 else
578 {
579 ShowScrollBar(GuiData->hWindow, SB_HORZ, FALSE);
580 }
581
582 /* Resize the window */
583 SetWindowPos(GuiData->hWindow, NULL, 0, 0, Width, Height,
584 SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
585 }
586
587 static BOOL
588 GuiConsoleHandleNcCreate(HWND hWnd, LPCREATESTRUCTW Create)
589 {
590 PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)Create->lpCreateParams;
591 PCONSOLE Console;
592 HDC Dc;
593 HFONT OldFont;
594 TEXTMETRICW Metrics;
595 SIZE CharSize;
596
597 if (NULL == GuiData)
598 {
599 DPRINT1("GuiConsoleNcCreate: No GUI data\n");
600 return FALSE;
601 }
602
603 Console = GuiData->Console;
604
605 GuiData->hWindow = hWnd;
606
607 GuiData->Font = CreateFontW(LOWORD(GuiData->GuiInfo.FontSize),
608 0, // HIWORD(GuiData->GuiInfo.FontSize),
609 0,
610 TA_BASELINE,
611 GuiData->GuiInfo.FontWeight,
612 FALSE,
613 FALSE,
614 FALSE,
615 OEM_CHARSET,
616 OUT_DEFAULT_PRECIS,
617 CLIP_DEFAULT_PRECIS,
618 NONANTIALIASED_QUALITY,
619 FIXED_PITCH | GuiData->GuiInfo.FontFamily /* FF_DONTCARE */,
620 GuiData->GuiInfo.FaceName);
621
622 if (NULL == GuiData->Font)
623 {
624 DPRINT1("GuiConsoleNcCreate: CreateFont failed\n");
625 GuiData->hWindow = NULL;
626 SetEvent(GuiData->hGuiInitEvent);
627 return FALSE;
628 }
629 Dc = GetDC(GuiData->hWindow);
630 if (NULL == Dc)
631 {
632 DPRINT1("GuiConsoleNcCreate: GetDC failed\n");
633 DeleteObject(GuiData->Font);
634 GuiData->hWindow = NULL;
635 SetEvent(GuiData->hGuiInitEvent);
636 return FALSE;
637 }
638 OldFont = SelectObject(Dc, GuiData->Font);
639 if (NULL == OldFont)
640 {
641 DPRINT1("GuiConsoleNcCreate: SelectObject failed\n");
642 ReleaseDC(GuiData->hWindow, Dc);
643 DeleteObject(GuiData->Font);
644 GuiData->hWindow = NULL;
645 SetEvent(GuiData->hGuiInitEvent);
646 return FALSE;
647 }
648 if (!GetTextMetricsW(Dc, &Metrics))
649 {
650 DPRINT1("GuiConsoleNcCreate: GetTextMetrics failed\n");
651 SelectObject(Dc, OldFont);
652 ReleaseDC(GuiData->hWindow, Dc);
653 DeleteObject(GuiData->Font);
654 GuiData->hWindow = NULL;
655 SetEvent(GuiData->hGuiInitEvent);
656 return FALSE;
657 }
658 GuiData->CharWidth = Metrics.tmMaxCharWidth;
659 GuiData->CharHeight = Metrics.tmHeight + Metrics.tmExternalLeading;
660
661 /* Measure real char width more precisely if possible. */
662 if (GetTextExtentPoint32W(Dc, L"R", 1, &CharSize))
663 GuiData->CharWidth = CharSize.cx;
664
665 SelectObject(Dc, OldFont);
666
667 ReleaseDC(GuiData->hWindow, Dc);
668
669 // FIXME: Keep these instructions here ? ///////////////////////////////////
670 Console->ActiveBuffer->CursorBlinkOn = TRUE;
671 Console->ActiveBuffer->ForceCursorOff = FALSE;
672 ////////////////////////////////////////////////////////////////////////////
673
674 SetWindowLongPtrW(GuiData->hWindow, GWLP_USERDATA, (DWORD_PTR)GuiData);
675
676 SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL);
677 GuiConsoleCreateSysMenu(GuiData->hWindow);
678
679 /* Move and resize the window to the user's values */
680 GuiConsoleMoveWindow(GuiData);
681 GuiData->WindowSizeLock = TRUE;
682 GuiConsoleResizeWindow(GuiData);
683 GuiData->WindowSizeLock = FALSE;
684
685 SetEvent(GuiData->hGuiInitEvent);
686
687 return (BOOL)DefWindowProcW(GuiData->hWindow, WM_NCCREATE, 0, (LPARAM)Create);
688 }
689
690 static VOID
691 SmallRectToRect(PGUI_CONSOLE_DATA GuiData, PRECT Rect, PSMALL_RECT SmallRect)
692 {
693 PCONSOLE Console = GuiData->Console;
694 PCONSOLE_SCREEN_BUFFER Buffer = Console->ActiveBuffer;
695
696 Rect->left = (SmallRect->Left - Buffer->ShowX) * GuiData->CharWidth;
697 Rect->top = (SmallRect->Top - Buffer->ShowY) * GuiData->CharHeight;
698 Rect->right = (SmallRect->Right + 1 - Buffer->ShowX) * GuiData->CharWidth;
699 Rect->bottom = (SmallRect->Bottom + 1 - Buffer->ShowY) * GuiData->CharHeight;
700 }
701
702 static VOID
703 GuiConsoleUpdateSelection(PCONSOLE Console, PCOORD coord)
704 {
705 PGUI_CONSOLE_DATA GuiData = Console->TermIFace.Data;
706 RECT oldRect, newRect;
707
708 SmallRectToRect(GuiData, &oldRect, &Console->Selection.srSelection);
709
710 if(coord != NULL)
711 {
712 SMALL_RECT rc;
713 /* exchange left/top with right/bottom if required */
714 rc.Left = min(Console->Selection.dwSelectionAnchor.X, coord->X);
715 rc.Top = min(Console->Selection.dwSelectionAnchor.Y, coord->Y);
716 rc.Right = max(Console->Selection.dwSelectionAnchor.X, coord->X);
717 rc.Bottom = max(Console->Selection.dwSelectionAnchor.Y, coord->Y);
718
719 SmallRectToRect(GuiData, &newRect, &rc);
720
721 if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
722 {
723 if (memcmp(&rc, &Console->Selection.srSelection, sizeof(SMALL_RECT)) != 0)
724 {
725 HRGN rgn1, rgn2;
726
727 /* calculate the region that needs to be updated */
728 if((rgn1 = CreateRectRgnIndirect(&oldRect)))
729 {
730 if((rgn2 = CreateRectRgnIndirect(&newRect)))
731 {
732 if(CombineRgn(rgn1, rgn2, rgn1, RGN_XOR) != ERROR)
733 {
734 InvalidateRgn(GuiData->hWindow, rgn1, FALSE);
735 }
736
737 DeleteObject(rgn2);
738 }
739 DeleteObject(rgn1);
740 }
741 }
742 }
743 else
744 {
745 InvalidateRect(GuiData->hWindow, &newRect, FALSE);
746 }
747 Console->Selection.dwFlags |= CONSOLE_SELECTION_NOT_EMPTY;
748 Console->Selection.srSelection = rc;
749 ConioPause(Console, PAUSED_FROM_SELECTION);
750 }
751 else
752 {
753 /* clear the selection */
754 if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
755 {
756 InvalidateRect(GuiData->hWindow, &oldRect, FALSE);
757 }
758 Console->Selection.dwFlags = CONSOLE_NO_SELECTION;
759 ConioUnpause(Console, PAUSED_FROM_SELECTION);
760 }
761 }
762
763 static VOID
764 GuiConsolePaint(PCONSOLE Console,
765 PGUI_CONSOLE_DATA GuiData,
766 HDC hDC,
767 PRECT rc)
768 {
769 PCONSOLE_SCREEN_BUFFER Buff;
770 ULONG TopLine, BottomLine, LeftChar, RightChar;
771 ULONG Line, Char, Start;
772 PBYTE From;
773 PWCHAR To;
774 BYTE LastAttribute, Attribute;
775 ULONG CursorX, CursorY, CursorHeight;
776 HBRUSH CursorBrush, OldBrush;
777 HFONT OldFont;
778
779 Buff = Console->ActiveBuffer;
780
781 /// LOCK /// EnterCriticalSection(&Buff->Header.Console->Lock);
782
783 TopLine = rc->top / GuiData->CharHeight + Buff->ShowY;
784 BottomLine = (rc->bottom + (GuiData->CharHeight - 1)) / GuiData->CharHeight - 1 + Buff->ShowY;
785 LeftChar = rc->left / GuiData->CharWidth + Buff->ShowX;
786 RightChar = (rc->right + (GuiData->CharWidth - 1)) / GuiData->CharWidth - 1 + Buff->ShowX;
787 LastAttribute = ConioCoordToPointer(Buff, LeftChar, TopLine)[1];
788
789 SetTextColor(hDC, RGBFromAttrib(Console, TextAttribFromAttrib(LastAttribute)));
790 SetBkColor(hDC, RGBFromAttrib(Console, BkgdAttribFromAttrib(LastAttribute)));
791
792 if (BottomLine >= Buff->ScreenBufferSize.Y) BottomLine = Buff->ScreenBufferSize.Y - 1;
793 if (RightChar >= Buff->ScreenBufferSize.X) RightChar = Buff->ScreenBufferSize.X - 1;
794
795 OldFont = SelectObject(hDC, GuiData->Font);
796
797 for (Line = TopLine; Line <= BottomLine; Line++)
798 {
799 WCHAR LineBuffer[80];
800 From = ConioCoordToPointer(Buff, LeftChar, Line);
801 Start = LeftChar;
802 To = LineBuffer;
803
804 for (Char = LeftChar; Char <= RightChar; Char++)
805 {
806 if (*(From + 1) != LastAttribute || (Char - Start == sizeof(LineBuffer) / sizeof(WCHAR)))
807 {
808 TextOutW(hDC,
809 (Start - Buff->ShowX) * GuiData->CharWidth,
810 (Line - Buff->ShowY) * GuiData->CharHeight,
811 LineBuffer,
812 Char - Start);
813 Start = Char;
814 To = LineBuffer;
815 Attribute = *(From + 1);
816 if (Attribute != LastAttribute)
817 {
818 SetTextColor(hDC, RGBFromAttrib(Console, TextAttribFromAttrib(Attribute)));
819 SetBkColor(hDC, RGBFromAttrib(Console, BkgdAttribFromAttrib(Attribute)));
820 LastAttribute = Attribute;
821 }
822 }
823
824 MultiByteToWideChar(Console->OutputCodePage,
825 0,
826 (PCHAR)From,
827 1,
828 To,
829 1);
830 To++;
831 From += 2;
832 }
833
834 TextOutW(hDC,
835 (Start - Buff->ShowX) * GuiData->CharWidth,
836 (Line - Buff->ShowY) * GuiData->CharHeight,
837 LineBuffer,
838 RightChar - Start + 1);
839 }
840
841 if (Buff->CursorInfo.bVisible && Buff->CursorBlinkOn &&
842 !Buff->ForceCursorOff)
843 {
844 CursorX = Buff->CursorPosition.X;
845 CursorY = Buff->CursorPosition.Y;
846 if (LeftChar <= CursorX && CursorX <= RightChar &&
847 TopLine <= CursorY && CursorY <= BottomLine)
848 {
849 CursorHeight = ConioEffectiveCursorSize(Console, GuiData->CharHeight);
850 From = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y) + 1;
851
852 if (*From != DEFAULT_SCREEN_ATTRIB)
853 {
854 CursorBrush = CreateSolidBrush(RGBFromAttrib(Console, *From));
855 }
856 else
857 {
858 CursorBrush = CreateSolidBrush(RGBFromAttrib(Console, Buff->ScreenDefaultAttrib));
859 }
860
861 OldBrush = SelectObject(hDC, CursorBrush);
862 PatBlt(hDC,
863 (CursorX - Buff->ShowX) * GuiData->CharWidth,
864 (CursorY - Buff->ShowY) * GuiData->CharHeight + (GuiData->CharHeight - CursorHeight),
865 GuiData->CharWidth,
866 CursorHeight,
867 PATCOPY);
868 SelectObject(hDC, OldBrush);
869 DeleteObject(CursorBrush);
870 }
871 }
872
873 /// LOCK /// LeaveCriticalSection(&Buff->Header.Console->Lock);
874
875 SelectObject(hDC, OldFont);
876 }
877
878 static VOID
879 GuiConsoleHandlePaint(PGUI_CONSOLE_DATA GuiData, HDC hDCPaint)
880 {
881 PCONSOLE Console = GuiData->Console;
882 HDC hDC;
883 PAINTSTRUCT ps;
884
885 if (Console->ActiveBuffer == NULL) return;
886
887 hDC = BeginPaint(GuiData->hWindow, &ps);
888 if (hDC != NULL &&
889 ps.rcPaint.left < ps.rcPaint.right &&
890 ps.rcPaint.top < ps.rcPaint.bottom)
891 {
892 if (Console->ActiveBuffer->Buffer != NULL)
893 {
894 EnterCriticalSection(&GuiData->Lock);
895
896 GuiConsolePaint(Console,
897 GuiData,
898 hDC,
899 &ps.rcPaint);
900
901 if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
902 {
903 RECT rc;
904 SmallRectToRect(GuiData, &rc, &Console->Selection.srSelection);
905
906 /* invert the selection */
907 if (IntersectRect(&rc,
908 &ps.rcPaint,
909 &rc))
910 {
911 PatBlt(hDC,
912 rc.left,
913 rc.top,
914 rc.right - rc.left,
915 rc.bottom - rc.top,
916 DSTINVERT);
917 }
918 }
919
920 LeaveCriticalSection(&GuiData->Lock);
921 }
922 }
923 EndPaint(GuiData->hWindow, &ps);
924 }
925
926 static VOID
927 GuiConsoleHandleKey(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam)
928 {
929 PCONSOLE Console = GuiData->Console;
930 MSG Message;
931
932 Message.hwnd = GuiData->hWindow;
933 Message.message = msg;
934 Message.wParam = wParam;
935 Message.lParam = lParam;
936
937 if(msg == WM_CHAR || msg == WM_SYSKEYDOWN)
938 {
939 /* clear the selection */
940 GuiConsoleUpdateSelection(Console, NULL);
941 }
942
943 ConioProcessKey(Console, &Message);
944 }
945
946 static VOID
947 GuiInvalidateCell(PCONSOLE Console, UINT x, UINT y)
948 {
949 SMALL_RECT CellRect = { x, y, x, y };
950 GuiDrawRegion(Console, &CellRect);
951 }
952
953 static VOID
954 GuiConsoleHandleTimer(PGUI_CONSOLE_DATA GuiData)
955 {
956 PCONSOLE Console = GuiData->Console;
957 PCONSOLE_SCREEN_BUFFER Buff;
958
959 SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CURSOR_BLINK_TIME, NULL);
960
961 Buff = Console->ActiveBuffer;
962 GuiInvalidateCell(Console, Buff->CursorPosition.X, Buff->CursorPosition.Y);
963 Buff->CursorBlinkOn = !Buff->CursorBlinkOn;
964
965 if((GuiData->OldCursor.x != Buff->CursorPosition.X) || (GuiData->OldCursor.y != Buff->CursorPosition.Y))
966 {
967 SCROLLINFO xScroll;
968 int OldScrollX = -1, OldScrollY = -1;
969 int NewScrollX = -1, NewScrollY = -1;
970
971 xScroll.cbSize = sizeof(SCROLLINFO);
972 xScroll.fMask = SIF_POS;
973 // Capture the original position of the scroll bars and save them.
974 if(GetScrollInfo(GuiData->hWindow, SB_HORZ, &xScroll))OldScrollX = xScroll.nPos;
975 if(GetScrollInfo(GuiData->hWindow, SB_VERT, &xScroll))OldScrollY = xScroll.nPos;
976
977 // If we successfully got the info for the horizontal scrollbar
978 if(OldScrollX >= 0)
979 {
980 if((Buff->CursorPosition.X < Buff->ShowX)||(Buff->CursorPosition.X >= (Buff->ShowX + Console->Size.X)))
981 {
982 // Handle the horizontal scroll bar
983 if(Buff->CursorPosition.X >= Console->Size.X) NewScrollX = Buff->CursorPosition.X - Console->Size.X + 1;
984 else NewScrollX = 0;
985 }
986 else
987 {
988 NewScrollX = OldScrollX;
989 }
990 }
991 // If we successfully got the info for the vertical scrollbar
992 if(OldScrollY >= 0)
993 {
994 if((Buff->CursorPosition.Y < Buff->ShowY) || (Buff->CursorPosition.Y >= (Buff->ShowY + Console->Size.Y)))
995 {
996 // Handle the vertical scroll bar
997 if(Buff->CursorPosition.Y >= Console->Size.Y) NewScrollY = Buff->CursorPosition.Y - Console->Size.Y + 1;
998 else NewScrollY = 0;
999 }
1000 else
1001 {
1002 NewScrollY = OldScrollY;
1003 }
1004 }
1005
1006 // Adjust scroll bars and refresh the window if the cursor has moved outside the visible area
1007 // NOTE: OldScroll# and NewScroll# will both be -1 (initial value) if the info for the respective scrollbar
1008 // was not obtained successfully in the previous steps. This means their difference is 0 (no scrolling)
1009 // and their associated scrollbar is left alone.
1010 if((OldScrollX != NewScrollX) || (OldScrollY != NewScrollY))
1011 {
1012 Buff->ShowX = NewScrollX;
1013 Buff->ShowY = NewScrollY;
1014 ScrollWindowEx(GuiData->hWindow,
1015 (OldScrollX - NewScrollX) * GuiData->CharWidth,
1016 (OldScrollY - NewScrollY) * GuiData->CharHeight,
1017 NULL,
1018 NULL,
1019 NULL,
1020 NULL,
1021 SW_INVALIDATE);
1022 if(NewScrollX >= 0)
1023 {
1024 xScroll.nPos = NewScrollX;
1025 SetScrollInfo(GuiData->hWindow, SB_HORZ, &xScroll, TRUE);
1026 }
1027 if(NewScrollY >= 0)
1028 {
1029 xScroll.nPos = NewScrollY;
1030 SetScrollInfo(GuiData->hWindow, SB_VERT, &xScroll, TRUE);
1031 }
1032 UpdateWindow(GuiData->hWindow);
1033 GuiData->OldCursor.x = Buff->CursorPosition.X;
1034 GuiData->OldCursor.y = Buff->CursorPosition.Y;
1035 }
1036 }
1037 }
1038
1039 static VOID
1040 GuiConsoleHandleClose(PGUI_CONSOLE_DATA GuiData)
1041 {
1042 PCONSOLE Console = GuiData->Console;
1043 PLIST_ENTRY current_entry;
1044 PCONSOLE_PROCESS_DATA current;
1045
1046 /// LOCK /// EnterCriticalSection(&Console->Lock);
1047
1048 /*
1049 * Loop through the process list, from the most recent process
1050 * (the active one) to the oldest one (the first created,
1051 * i.e. the console leader process), and for each, send a
1052 * CTRL-CLOSE event (new processes are inserted at the head
1053 * of the console process list).
1054 */
1055 current_entry = Console->ProcessList.Flink;
1056 while (current_entry != &Console->ProcessList)
1057 {
1058 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
1059 current_entry = current_entry->Flink;
1060
1061 /* FIXME: Windows will wait up to 5 seconds for the thread to exit.
1062 * We shouldn't wait here, though, since the console lock is entered.
1063 * A copy of the thread list probably needs to be made. */
1064 ConSrvConsoleCtrlEvent(CTRL_CLOSE_EVENT, current);
1065 }
1066
1067 /// LOCK /// LeaveCriticalSection(&Console->Lock);
1068 }
1069
1070 static VOID
1071 GuiConsoleHandleNcDestroy(PGUI_CONSOLE_DATA GuiData)
1072 {
1073 KillTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER);
1074 GetSystemMenu(GuiData->hWindow, TRUE);
1075
1076 SetWindowLongPtrW(GuiData->hWindow, GWLP_USERDATA, (DWORD_PTR)NULL);
1077 GuiData->hWindow = NULL;
1078 }
1079
1080 static COORD
1081 PointToCoord(PGUI_CONSOLE_DATA GuiData, LPARAM lParam)
1082 {
1083 PCONSOLE Console = GuiData->Console;
1084 PCONSOLE_SCREEN_BUFFER Buffer = Console->ActiveBuffer;
1085 COORD Coord;
1086
1087 Coord.X = Buffer->ShowX + ((short)LOWORD(lParam) / (int)GuiData->CharWidth);
1088 Coord.Y = Buffer->ShowY + ((short)HIWORD(lParam) / (int)GuiData->CharHeight);
1089
1090 /* Clip coordinate to ensure it's inside buffer */
1091 if (Coord.X < 0)
1092 Coord.X = 0;
1093 else if (Coord.X >= Buffer->ScreenBufferSize.X)
1094 Coord.X = Buffer->ScreenBufferSize.X - 1;
1095
1096 if (Coord.Y < 0)
1097 Coord.Y = 0;
1098 else if (Coord.Y >= Buffer->ScreenBufferSize.Y)
1099 Coord.Y = Buffer->ScreenBufferSize.Y - 1;
1100
1101 return Coord;
1102 }
1103
1104 static VOID
1105 GuiConsoleLeftMouseDown(PGUI_CONSOLE_DATA GuiData, LPARAM lParam)
1106 {
1107 PCONSOLE Console = GuiData->Console;
1108
1109 Console->Selection.dwSelectionAnchor = PointToCoord(GuiData, lParam);
1110 SetCapture(GuiData->hWindow);
1111 Console->Selection.dwFlags |= CONSOLE_SELECTION_IN_PROGRESS | CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN;
1112 GuiConsoleUpdateSelection(Console, &Console->Selection.dwSelectionAnchor);
1113 }
1114
1115 static VOID
1116 GuiConsoleLeftMouseUp(PGUI_CONSOLE_DATA GuiData, LPARAM lParam)
1117 {
1118 PCONSOLE Console = GuiData->Console;
1119 COORD c;
1120
1121 if (!(Console->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) return;
1122
1123 c = PointToCoord(GuiData, lParam);
1124 Console->Selection.dwFlags &= ~CONSOLE_MOUSE_DOWN;
1125 GuiConsoleUpdateSelection(Console, &c);
1126 ReleaseCapture();
1127 }
1128
1129 static VOID
1130 GuiConsoleMouseMove(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam)
1131 {
1132 PCONSOLE Console = GuiData->Console;
1133 COORD c;
1134
1135 if (!(wParam & MK_LBUTTON)) return;
1136
1137 if (!(Console->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) return;
1138
1139 c = PointToCoord(GuiData, lParam); /* TODO: Scroll buffer to bring c into view */
1140 GuiConsoleUpdateSelection(Console, &c);
1141 }
1142
1143 static VOID
1144 GuiConsoleCopy(PGUI_CONSOLE_DATA GuiData)
1145 {
1146 PCONSOLE Console = GuiData->Console;
1147
1148 if (OpenClipboard(GuiData->hWindow) == TRUE)
1149 {
1150 HANDLE hData;
1151 PBYTE ptr;
1152 LPSTR data, dstPos;
1153 ULONG selWidth, selHeight;
1154 ULONG xPos, yPos, size;
1155
1156 selWidth = Console->Selection.srSelection.Right - Console->Selection.srSelection.Left + 1;
1157 selHeight = Console->Selection.srSelection.Bottom - Console->Selection.srSelection.Top + 1;
1158 DPRINT("Selection is (%d|%d) to (%d|%d)\n",
1159 Console->Selection.srSelection.Left,
1160 Console->Selection.srSelection.Top,
1161 Console->Selection.srSelection.Right,
1162 Console->Selection.srSelection.Bottom);
1163
1164 /* Basic size for one line and termination */
1165 size = selWidth + 1;
1166 if (selHeight > 0)
1167 {
1168 /* Multiple line selections have to get \r\n appended */
1169 size += ((selWidth + 2) * (selHeight - 1));
1170 }
1171
1172 /* Allocate memory, it will be passed to the system and may not be freed here */
1173 hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, size);
1174 if (hData == NULL)
1175 {
1176 CloseClipboard();
1177 return;
1178 }
1179 data = GlobalLock(hData);
1180 if (data == NULL)
1181 {
1182 CloseClipboard();
1183 return;
1184 }
1185
1186 DPRINT("Copying %dx%d selection\n", selWidth, selHeight);
1187 dstPos = data;
1188
1189 for (yPos = 0; yPos < selHeight; yPos++)
1190 {
1191 ptr = ConioCoordToPointer(Console->ActiveBuffer,
1192 Console->Selection.srSelection.Left,
1193 yPos + Console->Selection.srSelection.Top);
1194 /* Copy only the characters, leave attributes alone */
1195 for (xPos = 0; xPos < selWidth; xPos++)
1196 {
1197 dstPos[xPos] = ptr[xPos * 2];
1198 }
1199 dstPos += selWidth;
1200 if (yPos != (selHeight - 1))
1201 {
1202 strcat(data, "\r\n");
1203 dstPos += 2;
1204 }
1205 }
1206
1207 DPRINT("Setting data <%s> to clipboard\n", data);
1208 GlobalUnlock(hData);
1209
1210 EmptyClipboard();
1211 SetClipboardData(CF_TEXT, hData);
1212 CloseClipboard();
1213 }
1214 }
1215
1216 static VOID
1217 GuiConsolePaste(PGUI_CONSOLE_DATA GuiData)
1218 {
1219 PCONSOLE Console = GuiData->Console;
1220
1221 if (OpenClipboard(GuiData->hWindow) == TRUE)
1222 {
1223 HANDLE hData;
1224 LPSTR str;
1225 size_t len;
1226
1227 hData = GetClipboardData(CF_TEXT);
1228 if (hData == NULL)
1229 {
1230 CloseClipboard();
1231 return;
1232 }
1233
1234 str = GlobalLock(hData);
1235 if (str == NULL)
1236 {
1237 CloseClipboard();
1238 return;
1239 }
1240 DPRINT("Got data <%s> from clipboard\n", str);
1241 len = strlen(str);
1242
1243 // TODO: Push the text into the input buffer.
1244 ConioWriteConsole(Console, Console->ActiveBuffer, str, len, TRUE);
1245
1246 GlobalUnlock(hData);
1247 CloseClipboard();
1248 }
1249 }
1250
1251 static VOID
1252 GuiConsoleRightMouseDown(PGUI_CONSOLE_DATA GuiData)
1253 {
1254 PCONSOLE Console = GuiData->Console;
1255
1256 if (!(Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY))
1257 {
1258 GuiConsolePaste(GuiData);
1259 }
1260 else
1261 {
1262 GuiConsoleCopy(GuiData);
1263
1264 /* Clear the selection */
1265 GuiConsoleUpdateSelection(Console, NULL);
1266 }
1267 }
1268
1269 static VOID
1270 GuiConsoleGetMinMaxInfo(PGUI_CONSOLE_DATA GuiData, PMINMAXINFO minMaxInfo)
1271 {
1272 PCONSOLE Console = GuiData->Console;
1273 DWORD windx, windy;
1274
1275 windx = CONGUI_MIN_WIDTH * GuiData->CharWidth + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
1276 windy = CONGUI_MIN_HEIGHT * GuiData->CharHeight + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
1277
1278 minMaxInfo->ptMinTrackSize.x = windx;
1279 minMaxInfo->ptMinTrackSize.y = windy;
1280
1281 windx = (Console->ActiveBuffer->ScreenBufferSize.X) * GuiData->CharWidth + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
1282 windy = (Console->ActiveBuffer->ScreenBufferSize.Y) * GuiData->CharHeight + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
1283
1284 if(Console->Size.X < Console->ActiveBuffer->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar
1285 if(Console->Size.Y < Console->ActiveBuffer->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar
1286
1287 minMaxInfo->ptMaxTrackSize.x = windx;
1288 minMaxInfo->ptMaxTrackSize.y = windy;
1289 }
1290
1291 static VOID
1292 GuiConsoleResize(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam)
1293 {
1294 PCONSOLE Console = GuiData->Console;
1295
1296 if ((GuiData->WindowSizeLock == FALSE) &&
1297 (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED || wParam == SIZE_MINIMIZED))
1298 {
1299 PCONSOLE_SCREEN_BUFFER Buff = Console->ActiveBuffer;
1300 DWORD windx, windy, charx, chary;
1301
1302 GuiData->WindowSizeLock = TRUE;
1303
1304 windx = LOWORD(lParam);
1305 windy = HIWORD(lParam);
1306
1307 // Compensate for existing scroll bars (because lParam values do not accommodate scroll bar)
1308 if(Console->Size.X < Buff->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar
1309 if(Console->Size.Y < Buff->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar
1310
1311 charx = windx / GuiData->CharWidth;
1312 chary = windy / GuiData->CharHeight;
1313
1314 // Character alignment (round size up or down)
1315 if((windx % GuiData->CharWidth) >= (GuiData->CharWidth / 2)) ++charx;
1316 if((windy % GuiData->CharHeight) >= (GuiData->CharHeight / 2)) ++chary;
1317
1318 // Compensate for added scroll bars in new window
1319 if(charx < Buff->ScreenBufferSize.X)windy -= GetSystemMetrics(SM_CYHSCROLL); // new window will have a horizontal scroll bar
1320 if(chary < Buff->ScreenBufferSize.Y)windx -= GetSystemMetrics(SM_CXVSCROLL); // new window will have a vertical scroll bar
1321
1322 charx = windx / GuiData->CharWidth;
1323 chary = windy / GuiData->CharHeight;
1324
1325 // Character alignment (round size up or down)
1326 if((windx % GuiData->CharWidth) >= (GuiData->CharWidth / 2)) ++charx;
1327 if((windy % GuiData->CharHeight) >= (GuiData->CharHeight / 2)) ++chary;
1328
1329 // Resize window
1330 if((charx != Console->Size.X) || (chary != Console->Size.Y))
1331 {
1332 Console->Size.X = (charx <= Buff->ScreenBufferSize.X) ? charx : Buff->ScreenBufferSize.X;
1333 Console->Size.Y = (chary <= Buff->ScreenBufferSize.Y) ? chary : Buff->ScreenBufferSize.Y;
1334 }
1335
1336 GuiConsoleResizeWindow(GuiData);
1337
1338 // Adjust the start of the visible area if we are attempting to show nonexistent areas
1339 if((Buff->ScreenBufferSize.X - Buff->ShowX) < Console->Size.X) Buff->ShowX = Buff->ScreenBufferSize.X - Console->Size.X;
1340 if((Buff->ScreenBufferSize.Y - Buff->ShowY) < Console->Size.Y) Buff->ShowY = Buff->ScreenBufferSize.Y - Console->Size.Y;
1341 InvalidateRect(GuiData->hWindow, NULL, TRUE);
1342
1343 GuiData->WindowSizeLock = FALSE;
1344 }
1345 }
1346
1347 /*
1348 // HACK: This functionality is standard for general scrollbars. Don't add it by hand.
1349
1350 VOID
1351 FASTCALL
1352 GuiConsoleHandleScrollbarMenu(VOID)
1353 {
1354 HMENU hMenu;
1355
1356 hMenu = CreatePopupMenu();
1357 if (hMenu == NULL)
1358 {
1359 DPRINT("CreatePopupMenu failed\n");
1360 return;
1361 }
1362
1363 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLHERE);
1364 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
1365 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLTOP);
1366 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLBOTTOM);
1367 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
1368 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_UP);
1369 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_DOWN);
1370 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
1371 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLUP);
1372 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLDOWN);
1373 }
1374 */
1375
1376 static
1377 LRESULT
1378 GuiConsoleHandleScroll(PGUI_CONSOLE_DATA GuiData, UINT uMsg, WPARAM wParam)
1379 {
1380 PCONSOLE Console = GuiData->Console;
1381 PCONSOLE_SCREEN_BUFFER Buff;
1382 SCROLLINFO sInfo;
1383 int fnBar;
1384 int old_pos, Maximum;
1385 PUSHORT pShowXY;
1386
1387 if (GuiData == NULL) return FALSE;
1388
1389 Buff = Console->ActiveBuffer;
1390
1391 if (uMsg == WM_HSCROLL)
1392 {
1393 fnBar = SB_HORZ;
1394 Maximum = Buff->ScreenBufferSize.X - Console->Size.X;
1395 pShowXY = &Buff->ShowX;
1396 }
1397 else
1398 {
1399 fnBar = SB_VERT;
1400 Maximum = Buff->ScreenBufferSize.Y - Console->Size.Y;
1401 pShowXY = &Buff->ShowY;
1402 }
1403
1404 /* set scrollbar sizes */
1405 sInfo.cbSize = sizeof(SCROLLINFO);
1406 sInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE | SIF_TRACKPOS;
1407
1408 if (!GetScrollInfo(GuiData->hWindow, fnBar, &sInfo))
1409 {
1410 return FALSE;
1411 }
1412
1413 old_pos = sInfo.nPos;
1414
1415 switch (LOWORD(wParam))
1416 {
1417 case SB_LINELEFT:
1418 sInfo.nPos -= 1;
1419 break;
1420
1421 case SB_LINERIGHT:
1422 sInfo.nPos += 1;
1423 break;
1424
1425 case SB_PAGELEFT:
1426 sInfo.nPos -= sInfo.nPage;
1427 break;
1428
1429 case SB_PAGERIGHT:
1430 sInfo.nPos += sInfo.nPage;
1431 break;
1432
1433 case SB_THUMBTRACK:
1434 sInfo.nPos = sInfo.nTrackPos;
1435 ConioPause(Console, PAUSED_FROM_SCROLLBAR);
1436 break;
1437
1438 case SB_THUMBPOSITION:
1439 ConioUnpause(Console, PAUSED_FROM_SCROLLBAR);
1440 break;
1441
1442 case SB_TOP:
1443 sInfo.nPos = sInfo.nMin;
1444 break;
1445
1446 case SB_BOTTOM:
1447 sInfo.nPos = sInfo.nMax;
1448 break;
1449
1450 default:
1451 break;
1452 }
1453
1454 sInfo.nPos = max(sInfo.nPos, 0);
1455 sInfo.nPos = min(sInfo.nPos, Maximum);
1456
1457 if (old_pos != sInfo.nPos)
1458 {
1459 USHORT OldX = Buff->ShowX;
1460 USHORT OldY = Buff->ShowY;
1461 *pShowXY = sInfo.nPos;
1462
1463 ScrollWindowEx(GuiData->hWindow,
1464 (OldX - Buff->ShowX) * GuiData->CharWidth,
1465 (OldY - Buff->ShowY) * GuiData->CharHeight,
1466 NULL,
1467 NULL,
1468 NULL,
1469 NULL,
1470 SW_INVALIDATE);
1471
1472 sInfo.fMask = SIF_POS;
1473 SetScrollInfo(GuiData->hWindow, fnBar, &sInfo, TRUE);
1474
1475 UpdateWindow(GuiData->hWindow);
1476 }
1477
1478 return 0;
1479 }
1480
1481 static LRESULT CALLBACK
1482 GuiConsoleWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1483 {
1484 LRESULT Result = 0;
1485 PGUI_CONSOLE_DATA GuiData = NULL;
1486
1487 /*
1488 * If it's the first time we create a window
1489 * for the terminal, just initialize it.
1490 */
1491 if (msg == WM_NCCREATE)
1492 {
1493 return (LRESULT)GuiConsoleHandleNcCreate(hWnd, (LPCREATESTRUCTW)lParam);
1494 }
1495
1496 /*
1497 * Now the terminal window is initialized.
1498 * Get the terminal data via the window's data.
1499 * If there is no data, just go away.
1500 */
1501 GuiData = GuiGetGuiData(hWnd);
1502 if (GuiData == NULL) return 0;
1503
1504 // TODO: If the console is about to be destroyed, leave the loop.
1505
1506 /* Lock the console */
1507 EnterCriticalSection(&GuiData->Console->Lock);
1508
1509 /* We have a console, start message dispatching. */
1510 switch (msg)
1511 {
1512 case WM_CLOSE:
1513 GuiConsoleHandleClose(GuiData);
1514 break;
1515
1516 case WM_NCDESTROY:
1517 GuiConsoleHandleNcDestroy(GuiData);
1518 break;
1519
1520 case WM_PAINT:
1521 GuiConsoleHandlePaint(GuiData, (HDC)wParam);
1522 break;
1523
1524 case WM_KEYDOWN:
1525 case WM_KEYUP:
1526 case WM_SYSKEYDOWN:
1527 case WM_SYSKEYUP:
1528 case WM_CHAR:
1529 GuiConsoleHandleKey(GuiData, msg, wParam, lParam);
1530 break;
1531
1532 case WM_TIMER:
1533 GuiConsoleHandleTimer(GuiData);
1534 break;
1535
1536 case WM_LBUTTONDOWN:
1537 GuiConsoleLeftMouseDown(GuiData, lParam);
1538 break;
1539
1540 case WM_LBUTTONUP:
1541 GuiConsoleLeftMouseUp(GuiData, lParam);
1542 break;
1543
1544 case WM_RBUTTONDOWN:
1545 GuiConsoleRightMouseDown(GuiData);
1546 break;
1547
1548 case WM_MOUSEMOVE:
1549 GuiConsoleMouseMove(GuiData, wParam, lParam);
1550 break;
1551
1552 case WM_SYSCOMMAND:
1553 Result = GuiConsoleHandleSysMenuCommand(GuiData, wParam, lParam);
1554 break;
1555
1556 case WM_HSCROLL:
1557 case WM_VSCROLL:
1558 Result = GuiConsoleHandleScroll(GuiData, msg, wParam);
1559 break;
1560
1561 case WM_GETMINMAXINFO:
1562 GuiConsoleGetMinMaxInfo(GuiData, (PMINMAXINFO)lParam);
1563 break;
1564
1565 case WM_SIZE:
1566 GuiConsoleResize(GuiData, wParam, lParam);
1567 break;
1568
1569 case PM_APPLY_CONSOLE_INFO:
1570 GuiApplyUserSettings(GuiData, (HANDLE)wParam, (BOOL)lParam);
1571 break;
1572
1573 case PM_CONSOLE_BEEP:
1574 DPRINT1("Beep !!\n");
1575 Beep(800, 200);
1576 break;
1577
1578 case PM_CONSOLE_SET_TITLE:
1579 SetWindowText(GuiData->hWindow, GuiData->Console->Title.Buffer);
1580 break;
1581
1582 default:
1583 Result = DefWindowProcW(GuiData->hWindow, msg, wParam, lParam);
1584 break;
1585 }
1586
1587 /* Unlock the console */
1588 LeaveCriticalSection(&GuiData->Console->Lock);
1589
1590 return Result;
1591 }
1592
1593
1594
1595 /******************************************************************************
1596 * GUI Terminal Initialization *
1597 ******************************************************************************/
1598
1599 static LRESULT CALLBACK
1600 GuiConsoleNotifyWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1601 {
1602 HWND NewWindow;
1603 LONG WindowCount;
1604 MSG Msg;
1605 PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)lParam;
1606 PCONSOLE Console = GuiData->Console;
1607
1608 switch (msg)
1609 {
1610 case WM_CREATE:
1611 {
1612 SetWindowLongW(hWnd, GWL_USERDATA, 0);
1613 return 0;
1614 }
1615
1616 case PM_CREATE_CONSOLE:
1617 {
1618 NewWindow = CreateWindowExW(WS_EX_CLIENTEDGE,
1619 GUI_CONSOLE_WINDOW_CLASS,
1620 Console->Title.Buffer,
1621 WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
1622 CW_USEDEFAULT,
1623 CW_USEDEFAULT,
1624 CW_USEDEFAULT,
1625 CW_USEDEFAULT,
1626 NULL,
1627 NULL,
1628 ConSrvDllInstance,
1629 (PVOID)GuiData);
1630 if (NULL != NewWindow)
1631 {
1632 WindowCount = GetWindowLongW(hWnd, GWL_USERDATA);
1633 WindowCount++;
1634 SetWindowLongW(hWnd, GWL_USERDATA, WindowCount);
1635
1636 // SetConsoleWndConsoleLeaderCID(Console);
1637
1638 DPRINT1("Set icons via PM_CREATE_CONSOLE\n");
1639 if (GuiData->hIcon == NULL)
1640 {
1641 DPRINT1("Not really /o\\...\n");
1642 GuiData->hIcon = ghDefaultIcon;
1643 GuiData->hIconSm = ghDefaultIconSm;
1644 }
1645 else if (GuiData->hIcon != ghDefaultIcon)
1646 {
1647 DPRINT1("Yes \\o/\n");
1648 SendMessageW(GuiData->hWindow, WM_SETICON, ICON_BIG, (LPARAM)GuiData->hIcon);
1649 SendMessageW(GuiData->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)GuiData->hIconSm);
1650 }
1651
1652 ShowWindow(NewWindow, (int)wParam);
1653 }
1654
1655 return (LRESULT)NewWindow;
1656 }
1657
1658 case PM_DESTROY_CONSOLE:
1659 {
1660 /*
1661 * Window creation is done using a PostMessage(), so it's possible
1662 * that the window that we want to destroy doesn't exist yet.
1663 * So first empty the message queue.
1664 */
1665 while(PeekMessageW(&Msg, NULL, 0, 0, PM_REMOVE))
1666 {
1667 TranslateMessage(&Msg);
1668 DispatchMessageW(&Msg);
1669 }
1670
1671 if (GuiData->hWindow != NULL) /* && DestroyWindow(GuiData->hWindow) */
1672 {
1673 DestroyWindow(GuiData->hWindow);
1674
1675 WindowCount = GetWindowLongW(hWnd, GWL_USERDATA);
1676 WindowCount--;
1677 SetWindowLongW(hWnd, GWL_USERDATA, WindowCount);
1678 if (0 == WindowCount)
1679 {
1680 NotifyWnd = NULL;
1681 DestroyWindow(hWnd);
1682 DPRINT1("CONSRV: Going to quit the Gui Thread!!\n");
1683 PostQuitMessage(0);
1684 }
1685 }
1686
1687 return 0;
1688 }
1689
1690 default:
1691 return DefWindowProcW(hWnd, msg, wParam, lParam);
1692 }
1693 }
1694
1695 static DWORD WINAPI
1696 GuiConsoleGuiThread(PVOID Data)
1697 {
1698 MSG msg;
1699 PHANDLE GraphicsStartupEvent = (PHANDLE)Data;
1700
1701 /*
1702 * This thread dispatches all the console notifications to the notify window.
1703 * It is common for all the console windows.
1704 */
1705
1706 PrivateCsrssManualGuiCheck(+1);
1707
1708 NotifyWnd = CreateWindowW(L"ConSrvCreateNotify",
1709 L"",
1710 WS_OVERLAPPEDWINDOW,
1711 CW_USEDEFAULT,
1712 CW_USEDEFAULT,
1713 CW_USEDEFAULT,
1714 CW_USEDEFAULT,
1715 NULL,
1716 NULL,
1717 ConSrvDllInstance,
1718 NULL);
1719 if (NULL == NotifyWnd)
1720 {
1721 PrivateCsrssManualGuiCheck(-1);
1722 SetEvent(*GraphicsStartupEvent);
1723 return 1;
1724 }
1725
1726 SetEvent(*GraphicsStartupEvent);
1727
1728 while(GetMessageW(&msg, NULL, 0, 0))
1729 {
1730 TranslateMessage(&msg);
1731 DispatchMessageW(&msg);
1732 }
1733
1734 DPRINT1("CONSRV: Quit the Gui Thread!!\n");
1735 PrivateCsrssManualGuiCheck(-1);
1736
1737 return 1;
1738 }
1739
1740 static BOOL
1741 GuiInit(VOID)
1742 {
1743 WNDCLASSEXW wc;
1744 ATOM ConsoleClassAtom;
1745
1746 /* Exit if we were already initialized */
1747 // if (ConsInitialized) return TRUE;
1748
1749 /*
1750 * Initialize and register the different window classes, if needed.
1751 */
1752 if (!ConsInitialized)
1753 {
1754 /* Initialize the notification window class */
1755 wc.cbSize = sizeof(WNDCLASSEXW);
1756 wc.lpszClassName = L"ConSrvCreateNotify";
1757 wc.lpfnWndProc = GuiConsoleNotifyWndProc;
1758 wc.style = 0;
1759 wc.hInstance = ConSrvDllInstance;
1760 wc.hIcon = NULL;
1761 wc.hIconSm = NULL;
1762 wc.hCursor = NULL;
1763 wc.hbrBackground = NULL;
1764 wc.lpszMenuName = NULL;
1765 wc.cbClsExtra = 0;
1766 wc.cbWndExtra = 0;
1767 if (RegisterClassExW(&wc) == 0)
1768 {
1769 DPRINT1("Failed to register GUI notify wndproc\n");
1770 return FALSE;
1771 }
1772
1773 /* Initialize the console window class */
1774 ghDefaultIcon = LoadImageW(ConSrvDllInstance, MAKEINTRESOURCEW(IDI_CONSOLE), IMAGE_ICON,
1775 GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON),
1776 LR_SHARED);
1777 ghDefaultIconSm = LoadImageW(ConSrvDllInstance, MAKEINTRESOURCEW(IDI_CONSOLE), IMAGE_ICON,
1778 GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
1779 LR_SHARED);
1780 ghDefaultCursor = LoadCursorW(NULL, IDC_ARROW);
1781 wc.cbSize = sizeof(WNDCLASSEXW);
1782 wc.lpszClassName = GUI_CONSOLE_WINDOW_CLASS;
1783 wc.lpfnWndProc = GuiConsoleWndProc;
1784 wc.style = 0;
1785 wc.hInstance = ConSrvDllInstance;
1786 wc.hIcon = ghDefaultIcon;
1787 wc.hIconSm = ghDefaultIconSm;
1788 wc.hCursor = ghDefaultCursor;
1789 wc.hbrBackground = CreateSolidBrush(RGB(0,0,0)); // FIXME: Use defaults from registry.
1790 wc.lpszMenuName = NULL;
1791 wc.cbClsExtra = 0;
1792 wc.cbWndExtra = GWLP_CONSOLEWND_ALLOC;
1793
1794 ConsoleClassAtom = RegisterClassExW(&wc);
1795 if (ConsoleClassAtom == 0)
1796 {
1797 DPRINT1("Failed to register GUI console wndproc\n");
1798 return FALSE;
1799 }
1800 else
1801 {
1802 NtUserConsoleControl(GuiConsoleWndClassAtom, &ConsoleClassAtom, sizeof(ATOM));
1803 }
1804
1805 ConsInitialized = TRUE;
1806 }
1807
1808 /*
1809 * Set-up the notification window
1810 */
1811 if (NULL == NotifyWnd)
1812 {
1813 HANDLE ThreadHandle;
1814 HANDLE GraphicsStartupEvent;
1815
1816 GraphicsStartupEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1817 if (NULL == GraphicsStartupEvent) return FALSE;
1818
1819 ThreadHandle = CreateThread(NULL,
1820 0,
1821 GuiConsoleGuiThread,
1822 (PVOID)&GraphicsStartupEvent,
1823 0,
1824 NULL);
1825 if (NULL == ThreadHandle)
1826 {
1827 CloseHandle(GraphicsStartupEvent);
1828 DPRINT1("CONSRV: Failed to create graphics console thread. Expect problems\n");
1829 return FALSE;
1830 }
1831 SetThreadPriority(ThreadHandle, THREAD_PRIORITY_HIGHEST);
1832 CloseHandle(ThreadHandle);
1833
1834 WaitForSingleObject(GraphicsStartupEvent, INFINITE);
1835 CloseHandle(GraphicsStartupEvent);
1836
1837 if (NULL == NotifyWnd)
1838 {
1839 DPRINT1("CONSRV: Failed to create notification window.\n");
1840 return FALSE;
1841 }
1842 }
1843
1844 // ConsInitialized = TRUE;
1845
1846 return TRUE;
1847 }
1848
1849
1850
1851 /******************************************************************************
1852 * GUI Console Driver *
1853 ******************************************************************************/
1854
1855 static VOID WINAPI
1856 GuiCleanupConsole(PCONSOLE Console)
1857 {
1858 PGUI_CONSOLE_DATA GuiData = Console->TermIFace.Data;
1859
1860 SendMessageW(NotifyWnd, PM_DESTROY_CONSOLE, 0, (LPARAM)GuiData);
1861
1862 DPRINT1("Destroying icons !! - GuiData->hIcon = 0x%p ; ghDefaultIcon = 0x%p ; GuiData->hIconSm = 0x%p ; ghDefaultIconSm = 0x%p\n",
1863 GuiData->hIcon, ghDefaultIcon, GuiData->hIconSm, ghDefaultIconSm);
1864 if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon)
1865 {
1866 DPRINT1("Destroy hIcon\n");
1867 DestroyIcon(GuiData->hIcon);
1868 }
1869 if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm)
1870 {
1871 DPRINT1("Destroy hIconSm\n");
1872 DestroyIcon(GuiData->hIconSm);
1873 }
1874
1875 Console->TermIFace.Data = NULL;
1876 DeleteCriticalSection(&GuiData->Lock);
1877 RtlFreeHeap(ConSrvHeap, 0, GuiData);
1878 }
1879
1880 static VOID WINAPI
1881 GuiWriteStream(PCONSOLE Console, SMALL_RECT* Region, LONG CursorStartX, LONG CursorStartY,
1882 UINT ScrolledLines, CHAR *Buffer, UINT Length)
1883 {
1884 PGUI_CONSOLE_DATA GuiData = Console->TermIFace.Data;
1885 PCONSOLE_SCREEN_BUFFER Buff = Console->ActiveBuffer;
1886 LONG CursorEndX, CursorEndY;
1887 RECT ScrollRect;
1888
1889 if (NULL == GuiData || NULL == GuiData->hWindow)
1890 {
1891 return;
1892 }
1893
1894 if (0 != ScrolledLines)
1895 {
1896 ScrollRect.left = 0;
1897 ScrollRect.top = 0;
1898 ScrollRect.right = Console->Size.X * GuiData->CharWidth;
1899 ScrollRect.bottom = Region->Top * GuiData->CharHeight;
1900
1901 ScrollWindowEx(GuiData->hWindow,
1902 0,
1903 -(ScrolledLines * GuiData->CharHeight),
1904 &ScrollRect,
1905 NULL,
1906 NULL,
1907 NULL,
1908 SW_INVALIDATE);
1909 }
1910
1911 GuiDrawRegion(Console, Region);
1912
1913 if (CursorStartX < Region->Left || Region->Right < CursorStartX
1914 || CursorStartY < Region->Top || Region->Bottom < CursorStartY)
1915 {
1916 GuiInvalidateCell(Console, CursorStartX, CursorStartY);
1917 }
1918
1919 CursorEndX = Buff->CursorPosition.X;
1920 CursorEndY = Buff->CursorPosition.Y;
1921 if ((CursorEndX < Region->Left || Region->Right < CursorEndX
1922 || CursorEndY < Region->Top || Region->Bottom < CursorEndY)
1923 && (CursorEndX != CursorStartX || CursorEndY != CursorStartY))
1924 {
1925 GuiInvalidateCell(Console, CursorEndX, CursorEndY);
1926 }
1927
1928 // Set up the update timer (very short interval) - this is a "hack" for getting the OS to
1929 // repaint the window without having it just freeze up and stay on the screen permanently.
1930 Buff->CursorBlinkOn = TRUE;
1931 SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL);
1932 }
1933
1934 static VOID WINAPI
1935 GuiDrawRegion(PCONSOLE Console, SMALL_RECT* Region)
1936 {
1937 PGUI_CONSOLE_DATA GuiData = Console->TermIFace.Data;
1938 RECT RegionRect;
1939
1940 SmallRectToRect(GuiData, &RegionRect, Region);
1941 InvalidateRect(GuiData->hWindow, &RegionRect, FALSE);
1942 }
1943
1944 static BOOL WINAPI
1945 GuiSetCursorInfo(PCONSOLE Console, PCONSOLE_SCREEN_BUFFER Buff)
1946 {
1947 if (Console->ActiveBuffer == Buff)
1948 {
1949 GuiInvalidateCell(Console, Buff->CursorPosition.X, Buff->CursorPosition.Y);
1950 }
1951
1952 return TRUE;
1953 }
1954
1955 static BOOL WINAPI
1956 GuiSetScreenInfo(PCONSOLE Console, PCONSOLE_SCREEN_BUFFER Buff, UINT OldCursorX, UINT OldCursorY)
1957 {
1958 if (Console->ActiveBuffer == Buff)
1959 {
1960 /* Redraw char at old position (removes cursor) */
1961 GuiInvalidateCell(Console, OldCursorX, OldCursorY);
1962 /* Redraw char at new position (shows cursor) */
1963 GuiInvalidateCell(Console, Buff->CursorPosition.X, Buff->CursorPosition.Y);
1964 }
1965
1966 return TRUE;
1967 }
1968
1969 static BOOL WINAPI
1970 GuiUpdateScreenInfo(PCONSOLE Console, PCONSOLE_SCREEN_BUFFER Buff)
1971 {
1972 return TRUE;
1973 }
1974
1975 static NTSTATUS WINAPI
1976 GuiResizeBuffer(PCONSOLE Console, PCONSOLE_SCREEN_BUFFER ScreenBuffer, COORD Size)
1977 {
1978 BYTE * Buffer;
1979 DWORD Offset = 0;
1980 BYTE * OldPtr;
1981 USHORT CurrentY;
1982 BYTE * OldBuffer;
1983 #ifdef HAVE_WMEMSET
1984 USHORT value = MAKEWORD(' ', ScreenBuffer->ScreenDefaultAttrib);
1985 #else
1986 DWORD i;
1987 #endif
1988 DWORD diff;
1989
1990 /* Buffer size is not allowed to be smaller than window size */
1991 if (Size.X < Console->Size.X || Size.Y < Console->Size.Y)
1992 return STATUS_INVALID_PARAMETER;
1993
1994 if (Size.X == ScreenBuffer->ScreenBufferSize.X && Size.Y == ScreenBuffer->ScreenBufferSize.Y)
1995 return STATUS_SUCCESS;
1996
1997 Buffer = RtlAllocateHeap(ConSrvHeap, 0, Size.X * Size.Y * 2);
1998 if (!Buffer)
1999 return STATUS_NO_MEMORY;
2000
2001 DPRINT1("Resizing (%d,%d) to (%d,%d)\n", ScreenBuffer->ScreenBufferSize.X, ScreenBuffer->ScreenBufferSize.Y, Size.X, Size.Y);
2002 OldBuffer = ScreenBuffer->Buffer;
2003
2004 for (CurrentY = 0; CurrentY < ScreenBuffer->ScreenBufferSize.Y && CurrentY < Size.Y; CurrentY++)
2005 {
2006 OldPtr = ConioCoordToPointer(ScreenBuffer, 0, CurrentY);
2007 if (Size.X <= ScreenBuffer->ScreenBufferSize.X)
2008 {
2009 /* reduce size */
2010 RtlCopyMemory(&Buffer[Offset], OldPtr, Size.X * 2);
2011 Offset += (Size.X * 2);
2012 }
2013 else
2014 {
2015 /* enlarge size */
2016 RtlCopyMemory(&Buffer[Offset], OldPtr, ScreenBuffer->ScreenBufferSize.X * 2);
2017 Offset += (ScreenBuffer->ScreenBufferSize.X * 2);
2018
2019 diff = Size.X - ScreenBuffer->ScreenBufferSize.X;
2020 /* zero new part of it */
2021 #ifdef HAVE_WMEMSET
2022 wmemset((PWCHAR)&Buffer[Offset], value, diff);
2023 #else
2024 for (i = 0; i < diff; i++)
2025 {
2026 Buffer[Offset++] = ' ';
2027 Buffer[Offset++] = ScreenBuffer->ScreenDefaultAttrib;
2028 }
2029 #endif
2030 }
2031 }
2032
2033 if (Size.Y > ScreenBuffer->ScreenBufferSize.Y)
2034 {
2035 diff = Size.X * (Size.Y - ScreenBuffer->ScreenBufferSize.Y);
2036 #ifdef HAVE_WMEMSET
2037 wmemset((PWCHAR)&Buffer[Offset], value, diff);
2038 #else
2039 for (i = 0; i < diff; i++)
2040 {
2041 Buffer[Offset++] = ' ';
2042 Buffer[Offset++] = ScreenBuffer->ScreenDefaultAttrib;
2043 }
2044 #endif
2045 }
2046
2047 (void)InterlockedExchangePointer((PVOID volatile*)&ScreenBuffer->Buffer, Buffer);
2048 RtlFreeHeap(ConSrvHeap, 0, OldBuffer);
2049 ScreenBuffer->ScreenBufferSize = Size;
2050 ScreenBuffer->VirtualY = 0;
2051
2052 /* Ensure cursor and window are within buffer */
2053 if (ScreenBuffer->CursorPosition.X >= Size.X)
2054 ScreenBuffer->CursorPosition.X = Size.X - 1;
2055 if (ScreenBuffer->CursorPosition.Y >= Size.Y)
2056 ScreenBuffer->CursorPosition.Y = Size.Y - 1;
2057 if (ScreenBuffer->ShowX > Size.X - Console->Size.X)
2058 ScreenBuffer->ShowX = Size.X - Console->Size.X;
2059 if (ScreenBuffer->ShowY > Size.Y - Console->Size.Y)
2060 ScreenBuffer->ShowY = Size.Y - Console->Size.Y;
2061
2062 /* TODO: Should update scrollbar, but can't use anything that
2063 * calls SendMessage or it could cause deadlock --> Use PostMessage */
2064 // TODO: Tell the terminal to resize its scrollbars.
2065
2066 return STATUS_SUCCESS;
2067 }
2068
2069 static BOOL WINAPI
2070 GuiProcessKeyCallback(PCONSOLE Console, MSG* msg, BYTE KeyStateMenu, DWORD ShiftState, UINT VirtualKeyCode, BOOL Down)
2071 {
2072 if ((ShiftState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED) || KeyStateMenu & 0x80) &&
2073 (VirtualKeyCode == VK_ESCAPE || VirtualKeyCode == VK_TAB || VirtualKeyCode == VK_SPACE))
2074 {
2075 DefWindowProcW(msg->hwnd, msg->message, msg->wParam, msg->lParam);
2076 return TRUE;
2077 }
2078
2079 return FALSE;
2080 }
2081
2082 static VOID WINAPI
2083 GuiChangeTitle(PCONSOLE Console)
2084 {
2085 PGUI_CONSOLE_DATA GuiData = Console->TermIFace.Data;
2086 PostMessageW(GuiData->hWindow, PM_CONSOLE_SET_TITLE, 0, 0);
2087 }
2088
2089 static BOOL WINAPI
2090 GuiChangeIcon(PCONSOLE Console, HICON hWindowIcon)
2091 {
2092 PGUI_CONSOLE_DATA GuiData = Console->TermIFace.Data;
2093 HICON hIcon, hIconSm;
2094
2095 if (hWindowIcon == NULL)
2096 {
2097 hIcon = ghDefaultIcon;
2098 hIconSm = ghDefaultIconSm;
2099 }
2100 else
2101 {
2102 hIcon = CopyIcon(hWindowIcon);
2103 hIconSm = CopyIcon(hWindowIcon);
2104 }
2105
2106 if (hIcon == NULL)
2107 {
2108 return FALSE;
2109 }
2110
2111 if (hIcon != GuiData->hIcon)
2112 {
2113 if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon)
2114 {
2115 DestroyIcon(GuiData->hIcon);
2116 }
2117 if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm)
2118 {
2119 DestroyIcon(GuiData->hIconSm);
2120 }
2121
2122 GuiData->hIcon = hIcon;
2123 GuiData->hIconSm = hIconSm;
2124
2125 DPRINT1("Set icons in GuiChangeIcon\n");
2126 PostMessageW(GuiData->hWindow, WM_SETICON, ICON_BIG, (LPARAM)GuiData->hIcon);
2127 PostMessageW(GuiData->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)GuiData->hIconSm);
2128 }
2129
2130 return TRUE;
2131 }
2132
2133 static HWND WINAPI
2134 GuiGetConsoleWindowHandle(PCONSOLE Console)
2135 {
2136 PGUI_CONSOLE_DATA GuiData = Console->TermIFace.Data;
2137 return GuiData->hWindow;
2138 }
2139
2140 static TERMINAL_VTBL GuiVtbl =
2141 {
2142 GuiCleanupConsole,
2143 GuiWriteStream,
2144 GuiDrawRegion,
2145 GuiSetCursorInfo,
2146 GuiSetScreenInfo,
2147 GuiUpdateScreenInfo,
2148 GuiResizeBuffer,
2149 GuiProcessKeyCallback,
2150 GuiChangeTitle,
2151 GuiChangeIcon,
2152 GuiGetConsoleWindowHandle
2153 };
2154
2155 NTSTATUS FASTCALL
2156 GuiInitConsole(PCONSOLE Console,
2157 LPCWSTR AppPath,
2158 PCONSOLE_INFO ConsoleInfo,
2159 LPCWSTR IconPath,
2160 INT IconIndex)
2161 {
2162 PGUI_CONSOLE_DATA GuiData;
2163
2164 if (Console == NULL || ConsoleInfo == NULL)
2165 return STATUS_INVALID_PARAMETER;
2166
2167 /* Initialize the GUI terminal emulator */
2168 if (!GuiInit()) return STATUS_UNSUCCESSFUL;
2169
2170 /* Initialize the console */
2171 Console->TermIFace.Vtbl = &GuiVtbl;
2172
2173 GuiData = RtlAllocateHeap(ConSrvHeap, HEAP_ZERO_MEMORY,
2174 sizeof(GUI_CONSOLE_DATA));
2175 if (!GuiData)
2176 {
2177 DPRINT1("CONSRV: Failed to create GUI_CONSOLE_DATA\n");
2178 return STATUS_UNSUCCESSFUL;
2179 }
2180 Console->TermIFace.Data = (PVOID)GuiData;
2181 GuiData->Console = Console;
2182 GuiData->hWindow = NULL;
2183
2184 InitializeCriticalSection(&GuiData->Lock);
2185
2186 /* Set up the GUI data */
2187 wcsncpy(GuiData->GuiInfo.FaceName, ConsoleInfo->u.GuiInfo.FaceName, LF_FACESIZE);
2188 GuiData->GuiInfo.FontFamily = ConsoleInfo->u.GuiInfo.FontFamily;
2189 GuiData->GuiInfo.FontSize = ConsoleInfo->u.GuiInfo.FontSize;
2190 GuiData->GuiInfo.FontWeight = ConsoleInfo->u.GuiInfo.FontWeight;
2191 GuiData->GuiInfo.UseRasterFonts = ConsoleInfo->u.GuiInfo.UseRasterFonts;
2192 GuiData->GuiInfo.ShowWindow = ConsoleInfo->u.GuiInfo.ShowWindow;
2193 GuiData->GuiInfo.AutoPosition = ConsoleInfo->u.GuiInfo.AutoPosition;
2194 GuiData->GuiInfo.WindowOrigin = ConsoleInfo->u.GuiInfo.WindowOrigin;
2195
2196 /* Initialize the icon handles to their default values */
2197 GuiData->hIcon = ghDefaultIcon;
2198 GuiData->hIconSm = ghDefaultIconSm;
2199
2200 /* Get the associated icon, if any */
2201 if (IconPath == NULL || *IconPath == L'\0')
2202 {
2203 IconPath = AppPath;
2204 IconIndex = 0;
2205 }
2206 DPRINT1("IconPath = %S ; IconIndex = %lu\n", (IconPath ? IconPath : L"n/a"), IconIndex);
2207 if (IconPath)
2208 {
2209 HICON hIcon = NULL, hIconSm = NULL;
2210 PrivateExtractIconExW(IconPath,
2211 IconIndex,
2212 &hIcon,
2213 &hIconSm,
2214 1);
2215 DPRINT1("hIcon = 0x%p ; hIconSm = 0x%p\n", hIcon, hIconSm);
2216 if (hIcon != NULL)
2217 {
2218 DPRINT1("Effectively set the icons\n");
2219 GuiData->hIcon = hIcon;
2220 GuiData->hIconSm = hIconSm;
2221 }
2222 }
2223
2224 /*
2225 * We need to wait until the GUI has been fully initialized
2226 * to retrieve custom settings i.e. WindowSize etc...
2227 * Ideally we could use SendNotifyMessage for this but its not
2228 * yet implemented.
2229 */
2230 GuiData->hGuiInitEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
2231
2232 /* Create the terminal window */
2233 PostMessageW(NotifyWnd, PM_CREATE_CONSOLE, GuiData->GuiInfo.ShowWindow, (LPARAM)GuiData);
2234
2235 /* Wait until initialization has finished */
2236 WaitForSingleObject(GuiData->hGuiInitEvent, INFINITE);
2237 CloseHandle(GuiData->hGuiInitEvent);
2238 GuiData->hGuiInitEvent = NULL;
2239
2240 /* Check whether we really succeeded in initializing the terminal window */
2241 if (GuiData->hWindow == NULL)
2242 {
2243 DPRINT1("GuiInitConsole - We failed at creating a new terminal window\n");
2244 // ConioCleanupConsole(Console);
2245 GuiCleanupConsole(Console);
2246 return STATUS_UNSUCCESSFUL;
2247 }
2248
2249 return STATUS_SUCCESS;
2250 }
2251
2252 /* EOF */