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