2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "../../SDL_internal.h"
22 #include "../SDL_sysvideo.h"
24 #define INCL_DOSERRORS
25 #define INCL_DOSPROCESS
26 #define INCL_DOSMODULEMGR
29 #define INCL_GPIBITMAPS /* GPI bit map functions */
31 #include "SDL_os2output.h"
32 #include "SDL_os2video.h"
36 typedef struct _VODATA {
44 ULONG ulScreenBytesPerLine;
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,
66 OS2VIDEOOUTPUT voVMan = {
77 static HMODULE hmodVMan = NULLHANDLE;
78 static FNVMIENTRY *pfnVMIEntry = NULL;
79 static ULONG ulVRAMAddress = 0;
81 VOID APIENTRY ExitVMan(VOID)
83 if (ulVRAMAddress != 0 && hmodVMan != NULLHANDLE) {
84 pfnVMIEntry(0, VMI_CMD_TERMPROC, NULL, NULL);
85 DosFreeModule(hmodVMan);
88 DosExitList(EXLST_EXIT, (PFNEXITLIST)NULL);
91 static BOOL _vmanInit(void)
95 INITPROCOUT stInitProcOut;
97 if (hmodVMan != NULLHANDLE) /* Already was initialized */
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;
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, "
113 DosFreeModule(hmodVMan);
114 hmodVMan = NULLHANDLE;
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");
124 DosFreeModule(hmodVMan);
125 hmodVMan = NULLHANDLE;
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");
139 static PRECTL _getRectlArray(PVODATA pVOData, ULONG cRects)
143 if (pVOData->cRectl >= cRects)
144 return pVOData->pRectl;
146 pRectl = SDL_realloc(pVOData->pRectl, cRects * sizeof(RECTL));
150 pVOData->pRectl = pRectl;
151 pVOData->cRectl = cRects;
155 static PBLTRECT _getBltRectArray(PVODATA pVOData, ULONG cRects)
159 if (pVOData->cBltRect >= cRects)
160 return pVOData->pBltRect;
162 pBltRect = SDL_realloc(pVOData->pBltRect, cRects * sizeof(BLTRECT));
163 if (pBltRect == NULL)
166 pVOData->pBltRect = pBltRect;
167 pVOData->cBltRect = cRects;
172 static BOOL voQueryInfo(VIDEOOUTPUTINFO *pInfo)
175 GDDMODEINFO sCurModeInfo;
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.");
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;
196 static PVODATA voOpen(void)
203 pVOData = SDL_calloc(1, sizeof(VODATA));
204 if (pVOData == NULL) {
212 static VOID voClose(PVODATA pVOData)
214 if (pVOData->pRectl != NULL)
215 SDL_free(pVOData->pRectl);
217 if (pVOData->pBltRect != NULL)
218 SDL_free(pVOData->pBltRect);
220 voVideoBufFree(pVOData);
223 static BOOL voSetVisibleRegion(PVODATA pVOData, HWND hwnd,
224 SDL_DisplayMode *pSDLDisplayMode,
225 HRGN hrgnShape, BOOL fVisible)
228 BOOL fSuccess = FALSE;
230 hps = WinGetPS(hwnd);
232 if (pVOData->hrgnVisible != NULLHANDLE) {
233 GpiDestroyRegion(hps, pVOData->hrgnVisible);
234 pVOData->hrgnVisible = NULLHANDLE;
238 /* Query visible rectangles */
239 pVOData->hrgnVisible = GpiCreateRegion(hps, 0, NULL);
240 if (pVOData->hrgnVisible == NULLHANDLE) {
241 SDL_SetError("GpiCreateRegion() failed");
243 if (WinQueryVisibleRegion(hwnd, pVOData->hrgnVisible) == RGN_ERROR) {
244 GpiDestroyRegion(hps, pVOData->hrgnVisible);
245 pVOData->hrgnVisible = NULLHANDLE;
247 if (hrgnShape != NULLHANDLE)
248 GpiCombineRegion(hps, pVOData->hrgnVisible, pVOData->hrgnVisible,
249 hrgnShape, CRGN_AND);
254 WinQueryWindowRect(hwnd, &pVOData->rectlWin);
255 WinMapWindowPoints(hwnd, HWND_DESKTOP, (PPOINTL)&pVOData->rectlWin, 2);
257 if (pSDLDisplayMode != NULL) {
258 pVOData->ulScreenHeight = pSDLDisplayMode->h;
259 pVOData->ulScreenBytesPerLine =
260 ((MODEDATA *)pSDLDisplayMode->driverdata)->ulScanLineBytes;
269 static PVOID voVideoBufAlloc(PVODATA pVOData, ULONG ulWidth, ULONG ulHeight,
270 ULONG ulBPP, ULONG fccColorEncoding,
271 PULONG pulScanLineSize)
274 ULONG ulScanLineSize = ulWidth * (ulBPP >> 3);
276 /* Destroy previous buffer */
277 voVideoBufFree(pVOData);
279 if (ulWidth == 0 || ulHeight == 0 || ulBPP == 0)
283 ulScanLineSize = (ulScanLineSize + 3) & ~3; /* 4-byte aligning */
284 *pulScanLineSize = ulScanLineSize;
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);
294 pVOData->ulBPP = ulBPP;
295 pVOData->ulScanLineSize = ulScanLineSize;
296 pVOData->ulWidth = ulWidth;
297 pVOData->ulHeight = ulHeight;
299 return pVOData->pBuffer;
302 static VOID voVideoBufFree(PVODATA pVOData)
306 if (pVOData->pBuffer == NULL)
309 ulRC = DosFreeMem(pVOData->pBuffer);
310 if (ulRC != NO_ERROR) {
311 debug_os2("DosFreeMem(), rc = %u", ulRC);
313 pVOData->pBuffer = NULL;
317 static BOOL voUpdate(PVODATA pVOData, HWND hwnd, SDL_Rect *pSDLRects,
320 PRECTL prectlDst, prectlScan;
324 SDL_Rect stSDLRectDef;
330 BITBLTINFO sBitbltInfo = { 0 };
332 /* RECTL rectlScreenUpdate;*/
334 if (pVOData->pBuffer == NULL)
337 if (pVOData->hrgnVisible == NULLHANDLE)
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;
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;
356 /* List of update rectangles. This is the intersection of requested
357 * rectangles and visible rectangles. */
358 if (cSDLRects == 0) {
359 /* Full update requested */
362 stSDLRectDef.w = bmiSrc.ulWidth;
363 stSDLRectDef.h = bmiSrc.ulHeight;
364 pSDLRects = &stSDLRectDef;
368 /* Make list of destination rectangles (prectlDst) list from the source
370 prectlDst = _getRectlArray(pVOData, cSDLRects);
371 if (prectlDst == NULL) {
372 debug_os2("Not enough memory");
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;
383 hps = WinGetPS(hwnd);
384 if (hps == NULLHANDLE)
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);
392 /* Get rectangles of the region to update */
395 rgnCtl.ulDirection = 1;
396 rgnCtl.crcReturned = 0;
397 GpiQueryRegionRects(hps, hrgnUpdate, NULL, &rgnCtl, NULL);
398 if (rgnCtl.crcReturned == 0) {
399 GpiDestroyRegion(hps, hrgnUpdate);
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);
412 rgnCtl.crc = rgnCtl.crcReturned;
413 rgnCtl.ulDirection = 1;
414 GpiQueryRegionRects(hps, hrgnUpdate, NULL, &rgnCtl, prectlDst);
415 GpiDestroyRegion(hps, hrgnUpdate);
417 cSDLRects = rgnCtl.crcReturned;
419 /* Now cRect/prectlDst is a list of regions in window (update && visible) */
421 /* Make lists for blitting from update regions */
422 pbrDst = _getBltRectArray(pVOData, cSDLRects);
423 if (pbrDst == NULL) {
424 debug_os2("Not enough memory");
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;
439 pptlSrcOrg = (PPOINTL)prectlDst;
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 */
452 rclSrcBounds.xLeft = 0;
453 rclSrcBounds.yBottom = 0;
454 rclSrcBounds.xRight = bmiSrc.ulWidth;
455 rclSrcBounds.yTop = bmiSrc.ulHeight;
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;
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 */
475 sHWReqIn.ulFlags = 0;
476 if (pfnVMIEntry(0, VMI_CMD_REQUESTHW, &sHWReqIn, NULL) != RC_SUCCESS) {
477 debug_os2("pfnVMIEntry(,VMI_CMD_REQUESTHW,,) failed");
481 return sHWReqIn.cScrChangeRects != 0;
484 /* vi: set ts=4 sw=4 expandtab: */