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