Imported Upstream version 2.0.14
[platform/upstream/SDL.git] / src / video / os2 / SDL_os2vman.c
1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4
5   This software is provided 'as-is', without any express or implied
6   warranty.  In no event will the authors be held liable for any damages
7   arising from the use of this software.
8
9   Permission is granted to anyone to use this software for any purpose,
10   including commercial applications, and to alter it and redistribute it
11   freely, subject to the following restrictions:
12
13   1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17   2. Altered source versions must be plainly marked as such, and must not be
18      misrepresented as being the original software.
19   3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22 #include "../SDL_sysvideo.h"
23
24 #define INCL_DOSERRORS
25 #define INCL_DOSPROCESS
26 #define INCL_DOSMODULEMGR
27 #define INCL_WIN
28 #define INCL_GPI
29 #define INCL_GPIBITMAPS /* GPI bit map functions */
30 #include <os2.h>
31 #include "SDL_os2output.h"
32 #include "SDL_os2video.h"
33
34 #include "my_gradd.h"
35
36 typedef struct _VODATA {
37   PVOID    pBuffer;
38   HRGN     hrgnVisible;
39   ULONG    ulBPP;
40   ULONG    ulScanLineSize;
41   ULONG    ulWidth;
42   ULONG    ulHeight;
43   ULONG    ulScreenHeight;
44   ULONG    ulScreenBytesPerLine;
45   RECTL    rectlWin;
46
47   PRECTL   pRectl;
48   ULONG    cRectl;
49   PBLTRECT pBltRect;
50   ULONG    cBltRect;
51 } VODATA;
52
53 static BOOL voQueryInfo(VIDEOOUTPUTINFO *pInfo);
54 static PVODATA voOpen();
55 static VOID voClose(PVODATA pVOData);
56 static BOOL voSetVisibleRegion(PVODATA pVOData, HWND hwnd,
57                                SDL_DisplayMode *pSDLDisplayMode,
58                                HRGN hrgnShape, BOOL fVisible);
59 static PVOID voVideoBufAlloc(PVODATA pVOData, ULONG ulWidth, ULONG ulHeight,
60                              ULONG ulBPP, ULONG fccColorEncoding,
61                              PULONG pulScanLineSize);
62 static VOID voVideoBufFree(PVODATA pVOData);
63 static BOOL voUpdate(PVODATA pVOData, HWND hwnd, SDL_Rect *pSDLRects,
64                      ULONG cSDLRects);
65
66 OS2VIDEOOUTPUT voVMan = {
67     voQueryInfo,
68     voOpen,
69     voClose,
70     voSetVisibleRegion,
71     voVideoBufAlloc,
72     voVideoBufFree,
73     voUpdate
74 };
75
76
77 static HMODULE  hmodVMan = NULLHANDLE;
78 static FNVMIENTRY *pfnVMIEntry = NULL;
79 static ULONG        ulVRAMAddress = 0;
80
81 VOID APIENTRY ExitVMan(VOID)
82 {
83     if (ulVRAMAddress != 0 && hmodVMan != NULLHANDLE) {
84         pfnVMIEntry(0, VMI_CMD_TERMPROC, NULL, NULL);
85         DosFreeModule(hmodVMan);
86     }
87
88     DosExitList(EXLST_EXIT, (PFNEXITLIST)NULL);
89 }
90
91 static BOOL _vmanInit(void)
92 {
93     ULONG       ulRC;
94     CHAR        acBuf[255];
95     INITPROCOUT stInitProcOut;
96
97     if (hmodVMan != NULLHANDLE) /* Already was initialized */
98         return TRUE;
99
100     /* Load vman.dll */
101     ulRC = DosLoadModule(acBuf, sizeof(acBuf), "VMAN", &hmodVMan);
102     if (ulRC != NO_ERROR) {
103         debug_os2("Could not load VMAN.DLL, rc = %u : %s", ulRC, acBuf);
104         hmodVMan = NULLHANDLE;
105         return FALSE;
106     }
107
108     /* Get VMIEntry */
109     ulRC = DosQueryProcAddr(hmodVMan, 0L, "VMIEntry", (PFN *)&pfnVMIEntry);
110     if (ulRC != NO_ERROR) {
111         debug_os2("Could not query address of pfnVMIEntry func. of VMAN.DLL, "
112                   "rc = %u", ulRC);
113         DosFreeModule(hmodVMan);
114         hmodVMan = NULLHANDLE;
115         return FALSE;
116     }
117
118     /* VMAN initialization */
119     stInitProcOut.ulLength = sizeof(stInitProcOut);
120     ulRC = pfnVMIEntry(0, VMI_CMD_INITPROC, NULL, &stInitProcOut);
121     if (ulRC != RC_SUCCESS) {
122         debug_os2("Could not initialize VMAN for this process");
123         pfnVMIEntry = NULL;
124         DosFreeModule(hmodVMan);
125         hmodVMan = NULLHANDLE;
126         return FALSE;
127     }
128
129     /* Store video memory virtual address */
130     ulVRAMAddress = stInitProcOut.ulVRAMVirt;
131     /* We use exit list for VMI_CMD_TERMPROC */
132     if (DosExitList(EXLST_ADD | 0x00001000, (PFNEXITLIST)ExitVMan) != NO_ERROR) {
133         debug_os2("DosExitList() failed");
134     }
135
136     return TRUE;
137 }
138
139 static PRECTL _getRectlArray(PVODATA pVOData, ULONG cRects)
140 {
141     PRECTL  pRectl;
142
143     if (pVOData->cRectl >= cRects)
144         return pVOData->pRectl;
145
146     pRectl = SDL_realloc(pVOData->pRectl, cRects * sizeof(RECTL));
147     if (pRectl == NULL)
148         return NULL;
149
150     pVOData->pRectl = pRectl;
151     pVOData->cRectl = cRects;
152     return pRectl;
153 }
154
155 static PBLTRECT _getBltRectArray(PVODATA pVOData, ULONG cRects)
156 {
157     PBLTRECT    pBltRect;
158
159     if (pVOData->cBltRect >= cRects)
160         return pVOData->pBltRect;
161
162     pBltRect = SDL_realloc(pVOData->pBltRect, cRects * sizeof(BLTRECT));
163     if (pBltRect == NULL)
164         return NULL;
165
166     pVOData->pBltRect = pBltRect;
167     pVOData->cBltRect = cRects;
168     return pBltRect;
169 }
170
171
172 static BOOL voQueryInfo(VIDEOOUTPUTINFO *pInfo)
173 {
174     ULONG       ulRC;
175     GDDMODEINFO sCurModeInfo;
176
177     if (!_vmanInit())
178         return FALSE;
179
180     /* Query current (desktop) mode */
181     ulRC = pfnVMIEntry(0, VMI_CMD_QUERYCURRENTMODE, NULL, &sCurModeInfo);
182     if (ulRC != RC_SUCCESS) {
183         debug_os2("Could not query desktop video mode.");
184         return FALSE;
185     }
186
187     pInfo->ulBPP             = sCurModeInfo.ulBpp;
188     pInfo->ulHorizResolution = sCurModeInfo.ulHorizResolution;
189     pInfo->ulVertResolution  = sCurModeInfo.ulVertResolution;
190     pInfo->ulScanLineSize    = sCurModeInfo.ulScanLineSize;
191     pInfo->fccColorEncoding  = sCurModeInfo.fccColorEncoding;
192
193     return TRUE;
194 }
195
196 static PVODATA voOpen(void)
197 {
198     PVODATA pVOData;
199
200     if (!_vmanInit())
201         return NULL;
202
203     pVOData = SDL_calloc(1, sizeof(VODATA));
204     if (pVOData == NULL) {
205         SDL_OutOfMemory();
206         return NULL;
207     }
208
209     return pVOData;
210 }
211
212 static VOID voClose(PVODATA pVOData)
213 {
214     if (pVOData->pRectl != NULL)
215         SDL_free(pVOData->pRectl);
216
217     if (pVOData->pBltRect != NULL)
218         SDL_free(pVOData->pBltRect);
219
220     voVideoBufFree(pVOData);
221 }
222
223 static BOOL voSetVisibleRegion(PVODATA pVOData, HWND hwnd,
224                                SDL_DisplayMode *pSDLDisplayMode,
225                                HRGN hrgnShape, BOOL fVisible)
226 {
227     HPS   hps;
228     BOOL  fSuccess = FALSE;
229
230     hps = WinGetPS(hwnd);
231
232     if (pVOData->hrgnVisible != NULLHANDLE) {
233         GpiDestroyRegion(hps, pVOData->hrgnVisible);
234         pVOData->hrgnVisible = NULLHANDLE;
235     }
236
237     if (fVisible) {
238         /* Query visible rectangles */
239         pVOData->hrgnVisible = GpiCreateRegion(hps, 0, NULL);
240         if (pVOData->hrgnVisible == NULLHANDLE) {
241             SDL_SetError("GpiCreateRegion() failed");
242         } else {
243             if (WinQueryVisibleRegion(hwnd, pVOData->hrgnVisible) == RGN_ERROR) {
244                 GpiDestroyRegion(hps, pVOData->hrgnVisible);
245                 pVOData->hrgnVisible = NULLHANDLE;
246             } else {
247                 if (hrgnShape != NULLHANDLE)
248                     GpiCombineRegion(hps, pVOData->hrgnVisible, pVOData->hrgnVisible,
249                                      hrgnShape, CRGN_AND);
250                 fSuccess = TRUE;
251             }
252         }
253
254         WinQueryWindowRect(hwnd, &pVOData->rectlWin);
255         WinMapWindowPoints(hwnd, HWND_DESKTOP, (PPOINTL)&pVOData->rectlWin, 2);
256
257         if (pSDLDisplayMode != NULL) {
258             pVOData->ulScreenHeight = pSDLDisplayMode->h;
259             pVOData->ulScreenBytesPerLine =
260                      ((MODEDATA *)pSDLDisplayMode->driverdata)->ulScanLineBytes;
261         }
262     }
263
264     WinReleasePS(hps);
265
266     return fSuccess;
267 }
268
269 static PVOID voVideoBufAlloc(PVODATA pVOData, ULONG ulWidth, ULONG ulHeight,
270                              ULONG ulBPP, ULONG fccColorEncoding,
271                              PULONG pulScanLineSize)
272 {
273     ULONG ulRC;
274     ULONG ulScanLineSize = ulWidth * (ulBPP >> 3);
275
276     /* Destroy previous buffer */
277     voVideoBufFree(pVOData);
278
279     if (ulWidth == 0 || ulHeight == 0 || ulBPP == 0)
280         return NULL;
281
282     /* Bytes per line */
283     ulScanLineSize  = (ulScanLineSize + 3) & ~3; /* 4-byte aligning */
284     *pulScanLineSize = ulScanLineSize;
285
286     ulRC = DosAllocMem(&pVOData->pBuffer,
287                        (ulHeight * ulScanLineSize) + sizeof(ULONG),
288                        PAG_COMMIT | PAG_EXECUTE | PAG_READ | PAG_WRITE);
289     if (ulRC != NO_ERROR) {
290         debug_os2("DosAllocMem(), rc = %u", ulRC);
291         return NULL;
292     }
293
294     pVOData->ulBPP          = ulBPP;
295     pVOData->ulScanLineSize = ulScanLineSize;
296     pVOData->ulWidth        = ulWidth;
297     pVOData->ulHeight       = ulHeight;
298
299     return pVOData->pBuffer;
300 }
301
302 static VOID voVideoBufFree(PVODATA pVOData)
303 {
304     ULONG ulRC;
305
306     if (pVOData->pBuffer == NULL)
307         return;
308
309     ulRC = DosFreeMem(pVOData->pBuffer);
310     if (ulRC != NO_ERROR) {
311         debug_os2("DosFreeMem(), rc = %u", ulRC);
312     } else {
313         pVOData->pBuffer = NULL;
314     }
315 }
316
317 static BOOL voUpdate(PVODATA pVOData, HWND hwnd, SDL_Rect *pSDLRects,
318                      ULONG cSDLRects)
319 {
320     PRECTL      prectlDst, prectlScan;
321     HPS         hps;
322     HRGN        hrgnUpdate;
323     RGNRECT     rgnCtl;
324     SDL_Rect    stSDLRectDef;
325     BMAPINFO    bmiSrc;
326     BMAPINFO    bmiDst;
327     PPOINTL     pptlSrcOrg;
328     PBLTRECT    pbrDst;
329     HWREQIN     sHWReqIn;
330     BITBLTINFO  sBitbltInfo = { 0 };
331     ULONG       ulIdx;
332 /*  RECTL       rectlScreenUpdate;*/
333
334     if (pVOData->pBuffer == NULL)
335         return FALSE;
336
337     if (pVOData->hrgnVisible == NULLHANDLE)
338         return TRUE;
339
340     bmiSrc.ulLength = sizeof(BMAPINFO);
341     bmiSrc.ulType = BMAP_MEMORY;
342     bmiSrc.ulWidth = pVOData->ulWidth;
343     bmiSrc.ulHeight = pVOData->ulHeight;
344     bmiSrc.ulBpp = pVOData->ulBPP;
345     bmiSrc.ulBytesPerLine = pVOData->ulScanLineSize;
346     bmiSrc.pBits = (PBYTE)pVOData->pBuffer;
347
348     bmiDst.ulLength = sizeof(BMAPINFO);
349     bmiDst.ulType = BMAP_VRAM;
350     bmiDst.pBits = (PBYTE)ulVRAMAddress;
351     bmiDst.ulWidth = bmiSrc.ulWidth;
352     bmiDst.ulHeight = bmiSrc.ulHeight;
353     bmiDst.ulBpp = bmiSrc.ulBpp;
354     bmiDst.ulBytesPerLine = pVOData->ulScreenBytesPerLine;
355
356     /* List of update rectangles. This is the intersection of requested
357      * rectangles and visible rectangles.  */
358     if (cSDLRects == 0) {
359         /* Full update requested */
360         stSDLRectDef.x = 0;
361         stSDLRectDef.y = 0;
362         stSDLRectDef.w = bmiSrc.ulWidth;
363         stSDLRectDef.h = bmiSrc.ulHeight;
364         pSDLRects = &stSDLRectDef;
365         cSDLRects = 1;
366     }
367
368     /* Make list of destination rectangles (prectlDst) list from the source
369      * list (prectl).  */
370     prectlDst = _getRectlArray(pVOData, cSDLRects);
371     if (prectlDst == NULL) {
372         debug_os2("Not enough memory");
373         return FALSE;
374     }
375     prectlScan = prectlDst;
376     for (ulIdx = 0; ulIdx < cSDLRects; ulIdx++, pSDLRects++, prectlScan++) {
377         prectlScan->xLeft   = pSDLRects->x;
378         prectlScan->yTop    = pVOData->ulHeight - pSDLRects->y;
379         prectlScan->xRight  = prectlScan->xLeft + pSDLRects->w;
380         prectlScan->yBottom = prectlScan->yTop - pSDLRects->h;
381     }
382
383     hps = WinGetPS(hwnd);
384     if (hps == NULLHANDLE)
385         return FALSE;
386
387     /* Make destination region to update */
388     hrgnUpdate = GpiCreateRegion(hps, cSDLRects, prectlDst);
389     /* "AND" on visible and destination regions, result is region to update */
390     GpiCombineRegion(hps, hrgnUpdate, hrgnUpdate, pVOData->hrgnVisible, CRGN_AND);
391
392     /* Get rectangles of the region to update */
393     rgnCtl.ircStart     = 1;
394     rgnCtl.crc          = 0;
395     rgnCtl.ulDirection  = 1;
396     rgnCtl.crcReturned  = 0;
397     GpiQueryRegionRects(hps, hrgnUpdate, NULL, &rgnCtl, NULL);
398     if (rgnCtl.crcReturned == 0) {
399         GpiDestroyRegion(hps, hrgnUpdate);
400         WinReleasePS(hps);
401         return TRUE;
402     }
403     /* We don't need prectlDst, use it again to store update regions */
404     prectlDst = _getRectlArray(pVOData, rgnCtl.crcReturned);
405     if (prectlDst == NULL) {
406         debug_os2("Not enough memory");
407         GpiDestroyRegion(hps, hrgnUpdate);
408         WinReleasePS(hps);
409         return FALSE;
410     }
411     rgnCtl.ircStart     = 1;
412     rgnCtl.crc          = rgnCtl.crcReturned;
413     rgnCtl.ulDirection  = 1;
414     GpiQueryRegionRects(hps, hrgnUpdate, NULL, &rgnCtl, prectlDst);
415     GpiDestroyRegion(hps, hrgnUpdate);
416     WinReleasePS(hps);
417     cSDLRects = rgnCtl.crcReturned;
418
419     /* Now cRect/prectlDst is a list of regions in window (update && visible) */
420
421     /* Make lists for blitting from update regions */
422     pbrDst = _getBltRectArray(pVOData, cSDLRects);
423     if (pbrDst == NULL) {
424         debug_os2("Not enough memory");
425         return FALSE;
426     }
427
428     prectlScan = prectlDst;
429     pptlSrcOrg = (PPOINTL)prectlDst; /* Yes, this memory block will be used again */
430     for (ulIdx = 0; ulIdx < cSDLRects; ulIdx++, prectlScan++, pptlSrcOrg++) {
431         pbrDst[ulIdx].ulXOrg = pVOData->rectlWin.xLeft + prectlScan->xLeft;
432         pbrDst[ulIdx].ulYOrg = pVOData->ulScreenHeight -
433                               (pVOData->rectlWin.yBottom + prectlScan->yTop);
434         pbrDst[ulIdx].ulXExt = prectlScan->xRight - prectlScan->xLeft;
435         pbrDst[ulIdx].ulYExt = prectlScan->yTop - prectlScan->yBottom;
436         pptlSrcOrg->x = prectlScan->xLeft;
437         pptlSrcOrg->y = bmiSrc.ulHeight - prectlScan->yTop;
438     }
439     pptlSrcOrg = (PPOINTL)prectlDst;
440
441     /* Request HW */
442     sHWReqIn.ulLength = sizeof(HWREQIN);
443     sHWReqIn.ulFlags = REQUEST_HW;
444     sHWReqIn.cScrChangeRects = 1;
445     sHWReqIn.arectlScreen = &pVOData->rectlWin;
446     if (pfnVMIEntry(0, VMI_CMD_REQUESTHW, &sHWReqIn, NULL) != RC_SUCCESS) {
447         debug_os2("pfnVMIEntry(,VMI_CMD_REQUESTHW,,) failed");
448         sHWReqIn.cScrChangeRects = 0; /* for fail signal only */
449     } else {
450         RECTL rclSrcBounds;
451
452         rclSrcBounds.xLeft = 0;
453         rclSrcBounds.yBottom = 0;
454         rclSrcBounds.xRight = bmiSrc.ulWidth;
455         rclSrcBounds.yTop = bmiSrc.ulHeight;
456
457         sBitbltInfo.ulLength = sizeof(BITBLTINFO);
458         sBitbltInfo.ulBltFlags = BF_DEFAULT_STATE | BF_ROP_INCL_SRC | BF_PAT_HOLLOW;
459         sBitbltInfo.cBlits = cSDLRects;
460         sBitbltInfo.ulROP = ROP_SRCCOPY;
461         sBitbltInfo.pSrcBmapInfo = &bmiSrc;
462         sBitbltInfo.pDstBmapInfo = &bmiDst;
463         sBitbltInfo.prclSrcBounds = &rclSrcBounds;
464         sBitbltInfo.prclDstBounds = &pVOData->rectlWin;
465         sBitbltInfo.aptlSrcOrg = pptlSrcOrg;
466         sBitbltInfo.abrDst = pbrDst;
467
468         /* Screen update */
469         if (pfnVMIEntry(0, VMI_CMD_BITBLT, &sBitbltInfo, NULL) != RC_SUCCESS) {
470             debug_os2("pfnVMIEntry(,VMI_CMD_BITBLT,,) failed");
471             sHWReqIn.cScrChangeRects = 0; /* for fail signal only */
472         }
473
474         /* Release HW */
475         sHWReqIn.ulFlags = 0;
476         if (pfnVMIEntry(0, VMI_CMD_REQUESTHW, &sHWReqIn, NULL) != RC_SUCCESS) {
477           debug_os2("pfnVMIEntry(,VMI_CMD_REQUESTHW,,) failed");
478         }
479     }
480
481     return sHWReqIn.cScrChangeRects != 0;
482 }
483
484 /* vi: set ts=4 sw=4 expandtab: */