c2a0a82a9517773c65a356e59e443cf3a1d5e392
[reactos.git] / reactos / win32ss / user / winsrv / consrv / condrv / graphics.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Driver DLL
4 * FILE: win32ss/user/winsrv/consrv/condrv/graphics.c
5 * PURPOSE: Console Output Functions for graphics-mode screen-buffers
6 * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
7 *
8 * NOTE: See http://blog.airesoft.co.uk/2012/10/things-ms-can-do-that-they-dont-tell-you-about-console-graphics/
9 * for more information.
10 */
11
12 /* INCLUDES *******************************************************************/
13
14 #include <consrv.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 /* PRIVATE FUNCTIONS **********************************************************/
20
21 CONSOLE_IO_OBJECT_TYPE
22 GRAPHICS_BUFFER_GetType(PCONSOLE_SCREEN_BUFFER This)
23 {
24 // return This->Header.Type;
25 return GRAPHICS_BUFFER;
26 }
27
28 static CONSOLE_SCREEN_BUFFER_VTBL GraphicsVtbl =
29 {
30 GRAPHICS_BUFFER_GetType,
31 };
32
33
34 NTSTATUS
35 CONSOLE_SCREEN_BUFFER_Initialize(OUT PCONSOLE_SCREEN_BUFFER* Buffer,
36 IN PCONSOLE Console,
37 IN PCONSOLE_SCREEN_BUFFER_VTBL Vtbl,
38 IN SIZE_T Size);
39 VOID
40 CONSOLE_SCREEN_BUFFER_Destroy(IN OUT PCONSOLE_SCREEN_BUFFER Buffer);
41
42
43 NTSTATUS
44 GRAPHICS_BUFFER_Initialize(OUT PCONSOLE_SCREEN_BUFFER* Buffer,
45 IN PCONSOLE Console,
46 IN HANDLE ProcessHandle,
47 IN PGRAPHICS_BUFFER_INFO GraphicsInfo)
48 {
49 NTSTATUS Status = STATUS_SUCCESS;
50 PGRAPHICS_SCREEN_BUFFER NewBuffer = NULL;
51
52 LARGE_INTEGER SectionSize;
53 ULONG ViewSize = 0;
54
55 if (Buffer == NULL || Console == NULL || GraphicsInfo == NULL)
56 return STATUS_INVALID_PARAMETER;
57
58 *Buffer = NULL;
59
60 Status = CONSOLE_SCREEN_BUFFER_Initialize((PCONSOLE_SCREEN_BUFFER*)&NewBuffer,
61 Console,
62 &GraphicsVtbl,
63 sizeof(GRAPHICS_SCREEN_BUFFER));
64 if (!NT_SUCCESS(Status)) return Status;
65 NewBuffer->Header.Type = GRAPHICS_BUFFER;
66
67 /*
68 * Remember the handle to the process so that we can close or unmap
69 * correctly the allocated resources when the client releases the
70 * screen buffer.
71 */
72 NewBuffer->ClientProcess = ProcessHandle;
73
74 /* Get infos from the graphics buffer information structure */
75 NewBuffer->BitMapInfoLength = GraphicsInfo->Info.dwBitMapInfoLength;
76
77 NewBuffer->BitMapInfo = ConsoleAllocHeap(HEAP_ZERO_MEMORY, NewBuffer->BitMapInfoLength);
78 if (NewBuffer->BitMapInfo == NULL)
79 {
80 CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
81 return STATUS_INSUFFICIENT_RESOURCES;
82 }
83
84 /* Adjust the bitmap height if needed (bottom-top vs. top-bottom). Use always bottom-up. */
85 if (GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biHeight > 0)
86 GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biHeight = -GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biHeight;
87
88 /* We do not use anything else than uncompressed bitmaps */
89 if (GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biCompression != BI_RGB)
90 {
91 DPRINT1("biCompression == %d != BI_RGB, fix that!\n",
92 GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biCompression);
93 GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biCompression = BI_RGB;
94 }
95
96 RtlCopyMemory(NewBuffer->BitMapInfo,
97 GraphicsInfo->Info.lpBitMapInfo,
98 GraphicsInfo->Info.dwBitMapInfoLength);
99
100 NewBuffer->BitMapUsage = GraphicsInfo->Info.dwUsage;
101
102 /* Set the screen buffer size. Fight against overflows. */
103 if ( GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biWidth <= 0xFFFF &&
104 -GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biHeight <= 0xFFFF )
105 {
106 /* Be careful about the sign of biHeight */
107 NewBuffer->ScreenBufferSize.X = (SHORT)GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biWidth ;
108 NewBuffer->ScreenBufferSize.Y = (SHORT)-GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biHeight;
109
110 NewBuffer->OldViewSize = NewBuffer->ViewSize =
111 NewBuffer->OldScreenBufferSize = NewBuffer->ScreenBufferSize;
112 }
113 else
114 {
115 Status = STATUS_INSUFFICIENT_RESOURCES;
116 ConsoleFreeHeap(NewBuffer->BitMapInfo);
117 CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
118 goto Quit;
119 }
120
121 /*
122 * Create a mutex to synchronize bitmap memory access
123 * between ourselves and the client.
124 */
125 Status = NtCreateMutant(&NewBuffer->Mutex, MUTANT_ALL_ACCESS, NULL, FALSE);
126 if (!NT_SUCCESS(Status))
127 {
128 DPRINT1("NtCreateMutant() failed: %lu\n", Status);
129 ConsoleFreeHeap(NewBuffer->BitMapInfo);
130 CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
131 goto Quit;
132 }
133
134 /*
135 * Duplicate the Mutex for the client. We must keep a trace of it
136 * so that we can close it when the client releases the screen buffer.
137 */
138 Status = NtDuplicateObject(NtCurrentProcess(),
139 NewBuffer->Mutex,
140 ProcessHandle,
141 &NewBuffer->ClientMutex,
142 0, 0, DUPLICATE_SAME_ACCESS);
143 if (!NT_SUCCESS(Status))
144 {
145 DPRINT1("NtDuplicateObject() failed: %lu\n", Status);
146 NtClose(NewBuffer->Mutex);
147 ConsoleFreeHeap(NewBuffer->BitMapInfo);
148 CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
149 goto Quit;
150 }
151
152 /*
153 * Create a memory section for the bitmap area, to share with the client.
154 */
155 SectionSize.QuadPart = NewBuffer->BitMapInfo->bmiHeader.biSizeImage;
156 Status = NtCreateSection(&NewBuffer->hSection,
157 SECTION_ALL_ACCESS,
158 NULL,
159 &SectionSize,
160 PAGE_READWRITE,
161 SEC_COMMIT,
162 NULL);
163 if (!NT_SUCCESS(Status))
164 {
165 DPRINT1("Error: Impossible to create a shared section, Status = 0x%08lx\n", Status);
166 NtClose(NewBuffer->ClientMutex);
167 NtClose(NewBuffer->Mutex);
168 ConsoleFreeHeap(NewBuffer->BitMapInfo);
169 CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
170 goto Quit;
171 }
172
173 /*
174 * Create a view for our needs.
175 */
176 ViewSize = 0;
177 NewBuffer->BitMap = NULL;
178 Status = NtMapViewOfSection(NewBuffer->hSection,
179 NtCurrentProcess(),
180 (PVOID*)&NewBuffer->BitMap,
181 0,
182 0,
183 NULL,
184 &ViewSize,
185 ViewUnmap,
186 0,
187 PAGE_READWRITE);
188 if (!NT_SUCCESS(Status))
189 {
190 DPRINT1("Error: Impossible to map the shared section, Status = 0x%08lx\n", Status);
191 NtClose(NewBuffer->hSection);
192 NtClose(NewBuffer->ClientMutex);
193 NtClose(NewBuffer->Mutex);
194 ConsoleFreeHeap(NewBuffer->BitMapInfo);
195 CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
196 goto Quit;
197 }
198
199 /*
200 * Create a view for the client. We must keep a trace of it so that
201 * we can unmap it when the client releases the screen buffer.
202 */
203 ViewSize = 0;
204 NewBuffer->ClientBitMap = NULL;
205 Status = NtMapViewOfSection(NewBuffer->hSection,
206 ProcessHandle,
207 (PVOID*)&NewBuffer->ClientBitMap,
208 0,
209 0,
210 NULL,
211 &ViewSize,
212 ViewUnmap,
213 0,
214 PAGE_READWRITE);
215 if (!NT_SUCCESS(Status))
216 {
217 DPRINT1("Error: Impossible to map the shared section, Status = 0x%08lx\n", Status);
218 NtUnmapViewOfSection(NtCurrentProcess(), NewBuffer->BitMap);
219 NtClose(NewBuffer->hSection);
220 NtClose(NewBuffer->ClientMutex);
221 NtClose(NewBuffer->Mutex);
222 ConsoleFreeHeap(NewBuffer->BitMapInfo);
223 CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
224 goto Quit;
225 }
226
227 NewBuffer->ViewOrigin.X = NewBuffer->ViewOrigin.Y = 0;
228 NewBuffer->VirtualY = 0;
229
230 NewBuffer->CursorBlinkOn = FALSE;
231 NewBuffer->ForceCursorOff = TRUE;
232 NewBuffer->CursorInfo.bVisible = FALSE;
233 NewBuffer->CursorInfo.dwSize = 0;
234 NewBuffer->CursorPosition.X = NewBuffer->CursorPosition.Y = 0;
235
236 NewBuffer->Mode = 0;
237
238 *Buffer = (PCONSOLE_SCREEN_BUFFER)NewBuffer;
239 Status = STATUS_SUCCESS;
240
241 Quit:
242 return Status;
243 }
244
245 VOID
246 GRAPHICS_BUFFER_Destroy(IN OUT PCONSOLE_SCREEN_BUFFER Buffer)
247 {
248 PGRAPHICS_SCREEN_BUFFER Buff = (PGRAPHICS_SCREEN_BUFFER)Buffer;
249
250 /*
251 * IMPORTANT !! Reinitialize the type so that we don't enter a recursive
252 * infinite loop when calling CONSOLE_SCREEN_BUFFER_Destroy.
253 */
254 Buffer->Header.Type = SCREEN_BUFFER;
255
256 /*
257 * Uninitialize the graphics screen buffer
258 * in the reverse way we initialized it.
259 */
260 NtUnmapViewOfSection(Buff->ClientProcess, Buff->ClientBitMap);
261 NtUnmapViewOfSection(NtCurrentProcess(), Buff->BitMap);
262 NtClose(Buff->hSection);
263 NtClose(Buff->ClientMutex);
264 NtClose(Buff->Mutex);
265 ConsoleFreeHeap(Buff->BitMapInfo);
266
267 CONSOLE_SCREEN_BUFFER_Destroy(Buffer);
268 }
269
270 /* EOF */