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