[CONSRV]
[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 SIZE_T Size);
38 VOID
39 CONSOLE_SCREEN_BUFFER_Destroy(IN OUT PCONSOLE_SCREEN_BUFFER Buffer);
40
41
42 NTSTATUS
43 GRAPHICS_BUFFER_Initialize(OUT PCONSOLE_SCREEN_BUFFER* Buffer,
44 IN OUT PCONSOLE Console,
45 IN PGRAPHICS_BUFFER_INFO GraphicsInfo)
46 {
47 NTSTATUS Status = STATUS_SUCCESS;
48 PGRAPHICS_SCREEN_BUFFER NewBuffer = NULL;
49
50 LARGE_INTEGER SectionSize;
51 ULONG ViewSize = 0;
52 HANDLE ProcessHandle;
53
54 if (Buffer == NULL || Console == NULL || GraphicsInfo == NULL)
55 return STATUS_INVALID_PARAMETER;
56
57 *Buffer = NULL;
58
59 Status = CONSOLE_SCREEN_BUFFER_Initialize((PCONSOLE_SCREEN_BUFFER*)&NewBuffer,
60 Console,
61 sizeof(GRAPHICS_SCREEN_BUFFER));
62 if (!NT_SUCCESS(Status)) return Status;
63 NewBuffer->Header.Type = GRAPHICS_BUFFER;
64 NewBuffer->Vtbl = &GraphicsVtbl;
65
66 /*
67 * Remember the handle to the process so that we can close or unmap
68 * correctly the allocated resources when the client releases the
69 * screen buffer.
70 */
71 ProcessHandle = CsrGetClientThread()->Process->ProcessHandle;
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, correct that!\n", GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biCompression);
92 GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biCompression = BI_RGB;
93 }
94
95 RtlCopyMemory(NewBuffer->BitMapInfo,
96 GraphicsInfo->Info.lpBitMapInfo,
97 GraphicsInfo->Info.dwBitMapInfoLength);
98
99 NewBuffer->BitMapUsage = GraphicsInfo->Info.dwUsage;
100
101 /* Set the screen buffer size. Fight against overflows. */
102 if ( GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biWidth <= 0xFFFF &&
103 -GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biHeight <= 0xFFFF )
104 {
105 /* Be careful about the sign of biHeight */
106 NewBuffer->ScreenBufferSize.X = (SHORT)GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biWidth ;
107 NewBuffer->ScreenBufferSize.Y = (SHORT)-GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biHeight;
108
109 NewBuffer->OldViewSize = NewBuffer->ViewSize =
110 NewBuffer->OldScreenBufferSize = NewBuffer->ScreenBufferSize;
111 }
112 else
113 {
114 Status = STATUS_INSUFFICIENT_RESOURCES;
115 ConsoleFreeHeap(NewBuffer->BitMapInfo);
116 CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
117 goto Quit;
118 }
119
120 /*
121 * Create a mutex to synchronize bitmap memory access
122 * between ourselves and the client.
123 */
124 Status = NtCreateMutant(&NewBuffer->Mutex, MUTANT_ALL_ACCESS, NULL, FALSE);
125 if (!NT_SUCCESS(Status))
126 {
127 DPRINT1("NtCreateMutant() failed: %lu\n", Status);
128 ConsoleFreeHeap(NewBuffer->BitMapInfo);
129 CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
130 goto Quit;
131 }
132
133 /*
134 * Duplicate the Mutex for the client. We must keep a trace of it
135 * so that we can close it when the client releases the screen buffer.
136 */
137 Status = NtDuplicateObject(NtCurrentProcess(),
138 NewBuffer->Mutex,
139 ProcessHandle,
140 &NewBuffer->ClientMutex,
141 0, 0, DUPLICATE_SAME_ACCESS);
142 if (!NT_SUCCESS(Status))
143 {
144 DPRINT1("NtDuplicateObject() failed: %lu\n", Status);
145 NtClose(NewBuffer->Mutex);
146 ConsoleFreeHeap(NewBuffer->BitMapInfo);
147 CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
148 goto Quit;
149 }
150
151 /*
152 * Create a memory section for the bitmap area, to share with the client.
153 */
154 SectionSize.QuadPart = NewBuffer->BitMapInfo->bmiHeader.biSizeImage;
155 Status = NtCreateSection(&NewBuffer->hSection,
156 SECTION_ALL_ACCESS,
157 NULL,
158 &SectionSize,
159 PAGE_READWRITE,
160 SEC_COMMIT,
161 NULL);
162 if (!NT_SUCCESS(Status))
163 {
164 DPRINT1("Error: Impossible to create a shared section ; Status = %lu\n", Status);
165 NtClose(NewBuffer->ClientMutex);
166 NtClose(NewBuffer->Mutex);
167 ConsoleFreeHeap(NewBuffer->BitMapInfo);
168 CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
169 goto Quit;
170 }
171
172 /*
173 * Create a view for our needs.
174 */
175 ViewSize = 0;
176 NewBuffer->BitMap = NULL;
177 Status = NtMapViewOfSection(NewBuffer->hSection,
178 NtCurrentProcess(),
179 (PVOID*)&NewBuffer->BitMap,
180 0,
181 0,
182 NULL,
183 &ViewSize,
184 ViewUnmap,
185 0,
186 PAGE_READWRITE);
187 if (!NT_SUCCESS(Status))
188 {
189 DPRINT1("Error: Impossible to map the shared section ; Status = %lu\n", Status);
190 NtClose(NewBuffer->hSection);
191 NtClose(NewBuffer->ClientMutex);
192 NtClose(NewBuffer->Mutex);
193 ConsoleFreeHeap(NewBuffer->BitMapInfo);
194 CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
195 goto Quit;
196 }
197
198 /*
199 * Create a view for the client. We must keep a trace of it so that
200 * we can unmap it when the client releases the screen buffer.
201 */
202 ViewSize = 0;
203 NewBuffer->ClientBitMap = NULL;
204 Status = NtMapViewOfSection(NewBuffer->hSection,
205 ProcessHandle,
206 (PVOID*)&NewBuffer->ClientBitMap,
207 0,
208 0,
209 NULL,
210 &ViewSize,
211 ViewUnmap,
212 0,
213 PAGE_READWRITE);
214 if (!NT_SUCCESS(Status))
215 {
216 DPRINT1("Error: Impossible to map the shared section ; Status = %lu\n", Status);
217 NtUnmapViewOfSection(NtCurrentProcess(), NewBuffer->BitMap);
218 NtClose(NewBuffer->hSection);
219 NtClose(NewBuffer->ClientMutex);
220 NtClose(NewBuffer->Mutex);
221 ConsoleFreeHeap(NewBuffer->BitMapInfo);
222 CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
223 goto Quit;
224 }
225
226 NewBuffer->ViewOrigin.X = NewBuffer->ViewOrigin.Y = 0;
227 NewBuffer->VirtualY = 0;
228
229 NewBuffer->CursorBlinkOn = FALSE;
230 NewBuffer->ForceCursorOff = TRUE;
231 NewBuffer->CursorInfo.bVisible = FALSE;
232 NewBuffer->CursorInfo.dwSize = 0;
233 NewBuffer->CursorPosition.X = NewBuffer->CursorPosition.Y = 0;
234
235 NewBuffer->Mode = 0;
236
237 *Buffer = (PCONSOLE_SCREEN_BUFFER)NewBuffer;
238 Status = STATUS_SUCCESS;
239
240 Quit:
241 return Status;
242 }
243
244 VOID
245 GRAPHICS_BUFFER_Destroy(IN OUT PCONSOLE_SCREEN_BUFFER Buffer)
246 {
247 PGRAPHICS_SCREEN_BUFFER Buff = (PGRAPHICS_SCREEN_BUFFER)Buffer;
248
249 /*
250 * IMPORTANT !! Reinitialize the type so that we don't enter a recursive
251 * infinite loop when calling CONSOLE_SCREEN_BUFFER_Destroy.
252 */
253 Buffer->Header.Type = SCREEN_BUFFER;
254
255 /*
256 * Uninitialize the graphics screen buffer
257 * in the reverse way we initialized it.
258 */
259 NtUnmapViewOfSection(Buff->ClientProcess, Buff->ClientBitMap);
260 NtUnmapViewOfSection(NtCurrentProcess(), Buff->BitMap);
261 NtClose(Buff->hSection);
262 NtClose(Buff->ClientMutex);
263 NtClose(Buff->Mutex);
264 ConsoleFreeHeap(Buff->BitMapInfo);
265
266 CONSOLE_SCREEN_BUFFER_Destroy(Buffer);
267 }
268
269 /* EOF */