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