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