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     SIZE_T 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         NtDuplicateObject(ProcessHandle, NewBuffer->ClientMutex,
167                           NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
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 = 0x%08lx\n", Status);
192         NtClose(NewBuffer->hSection);
193         NtDuplicateObject(ProcessHandle, NewBuffer->ClientMutex,
194                           NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
195         NtClose(NewBuffer->Mutex);
196         ConsoleFreeHeap(NewBuffer->BitMapInfo);
197         CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
198         goto Quit;
199     }
200 
201     /*
202      * Create a view for the client. We must keep a trace of it so that
203      * we can unmap it when the client releases the screen buffer.
204      */
205     ViewSize = 0;
206     NewBuffer->ClientBitMap = NULL;
207     Status = NtMapViewOfSection(NewBuffer->hSection,
208                                 ProcessHandle,
209                                 (PVOID*)&NewBuffer->ClientBitMap,
210                                 0,
211                                 0,
212                                 NULL,
213                                 &ViewSize,
214                                 ViewUnmap,
215                                 0,
216                                 PAGE_READWRITE);
217     if (!NT_SUCCESS(Status))
218     {
219         DPRINT1("Error: Impossible to map the shared section, Status = 0x%08lx\n", Status);
220         NtUnmapViewOfSection(NtCurrentProcess(), NewBuffer->BitMap);
221         NtClose(NewBuffer->hSection);
222         NtDuplicateObject(ProcessHandle, NewBuffer->ClientMutex,
223                           NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
224         NtClose(NewBuffer->Mutex);
225         ConsoleFreeHeap(NewBuffer->BitMapInfo);
226         CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
227         goto Quit;
228     }
229 
230     NewBuffer->ViewOrigin.X = NewBuffer->ViewOrigin.Y = 0;
231     NewBuffer->VirtualY = 0;
232 
233     NewBuffer->CursorBlinkOn  = FALSE;
234     NewBuffer->ForceCursorOff = TRUE;
235     NewBuffer->CursorInfo.bVisible = FALSE;
236     NewBuffer->CursorInfo.dwSize   = 0;
237     NewBuffer->CursorPosition.X = NewBuffer->CursorPosition.Y = 0;
238 
239     NewBuffer->Mode = 0;
240 
241     *Buffer = (PCONSOLE_SCREEN_BUFFER)NewBuffer;
242     Status = STATUS_SUCCESS;
243 
244 Quit:
245     return Status;
246 }
247 
248 VOID
249 GRAPHICS_BUFFER_Destroy(IN OUT PCONSOLE_SCREEN_BUFFER Buffer)
250 {
251     PGRAPHICS_SCREEN_BUFFER Buff = (PGRAPHICS_SCREEN_BUFFER)Buffer;
252 
253     /*
254      * IMPORTANT !! Reinitialize the type so that we don't enter a recursive
255      * infinite loop when calling CONSOLE_SCREEN_BUFFER_Destroy.
256      */
257     Buffer->Header.Type = SCREEN_BUFFER;
258 
259     /*
260      * Uninitialize the graphics screen buffer
261      * in the reverse way we initialized it.
262      */
263     NtUnmapViewOfSection(Buff->ClientProcess, Buff->ClientBitMap);
264     NtUnmapViewOfSection(NtCurrentProcess(), Buff->BitMap);
265     NtClose(Buff->hSection);
266     NtDuplicateObject(Buff->ClientProcess, Buff->ClientMutex,
267                       NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
268     NtClose(Buff->Mutex);
269     ConsoleFreeHeap(Buff->BitMapInfo);
270 
271     CONSOLE_SCREEN_BUFFER_Destroy(Buffer);
272 }
273 
274 /* EOF */
275