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