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