485 lines
15 KiB
C
Executable File
485 lines
15 KiB
C
Executable File
/*
|
|
Simple DirectMedia Layer
|
|
Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
*/
|
|
#include "../../SDL_internal.h"
|
|
#include "../SDL_sysvideo.h"
|
|
|
|
#define INCL_DOSERRORS
|
|
#define INCL_DOSPROCESS
|
|
#define INCL_DOSMODULEMGR
|
|
#define INCL_WIN
|
|
#define INCL_GPI
|
|
#define INCL_GPIBITMAPS /* GPI bit map functions */
|
|
#include <os2.h>
|
|
#include "SDL_os2output.h"
|
|
#include "SDL_os2video.h"
|
|
|
|
#include "SDL_gradd.h"
|
|
|
|
typedef struct _VODATA {
|
|
PVOID pBuffer;
|
|
HRGN hrgnVisible;
|
|
ULONG ulBPP;
|
|
ULONG ulScanLineSize;
|
|
ULONG ulWidth;
|
|
ULONG ulHeight;
|
|
ULONG ulScreenHeight;
|
|
ULONG ulScreenBytesPerLine;
|
|
RECTL rectlWin;
|
|
|
|
PRECTL pRectl;
|
|
ULONG cRectl;
|
|
PBLTRECT pBltRect;
|
|
ULONG cBltRect;
|
|
} VODATA;
|
|
|
|
static BOOL voQueryInfo(VIDEOOUTPUTINFO *pInfo);
|
|
static PVODATA voOpen();
|
|
static VOID voClose(PVODATA pVOData);
|
|
static BOOL voSetVisibleRegion(PVODATA pVOData, HWND hwnd,
|
|
SDL_DisplayMode *pSDLDisplayMode,
|
|
HRGN hrgnShape, BOOL fVisible);
|
|
static PVOID voVideoBufAlloc(PVODATA pVOData, ULONG ulWidth, ULONG ulHeight,
|
|
ULONG ulBPP, ULONG fccColorEncoding,
|
|
PULONG pulScanLineSize);
|
|
static VOID voVideoBufFree(PVODATA pVOData);
|
|
static BOOL voUpdate(PVODATA pVOData, HWND hwnd, SDL_Rect *pSDLRects,
|
|
ULONG cSDLRects);
|
|
|
|
OS2VIDEOOUTPUT voVMan = {
|
|
voQueryInfo,
|
|
voOpen,
|
|
voClose,
|
|
voSetVisibleRegion,
|
|
voVideoBufAlloc,
|
|
voVideoBufFree,
|
|
voUpdate
|
|
};
|
|
|
|
|
|
static HMODULE hmodVMan = NULLHANDLE;
|
|
static FNVMIENTRY *pfnVMIEntry = NULL;
|
|
static ULONG ulVRAMAddress = 0;
|
|
|
|
VOID APIENTRY ExitVMan(VOID)
|
|
{
|
|
if (ulVRAMAddress != 0 && hmodVMan != NULLHANDLE) {
|
|
pfnVMIEntry(0, VMI_CMD_TERMPROC, NULL, NULL);
|
|
DosFreeModule(hmodVMan);
|
|
}
|
|
|
|
DosExitList(EXLST_EXIT, (PFNEXITLIST)NULL);
|
|
}
|
|
|
|
static BOOL _vmanInit(void)
|
|
{
|
|
ULONG ulRC;
|
|
CHAR acBuf[255];
|
|
INITPROCOUT stInitProcOut;
|
|
|
|
if (hmodVMan != NULLHANDLE) /* Already was initialized */
|
|
return TRUE;
|
|
|
|
/* Load vman.dll */
|
|
ulRC = DosLoadModule(acBuf, sizeof(acBuf), "VMAN", &hmodVMan);
|
|
if (ulRC != NO_ERROR) {
|
|
debug_os2("Could not load VMAN.DLL, rc = %u : %s", ulRC, acBuf);
|
|
hmodVMan = NULLHANDLE;
|
|
return FALSE;
|
|
}
|
|
|
|
/* Get VMIEntry */
|
|
ulRC = DosQueryProcAddr(hmodVMan, 0L, "VMIEntry", (PFN *)&pfnVMIEntry);
|
|
if (ulRC != NO_ERROR) {
|
|
debug_os2("Could not query address of pfnVMIEntry func. of VMAN.DLL, "
|
|
"rc = %u", ulRC);
|
|
DosFreeModule(hmodVMan);
|
|
hmodVMan = NULLHANDLE;
|
|
return FALSE;
|
|
}
|
|
|
|
/* VMAN initialization */
|
|
stInitProcOut.ulLength = sizeof(stInitProcOut);
|
|
ulRC = pfnVMIEntry(0, VMI_CMD_INITPROC, NULL, &stInitProcOut);
|
|
if (ulRC != RC_SUCCESS) {
|
|
debug_os2("Could not initialize VMAN for this process");
|
|
pfnVMIEntry = NULL;
|
|
DosFreeModule(hmodVMan);
|
|
hmodVMan = NULLHANDLE;
|
|
return FALSE;
|
|
}
|
|
|
|
/* Store video memory virtual address */
|
|
ulVRAMAddress = stInitProcOut.ulVRAMVirt;
|
|
/* We use exit list for VMI_CMD_TERMPROC */
|
|
if (DosExitList(EXLST_ADD | 0x00001000, (PFNEXITLIST)ExitVMan) != NO_ERROR) {
|
|
debug_os2("DosExitList() failed");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static PRECTL _getRectlArray(PVODATA pVOData, ULONG cRects)
|
|
{
|
|
PRECTL pRectl;
|
|
|
|
if (pVOData->cRectl >= cRects)
|
|
return pVOData->pRectl;
|
|
|
|
pRectl = SDL_realloc(pVOData->pRectl, cRects * sizeof(RECTL));
|
|
if (pRectl == NULL)
|
|
return NULL;
|
|
|
|
pVOData->pRectl = pRectl;
|
|
pVOData->cRectl = cRects;
|
|
return pRectl;
|
|
}
|
|
|
|
static PBLTRECT _getBltRectArray(PVODATA pVOData, ULONG cRects)
|
|
{
|
|
PBLTRECT pBltRect;
|
|
|
|
if (pVOData->cBltRect >= cRects)
|
|
return pVOData->pBltRect;
|
|
|
|
pBltRect = SDL_realloc(pVOData->pBltRect, cRects * sizeof(BLTRECT));
|
|
if (pBltRect == NULL)
|
|
return NULL;
|
|
|
|
pVOData->pBltRect = pBltRect;
|
|
pVOData->cBltRect = cRects;
|
|
return pBltRect;
|
|
}
|
|
|
|
|
|
static BOOL voQueryInfo(VIDEOOUTPUTINFO *pInfo)
|
|
{
|
|
ULONG ulRC;
|
|
GDDMODEINFO sCurModeInfo;
|
|
|
|
if (!_vmanInit())
|
|
return FALSE;
|
|
|
|
/* Query current (desktop) mode */
|
|
ulRC = pfnVMIEntry(0, VMI_CMD_QUERYCURRENTMODE, NULL, &sCurModeInfo);
|
|
if (ulRC != RC_SUCCESS) {
|
|
debug_os2("Could not query desktop video mode.");
|
|
return FALSE;
|
|
}
|
|
|
|
pInfo->ulBPP = sCurModeInfo.ulBpp;
|
|
pInfo->ulHorizResolution = sCurModeInfo.ulHorizResolution;
|
|
pInfo->ulVertResolution = sCurModeInfo.ulVertResolution;
|
|
pInfo->ulScanLineSize = sCurModeInfo.ulScanLineSize;
|
|
pInfo->fccColorEncoding = sCurModeInfo.fccColorEncoding;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static PVODATA voOpen(void)
|
|
{
|
|
PVODATA pVOData;
|
|
|
|
if (!_vmanInit())
|
|
return NULL;
|
|
|
|
pVOData = SDL_calloc(1, sizeof(VODATA));
|
|
if (pVOData == NULL) {
|
|
SDL_OutOfMemory();
|
|
return NULL;
|
|
}
|
|
|
|
return pVOData;
|
|
}
|
|
|
|
static VOID voClose(PVODATA pVOData)
|
|
{
|
|
if (pVOData->pRectl != NULL)
|
|
SDL_free(pVOData->pRectl);
|
|
|
|
if (pVOData->pBltRect != NULL)
|
|
SDL_free(pVOData->pBltRect);
|
|
|
|
voVideoBufFree(pVOData);
|
|
}
|
|
|
|
static BOOL voSetVisibleRegion(PVODATA pVOData, HWND hwnd,
|
|
SDL_DisplayMode *pSDLDisplayMode,
|
|
HRGN hrgnShape, BOOL fVisible)
|
|
{
|
|
HPS hps;
|
|
BOOL fSuccess = FALSE;
|
|
|
|
hps = WinGetPS(hwnd);
|
|
|
|
if (pVOData->hrgnVisible != NULLHANDLE) {
|
|
GpiDestroyRegion(hps, pVOData->hrgnVisible);
|
|
pVOData->hrgnVisible = NULLHANDLE;
|
|
}
|
|
|
|
if (fVisible) {
|
|
/* Query visible rectangles */
|
|
pVOData->hrgnVisible = GpiCreateRegion(hps, 0, NULL);
|
|
if (pVOData->hrgnVisible == NULLHANDLE) {
|
|
SDL_SetError("GpiCreateRegion() failed");
|
|
} else {
|
|
if (WinQueryVisibleRegion(hwnd, pVOData->hrgnVisible) == RGN_ERROR) {
|
|
GpiDestroyRegion(hps, pVOData->hrgnVisible);
|
|
pVOData->hrgnVisible = NULLHANDLE;
|
|
} else {
|
|
if (hrgnShape != NULLHANDLE)
|
|
GpiCombineRegion(hps, pVOData->hrgnVisible, pVOData->hrgnVisible,
|
|
hrgnShape, CRGN_AND);
|
|
fSuccess = TRUE;
|
|
}
|
|
}
|
|
|
|
WinQueryWindowRect(hwnd, &pVOData->rectlWin);
|
|
WinMapWindowPoints(hwnd, HWND_DESKTOP, (PPOINTL)&pVOData->rectlWin, 2);
|
|
|
|
if (pSDLDisplayMode != NULL) {
|
|
pVOData->ulScreenHeight = pSDLDisplayMode->h;
|
|
pVOData->ulScreenBytesPerLine =
|
|
((MODEDATA *)pSDLDisplayMode->driverdata)->ulScanLineBytes;
|
|
}
|
|
}
|
|
|
|
WinReleasePS(hps);
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
static PVOID voVideoBufAlloc(PVODATA pVOData, ULONG ulWidth, ULONG ulHeight,
|
|
ULONG ulBPP, ULONG fccColorEncoding,
|
|
PULONG pulScanLineSize)
|
|
{
|
|
ULONG ulRC;
|
|
ULONG ulScanLineSize = ulWidth * (ulBPP >> 3);
|
|
|
|
/* Destroy previous buffer */
|
|
voVideoBufFree(pVOData);
|
|
|
|
if (ulWidth == 0 || ulHeight == 0 || ulBPP == 0)
|
|
return NULL;
|
|
|
|
/* Bytes per line */
|
|
ulScanLineSize = (ulScanLineSize + 3) & ~3; /* 4-byte aligning */
|
|
*pulScanLineSize = ulScanLineSize;
|
|
|
|
ulRC = DosAllocMem(&pVOData->pBuffer,
|
|
(ulHeight * ulScanLineSize) + sizeof(ULONG),
|
|
PAG_COMMIT | PAG_EXECUTE | PAG_READ | PAG_WRITE);
|
|
if (ulRC != NO_ERROR) {
|
|
debug_os2("DosAllocMem(), rc = %u", ulRC);
|
|
return NULL;
|
|
}
|
|
|
|
pVOData->ulBPP = ulBPP;
|
|
pVOData->ulScanLineSize = ulScanLineSize;
|
|
pVOData->ulWidth = ulWidth;
|
|
pVOData->ulHeight = ulHeight;
|
|
|
|
return pVOData->pBuffer;
|
|
}
|
|
|
|
static VOID voVideoBufFree(PVODATA pVOData)
|
|
{
|
|
ULONG ulRC;
|
|
|
|
if (pVOData->pBuffer == NULL)
|
|
return;
|
|
|
|
ulRC = DosFreeMem(pVOData->pBuffer);
|
|
if (ulRC != NO_ERROR) {
|
|
debug_os2("DosFreeMem(), rc = %u", ulRC);
|
|
} else {
|
|
pVOData->pBuffer = NULL;
|
|
}
|
|
}
|
|
|
|
static BOOL voUpdate(PVODATA pVOData, HWND hwnd, SDL_Rect *pSDLRects,
|
|
ULONG cSDLRects)
|
|
{
|
|
PRECTL prectlDst, prectlScan;
|
|
HPS hps;
|
|
HRGN hrgnUpdate;
|
|
RGNRECT rgnCtl;
|
|
SDL_Rect stSDLRectDef;
|
|
BMAPINFO bmiSrc;
|
|
BMAPINFO bmiDst;
|
|
PPOINTL pptlSrcOrg;
|
|
PBLTRECT pbrDst;
|
|
HWREQIN sHWReqIn;
|
|
BITBLTINFO sBitbltInfo = { 0 };
|
|
ULONG ulIdx;
|
|
/* RECTL rectlScreenUpdate;*/
|
|
|
|
if (pVOData->pBuffer == NULL)
|
|
return FALSE;
|
|
|
|
if (pVOData->hrgnVisible == NULLHANDLE)
|
|
return TRUE;
|
|
|
|
bmiSrc.ulLength = sizeof(BMAPINFO);
|
|
bmiSrc.ulType = BMAP_MEMORY;
|
|
bmiSrc.ulWidth = pVOData->ulWidth;
|
|
bmiSrc.ulHeight = pVOData->ulHeight;
|
|
bmiSrc.ulBpp = pVOData->ulBPP;
|
|
bmiSrc.ulBytesPerLine = pVOData->ulScanLineSize;
|
|
bmiSrc.pBits = (PBYTE)pVOData->pBuffer;
|
|
|
|
bmiDst.ulLength = sizeof(BMAPINFO);
|
|
bmiDst.ulType = BMAP_VRAM;
|
|
bmiDst.pBits = (PBYTE)ulVRAMAddress;
|
|
bmiDst.ulWidth = bmiSrc.ulWidth;
|
|
bmiDst.ulHeight = bmiSrc.ulHeight;
|
|
bmiDst.ulBpp = bmiSrc.ulBpp;
|
|
bmiDst.ulBytesPerLine = pVOData->ulScreenBytesPerLine;
|
|
|
|
/* List of update rectangles. This is the intersection of requested
|
|
* rectangles and visible rectangles. */
|
|
if (cSDLRects == 0) {
|
|
/* Full update requested */
|
|
stSDLRectDef.x = 0;
|
|
stSDLRectDef.y = 0;
|
|
stSDLRectDef.w = bmiSrc.ulWidth;
|
|
stSDLRectDef.h = bmiSrc.ulHeight;
|
|
pSDLRects = &stSDLRectDef;
|
|
cSDLRects = 1;
|
|
}
|
|
|
|
/* Make list of destination rectangles (prectlDst) list from the source
|
|
* list (prectl). */
|
|
prectlDst = _getRectlArray(pVOData, cSDLRects);
|
|
if (prectlDst == NULL) {
|
|
debug_os2("Not enough memory");
|
|
return FALSE;
|
|
}
|
|
prectlScan = prectlDst;
|
|
for (ulIdx = 0; ulIdx < cSDLRects; ulIdx++, pSDLRects++, prectlScan++) {
|
|
prectlScan->xLeft = pSDLRects->x;
|
|
prectlScan->yTop = pVOData->ulHeight - pSDLRects->y;
|
|
prectlScan->xRight = prectlScan->xLeft + pSDLRects->w;
|
|
prectlScan->yBottom = prectlScan->yTop - pSDLRects->h;
|
|
}
|
|
|
|
hps = WinGetPS(hwnd);
|
|
if (hps == NULLHANDLE)
|
|
return FALSE;
|
|
|
|
/* Make destination region to update */
|
|
hrgnUpdate = GpiCreateRegion(hps, cSDLRects, prectlDst);
|
|
/* "AND" on visible and destination regions, result is region to update */
|
|
GpiCombineRegion(hps, hrgnUpdate, hrgnUpdate, pVOData->hrgnVisible, CRGN_AND);
|
|
|
|
/* Get rectangles of the region to update */
|
|
rgnCtl.ircStart = 1;
|
|
rgnCtl.crc = 0;
|
|
rgnCtl.ulDirection = 1;
|
|
rgnCtl.crcReturned = 0;
|
|
GpiQueryRegionRects(hps, hrgnUpdate, NULL, &rgnCtl, NULL);
|
|
if (rgnCtl.crcReturned == 0) {
|
|
GpiDestroyRegion(hps, hrgnUpdate);
|
|
WinReleasePS(hps);
|
|
return TRUE;
|
|
}
|
|
/* We don't need prectlDst, use it again to store update regions */
|
|
prectlDst = _getRectlArray(pVOData, rgnCtl.crcReturned);
|
|
if (prectlDst == NULL) {
|
|
debug_os2("Not enough memory");
|
|
GpiDestroyRegion(hps, hrgnUpdate);
|
|
WinReleasePS(hps);
|
|
return FALSE;
|
|
}
|
|
rgnCtl.ircStart = 1;
|
|
rgnCtl.crc = rgnCtl.crcReturned;
|
|
rgnCtl.ulDirection = 1;
|
|
GpiQueryRegionRects(hps, hrgnUpdate, NULL, &rgnCtl, prectlDst);
|
|
GpiDestroyRegion(hps, hrgnUpdate);
|
|
WinReleasePS(hps);
|
|
cSDLRects = rgnCtl.crcReturned;
|
|
|
|
/* Now cRect/prectlDst is a list of regions in window (update && visible) */
|
|
|
|
/* Make lists for blitting from update regions */
|
|
pbrDst = _getBltRectArray(pVOData, cSDLRects);
|
|
if (pbrDst == NULL) {
|
|
debug_os2("Not enough memory");
|
|
return FALSE;
|
|
}
|
|
|
|
prectlScan = prectlDst;
|
|
pptlSrcOrg = (PPOINTL)prectlDst; /* Yes, this memory block will be used again */
|
|
for (ulIdx = 0; ulIdx < cSDLRects; ulIdx++, prectlScan++, pptlSrcOrg++) {
|
|
pbrDst[ulIdx].ulXOrg = pVOData->rectlWin.xLeft + prectlScan->xLeft;
|
|
pbrDst[ulIdx].ulYOrg = pVOData->ulScreenHeight -
|
|
(pVOData->rectlWin.yBottom + prectlScan->yTop);
|
|
pbrDst[ulIdx].ulXExt = prectlScan->xRight - prectlScan->xLeft;
|
|
pbrDst[ulIdx].ulYExt = prectlScan->yTop - prectlScan->yBottom;
|
|
pptlSrcOrg->x = prectlScan->xLeft;
|
|
pptlSrcOrg->y = bmiSrc.ulHeight - prectlScan->yTop;
|
|
}
|
|
pptlSrcOrg = (PPOINTL)prectlDst;
|
|
|
|
/* Request HW */
|
|
sHWReqIn.ulLength = sizeof(HWREQIN);
|
|
sHWReqIn.ulFlags = REQUEST_HW;
|
|
sHWReqIn.cScrChangeRects = 1;
|
|
sHWReqIn.arectlScreen = &pVOData->rectlWin;
|
|
if (pfnVMIEntry(0, VMI_CMD_REQUESTHW, &sHWReqIn, NULL) != RC_SUCCESS) {
|
|
debug_os2("pfnVMIEntry(,VMI_CMD_REQUESTHW,,) failed");
|
|
sHWReqIn.cScrChangeRects = 0; /* for fail signal only */
|
|
} else {
|
|
RECTL rclSrcBounds;
|
|
|
|
rclSrcBounds.xLeft = 0;
|
|
rclSrcBounds.yBottom = 0;
|
|
rclSrcBounds.xRight = bmiSrc.ulWidth;
|
|
rclSrcBounds.yTop = bmiSrc.ulHeight;
|
|
|
|
sBitbltInfo.ulLength = sizeof(BITBLTINFO);
|
|
sBitbltInfo.ulBltFlags = BF_DEFAULT_STATE | BF_ROP_INCL_SRC | BF_PAT_HOLLOW;
|
|
sBitbltInfo.cBlits = cSDLRects;
|
|
sBitbltInfo.ulROP = ROP_SRCCOPY;
|
|
sBitbltInfo.pSrcBmapInfo = &bmiSrc;
|
|
sBitbltInfo.pDstBmapInfo = &bmiDst;
|
|
sBitbltInfo.prclSrcBounds = &rclSrcBounds;
|
|
sBitbltInfo.prclDstBounds = &pVOData->rectlWin;
|
|
sBitbltInfo.aptlSrcOrg = pptlSrcOrg;
|
|
sBitbltInfo.abrDst = pbrDst;
|
|
|
|
/* Screen update */
|
|
if (pfnVMIEntry(0, VMI_CMD_BITBLT, &sBitbltInfo, NULL) != RC_SUCCESS) {
|
|
debug_os2("pfnVMIEntry(,VMI_CMD_BITBLT,,) failed");
|
|
sHWReqIn.cScrChangeRects = 0; /* for fail signal only */
|
|
}
|
|
|
|
/* Release HW */
|
|
sHWReqIn.ulFlags = 0;
|
|
if (pfnVMIEntry(0, VMI_CMD_REQUESTHW, &sHWReqIn, NULL) != RC_SUCCESS) {
|
|
debug_os2("pfnVMIEntry(,VMI_CMD_REQUESTHW,,) failed");
|
|
}
|
|
}
|
|
|
|
return sHWReqIn.cScrChangeRects != 0;
|
|
}
|
|
|
|
/* vi: set ts=4 sw=4 expandtab: */
|