SDL_os2vman.c (14924B)
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: */