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