Partial merge of the condrv_restructure branch, including:
[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 OUT 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 OUT PCONSOLE Console,
46 IN PGRAPHICS_BUFFER_INFO GraphicsInfo)
47 {
48 NTSTATUS Status = STATUS_SUCCESS;
49 PGRAPHICS_SCREEN_BUFFER NewBuffer = NULL;
50
51 LARGE_INTEGER SectionSize;
52 ULONG ViewSize = 0;
53 HANDLE ProcessHandle;
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 ProcessHandle = CsrGetClientThread()->Process->ProcessHandle;
73 NewBuffer->ClientProcess = ProcessHandle;
74
75 /* Get infos from the graphics buffer information structure */
76 NewBuffer->BitMapInfoLength = GraphicsInfo->Info.dwBitMapInfoLength;
77
78 NewBuffer->BitMapInfo = ConsoleAllocHeap(HEAP_ZERO_MEMORY, NewBuffer->BitMapInfoLength);
79 if (NewBuffer->BitMapInfo == NULL)
80 {
81 CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
82 return STATUS_INSUFFICIENT_RESOURCES;
83 }
84
85 /* Adjust the bitmap height if needed (bottom-top vs. top-bottom). Use always bottom-up. */
86 if (GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biHeight > 0)
87 GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biHeight = -GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biHeight;
88
89 /* We do not use anything else than uncompressed bitmaps */
90 if (GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biCompression != BI_RGB)
91 {
92 DPRINT1("biCompression == %d != BI_RGB, fix that!\n",
93 GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biCompression);
94 GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biCompression = BI_RGB;
95 }
96
97 RtlCopyMemory(NewBuffer->BitMapInfo,
98 GraphicsInfo->Info.lpBitMapInfo,
99 GraphicsInfo->Info.dwBitMapInfoLength);
100
101 NewBuffer->BitMapUsage = GraphicsInfo->Info.dwUsage;
102
103 /* Set the screen buffer size. Fight against overflows. */
104 if ( GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biWidth <= 0xFFFF &&
105 -GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biHeight <= 0xFFFF )
106 {
107 /* Be careful about the sign of biHeight */
108 NewBuffer->ScreenBufferSize.X = (SHORT)GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biWidth ;
109 NewBuffer->ScreenBufferSize.Y = (SHORT)-GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biHeight;
110
111 NewBuffer->OldViewSize = NewBuffer->ViewSize =
112 NewBuffer->OldScreenBufferSize = NewBuffer->ScreenBufferSize;
113 }
114 else
115 {
116 Status = STATUS_INSUFFICIENT_RESOURCES;
117 ConsoleFreeHeap(NewBuffer->BitMapInfo);
118 CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
119 goto Quit;
120 }
121
122 /*
123 * Create a mutex to synchronize bitmap memory access
124 * between ourselves and the client.
125 */
126 Status = NtCreateMutant(&NewBuffer->Mutex, MUTANT_ALL_ACCESS, NULL, FALSE);
127 if (!NT_SUCCESS(Status))
128 {
129 DPRINT1("NtCreateMutant() failed: %lu\n", Status);
130 ConsoleFreeHeap(NewBuffer->BitMapInfo);
131 CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
132 goto Quit;
133 }
134
135 /*
136 * Duplicate the Mutex for the client. We must keep a trace of it
137 * so that we can close it when the client releases the screen buffer.
138 */
139 Status = NtDuplicateObject(NtCurrentProcess(),
140 NewBuffer->Mutex,
141 ProcessHandle,
142 &NewBuffer->ClientMutex,
143 0, 0, DUPLICATE_SAME_ACCESS);
144 if (!NT_SUCCESS(Status))
145 {
146 DPRINT1("NtDuplicateObject() failed: %lu\n", Status);
147 NtClose(NewBuffer->Mutex);
148 ConsoleFreeHeap(NewBuffer->BitMapInfo);
149 CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
150 goto Quit;
151 }
152
153 /*
154 * Create a memory section for the bitmap area, to share with the client.
155 */
156 SectionSize.QuadPart = NewBuffer->BitMapInfo->bmiHeader.biSizeImage;
157 Status = NtCreateSection(&NewBuffer->hSection,
158 SECTION_ALL_ACCESS,
159 NULL,
160 &SectionSize,
161 PAGE_READWRITE,
162 SEC_COMMIT,
163 NULL);
164 if (!NT_SUCCESS(Status))
165 {
166 DPRINT1("Error: Impossible to create a shared section ; Status = %lu\n", Status);
167 NtClose(NewBuffer->ClientMutex);
168 NtClose(NewBuffer->Mutex);
169 ConsoleFreeHeap(NewBuffer->BitMapInfo);
170 CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
171 goto Quit;
172 }
173
174 /*
175 * Create a view for our needs.
176 */
177 ViewSize = 0;
178 NewBuffer->BitMap = NULL;
179 Status = NtMapViewOfSection(NewBuffer->hSection,
180 NtCurrentProcess(),
181 (PVOID*)&NewBuffer->BitMap,
182 0,
183 0,
184 NULL,
185 &ViewSize,
186 ViewUnmap,
187 0,
188 PAGE_READWRITE);
189 if (!NT_SUCCESS(Status))
190 {
191 DPRINT1("Error: Impossible to map the shared section ; Status = %lu\n", Status);
192 NtClose(NewBuffer->hSection);
193 NtClose(NewBuffer->ClientMutex);
194 NtClose(NewBuffer->Mutex);
195 ConsoleFreeHeap(NewBuffer->BitMapInfo);
196 CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
197 goto Quit;
198 }
199
200 /*
201 * Create a view for the client. We must keep a trace of it so that
202 * we can unmap it when the client releases the screen buffer.
203 */
204 ViewSize = 0;
205 NewBuffer->ClientBitMap = NULL;
206 Status = NtMapViewOfSection(NewBuffer->hSection,
207 ProcessHandle,
208 (PVOID*)&NewBuffer->ClientBitMap,
209 0,
210 0,
211 NULL,
212 &ViewSize,
213 ViewUnmap,
214 0,
215 PAGE_READWRITE);
216 if (!NT_SUCCESS(Status))
217 {
218 DPRINT1("Error: Impossible to map the shared section ; Status = %lu\n", Status);
219 NtUnmapViewOfSection(NtCurrentProcess(), NewBuffer->BitMap);
220 NtClose(NewBuffer->hSection);
221 NtClose(NewBuffer->ClientMutex);
222 NtClose(NewBuffer->Mutex);
223 ConsoleFreeHeap(NewBuffer->BitMapInfo);
224 CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
225 goto Quit;
226 }
227
228 NewBuffer->ViewOrigin.X = NewBuffer->ViewOrigin.Y = 0;
229 NewBuffer->VirtualY = 0;
230
231 NewBuffer->CursorBlinkOn = FALSE;
232 NewBuffer->ForceCursorOff = TRUE;
233 NewBuffer->CursorInfo.bVisible = FALSE;
234 NewBuffer->CursorInfo.dwSize = 0;
235 NewBuffer->CursorPosition.X = NewBuffer->CursorPosition.Y = 0;
236
237 NewBuffer->Mode = 0;
238
239 *Buffer = (PCONSOLE_SCREEN_BUFFER)NewBuffer;
240 Status = STATUS_SUCCESS;
241
242 Quit:
243 return Status;
244 }
245
246 VOID
247 GRAPHICS_BUFFER_Destroy(IN OUT PCONSOLE_SCREEN_BUFFER Buffer)
248 {
249 PGRAPHICS_SCREEN_BUFFER Buff = (PGRAPHICS_SCREEN_BUFFER)Buffer;
250
251 /*
252 * IMPORTANT !! Reinitialize the type so that we don't enter a recursive
253 * infinite loop when calling CONSOLE_SCREEN_BUFFER_Destroy.
254 */
255 Buffer->Header.Type = SCREEN_BUFFER;
256
257 /*
258 * Uninitialize the graphics screen buffer
259 * in the reverse way we initialized it.
260 */
261 NtUnmapViewOfSection(Buff->ClientProcess, Buff->ClientBitMap);
262 NtUnmapViewOfSection(NtCurrentProcess(), Buff->BitMap);
263 NtClose(Buff->hSection);
264 NtClose(Buff->ClientMutex);
265 NtClose(Buff->Mutex);
266 ConsoleFreeHeap(Buff->BitMapInfo);
267
268 CONSOLE_SCREEN_BUFFER_Destroy(Buffer);
269 }
270
271 /* EOF */