[csrss] Don't expect ReactOS to always boot from C:\ReactOS (that's not the case...
[reactos.git] / reactos / subsystems / win32 / csrss / win32csr / tuiconsole.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: subsys/csrss/win32csr/tuiconsole.c
6 * PURPOSE: Implementation of text-mode consoles
7 */
8
9 #define NDEBUG
10 #include "w32csr.h"
11 #include <debug.h>
12
13 CRITICAL_SECTION ActiveConsoleLock;
14 static COORD PhysicalConsoleSize;
15 static HANDLE ConsoleDeviceHandle;
16 static PCSRSS_CONSOLE ActiveConsole;
17
18 static BOOL ConsInitialized = FALSE;
19
20 static LRESULT CALLBACK
21 TuiConsoleWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
22 {
23 if (msg == WM_ACTIVATE)
24 {
25 if (LOWORD(wParam) != WA_INACTIVE)
26 {
27 SetFocus(hWnd);
28 ConioDrawConsole(ActiveConsole);
29 }
30 }
31 return DefWindowProcW(hWnd, msg, wParam, lParam);
32 }
33
34 static BOOL FASTCALL
35 TuiStartService(LPCWSTR lpServiceName)
36 {
37 SC_HANDLE hSCManager = NULL;
38 SC_HANDLE hService = NULL;
39 BOOL ret = FALSE;
40
41 hSCManager = OpenSCManagerW(NULL, NULL, 0);
42 if (hSCManager == NULL)
43 goto cleanup;
44
45 hService = OpenServiceW(hSCManager, lpServiceName, SERVICE_START);
46 if (hService == NULL)
47 goto cleanup;
48
49 ret = StartServiceW(hService, 0, NULL);
50 if (!ret)
51 goto cleanup;
52
53 ret = TRUE;
54
55 cleanup:
56 if (hSCManager != NULL)
57 CloseServiceHandle(hSCManager);
58 if (hService != NULL)
59 CloseServiceHandle(hService);
60 return ret;
61 }
62
63 static BOOL FASTCALL
64 TuiInit(DWORD OemCP)
65 {
66 CONSOLE_SCREEN_BUFFER_INFO ScrInfo;
67 DWORD BytesReturned;
68 WNDCLASSEXW wc;
69 USHORT TextAttribute = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
70
71 TuiStartService(L"Blue");
72
73 ConsoleDeviceHandle = CreateFileW(L"\\\\.\\BlueScreen", FILE_ALL_ACCESS, 0, NULL,
74 OPEN_EXISTING, 0, NULL);
75 if (INVALID_HANDLE_VALUE == ConsoleDeviceHandle)
76 {
77 DPRINT1("Failed to open BlueScreen.\n");
78 return FALSE;
79 }
80
81 if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_LOADFONT,
82 &OemCP, sizeof(OemCP), NULL, 0,
83 &BytesReturned, NULL))
84 {
85 DPRINT1("Failed to load the font for codepage %d\n", OemCP);
86 /* Let's suppose the font is good enough to continue */
87 }
88
89 if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_SET_TEXT_ATTRIBUTE,
90 &TextAttribute, sizeof(TextAttribute), NULL, 0,
91 &BytesReturned, NULL))
92 {
93 DPRINT1("Failed to set text attribute\n");
94 }
95
96 ActiveConsole = NULL;
97 InitializeCriticalSection(&ActiveConsoleLock);
98 if (! DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_GET_SCREEN_BUFFER_INFO,
99 NULL, 0, &ScrInfo, sizeof(ScrInfo), &BytesReturned, NULL))
100 {
101 DPRINT1("Failed to get console info\n");
102 return FALSE;
103 }
104 PhysicalConsoleSize = ScrInfo.dwSize;
105
106 RtlZeroMemory(&wc, sizeof(WNDCLASSEXW));
107 wc.cbSize = sizeof(WNDCLASSEXW);
108 wc.lpszClassName = L"TuiConsoleWindowClass";
109 wc.lpfnWndProc = TuiConsoleWndProc;
110 wc.hInstance = (HINSTANCE) GetModuleHandleW(NULL);
111 if (RegisterClassExW(&wc) == 0)
112 {
113 DPRINT1("Failed to register console wndproc\n");
114 return FALSE;
115 }
116
117 return TRUE;
118 }
119
120 static VOID WINAPI
121 TuiInitScreenBuffer(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER Buffer)
122 {
123 Buffer->DefaultAttrib = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
124 }
125
126 static void FASTCALL
127 TuiCopyRect(char *Dest, PCSRSS_SCREEN_BUFFER Buff, RECT *Region)
128 {
129 UINT SrcDelta, DestDelta;
130 LONG i;
131 PBYTE Src, SrcEnd;
132
133 Src = ConioCoordToPointer(Buff, Region->left, Region->top);
134 SrcDelta = Buff->MaxX * 2;
135 SrcEnd = Buff->Buffer + Buff->MaxY * Buff->MaxX * 2;
136 DestDelta = ConioRectWidth(Region) * 2;
137 for (i = Region->top; i <= Region->bottom; i++)
138 {
139 memcpy(Dest, Src, DestDelta);
140 Src += SrcDelta;
141 if (SrcEnd <= Src)
142 {
143 Src -= Buff->MaxY * Buff->MaxX * 2;
144 }
145 Dest += DestDelta;
146 }
147 }
148
149 static VOID WINAPI
150 TuiDrawRegion(PCSRSS_CONSOLE Console, RECT *Region)
151 {
152 DWORD BytesReturned;
153 PCSRSS_SCREEN_BUFFER Buff = Console->ActiveBuffer;
154 PCONSOLE_DRAW ConsoleDraw;
155 UINT ConsoleDrawSize;
156
157 if (ActiveConsole != Console)
158 {
159 return;
160 }
161
162 ConsoleDrawSize = sizeof(CONSOLE_DRAW) +
163 (ConioRectWidth(Region) * ConioRectHeight(Region)) * 2;
164 ConsoleDraw = HeapAlloc(Win32CsrApiHeap, 0, ConsoleDrawSize);
165 if (NULL == ConsoleDraw)
166 {
167 DPRINT1("HeapAlloc failed\n");
168 return;
169 }
170 ConsoleDraw->X = Region->left;
171 ConsoleDraw->Y = Region->top;
172 ConsoleDraw->SizeX = ConioRectWidth(Region);
173 ConsoleDraw->SizeY = ConioRectHeight(Region);
174 ConsoleDraw->CursorX = Buff->CurrentX;
175 ConsoleDraw->CursorY = Buff->CurrentY;
176
177 TuiCopyRect((char *) (ConsoleDraw + 1), Buff, Region);
178
179 if (! DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_DRAW,
180 NULL, 0, ConsoleDraw, ConsoleDrawSize, &BytesReturned, NULL))
181 {
182 DPRINT1("Failed to draw console\n");
183 HeapFree(Win32CsrApiHeap, 0, ConsoleDraw);
184 return;
185 }
186
187 HeapFree(Win32CsrApiHeap, 0, ConsoleDraw);
188 }
189
190 static VOID WINAPI
191 TuiWriteStream(PCSRSS_CONSOLE Console, RECT *Region, LONG CursorStartX, LONG CursorStartY,
192 UINT ScrolledLines, CHAR *Buffer, UINT Length)
193 {
194 DWORD BytesWritten;
195 PCSRSS_SCREEN_BUFFER Buff = Console->ActiveBuffer;
196
197 if (ActiveConsole->ActiveBuffer != Buff)
198 {
199 return;
200 }
201
202 if (! WriteFile(ConsoleDeviceHandle, Buffer, Length, &BytesWritten, NULL))
203 {
204 DPRINT1("Error writing to BlueScreen\n");
205 }
206 }
207
208 static BOOL WINAPI
209 TuiSetCursorInfo(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER Buff)
210 {
211 DWORD BytesReturned;
212
213 if (ActiveConsole->ActiveBuffer != Buff)
214 {
215 return TRUE;
216 }
217
218 if (! DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_SET_CURSOR_INFO,
219 &Buff->CursorInfo, sizeof(Buff->CursorInfo), NULL, 0,
220 &BytesReturned, NULL))
221 {
222 DPRINT1( "Failed to set cursor info\n" );
223 return FALSE;
224 }
225
226 return TRUE;
227 }
228
229 static BOOL WINAPI
230 TuiSetScreenInfo(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER Buff, UINT OldCursorX, UINT OldCursorY)
231 {
232 CONSOLE_SCREEN_BUFFER_INFO Info;
233 DWORD BytesReturned;
234
235 if (ActiveConsole->ActiveBuffer != Buff)
236 {
237 return TRUE;
238 }
239
240 Info.dwCursorPosition.X = Buff->CurrentX;
241 Info.dwCursorPosition.Y = Buff->CurrentY;
242 Info.wAttributes = Buff->DefaultAttrib;
243
244 if (! DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO,
245 &Info, sizeof(CONSOLE_SCREEN_BUFFER_INFO), NULL, 0,
246 &BytesReturned, NULL))
247 {
248 DPRINT1( "Failed to set cursor position\n" );
249 return FALSE;
250 }
251
252 return TRUE;
253 }
254
255 static BOOL WINAPI
256 TuiUpdateScreenInfo(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER Buff)
257 {
258 return TRUE;
259 }
260
261 static BOOL WINAPI
262 TuiChangeTitle(PCSRSS_CONSOLE Console)
263 {
264 return TRUE;
265 }
266
267 static VOID WINAPI
268 TuiCleanupConsole(PCSRSS_CONSOLE Console)
269 {
270 DestroyWindow(Console->hWindow);
271
272 EnterCriticalSection(&ActiveConsoleLock);
273
274 /* Switch to next console */
275 if (ActiveConsole == Console)
276 {
277 ActiveConsole = Console->Next != Console ? Console->Next : NULL;
278 }
279
280 if (Console->Next != Console)
281 {
282 Console->Prev->Next = Console->Next;
283 Console->Next->Prev = Console->Prev;
284 }
285 LeaveCriticalSection(&ActiveConsoleLock);
286
287 if (NULL != ActiveConsole)
288 {
289 ConioDrawConsole(ActiveConsole);
290 }
291 }
292
293 DWORD WINAPI
294 TuiConsoleThread (PVOID Data)
295 {
296 PCSRSS_CONSOLE Console = (PCSRSS_CONSOLE) Data;
297 HWND NewWindow;
298 MSG msg;
299
300 NewWindow = CreateWindowW(L"TuiConsoleWindowClass",
301 Console->Title.Buffer,
302 0,
303 -32000, -32000, 0, 0,
304 NULL, NULL,
305 (HINSTANCE) GetModuleHandleW(NULL),
306 (PVOID) Console);
307 Console->hWindow = NewWindow;
308 if (NULL == NewWindow)
309 {
310 DPRINT1("CSR: Unable to create console window\n");
311 return 1;
312 }
313
314 SetForegroundWindow(Console->hWindow);
315
316 while (TRUE)
317 {
318 GetMessageW(&msg, 0, 0, 0);
319 DispatchMessage(&msg);
320 TranslateMessage(&msg);
321
322 if (msg.message == WM_CHAR || msg.message == WM_SYSCHAR ||
323 msg.message == WM_KEYDOWN || msg.message == WM_KEYUP ||
324 msg.message == WM_SYSKEYDOWN || msg.message == WM_SYSKEYUP)
325 {
326 ConioProcessKey(&msg, Console, TRUE);
327 }
328 }
329
330 return 0;
331 }
332
333 static CSRSS_CONSOLE_VTBL TuiVtbl =
334 {
335 TuiInitScreenBuffer,
336 TuiWriteStream,
337 TuiDrawRegion,
338 TuiSetCursorInfo,
339 TuiSetScreenInfo,
340 TuiUpdateScreenInfo,
341 TuiChangeTitle,
342 TuiCleanupConsole,
343 NULL // ChangeIcon
344 };
345
346 NTSTATUS FASTCALL
347 TuiInitConsole(PCSRSS_CONSOLE Console)
348 {
349 HANDLE ThreadHandle;
350
351 if (! ConsInitialized)
352 {
353 ConsInitialized = TRUE;
354 if (! TuiInit(Console->CodePage))
355 {
356 ConsInitialized = FALSE;
357 return STATUS_UNSUCCESSFUL;
358 }
359 }
360
361 Console->Vtbl = &TuiVtbl;
362 Console->hWindow = NULL;
363 Console->Size = PhysicalConsoleSize;
364 Console->ActiveBuffer->MaxX = PhysicalConsoleSize.X;
365 Console->ActiveBuffer->MaxY = PhysicalConsoleSize.Y;
366
367 ThreadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) TuiConsoleThread,
368 Console, 0, NULL);
369 if (NULL == ThreadHandle)
370 {
371 DPRINT1("CSR: Unable to create console thread\n");
372 return STATUS_UNSUCCESSFUL;
373 }
374 CloseHandle(ThreadHandle);
375
376 EnterCriticalSection(&ActiveConsoleLock);
377 if (NULL != ActiveConsole)
378 {
379 Console->Prev = ActiveConsole;
380 Console->Next = ActiveConsole->Next;
381 ActiveConsole->Next->Prev = Console;
382 ActiveConsole->Next = Console;
383 }
384 else
385 {
386 Console->Prev = Console;
387 Console->Next = Console;
388 }
389 ActiveConsole = Console;
390 LeaveCriticalSection(&ActiveConsoleLock);
391
392 return STATUS_SUCCESS;
393 }
394
395 PCSRSS_CONSOLE FASTCALL
396 TuiGetFocusConsole(VOID)
397 {
398 return ActiveConsole;
399 }
400
401 BOOL FASTCALL
402 TuiSwapConsole(int Next)
403 {
404 static PCSRSS_CONSOLE SwapConsole = NULL; /* console we are thinking about swapping with */
405 DWORD BytesReturned;
406 ANSI_STRING Title;
407 void * Buffer;
408 COORD *pos;
409
410 if (0 != Next)
411 {
412 /* alt-tab, swap consoles */
413 /* move SwapConsole to next console, and print its title */
414 EnterCriticalSection(&ActiveConsoleLock);
415 if (! SwapConsole)
416 {
417 SwapConsole = ActiveConsole;
418 }
419
420 SwapConsole = (0 < Next ? SwapConsole->Next : SwapConsole->Prev);
421 Title.MaximumLength = RtlUnicodeStringToAnsiSize(&SwapConsole->Title);
422 Title.Length = 0;
423 Buffer = HeapAlloc(Win32CsrApiHeap,
424 0,
425 sizeof(COORD) + Title.MaximumLength);
426 pos = (COORD *)Buffer;
427 Title.Buffer = (PVOID)((ULONG_PTR)Buffer + sizeof( COORD ));
428
429 RtlUnicodeStringToAnsiString(&Title, &SwapConsole->Title, FALSE);
430 pos->Y = PhysicalConsoleSize.Y / 2;
431 pos->X = (PhysicalConsoleSize.X - Title.Length) / 2;
432 /* redraw the console to clear off old title */
433 ConioDrawConsole(ActiveConsole);
434 if (! DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_WRITE_OUTPUT_CHARACTER,
435 NULL, 0, Buffer, sizeof(COORD) + Title.Length,
436 &BytesReturned, NULL))
437 {
438 DPRINT1( "Error writing to console\n" );
439 }
440 HeapFree(Win32CsrApiHeap, 0, Buffer);
441 LeaveCriticalSection(&ActiveConsoleLock);
442
443 return TRUE;
444 }
445 else if (NULL != SwapConsole)
446 {
447 EnterCriticalSection(&ActiveConsoleLock);
448 if (SwapConsole != ActiveConsole)
449 {
450 /* first remove swapconsole from the list */
451 SwapConsole->Prev->Next = SwapConsole->Next;
452 SwapConsole->Next->Prev = SwapConsole->Prev;
453 /* now insert before activeconsole */
454 SwapConsole->Next = ActiveConsole;
455 SwapConsole->Prev = ActiveConsole->Prev;
456 ActiveConsole->Prev->Next = SwapConsole;
457 ActiveConsole->Prev = SwapConsole;
458 }
459 ActiveConsole = SwapConsole;
460 SwapConsole = NULL;
461 ConioDrawConsole(ActiveConsole);
462 LeaveCriticalSection(&ActiveConsoleLock);
463 return TRUE;
464 }
465 else
466 {
467 return FALSE;
468 }
469 }
470
471 /* EOF */