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