
/*
 *@@sourcefile gpih.c:
 *      contains GPI helper functions that are
 *      independent of a single application, i.e. these can be
 *      used w/out the rest of the XFolder source in any PM
 *      program.
 *
 *      Function prefixes (new with V0.81):
 *      --  gpih*   GPI helper functions
 *
 *@@include #include <os2.h>
 *@@include #include "gpih.h"
 */

/*
 *      Copyright (C) 1997-99 Ulrich M”ller.
 *      This file is part of the XFolder source package.
 *      XFolder is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published
 *      by the Free Software Foundation, in version 2 as it comes in the
 *      "COPYING" file of the XFolder main distribution.
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 */

#define INCL_DOSDEVIOCTL
#define INCL_DOS
#define INCL_DOSERRORS
#define INCL_WIN
#define INCL_GPI
#include <os2.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "winh.h"
#include "gpih.h"

// array for querying device capabilities (gpihQueryDisplayCaps)
LONG            DisplayCaps[CAPS_DEVICE_POLYSET_POINTS] = {0};
BOOL            fCapsQueried = FALSE;

/*
 *@@ gpihQueryDisplayCaps:
 *      this returns certain device capabilities of
 *      the Display device. ulIndex must be one of
 *      the indices as described in DevQueryCaps.
 *      This function will load all the device capabilities
 *      only once and reuse them afterwards.
 */

ULONG gpihQueryDisplayCaps(ULONG ulIndex)
{
    if (!fCapsQueried)
    {
        HPS hps = WinGetScreenPS(HWND_DESKTOP);
        HDC hdc = GpiQueryDevice(hps);
        DevQueryCaps(hdc, 0, CAPS_DEVICE_POLYSET_POINTS, &DisplayCaps[0]);
    }

    return (DisplayCaps[ulIndex]);
}

/*
 *@@ gpihQueryLineSpacing:
 *      this returns the optimal line spacing for text
 *      output with the current HPS; this is computed
 *      by evaluating those incredible FONTMETRICS
 */

LONG gpihQueryLineSpacing(HPS hps,
                          PSZ pszText)      // in:  text to output
{
    FONTMETRICS fm;
    POINTL aptlText[TXTBOX_COUNT];
    GpiQueryTextBox(hps, strlen(pszText), pszText,
             TXTBOX_COUNT, (PPOINTL)&aptlText);

    if (GpiQueryFontMetrics(hps, sizeof(FONTMETRICS), &fm))
        return ( (  fm.lMaxBaselineExt     // max vertical font space
                   +fm.lExternalLeading)    // space advised by font designer
                 // * 12 / 10
               );
    else
        return (15);
}

/*
 *@@ gpihDraw3DFrame:
 *      this draws a rectl in 3D style with a given line width;
 *      if (fRaised), the 3D line is given a "raised" look,
 *      "sunken" otherwise
 */

VOID gpihDraw3DFrame(HPS hps,
                     PRECTL prcl,       // in:  rectangle
                     USHORT usWidth,    // in:  line width (>= 1)
                     BOOL fRaised)      // in:  TRUE for "raised", FALSE "sunken"
{
    RECTL rcl2 = *prcl;
    LONG lColorLeft, lColorRight;
    USHORT us;
    POINTL ptl1;

    if (fRaised) {
        lColorLeft = WinQuerySysColor(HWND_DESKTOP, SYSCLR_BUTTONLIGHT, 0);
        lColorRight = WinQuerySysColor(HWND_DESKTOP, SYSCLR_BUTTONDARK, 0);
    } else {
        lColorLeft = WinQuerySysColor(HWND_DESKTOP, SYSCLR_BUTTONDARK, 0);
        lColorRight = WinQuerySysColor(HWND_DESKTOP, SYSCLR_BUTTONLIGHT, 0);
    }

    for (us = 0; us < usWidth; us++)
    {
        GpiSetColor(hps, lColorLeft);
        ptl1.x = rcl2.xLeft;
        ptl1.y = rcl2.yBottom;
        GpiMove(hps, &ptl1);
        ptl1.y = rcl2.yTop-1;
        GpiLine(hps, &ptl1);
        ptl1.x = rcl2.xRight-1;
        GpiLine(hps, &ptl1);
        GpiSetColor(hps, lColorRight);
        ptl1.y = rcl2.yBottom;
        GpiLine(hps, &ptl1);
        ptl1.x = rcl2.xLeft;
        GpiLine(hps, &ptl1);

        rcl2.xLeft++;
        rcl2.yBottom++;
        rcl2.xRight--;
        rcl2.yTop--;
    }
}


