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