Synchronize up to trunk's revision r57689.
[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 DWORD Temp = Console->NumberOfHistoryBuffers;
438 RegSetValueExW(hKey, L"NumberOfHistoryBuffers", 0, REG_DWORD, (const BYTE *)&Temp, sizeof(DWORD));
439 }
440
441 if (Console->HistoryBufferSize == 50)
442 {
443 RegDeleteKeyW(hKey, L"HistoryBufferSize");
444 }
445 else
446 {
447 DWORD Temp = Console->HistoryBufferSize;
448 RegSetValueExW(hKey, L"HistoryBufferSize", 0, REG_DWORD, (const BYTE *)&Temp, 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, PCSRSS_CONSOLE Console, PGUI_CONSOLE_DATA GuiData, PCSRSS_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(PCSRSS_CONSOLE Console, PGUI_CONSOLE_DATA GuiData, PCSRSS_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(PCSRSS_CONSOLE 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, CREATESTRUCTW *Create)
699 {
700 PCSRSS_CONSOLE Console = (PCSRSS_CONSOLE) 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, GWL_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(PCSRSS_CONSOLE Console, PRECT Rect, PSMALL_RECT SmallRect)
809 {
810 PCSRSS_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(PCSRSS_CONSOLE 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(PCSRSS_CONSOLE Console,
882 PGUI_CONSOLE_DATA GuiData,
883 HDC hDC,
884 PRECT rc)
885 {
886 PCSRSS_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 PCSRSS_CONSOLE 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 PCSRSS_CONSOLE 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(PCSRSS_CONSOLE 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(PCSRSS_CONSOLE 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(PCSRSS_CONSOLE 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 PCSRSS_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(PCSRSS_CONSOLE Console, PCSRSS_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(PCSRSS_CONSOLE Console, PCSRSS_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(PCSRSS_CONSOLE Console, PCSRSS_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 PCSRSS_CONSOLE Console;
1188 PGUI_CONSOLE_DATA GuiData;
1189 PCSRSS_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 PCSRSS_CONSOLE 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 PCSRSS_CONSOLE 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(PCSRSS_CONSOLE Console, LPARAM lParam)
1320 {
1321 PCSRSS_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 PCSRSS_CONSOLE 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 PCSRSS_CONSOLE 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 PCSRSS_CONSOLE 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, PCSRSS_CONSOLE 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, PCSRSS_CONSOLE 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 PCSRSS_CONSOLE 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
1518 static VOID
1519 GuiConsoleShowConsoleProperties(HWND hWnd, BOOL Defaults, PGUI_CONSOLE_DATA GuiData)
1520 {
1521 PCSRSS_CONSOLE Console;
1522 APPLET_PROC CPLFunc;
1523 TCHAR szBuffer[MAX_PATH];
1524 ConsoleInfo SharedInfo;
1525
1526 DPRINT("GuiConsoleShowConsoleProperties entered\n");
1527
1528 GuiConsoleGetDataPointers(hWnd, &Console, &GuiData);
1529
1530 if (GuiData == NULL)
1531 {
1532 DPRINT("GuiConsoleGetDataPointers failed\n");
1533 return;
1534 }
1535
1536 if (GuiData->ConsoleLibrary == NULL)
1537 {
1538 GetWindowsDirectory(szBuffer,MAX_PATH);
1539 _tcscat(szBuffer, _T("\\system32\\console.dll"));
1540 GuiData->ConsoleLibrary = LoadLibrary(szBuffer);
1541
1542 if (GuiData->ConsoleLibrary == NULL)
1543 {
1544 DPRINT1("failed to load console.dll");
1545 return;
1546 }
1547 }
1548
1549 CPLFunc = (APPLET_PROC) GetProcAddress(GuiData->ConsoleLibrary, _T("CPlApplet"));
1550 if (!CPLFunc)
1551 {
1552 DPRINT("Error: Console.dll misses CPlApplet export\n");
1553 return;
1554 }
1555
1556 /* setup struct */
1557 SharedInfo.InsertMode = GuiData->InsertMode;
1558 SharedInfo.HistoryBufferSize = Console->HistoryBufferSize;
1559 SharedInfo.NumberOfHistoryBuffers = Console->NumberOfHistoryBuffers;
1560 SharedInfo.ScreenText = GuiData->ScreenText;
1561 SharedInfo.ScreenBackground = GuiData->ScreenBackground;
1562 SharedInfo.PopupText = GuiData->PopupText;
1563 SharedInfo.PopupBackground = GuiData->PopupBackground;
1564 SharedInfo.WindowSize = (DWORD)MAKELONG(Console->Size.X, Console->Size.Y);
1565 SharedInfo.WindowPosition = GuiData->WindowPosition;
1566 SharedInfo.ScreenBuffer = (DWORD)MAKELONG(Console->ActiveBuffer->MaxX, Console->ActiveBuffer->MaxY);
1567 SharedInfo.UseRasterFonts = GuiData->UseRasterFonts;
1568 SharedInfo.FontSize = (DWORD)GuiData->FontSize;
1569 SharedInfo.FontWeight = GuiData->FontWeight;
1570 SharedInfo.CursorSize = Console->ActiveBuffer->CursorInfo.dwSize;
1571 SharedInfo.HistoryNoDup = Console->HistoryNoDup;
1572 SharedInfo.FullScreen = GuiData->FullScreen;
1573 SharedInfo.QuickEdit = GuiData->QuickEdit;
1574 memcpy(&SharedInfo.Colors[0], GuiData->Colors, sizeof(s_Colors));
1575
1576 if (!CPLFunc(hWnd, CPL_INIT, 0, 0))
1577 {
1578 DPRINT("Error: failed to initialize console.dll\n");
1579 return;
1580 }
1581
1582 if (CPLFunc(hWnd, CPL_GETCOUNT, 0, 0) != 1)
1583 {
1584 DPRINT("Error: console.dll returned unexpected CPL count\n");
1585 return;
1586 }
1587
1588 CPLFunc(hWnd, CPL_DBLCLK, (LPARAM)&SharedInfo, Defaults);
1589 }
1590 static LRESULT
1591 GuiConsoleHandleSysMenuCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
1592 {
1593 LRESULT Ret = TRUE;
1594 PCSRSS_CONSOLE Console;
1595 PGUI_CONSOLE_DATA GuiData;
1596 COORD bottomRight = { 0, 0 };
1597
1598 GuiConsoleGetDataPointers(hWnd, &Console, &GuiData);
1599
1600 switch(wParam)
1601 {
1602 case ID_SYSTEM_EDIT_MARK:
1603 DPRINT1("Marking not handled yet\n");
1604 break;
1605
1606 case ID_SYSTEM_EDIT_COPY:
1607 GuiConsoleCopy(hWnd, Console);
1608 break;
1609
1610 case ID_SYSTEM_EDIT_PASTE:
1611 GuiConsolePaste(hWnd, Console);
1612 break;
1613
1614 case ID_SYSTEM_EDIT_SELECTALL:
1615 bottomRight.X = Console->Size.X - 1;
1616 bottomRight.Y = Console->Size.Y - 1;
1617 GuiConsoleUpdateSelection(Console, &bottomRight);
1618 break;
1619
1620 case ID_SYSTEM_EDIT_SCROLL:
1621 DPRINT1("Scrolling is not handled yet\n");
1622 break;
1623
1624 case ID_SYSTEM_EDIT_FIND:
1625 DPRINT1("Finding is not handled yet\n");
1626 break;
1627
1628 case ID_SYSTEM_DEFAULTS:
1629 GuiConsoleShowConsoleProperties(hWnd, TRUE, GuiData);
1630 break;
1631
1632 case ID_SYSTEM_PROPERTIES:
1633 GuiConsoleShowConsoleProperties(hWnd, FALSE, GuiData);
1634 break;
1635
1636 default:
1637 Ret = DefWindowProcW(hWnd, WM_SYSCOMMAND, wParam, lParam);
1638 break;
1639 }
1640 return Ret;
1641 }
1642
1643 static VOID
1644 GuiConsoleGetMinMaxInfo(HWND hWnd, PMINMAXINFO minMaxInfo)
1645 {
1646 PCSRSS_CONSOLE Console;
1647 PGUI_CONSOLE_DATA GuiData;
1648 DWORD windx, windy;
1649
1650 GuiConsoleGetDataPointers(hWnd, &Console, &GuiData);
1651 if((Console == NULL)|| (GuiData == NULL)) return;
1652
1653 windx = CONGUI_MIN_WIDTH * GuiData->CharWidth + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
1654 windy = CONGUI_MIN_HEIGHT * GuiData->CharHeight + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
1655
1656 minMaxInfo->ptMinTrackSize.x = windx;
1657 minMaxInfo->ptMinTrackSize.y = windy;
1658
1659 windx = (Console->ActiveBuffer->MaxX) * GuiData->CharWidth + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
1660 windy = (Console->ActiveBuffer->MaxY) * GuiData->CharHeight + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
1661
1662 if(Console->Size.X < Console->ActiveBuffer->MaxX) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar
1663 if(Console->Size.Y < Console->ActiveBuffer->MaxY) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar
1664
1665 minMaxInfo->ptMaxTrackSize.x = windx;
1666 minMaxInfo->ptMaxTrackSize.y = windy;
1667 }
1668 static VOID
1669 GuiConsoleResize(HWND hWnd, WPARAM wParam, LPARAM lParam)
1670 {
1671 PCSRSS_CONSOLE Console;
1672 PGUI_CONSOLE_DATA GuiData;
1673 GuiConsoleGetDataPointers(hWnd, &Console, &GuiData);
1674 if((Console == NULL) || (GuiData == NULL)) return;
1675
1676 if ((GuiData->WindowSizeLock == FALSE) && (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED || wParam == SIZE_MINIMIZED))
1677 {
1678 PCSRSS_SCREEN_BUFFER Buff = Console->ActiveBuffer;
1679 DWORD windx, windy, charx, chary;
1680
1681 GuiData->WindowSizeLock = TRUE;
1682
1683 windx = LOWORD(lParam);
1684 windy = HIWORD(lParam);
1685
1686 // Compensate for existing scroll bars (because lParam values do not accommodate scroll bar)
1687 if(Console->Size.X < Buff->MaxX) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar
1688 if(Console->Size.Y < Buff->MaxY) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar
1689
1690 charx = windx / GuiData->CharWidth;
1691 chary = windy / GuiData->CharHeight;
1692
1693 // Character alignment (round size up or down)
1694 if((windx % GuiData->CharWidth) >= (GuiData->CharWidth / 2)) ++charx;
1695 if((windy % GuiData->CharHeight) >= (GuiData->CharHeight / 2)) ++chary;
1696
1697 // Compensate for added scroll bars in new window
1698 if(charx < Buff->MaxX)windy -= GetSystemMetrics(SM_CYHSCROLL); // new window will have a horizontal scroll bar
1699 if(chary < Buff->MaxY)windx -= GetSystemMetrics(SM_CXVSCROLL); // new window will have a vertical scroll bar
1700
1701 charx = windx / GuiData->CharWidth;
1702 chary = windy / GuiData->CharHeight;
1703
1704 // Character alignment (round size up or down)
1705 if((windx % GuiData->CharWidth) >= (GuiData->CharWidth / 2)) ++charx;
1706 if((windy % GuiData->CharHeight) >= (GuiData->CharHeight / 2)) ++chary;
1707
1708 // Resize window
1709 if((charx != Console->Size.X) || (chary != Console->Size.Y))
1710 {
1711 Console->Size.X = (charx <= Buff->MaxX) ? charx : Buff->MaxX;
1712 Console->Size.Y = (chary <= Buff->MaxY) ? chary : Buff->MaxY;
1713 }
1714
1715 GuiConsoleInitScrollbar(Console, hWnd);
1716
1717 // Adjust the start of the visible area if we are attempting to show nonexistent areas
1718 if((Buff->MaxX - Buff->ShowX) < Console->Size.X) Buff->ShowX = Buff->MaxX - Console->Size.X;
1719 if((Buff->MaxY - Buff->ShowY) < Console->Size.Y) Buff->ShowY = Buff->MaxY - Console->Size.Y;
1720 InvalidateRect(hWnd, NULL, TRUE);
1721
1722 GuiData->WindowSizeLock = FALSE;
1723 }
1724 }
1725
1726 VOID
1727 FASTCALL
1728 GuiConsoleHandleScrollbarMenu(VOID)
1729 {
1730 HMENU hMenu;
1731
1732 hMenu = CreatePopupMenu();
1733 if (hMenu == NULL)
1734 {
1735 DPRINT("CreatePopupMenu failed\n");
1736 return;
1737 }
1738 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLHERE);
1739 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
1740 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLTOP);
1741 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLBOTTOM);
1742 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
1743 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_UP);
1744 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_DOWN);
1745 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
1746 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLUP);
1747 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLDOWN);
1748
1749 }
1750
1751 static NTSTATUS WINAPI
1752 GuiResizeBuffer(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER ScreenBuffer, COORD Size)
1753 {
1754 BYTE * Buffer;
1755 DWORD Offset = 0;
1756 BYTE * OldPtr;
1757 USHORT CurrentY;
1758 BYTE * OldBuffer;
1759 #if HAVE_WMEMSET
1760 USHORT value = MAKEWORD(' ', ScreenBuffer->DefaultAttrib);
1761 #endif
1762 DWORD diff;
1763 DWORD i;
1764
1765 /* Buffer size is not allowed to be smaller than window size */
1766 if (Size.X < Console->Size.X || Size.Y < Console->Size.Y)
1767 return STATUS_INVALID_PARAMETER;
1768
1769 if (Size.X == ScreenBuffer->MaxX && Size.Y == ScreenBuffer->MaxY)
1770 return STATUS_SUCCESS;
1771
1772 Buffer = HeapAlloc(ConSrvHeap, 0, Size.X * Size.Y * 2);
1773 if (!Buffer)
1774 return STATUS_NO_MEMORY;
1775
1776 DPRINT1("Resizing (%d,%d) to (%d,%d)\n", ScreenBuffer->MaxX, ScreenBuffer->MaxY, Size.X, Size.Y);
1777 OldBuffer = ScreenBuffer->Buffer;
1778
1779 for (CurrentY = 0; CurrentY < ScreenBuffer->MaxY && CurrentY < Size.Y; CurrentY++)
1780 {
1781 OldPtr = ConioCoordToPointer(ScreenBuffer, 0, CurrentY);
1782 if (Size.X <= ScreenBuffer->MaxX)
1783 {
1784 /* reduce size */
1785 RtlCopyMemory(&Buffer[Offset], OldPtr, Size.X * 2);
1786 Offset += (Size.X * 2);
1787 }
1788 else
1789 {
1790 /* enlarge size */
1791 RtlCopyMemory(&Buffer[Offset], OldPtr, ScreenBuffer->MaxX * 2);
1792 Offset += (ScreenBuffer->MaxX * 2);
1793
1794 diff = Size.X - ScreenBuffer->MaxX;
1795 /* zero new part of it */
1796 #if HAVE_WMEMSET
1797 wmemset((WCHAR*)&Buffer[Offset], value, diff);
1798 #else
1799 for (i = 0; i < diff; i++)
1800 {
1801 Buffer[Offset++] = ' ';
1802 Buffer[Offset++] = ScreenBuffer->DefaultAttrib;
1803 }
1804 #endif
1805 }
1806 }
1807
1808 if (Size.Y > ScreenBuffer->MaxY)
1809 {
1810 diff = Size.X * (Size.Y - ScreenBuffer->MaxY);
1811 #if HAVE_WMEMSET
1812 wmemset((WCHAR*)&Buffer[Offset], value, diff);
1813 #else
1814 for (i = 0; i < diff; i++)
1815 {
1816 Buffer[Offset++] = ' ';
1817 Buffer[Offset++] = ScreenBuffer->DefaultAttrib;
1818 }
1819 #endif
1820 }
1821
1822 (void)InterlockedExchangePointer((PVOID volatile *)&ScreenBuffer->Buffer, Buffer);
1823 HeapFree(ConSrvHeap, 0, OldBuffer);
1824 ScreenBuffer->MaxX = Size.X;
1825 ScreenBuffer->MaxY = Size.Y;
1826 ScreenBuffer->VirtualY = 0;
1827
1828 /* Ensure cursor and window are within buffer */
1829 if (ScreenBuffer->CurrentX >= Size.X)
1830 ScreenBuffer->CurrentX = Size.X - 1;
1831 if (ScreenBuffer->CurrentY >= Size.Y)
1832 ScreenBuffer->CurrentY = Size.Y - 1;
1833 if (ScreenBuffer->ShowX > Size.X - Console->Size.X)
1834 ScreenBuffer->ShowX = Size.X - Console->Size.X;
1835 if (ScreenBuffer->ShowY > Size.Y - Console->Size.Y)
1836 ScreenBuffer->ShowY = Size.Y - Console->Size.Y;
1837
1838 /* TODO: Should update scrollbar, but can't use anything that
1839 * calls SendMessage or it could cause deadlock */
1840
1841 return STATUS_SUCCESS;
1842 }
1843
1844 static VOID
1845 GuiApplyUserSettings(PCSRSS_CONSOLE Console, PGUI_CONSOLE_DATA GuiData, PConsoleInfo pConInfo)
1846 {
1847 DWORD windx, windy;
1848 PCSRSS_SCREEN_BUFFER ActiveBuffer = Console->ActiveBuffer;
1849 COORD BufSize;
1850 BOOL SizeChanged = FALSE;
1851
1852 EnterCriticalSection(&Console->Lock);
1853
1854 /* apply text / background color */
1855 GuiData->ScreenText = pConInfo->ScreenText;
1856 GuiData->ScreenBackground = pConInfo->ScreenBackground;
1857
1858 /* apply cursor size */
1859 ActiveBuffer->CursorInfo.dwSize = min(max(pConInfo->CursorSize, 1), 100);
1860
1861 windx = LOWORD(pConInfo->WindowSize);
1862 windy = HIWORD(pConInfo->WindowSize);
1863
1864 if (windx != Console->Size.X || windy != Console->Size.Y)
1865 {
1866 /* resize window */
1867 Console->Size.X = windx;
1868 Console->Size.Y = windy;
1869 SizeChanged = TRUE;
1870 }
1871
1872 BufSize.X = LOWORD(pConInfo->ScreenBuffer);
1873 BufSize.Y = HIWORD(pConInfo->ScreenBuffer);
1874 if (BufSize.X != ActiveBuffer->MaxX || BufSize.Y != ActiveBuffer->MaxY)
1875 {
1876 if (NT_SUCCESS(GuiResizeBuffer(Console, ActiveBuffer, BufSize)))
1877 SizeChanged = TRUE;
1878 }
1879
1880 if (SizeChanged)
1881 {
1882 GuiData->WindowSizeLock = TRUE;
1883 GuiConsoleInitScrollbar(Console, pConInfo->hConsoleWindow);
1884 GuiData->WindowSizeLock = FALSE;
1885 }
1886
1887 LeaveCriticalSection(&Console->Lock);
1888 InvalidateRect(pConInfo->hConsoleWindow, NULL, TRUE);
1889 }
1890
1891 static
1892 LRESULT
1893 GuiConsoleHandleScroll(HWND hwnd, UINT uMsg, WPARAM wParam)
1894 {
1895 PCSRSS_CONSOLE Console;
1896 PCSRSS_SCREEN_BUFFER Buff;
1897 PGUI_CONSOLE_DATA GuiData;
1898 SCROLLINFO sInfo;
1899 int fnBar;
1900 int old_pos, Maximum;
1901 PUSHORT pShowXY;
1902
1903 GuiConsoleGetDataPointers(hwnd, &Console, &GuiData);
1904 if (Console == NULL || GuiData == NULL)
1905 return FALSE;
1906 Buff = Console->ActiveBuffer;
1907
1908 if (uMsg == WM_HSCROLL)
1909 {
1910 fnBar = SB_HORZ;
1911 Maximum = Buff->MaxX - Console->Size.X;
1912 pShowXY = &Buff->ShowX;
1913 }
1914 else
1915 {
1916 fnBar = SB_VERT;
1917 Maximum = Buff->MaxY - Console->Size.Y;
1918 pShowXY = &Buff->ShowY;
1919 }
1920
1921 /* set scrollbar sizes */
1922 sInfo.cbSize = sizeof(SCROLLINFO);
1923 sInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE | SIF_TRACKPOS;
1924
1925 if (!GetScrollInfo(hwnd, fnBar, &sInfo))
1926 {
1927 return FALSE;
1928 }
1929
1930 old_pos = sInfo.nPos;
1931
1932 switch(LOWORD(wParam))
1933 {
1934 case SB_LINELEFT:
1935 sInfo.nPos -= 1;
1936 break;
1937
1938 case SB_LINERIGHT:
1939 sInfo.nPos += 1;
1940 break;
1941
1942 case SB_PAGELEFT:
1943 sInfo.nPos -= sInfo.nPage;
1944 break;
1945
1946 case SB_PAGERIGHT:
1947 sInfo.nPos += sInfo.nPage;
1948 break;
1949
1950 case SB_THUMBTRACK:
1951 sInfo.nPos = sInfo.nTrackPos;
1952 ConioPause(Console, PAUSED_FROM_SCROLLBAR);
1953 break;
1954
1955 case SB_THUMBPOSITION:
1956 ConioUnpause(Console, PAUSED_FROM_SCROLLBAR);
1957 break;
1958
1959 case SB_TOP:
1960 sInfo.nPos = sInfo.nMin;
1961 break;
1962
1963 case SB_BOTTOM:
1964 sInfo.nPos = sInfo.nMax;
1965 break;
1966
1967 default:
1968 break;
1969 }
1970
1971 sInfo.nPos = max(sInfo.nPos, 0);
1972 sInfo.nPos = min(sInfo.nPos, Maximum);
1973
1974 if (old_pos != sInfo.nPos)
1975 {
1976 USHORT OldX = Buff->ShowX;
1977 USHORT OldY = Buff->ShowY;
1978 *pShowXY = sInfo.nPos;
1979
1980 ScrollWindowEx(hwnd,
1981 (OldX - Buff->ShowX) * GuiData->CharWidth,
1982 (OldY - Buff->ShowY) * GuiData->CharHeight,
1983 NULL,
1984 NULL,
1985 NULL,
1986 NULL,
1987 SW_INVALIDATE);
1988
1989 sInfo.fMask = SIF_POS;
1990 SetScrollInfo(hwnd, fnBar, &sInfo, TRUE);
1991
1992 UpdateWindow(hwnd);
1993 }
1994 return 0;
1995 }
1996
1997 static LRESULT CALLBACK
1998 GuiConsoleWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1999 {
2000 LRESULT Result = 0;
2001 PGUI_CONSOLE_DATA GuiData = NULL;
2002 PCSRSS_CONSOLE Console = NULL;
2003
2004 GuiConsoleGetDataPointers(hWnd, &Console, &GuiData);
2005
2006 switch(msg)
2007 {
2008 case WM_NCCREATE:
2009 Result = (LRESULT) GuiConsoleHandleNcCreate(hWnd, (CREATESTRUCTW *) lParam);
2010 break;
2011 case WM_PAINT:
2012 GuiConsoleHandlePaint(hWnd, (HDC)wParam);
2013 break;
2014 case WM_KEYDOWN:
2015 case WM_KEYUP:
2016 case WM_SYSKEYDOWN:
2017 case WM_SYSKEYUP:
2018 case WM_CHAR:
2019 GuiConsoleHandleKey(hWnd, msg, wParam, lParam);
2020 break;
2021 case WM_TIMER:
2022 GuiConsoleHandleTimer(hWnd);
2023 break;
2024 case WM_CLOSE:
2025 GuiConsoleHandleClose(hWnd);
2026 break;
2027 case WM_NCDESTROY:
2028 GuiConsoleHandleNcDestroy(hWnd);
2029 break;
2030 case WM_LBUTTONDOWN:
2031 GuiConsoleLeftMouseDown(hWnd, lParam);
2032 break;
2033 case WM_LBUTTONUP:
2034 GuiConsoleLeftMouseUp(hWnd, lParam);
2035 break;
2036 case WM_RBUTTONDOWN:
2037 GuiConsoleRightMouseDown(hWnd);
2038 break;
2039 case WM_MOUSEMOVE:
2040 GuiConsoleMouseMove(hWnd, wParam, lParam);
2041 break;
2042 case WM_SYSCOMMAND:
2043 Result = GuiConsoleHandleSysMenuCommand(hWnd, wParam, lParam);
2044 break;
2045 case WM_HSCROLL:
2046 case WM_VSCROLL:
2047 Result = GuiConsoleHandleScroll(hWnd, msg, wParam);
2048 break;
2049 case WM_GETMINMAXINFO:
2050 GuiConsoleGetMinMaxInfo(hWnd, (PMINMAXINFO)lParam);
2051 break;
2052 case WM_SIZE:
2053 GuiConsoleResize(hWnd, wParam, lParam);
2054 break;
2055 case PM_APPLY_CONSOLE_INFO:
2056 GuiApplyUserSettings(Console, GuiData, (PConsoleInfo)wParam);
2057 if (lParam)
2058 {
2059 GuiConsoleWriteUserSettings(Console, GuiData);
2060 }
2061 break;
2062 default:
2063 Result = DefWindowProcW(hWnd, msg, wParam, lParam);
2064 break;
2065 }
2066
2067 return Result;
2068 }
2069
2070 static LRESULT CALLBACK
2071 GuiConsoleNotifyWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
2072 {
2073 HWND NewWindow;
2074 LONG WindowCount;
2075 MSG Msg;
2076 PWCHAR Buffer, Title;
2077 PCSRSS_CONSOLE Console = (PCSRSS_CONSOLE) lParam;
2078
2079
2080
2081 switch(msg)
2082 {
2083 case WM_CREATE:
2084 SetWindowLongW(hWnd, GWL_USERDATA, 0);
2085 return 0;
2086 case PM_CREATE_CONSOLE:
2087 Buffer = HeapAlloc(ConSrvHeap, 0,
2088 Console->Title.Length + sizeof(WCHAR));
2089 if (NULL != Buffer)
2090 {
2091 memcpy(Buffer, Console->Title.Buffer, Console->Title.Length);
2092 Buffer[Console->Title.Length / sizeof(WCHAR)] = L'\0';
2093 Title = Buffer;
2094 }
2095 else
2096 {
2097 Title = L"";
2098 }
2099 NewWindow = CreateWindowExW(WS_EX_CLIENTEDGE,
2100 L"ConsoleWindowClass",
2101 Title,
2102 WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
2103 CW_USEDEFAULT,
2104 CW_USEDEFAULT,
2105 CW_USEDEFAULT,
2106 CW_USEDEFAULT,
2107 NULL,
2108 NULL,
2109 (HINSTANCE) GetModuleHandleW(NULL),
2110 (PVOID) Console);
2111 if (NULL != Buffer)
2112 {
2113 HeapFree(ConSrvHeap, 0, Buffer);
2114 }
2115 if (NULL != NewWindow)
2116 {
2117 SetWindowLongW(hWnd, GWL_USERDATA, GetWindowLongW(hWnd, GWL_USERDATA) + 1);
2118 ShowWindow(NewWindow, (int)wParam);
2119 }
2120 return (LRESULT) NewWindow;
2121 case PM_DESTROY_CONSOLE:
2122 /* Window creation is done using a PostMessage(), so it's possible that the
2123 * window that we want to destroy doesn't exist yet. So first empty the message
2124 * queue */
2125 while(PeekMessageW(&Msg, NULL, 0, 0, PM_REMOVE))
2126 {
2127 TranslateMessage(&Msg);
2128 DispatchMessageW(&Msg);
2129 }
2130 DestroyWindow(Console->hWindow);
2131 Console->hWindow = NULL;
2132 WindowCount = GetWindowLongW(hWnd, GWL_USERDATA);
2133 WindowCount--;
2134 SetWindowLongW(hWnd, GWL_USERDATA, WindowCount);
2135 if (0 == WindowCount)
2136 {
2137 NotifyWnd = NULL;
2138 DestroyWindow(hWnd);
2139 PrivateCsrssManualGuiCheck(-1);
2140 PostQuitMessage(0);
2141 }
2142 return 0;
2143 default:
2144 return DefWindowProcW(hWnd, msg, wParam, lParam);
2145 }
2146 }
2147
2148 static DWORD WINAPI
2149 GuiConsoleGuiThread(PVOID Data)
2150 {
2151 MSG msg;
2152 PHANDLE GraphicsStartupEvent = (PHANDLE) Data;
2153
2154 NotifyWnd = CreateWindowW(L"Win32CsrCreateNotify",
2155 L"",
2156 WS_OVERLAPPEDWINDOW,
2157 CW_USEDEFAULT,
2158 CW_USEDEFAULT,
2159 CW_USEDEFAULT,
2160 CW_USEDEFAULT,
2161 NULL,
2162 NULL,
2163 (HINSTANCE) GetModuleHandleW(NULL),
2164 NULL);
2165 if (NULL == NotifyWnd)
2166 {
2167 PrivateCsrssManualGuiCheck(-1);
2168 SetEvent(*GraphicsStartupEvent);
2169 return 1;
2170 }
2171
2172 SetEvent(*GraphicsStartupEvent);
2173
2174 while(GetMessageW(&msg, NULL, 0, 0))
2175 {
2176 TranslateMessage(&msg);
2177 DispatchMessageW(&msg);
2178 }
2179
2180 return 1;
2181 }
2182
2183 static BOOL
2184 GuiInit(VOID)
2185 {
2186 WNDCLASSEXW wc;
2187
2188 if (NULL == NotifyWnd)
2189 {
2190 PrivateCsrssManualGuiCheck(+1);
2191 }
2192
2193 wc.cbSize = sizeof(WNDCLASSEXW);
2194 wc.lpszClassName = L"Win32CsrCreateNotify";
2195 wc.lpfnWndProc = GuiConsoleNotifyWndProc;
2196 wc.style = 0;
2197 wc.hInstance = (HINSTANCE)GetModuleHandleW(NULL);
2198 wc.hIcon = NULL;
2199 wc.hCursor = NULL;
2200 wc.hbrBackground = NULL;
2201 wc.lpszMenuName = NULL;
2202 wc.cbClsExtra = 0;
2203 wc.cbWndExtra = 0;
2204 wc.hIconSm = NULL;
2205 if (RegisterClassExW(&wc) == 0)
2206 {
2207 DPRINT1("Failed to register notify wndproc\n");
2208 return FALSE;
2209 }
2210
2211 wc.cbSize = sizeof(WNDCLASSEXW);
2212 wc.lpszClassName = L"ConsoleWindowClass";
2213 wc.lpfnWndProc = GuiConsoleWndProc;
2214 wc.style = 0;
2215 wc.hInstance = (HINSTANCE)GetModuleHandleW(NULL);
2216 wc.hIcon = LoadIconW(ConSrvDllInstance, MAKEINTRESOURCEW(1));
2217 wc.hCursor = LoadCursorW(NULL, (LPCWSTR) IDC_ARROW);
2218 wc.hbrBackground = CreateSolidBrush(RGB(0,0,0));
2219 wc.lpszMenuName = NULL;
2220 wc.cbClsExtra = 0;
2221 wc.cbWndExtra = 0;
2222 wc.hIconSm = LoadImageW(ConSrvDllInstance, MAKEINTRESOURCEW(1), IMAGE_ICON,
2223 GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
2224 LR_SHARED);
2225 if (RegisterClassExW(&wc) == 0)
2226 {
2227 DPRINT1("Failed to register console wndproc\n");
2228 return FALSE;
2229 }
2230
2231 return TRUE;
2232 }
2233
2234 static VOID WINAPI
2235 GuiInitScreenBuffer(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER Buffer)
2236 {
2237 Buffer->DefaultAttrib = DEFAULT_ATTRIB;
2238 }
2239
2240 static BOOL WINAPI
2241 GuiChangeTitle(PCSRSS_CONSOLE Console)
2242 {
2243 PWCHAR Buffer, Title;
2244
2245 Buffer = HeapAlloc(ConSrvHeap, 0,
2246 Console->Title.Length + sizeof(WCHAR));
2247 if (NULL != Buffer)
2248 {
2249 memcpy(Buffer, Console->Title.Buffer, Console->Title.Length);
2250 Buffer[Console->Title.Length / sizeof(WCHAR)] = L'\0';
2251 Title = Buffer;
2252 }
2253 else
2254 {
2255 Title = L"";
2256 }
2257
2258 SendMessageW(Console->hWindow, WM_SETTEXT, 0, (LPARAM)Title);
2259
2260 if (NULL != Buffer)
2261 {
2262 HeapFree(ConSrvHeap, 0, Buffer);
2263 }
2264
2265 return TRUE;
2266 }
2267
2268 static BOOL WINAPI
2269 GuiChangeIcon(PCSRSS_CONSOLE Console, HICON hWindowIcon)
2270 {
2271 SendMessageW(Console->hWindow, WM_SETICON, ICON_BIG, (LPARAM)hWindowIcon);
2272 SendMessageW(Console->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)hWindowIcon);
2273
2274 return TRUE;
2275 }
2276
2277 static VOID WINAPI
2278 GuiCleanupConsole(PCSRSS_CONSOLE Console)
2279 {
2280 SendMessageW(NotifyWnd, PM_DESTROY_CONSOLE, 0, (LPARAM)Console);
2281 }
2282
2283 static CSRSS_CONSOLE_VTBL GuiVtbl =
2284 {
2285 GuiInitScreenBuffer,
2286 GuiWriteStream,
2287 GuiDrawRegion,
2288 GuiSetCursorInfo,
2289 GuiSetScreenInfo,
2290 GuiUpdateScreenInfo,
2291 GuiChangeTitle,
2292 GuiCleanupConsole,
2293 GuiChangeIcon,
2294 GuiResizeBuffer,
2295 };
2296
2297 NTSTATUS FASTCALL
2298 GuiInitConsole(PCSRSS_CONSOLE Console, int ShowCmd)
2299 {
2300 HANDLE GraphicsStartupEvent;
2301 HANDLE ThreadHandle;
2302 PGUI_CONSOLE_DATA GuiData;
2303
2304 if (! ConsInitialized)
2305 {
2306 ConsInitialized = TRUE;
2307 if (! GuiInit())
2308 {
2309 ConsInitialized = FALSE;
2310 return STATUS_UNSUCCESSFUL;
2311 }
2312 }
2313
2314 Console->Vtbl = &GuiVtbl;
2315 if (NULL == NotifyWnd)
2316 {
2317 GraphicsStartupEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
2318 if (NULL == GraphicsStartupEvent)
2319 {
2320 return STATUS_UNSUCCESSFUL;
2321 }
2322
2323 ThreadHandle = CreateThread(NULL,
2324 0,
2325 GuiConsoleGuiThread,
2326 (PVOID) &GraphicsStartupEvent,
2327 0,
2328 NULL);
2329 if (NULL == ThreadHandle)
2330 {
2331 CloseHandle(GraphicsStartupEvent);
2332 DPRINT1("Win32Csr: Failed to create graphics console thread. Expect problems\n");
2333 return STATUS_UNSUCCESSFUL;
2334 }
2335 SetThreadPriority(ThreadHandle, THREAD_PRIORITY_HIGHEST);
2336 CloseHandle(ThreadHandle);
2337
2338 WaitForSingleObject(GraphicsStartupEvent, INFINITE);
2339 CloseHandle(GraphicsStartupEvent);
2340
2341 if (NULL == NotifyWnd)
2342 {
2343 DPRINT1("Win32Csr: Failed to create notification window.\n");
2344 return STATUS_UNSUCCESSFUL;
2345 }
2346 }
2347 GuiData = HeapAlloc(ConSrvHeap, HEAP_ZERO_MEMORY,
2348 sizeof(GUI_CONSOLE_DATA));
2349 if (!GuiData)
2350 {
2351 DPRINT1("Win32Csr: Failed to create GUI_CONSOLE_DATA\n");
2352 return STATUS_UNSUCCESSFUL;
2353 }
2354
2355 Console->PrivateData = (PVOID) GuiData;
2356 /*
2357 * we need to wait untill the GUI has been fully initialized
2358 * to retrieve custom settings i.e. WindowSize etc..
2359 * Ideally we could use SendNotifyMessage for this but its not
2360 * yet implemented.
2361 *
2362 */
2363 GuiData->hGuiInitEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
2364 /* create console */
2365 PostMessageW(NotifyWnd, PM_CREATE_CONSOLE, ShowCmd, (LPARAM)Console);
2366
2367 /* wait untill initialization has finished */
2368 WaitForSingleObject(GuiData->hGuiInitEvent, INFINITE);
2369 DPRINT("received event Console %p GuiData %p X %d Y %d\n", Console, Console->PrivateData, Console->Size.X, Console->Size.Y);
2370 CloseHandle(GuiData->hGuiInitEvent);
2371 GuiData->hGuiInitEvent = NULL;
2372
2373 return STATUS_SUCCESS;
2374 }
2375
2376 /* EOF */