/*
 *@@ gpihCreateMemPS:
 *      creates a memory device context and presentation space so
 *      they are compatible with the screen device context and
 *      presentation space. These are stored in *hdcMem and *hpsMem.
 *      You must free these with GpiDestroyPS(hpsMem) and
 *      DevCloseDC(hdcMem) when you don't need these any more.
 *      Returns FALSE upon errors.
 */

BOOL gpihCreateMemPS(HAB hab,        // in: anchor block
                    HDC *hdcMem,     // out: memory DC
                    HPS *hpsMem)     // out: memory PS
{
    BOOL brc = FALSE;
    PSZ pszData[4] = { "Display", NULL, NULL, NULL };
    SIZEL sizlPage = {0, 0};

    if (*hdcMem = DevOpenDC(hab,
                            OD_MEMORY,
                            "*", 4,
                            (PDEVOPENDATA)pszData,
                            NULLHANDLE))
        if (*hpsMem = GpiCreatePS(hab, *hdcMem,
                                    &sizlPage,
                                    PU_PELS | GPIA_ASSOC | GPIT_MICRO))
            brc = TRUE;
        else
            // error: close memory DC again
            DevCloseDC(*hdcMem);

    return (brc);
}

/*
 *@@ gpihCreateBitmap:
 *      creates a new bitmap for a given memory PS.
 *      This new bitmap has the size specified in *prcl.
 *      Note that the bitmap is _not_ selected in the mem PS.
 *      Returns the bitmap handle or NULLHANDLE upon errors.
 */

HBITMAP gpihCreateBitmap(HPS hpsMem,        // memory DC
                         PRECTL prcl)       // size of bitmap
{
    HBITMAP hbm = NULLHANDLE;
    LONG alData[2];
    BITMAPINFOHEADER2 bmp;
    PBITMAPINFO2 pbmi = NULL;

    // determine the device's plane/bit-count format.
    if (GpiQueryDeviceBitmapFormats(hpsMem, 2, alData))
    {
        // set up the BITMAPINFOHEADER2 and BITMAPINFO2 structures
        bmp.cbFix = (ULONG)sizeof(BITMAPINFOHEADER2);
        bmp.cx = (prcl->xRight - prcl->xLeft);
        bmp.cy = (prcl->yTop - prcl->yBottom);
        bmp.cPlanes = alData[0];
        bmp.cBitCount = alData[1];
        bmp.ulCompression = BCA_UNCOMP;
        bmp.cbImage = (((bmp.cx *
            (1 << bmp.cPlanes) * (1 << bmp.cBitCount)) + 31) / 32) * bmp.cy;
        bmp.cxResolution = 70;
        bmp.cyResolution = 70;
        bmp.cclrUsed = 2;
        bmp.cclrImportant = 0;
        bmp.usUnits = BRU_METRIC;
        bmp.usReserved = 0;
        bmp.usRecording = BRA_BOTTOMUP;
        bmp.usRendering = BRH_NOTHALFTONED;
        bmp.cSize1 = 0;
        bmp.cSize2 = 0;
        bmp.ulColorEncoding = BCE_RGB;
        bmp.ulIdentifier = 0;

        // allocate memory for info header
        if (DosAllocMem((PPVOID)&pbmi, sizeof(BITMAPINFO2) +
            (sizeof(RGB2) * (1 << bmp.cPlanes) * (1 << bmp.cBitCount)),
            PAG_COMMIT | PAG_READ | PAG_WRITE) == NO_ERROR)
        {
            pbmi->cbFix = bmp.cbFix;
            pbmi->cx = bmp.cx;
            pbmi->cy = bmp.cy;
            pbmi->cPlanes = bmp.cPlanes;
            pbmi->cBitCount = bmp.cBitCount;
            pbmi->ulCompression = BCA_UNCOMP;
            pbmi->cbImage = ((bmp.cx+31)/32) * bmp.cy;
            pbmi->cxResolution = 70;
            pbmi->cyResolution = 70;
            pbmi->cclrUsed = 2;
            pbmi->cclrImportant = 0;
            pbmi->usUnits = BRU_METRIC;
            pbmi->usReserved = 0;
            pbmi->usRecording = BRA_BOTTOMUP;
            pbmi->usRendering = BRH_NOTHALFTONED;
            pbmi->cSize1 = 0;
            pbmi->cSize2 = 0;
            pbmi->ulColorEncoding = BCE_RGB;
            pbmi->ulIdentifier = 0;

            // Create a bit map that is compatible with the display.
            hbm = GpiCreateBitmap(hpsMem, &bmp, FALSE, NULL, pbmi);

            // free the memory we allocated previously; GPI has
            // allocated all the resources it needs itself, so
            // we can release this
            DosFreeMem(pbmi);
        }
    }

    return (hbm);
}

