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