/*
 *@@ gpihCreateBmpFromPS:
 *      this creates a new bitmap, which is compatible with the
 *      device associated with hpsScreen, and then copies the
 *      rectangle prcl into it; returns the handle of the bitmap,
 *      which can then be used for WinDrawBitmap and such
 */

HBITMAP gpihCreateBmpFromPS(HAB hab,        // in: anchor block
                            HPS hpsScreen,  // in: screen PS to copy from
                            PRECTL prcl)    // in: rectangle to copy
{

    /* To copy an image from a display screen to a bit map:
      1. Associate the memory device context with a presentation space.
      2. Create a bit map.
      3. Select the bit map into the memory device context by calling GpiSetBitmap.
      4. Determine the location (in device coordinates) of the image.
      5. Call GpiBitBlt and copy the image to the bit map. */

    HDC hdcMem;
    // PSZ pszData[4] = { "Display", NULL, NULL, NULL };
    HPS hpsMem;
    HBITMAP hbm = NULLHANDLE;
    POINTL aptl[3];

    if (gpihCreateMemPS(hab, &hdcMem, &hpsMem))
    {
        if (hbm = gpihCreateBitmap(hpsMem, prcl))
        {
            // Associate the bit map and the memory presentation space.
            if (GpiSetBitmap(hpsMem, hbm)
                    != HBM_ERROR)
            {
                // Copy the screen to the bit map.
                aptl[0].x = 0;              // lower-left corner of destination rectangle
                aptl[0].y = 0;
                aptl[1].x = prcl->xRight;   // upper-right corner for both
                aptl[1].y = prcl->yTop;
                aptl[2].x = prcl->xLeft;    // lower-left corner of source rectangle
                aptl[2].y = prcl->yBottom;

                if (GpiBitBlt(hpsMem, hpsScreen,
                        sizeof(aptl) / sizeof(POINTL), // Number of points in aptl
                        aptl,
                        ROP_SRCCOPY,
                        BBO_IGNORE) == GPI_ERROR)
                {
                    // error during bitblt:
                    GpiDeleteBitmap(hbm);
                    hbm = NULLHANDLE; // for return code
                }
            } else {
                // error selecting bitmap for hpsMem:
                GpiDeleteBitmap(hbm);
                hbm = NULLHANDLE; // for return code
            }
        }

        GpiDestroyPS(hpsMem);
        DevCloseDC(hdcMem);
    } // end if (hdcMem = DevOpenDC())

    return (hbm);
}

/*
 *@@ gpihLoadBitmapFile:
 *      this loads the specified bitmap file into
 *      the given HPS. Note that the bitmap is _not_
 *      selected into the HPS yet.
 *
 *      This function can only handle OS/2 1.3 bitmaps.
 *      Returns the new bitmap handle or NULL upon errors.
 *      In the latter case, *pulError is set to one of
 *      the following:
 *      --  -1:      file not found
 *      --  -2:      memory allocation failed
 *      --  -3:      the bitmap data could not be loaded
 *      --  -4:      file format not recognized
 *      --  -5:      GpiCreateBitmap error
 */

HBITMAP gpihLoadBitmapFile(HPS hps,             // in: HPS for bmp
                           PSZ pszBmpFile,      // in: bitmap filename
                           PULONG pulError)     // out: set to error
{
    HBITMAP hbm;
    PBITMAPFILEHEADER2  pbfh;

    struct stat st;
    PUCHAR      pBmpData;
    FILE        *BmpFile;
    PUCHAR      fn = "loadData";

    if (stat(pszBmpFile, &st) == 0)
    {

        if ((pBmpData = malloc(st.st_size)))
        {
            // open bmp file
            if ((BmpFile = fopen(pszBmpFile, "rb")))
            {
                // read bmp data
                fread(pBmpData, 1, st.st_size, BmpFile);
                fclose(BmpFile);

                // check bitmap magic codes
                if (pBmpData[0] == 'B' && pBmpData[1] == 'M')
                {
                    pbfh = (PBITMAPFILEHEADER2)pBmpData;
                    hbm = GpiCreateBitmap(hps,
                                &pbfh->bmp2,
                                CBM_INIT,
                                (pBmpData + pbfh->offBits),
                                (PBITMAPINFO2)&pbfh->bmp2);

                    if (hbm == NULLHANDLE)
                    {
                        if (pulError)
                        *pulError = -5;
                    }
                }
                else if (pulError)
                        *pulError = -4;

            }
            else if (pulError)
                    *pulError = -3;

            free(pBmpData);
        }
        else if (pulError)
                *pulError = -2;
    }
    else if (pulError)
            *pulError = -1;

    return (hbm);
}


