Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /* vim: set sw=4 sts=4 et cin: */
  2. /* cairo - a vector graphics library with display and print output
  3.  *
  4.  * Copyright (c) 2005-2006 netlabs.org
  5.  *
  6.  * This library is free software; you can redistribute it and/or
  7.  * modify it either under the terms of the GNU Lesser General Public
  8.  * License version 2.1 as published by the Free Software Foundation
  9.  * (the "LGPL") or, at your option, under the terms of the Mozilla
  10.  * Public License Version 1.1 (the "MPL"). If you do not alter this
  11.  * notice, a recipient may use your version of this file under either
  12.  * the MPL or the LGPL.
  13.  *
  14.  * You should have received a copy of the LGPL along with this library
  15.  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
  16.  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
  17.  * You should have received a copy of the MPL along with this library
  18.  * in the file COPYING-MPL-1.1
  19.  *
  20.  * The contents of this file are subject to the Mozilla Public License
  21.  * Version 1.1 (the "License"); you may not use this file except in
  22.  * compliance with the License. You may obtain a copy of the License at
  23.  * http://www.mozilla.org/MPL/
  24.  *
  25.  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
  26.  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
  27.  * the specific language governing rights and limitations.
  28.  *
  29.  * The Original Code is the cairo graphics library.
  30.  *
  31.  * The Initial Developer of the Original Code is
  32.  *     Doodle <doodle@scenergy.dfmk.hu>
  33.  *
  34.  * Contributor(s):
  35.  *     Peter Weilbacher <mozilla@Weilbacher.org>
  36.  *     Rich Walsh <dragtext@e-vertise.com>
  37.  */
  38.  
  39. #include "cairoint.h"
  40.  
  41. #include "cairo-os2-private.h"
  42. #include "cairo-default-context-private.h"
  43. #include "cairo-error-private.h"
  44. #include "cairo-surface-fallback-private.h"
  45. #include "cairo-image-surface-private.h"
  46.  
  47. #if CAIRO_HAS_FC_FONT
  48. #include <fontconfig/fontconfig.h>
  49. #endif
  50.  
  51. #include <float.h>
  52. #ifdef BUILD_CAIRO_DLL
  53. # include "cairo-os2.h"
  54. # ifndef __WATCOMC__
  55. #  include <emx/startup.h>
  56. # endif
  57. #endif
  58.  
  59. /*
  60.  * Here comes the extra API for the OS/2 platform. Currently it consists
  61.  * of two extra functions, the cairo_os2_init() and the
  62.  * cairo_os2_fini(). Both of them are called automatically if
  63.  * Cairo is compiled to be a DLL file, but you have to call them before
  64.  * using the Cairo API if you link to Cairo statically!
  65.  *
  66.  * You'll also find the code in here which deals with DLL initialization
  67.  * and termination, if the code is built to be a DLL.
  68.  * (if BUILD_CAIRO_DLL is defined)
  69.  */
  70.  
  71. /* Initialization counter: */
  72. static int cairo_os2_initialization_count = 0;
  73.  
  74. static inline void
  75. DisableFPUException (void)
  76. {
  77.     unsigned short usCW;
  78.  
  79.     /* Some OS/2 PM API calls modify the FPU Control Word,
  80.      * but forget to restore it.
  81.      *
  82.      * This can result in XCPT_FLOAT_INVALID_OPCODE exceptions,
  83.      * so to be sure, we disable Invalid Opcode FPU exception
  84.      * before using FPU stuffs.
  85.      */
  86.     usCW = _control87 (0, 0);
  87.     usCW = usCW | EM_INVALID | 0x80;
  88.     _control87 (usCW, MCW_EM | 0x80);
  89. }
  90.  
  91. /**
  92.  * cairo_os2_init:
  93.  *
  94.  * Initializes the Cairo library. This function is automatically called if
  95.  * Cairo was compiled to be a DLL (however it's not a problem if it's called
  96.  * multiple times). But if you link to Cairo statically, you have to call it
  97.  * once to set up Cairo's internal structures and mutexes.
  98.  *
  99.  * Since: 1.4
  100.  **/
  101. cairo_public void
  102. cairo_os2_init (void)
  103. {
  104.     /* This may initialize some stuffs, like create mutex semaphores etc.. */
  105.  
  106.     cairo_os2_initialization_count++;
  107.     if (cairo_os2_initialization_count > 1) return;
  108.  
  109.     DisableFPUException ();
  110.  
  111. #if CAIRO_HAS_FC_FONT
  112.     /* Initialize FontConfig */
  113.     FcInit ();
  114. #endif
  115.  
  116.     CAIRO_MUTEX_INITIALIZE ();
  117. }
  118.  
  119. /**
  120.  * cairo_os2_fini:
  121.  *
  122.  * Uninitializes the Cairo library. This function is automatically called if
  123.  * Cairo was compiled to be a DLL (however it's not a problem if it's called
  124.  * multiple times). But if you link to Cairo statically, you have to call it
  125.  * once to shut down Cairo, to let it free all the resources it has allocated.
  126.  *
  127.  * Since: 1.4
  128.  **/
  129. cairo_public void
  130. cairo_os2_fini (void)
  131. {
  132.     /* This has to uninitialize some stuffs, like destroy mutex semaphores etc.. */
  133.  
  134.     if (cairo_os2_initialization_count <= 0) return;
  135.     cairo_os2_initialization_count--;
  136.     if (cairo_os2_initialization_count > 0) return;
  137.  
  138.     DisableFPUException ();
  139.  
  140.     cairo_debug_reset_static_data ();
  141.  
  142. #if CAIRO_HAS_FC_FONT
  143. # if HAVE_FCFINI
  144.     /* Uninitialize FontConfig */
  145.     FcFini ();
  146. # endif
  147. #endif
  148.  
  149. #ifdef __WATCOMC__
  150.     /* It can happen that the libraries we use have memory leaks,
  151.      * so there are still memory chunks allocated at this point.
  152.      * In these cases, Watcom might still have a bigger memory chunk,
  153.      * called "the heap" allocated from the OS.
  154.      * As we want to minimize the memory we lose from the point of
  155.      * view of the OS, we call this function to shrink that heap
  156.      * as much as possible.
  157.      */
  158.     _heapshrink ();
  159. #else
  160.     /* GCC has a heapmin function that approximately corresponds to
  161.      * what the Watcom function does
  162.      */
  163.     _heapmin ();
  164. #endif
  165. }
  166.  
  167. /*
  168.  * This function calls the allocation function depending on which
  169.  * method was compiled into the library: it can be native allocation
  170.  * (DosAllocMem/DosFreeMem) or C-Library based allocation (malloc/free).
  171.  * Actually, for pixel buffers that we use this function for, cairo
  172.  * uses _cairo_malloc_abc, so we use that here, too. And use the
  173.  * change to check the size argument
  174.  */
  175. void *_buffer_alloc (size_t a, size_t b, const unsigned int size)
  176. {
  177.     size_t nbytes;
  178.     void  *buffer = NULL;
  179.  
  180.     if (!a || !b || !size ||
  181.         a >= INT32_MAX / b || a*b >= INT32_MAX / size) {
  182.         return NULL;
  183.     }
  184.     nbytes = a * b * size;
  185.  
  186. #ifdef OS2_USE_PLATFORM_ALLOC
  187.     /* Using OBJ_ANY on a machine that isn't configured for hi-mem
  188.      * will cause ERROR_INVALID_PARAMETER.  If this occurs, or this
  189.      * build doesn't have hi-mem enabled, fall back to using lo-mem.
  190.      */
  191. #ifdef OS2_HIGH_MEMORY
  192.     if (!DosAllocMem (&buffer, nbytes,
  193.                       OBJ_ANY | PAG_READ | PAG_WRITE | PAG_COMMIT))
  194.         return buffer;
  195. #endif
  196.     if (DosAllocMem (&buffer, nbytes,
  197.                      PAG_READ | PAG_WRITE | PAG_COMMIT))
  198.         return NULL;
  199. #else
  200.     /* Clear the malloc'd buffer the way DosAllocMem() does. */
  201.     buffer = malloc (nbytes);
  202.     if (buffer) {
  203.         memset (buffer, 0, nbytes);
  204.     }
  205. #endif
  206.     return buffer;
  207. }
  208.  
  209. /*
  210.  * This function selects the free function depending on which
  211.  * allocation method was compiled into the library
  212.  */
  213. void _buffer_free (void *buffer)
  214. {
  215. #ifdef OS2_USE_PLATFORM_ALLOC
  216.     DosFreeMem (buffer);
  217. #else
  218.     free (buffer);
  219. #endif
  220. }
  221.  
  222. /* XXX
  223.  * The cairo_os2_ini() and cairo_os2_fini() functions should be removed and
  224.  * the LibMain code moved to cairo-system.c.  It should also call
  225.  * cairo_debug_reset_static_data() instead of duplicating its logic...
  226.  */
  227.  
  228. #ifdef BUILD_CAIRO_DLL
  229. /* The main DLL entry for DLL initialization and uninitialization */
  230. /* Only include this code if we're about to build a DLL.          */
  231.  
  232. #ifdef __WATCOMC__
  233. unsigned _System
  234. LibMain (unsigned hmod,
  235.          unsigned termination)
  236. #else
  237. unsigned long _System
  238. _DLL_InitTerm (unsigned long hModule,
  239.                unsigned long termination)
  240. #endif
  241. {
  242.     if (termination) {
  243.         /* Unloading the DLL */
  244.         cairo_os2_fini ();
  245.  
  246. #ifndef __WATCOMC__
  247.         /* Uninitialize RTL of GCC */
  248.         __ctordtorTerm ();
  249.         _CRT_term ();
  250. #endif
  251.         return 1;
  252.     } else {
  253.         /* Loading the DLL */
  254. #ifndef __WATCOMC__
  255.         /* Initialize RTL of GCC */
  256.         if (_CRT_init () != 0)
  257.             return 0;
  258.         __ctordtorInit ();
  259. #endif
  260.  
  261.         cairo_os2_init ();
  262.         return 1;
  263.     }
  264. }
  265.  
  266. #endif /* BUILD_CAIRO_DLL */
  267.  
  268. /*
  269.  * The following part of the source file contains the code which might
  270.  * be called the "core" of the OS/2 backend support. This contains the
  271.  * OS/2 surface support functions and structures.
  272.  */
  273.  
  274. /* Forward declaration */
  275. static const cairo_surface_backend_t cairo_os2_surface_backend;
  276.  
  277. /* Unpublished API:
  278.  *   GpiEnableYInversion = PMGPI.723
  279.  *   GpiQueryYInversion = PMGPI.726
  280.  *   BOOL APIENTRY GpiEnableYInversion (HPS hps, LONG lHeight);
  281.  *   LONG APIENTRY GpiQueryYInversion (HPS hps);
  282.  */
  283. BOOL APIENTRY GpiEnableYInversion (HPS hps, LONG lHeight);
  284. LONG APIENTRY GpiQueryYInversion (HPS hps);
  285.  
  286. #ifdef __WATCOMC__
  287. /* Function declaration for GpiDrawBits () (missing from OpenWatcom headers) */
  288. LONG APIENTRY GpiDrawBits (HPS hps,
  289.                            PVOID pBits,
  290.                            PBITMAPINFO2 pbmiInfoTable,
  291.                            LONG lCount,
  292.                            PPOINTL aptlPoints,
  293.                            LONG lRop,
  294.                            ULONG flOptions);
  295. #endif
  296.  
  297. static void
  298. _cairo_os2_surface_blit_pixels (cairo_os2_surface_t *surface,
  299.                                 HPS                  hps_begin_paint,
  300.                                 PRECTL               prcl_begin_paint_rect)
  301. {
  302.     POINTL aptlPoints[4];
  303.     LONG   lOldYInversion;
  304.     LONG   rc = GPI_OK;
  305.  
  306.     /* Check the limits (may not be necessary) */
  307.     if (prcl_begin_paint_rect->xLeft < 0)
  308.         prcl_begin_paint_rect->xLeft = 0;
  309.     if (prcl_begin_paint_rect->yBottom < 0)
  310.         prcl_begin_paint_rect->yBottom = 0;
  311.     if (prcl_begin_paint_rect->xRight > (LONG) surface->bitmap_info.cx)
  312.         prcl_begin_paint_rect->xRight = (LONG) surface->bitmap_info.cx;
  313.     if (prcl_begin_paint_rect->yTop > (LONG) surface->bitmap_info.cy)
  314.         prcl_begin_paint_rect->yTop = (LONG) surface->bitmap_info.cy;
  315.  
  316.     /* Exit if the rectangle is empty */
  317.     if (prcl_begin_paint_rect->xLeft   >= prcl_begin_paint_rect->xRight ||
  318.         prcl_begin_paint_rect->yBottom >= prcl_begin_paint_rect->yTop)
  319.         return;
  320.  
  321.     /* Set the Target & Source coordinates */
  322.     *((PRECTL)&aptlPoints[0]) = *prcl_begin_paint_rect;
  323.     *((PRECTL)&aptlPoints[2]) = *prcl_begin_paint_rect;
  324.  
  325.     /* Make the Target coordinates non-inclusive */
  326.     aptlPoints[1].x -= 1;
  327.     aptlPoints[1].y -= 1;
  328.  
  329.     /* Enable Y Inversion for the HPS, so  GpiDrawBits will
  330.      * work with upside-top image, not with upside-down image!
  331.      */
  332.     lOldYInversion = GpiQueryYInversion (hps_begin_paint);
  333.     GpiEnableYInversion (hps_begin_paint, surface->bitmap_info.cy-1);
  334.  
  335.     /* Debug code to draw rectangle limits */
  336. #if 0
  337.     {
  338.         int x, y;
  339.         unsigned char *pixels;
  340.  
  341.         pixels = surface->pixels;
  342.         for (x = 0; x < surface->bitmap_info.cx; x++) {
  343.             for (y = 0; y < surface->bitmap_info.cy; y++) {
  344.                 if ((x == 0) ||
  345.                     (y == 0) ||
  346.                     (x == y) ||
  347.                     (x >= surface->bitmap_info.cx-1) ||
  348.                     (y >= surface->bitmap_info.cy-1))
  349.                 {
  350.                     pixels[y*surface->bitmap_info.cx*4+x*4] = 255;
  351.                 }
  352.             }
  353.         }
  354.     }
  355. #endif
  356.     if (!surface->use_24bpp) {
  357.         rc = GpiDrawBits (hps_begin_paint,
  358.                           surface->pixels,
  359.                           &(surface->bitmap_info),
  360.                           4,
  361.                           aptlPoints,
  362.                           ROP_SRCCOPY,
  363.                           BBO_IGNORE);
  364.         if (rc != GPI_OK)
  365.             surface->use_24bpp = TRUE;
  366.     }
  367.  
  368.     if (surface->use_24bpp) {
  369.         /* If GpiDrawBits () failed then this is most likely because the
  370.          * display driver could not handle a 32bit bitmap. So we need to
  371.          * - create a buffer that only contains 3 bytes per pixel
  372.          * - change the bitmap info header to contain 24bit
  373.          * - pass the new buffer to GpiDrawBits () again
  374.          * - clean up the new buffer
  375.          */
  376.         BITMAPINFO2       bmpinfo;
  377.         unsigned char    *pchPixBuf;
  378.         unsigned char    *pchTarget;
  379.         ULONG            *pulSource;
  380.         ULONG             ulX;
  381.         ULONG             ulY;
  382.         ULONG             ulPad;
  383.  
  384.         /* Set up the bitmap header, but this time for 24bit depth. */
  385.         bmpinfo = surface->bitmap_info;
  386.         bmpinfo.cBitCount = 24;
  387.  
  388.         /* The start of each row has to be DWORD aligned.  Calculate the
  389.          * of number aligned bytes per row, the total size of the bitmap,
  390.          * and the number of padding bytes at the end of each row.
  391.          */
  392.         ulX = (((bmpinfo.cx * bmpinfo.cBitCount) + 31) / 32) * 4;
  393.         bmpinfo.cbImage = ulX * bmpinfo.cy;
  394.         ulPad = ulX - bmpinfo.cx * 3;
  395.  
  396.         /* Allocate temporary pixel buffer.  If the rows don't need
  397.          * padding, it has to be 1 byte larger than the size of the
  398.          * bitmap  or else the high-order byte from the last source
  399.          * row will end up in unallocated memory.
  400.          */
  401.         pchPixBuf = (unsigned char *)_buffer_alloc (1, 1,
  402.                                         bmpinfo.cbImage + (ulPad ? 0 : 1));
  403.  
  404.         if (pchPixBuf) {
  405.             /* Copy 4 bytes from the source but advance the target ptr only
  406.              * 3 bytes, so the high-order alpha byte will be overwritten by
  407.              * the next copy. At the end of each row, skip over the padding.
  408.              */
  409.             pchTarget = pchPixBuf;
  410.             pulSource = (ULONG*)surface->pixels;
  411.             for (ulY = bmpinfo.cy; ulY; ulY--) {
  412.                 for (ulX = bmpinfo.cx; ulX; ulX--) {
  413.                     *((ULONG*)pchTarget) = *pulSource++;
  414.                     pchTarget += 3;
  415.                 }
  416.                 pchTarget += ulPad;
  417.             }
  418.  
  419.             rc = GpiDrawBits (hps_begin_paint,
  420.                               pchPixBuf,
  421.                               &bmpinfo,
  422.                               4,
  423.                               aptlPoints,
  424.                               ROP_SRCCOPY,
  425.                               BBO_IGNORE);
  426.             if (rc != GPI_OK)
  427.                 surface->use_24bpp = FALSE;
  428.  
  429.             _buffer_free (pchPixBuf);
  430.         }
  431.     }
  432.  
  433.     /* Restore Y inversion */
  434.     GpiEnableYInversion (hps_begin_paint, lOldYInversion);
  435. }
  436.  
  437. static void
  438. _cairo_os2_surface_get_pixels_from_screen (cairo_os2_surface_t *surface,
  439.                                            HPS                  hps_begin_paint,
  440.                                            PRECTL               prcl_begin_paint_rect)
  441. {
  442.     HPS hps;
  443.     HDC hdc;
  444.     SIZEL sizlTemp;
  445.     HBITMAP hbmpTemp;
  446.     BITMAPINFO2 bmi2Temp;
  447.     POINTL aptlPoints[4];
  448.     int y;
  449.     unsigned char *pchTemp;
  450.  
  451.     /* To copy pixels from screen to our buffer, we do the following steps:
  452.      *
  453.      * - Blit pixels from screen to a HBITMAP:
  454.      *   -- Create Memory Device Context
  455.      *   -- Create a PS into it
  456.      *   -- Create a HBITMAP
  457.      *   -- Select HBITMAP into memory PS
  458.      *   -- Blit dirty pixels from screen to HBITMAP
  459.      * - Copy HBITMAP lines (pixels) into our buffer
  460.      * - Free resources
  461.      */
  462.  
  463.     /* Create a memory device context */
  464.     hdc = DevOpenDC (0, OD_MEMORY,"*",0L, NULL, NULLHANDLE);
  465.     if (!hdc) {
  466.         return;
  467.     }
  468.  
  469.     /* Create a memory PS */
  470.     sizlTemp.cx = prcl_begin_paint_rect->xRight - prcl_begin_paint_rect->xLeft;
  471.     sizlTemp.cy = prcl_begin_paint_rect->yTop - prcl_begin_paint_rect->yBottom;
  472.     hps = GpiCreatePS (0,
  473.                        hdc,
  474.                        &sizlTemp,
  475.                        PU_PELS | GPIT_NORMAL | GPIA_ASSOC);
  476.     if (!hps) {
  477.         DevCloseDC (hdc);
  478.         return;
  479.     }
  480.  
  481.     /* Create an uninitialized bitmap. */
  482.     /* Prepare BITMAPINFO2 structure for our buffer */
  483.     memset (&bmi2Temp, 0, sizeof (bmi2Temp));
  484.     bmi2Temp.cbFix = sizeof (BITMAPINFOHEADER2);
  485.     bmi2Temp.cx = sizlTemp.cx;
  486.     bmi2Temp.cy = sizlTemp.cy;
  487.     bmi2Temp.cPlanes = 1;
  488.     bmi2Temp.cBitCount = 32;
  489.  
  490.     hbmpTemp = GpiCreateBitmap (hps,
  491.                                 (PBITMAPINFOHEADER2) &bmi2Temp,
  492.                                 0,
  493.                                 NULL,
  494.                                 NULL);
  495.  
  496.     if (!hbmpTemp) {
  497.         GpiDestroyPS (hps);
  498.         DevCloseDC (hdc);
  499.         return;
  500.     }
  501.  
  502.     /* Select the bitmap into the memory device context. */
  503.     GpiSetBitmap (hps, hbmpTemp);
  504.  
  505.     /* Target coordinates (Noninclusive) */
  506.     aptlPoints[0].x = 0;
  507.     aptlPoints[0].y = 0;
  508.  
  509.     aptlPoints[1].x = sizlTemp.cx;
  510.     aptlPoints[1].y = sizlTemp.cy;
  511.  
  512.     /* Source coordinates (Inclusive) */
  513.     aptlPoints[2].x = prcl_begin_paint_rect->xLeft;
  514.     aptlPoints[2].y = surface->bitmap_info.cy - prcl_begin_paint_rect->yBottom;
  515.  
  516.     aptlPoints[3].x = prcl_begin_paint_rect->xRight;
  517.     aptlPoints[3].y = surface->bitmap_info.cy - prcl_begin_paint_rect->yTop;
  518.  
  519.     /* Blit pixels from screen to bitmap */
  520.     GpiBitBlt (hps,
  521.                hps_begin_paint,
  522.                4,
  523.                aptlPoints,
  524.                ROP_SRCCOPY,
  525.                BBO_IGNORE);
  526.  
  527.     /* Now we have to extract the pixels from the bitmap. */
  528.     pchTemp =
  529.         surface->pixels +
  530.         (prcl_begin_paint_rect->yBottom)*surface->bitmap_info.cx*4 +
  531.         prcl_begin_paint_rect->xLeft*4;
  532.     for (y = 0; y < sizlTemp.cy; y++) {
  533.         /* Get one line of pixels */
  534.         GpiQueryBitmapBits (hps,
  535.                             sizlTemp.cy - y - 1, /* lScanStart */
  536.                             1,                   /* lScans */
  537.                             (PBYTE)pchTemp,
  538.                             &bmi2Temp);
  539.  
  540.         /* Go for next line */
  541.         pchTemp += surface->bitmap_info.cx*4;
  542.     }
  543.  
  544.     /* Clean up resources */
  545.     GpiSetBitmap (hps, (HBITMAP) NULL);
  546.     GpiDeleteBitmap (hbmpTemp);
  547.     GpiDestroyPS (hps);
  548.     DevCloseDC (hdc);
  549. }
  550.  
  551. static cairo_status_t
  552. _cairo_os2_surface_acquire_source_image (void                   *abstract_surface,
  553.                                          cairo_image_surface_t **image_out,
  554.                                          void                  **image_extra)
  555. {
  556.     cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface;
  557.  
  558.     DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
  559.  
  560.     /* Increase lend counter */
  561.     surface->pixel_array_lend_count++;
  562.  
  563.     *image_out = surface->image_surface;
  564.     *image_extra = NULL;
  565.  
  566.     DosReleaseMutexSem (surface->hmtx_use_private_fields);
  567.  
  568.     return CAIRO_STATUS_SUCCESS;
  569. }
  570.  
  571. static void
  572. _cairo_os2_surface_release_source_image (void                  *abstract_surface,
  573.                                          cairo_image_surface_t *image,
  574.                                          void                  *image_extra)
  575. {
  576.     cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface;
  577.  
  578.     /* Decrease Lend counter! */
  579.     DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
  580.  
  581.     if (surface->pixel_array_lend_count > 0)
  582.         surface->pixel_array_lend_count--;
  583.     DosPostEventSem (surface->hev_pixel_array_came_back);
  584.  
  585.     DosReleaseMutexSem (surface->hmtx_use_private_fields);
  586. }
  587.  
  588. static cairo_image_surface_t *
  589. _cairo_os2_surface_map_to_image (void *abstract_surface,
  590.                                  const cairo_rectangle_int_t *extents)
  591. {
  592.     cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface;
  593.  
  594.     DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
  595.     /* Increase lend counter */
  596.     surface->pixel_array_lend_count++;
  597.     DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
  598.  
  599.     /* XXX: BROKEN! */
  600.     *image_out = _cairo_surface_create_for_rectangle_int (surface->image_surface,
  601.                                                           extents);
  602.  
  603.     return CAIRO_STATUS_SUCCESS;
  604. }
  605.  
  606. static cairo_int_status_t
  607. _cairo_os2_surface_unmap_image (void *abstract_surface,
  608.                                 cairo_image_surface_t *image)
  609. {
  610.     cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface;
  611.  
  612.     /* So, we got back the image, and if all goes well, then
  613.      * something has been changed inside the interest_rect.
  614.      * So, we blit it to the screen!
  615.      */
  616.     if (surface->blit_as_changes) {
  617.         RECTL rclToBlit;
  618.  
  619.         /* Get mutex, we'll work with the pixel array! */
  620.         if (DosRequestMutexSem (surface->hmtx_use_private_fields,
  621.                                 SEM_INDEFINITE_WAIT) != NO_ERROR)
  622.         {
  623.             /* Could not get mutex! */
  624.             return;
  625.         }
  626.  
  627.         rclToBlit.xLeft = image->base.device_transform_inverse.x0;
  628.         rclToBlit.xRight = rclToBlit.xLeft + image->width; /* Noninclusive */
  629.         rclToBlit.yTop = image->base.device_transform_inverse.y0;
  630.         rclToBlit.yBottom = rclToBlit.yTop + image->height; /* Noninclusive */
  631.  
  632.         if (surface->hwnd_client_window) {
  633.             /* We know the HWND, so let's invalidate the window region,
  634.              * so the application will redraw itself, using the
  635.              * cairo_os2_surface_refresh_window () API from its own PM thread.
  636.              *
  637.              * This is the safe method, which should be preferred every time.
  638.              */
  639.             rclToBlit.yTop = surface->bitmap_info.cy - rclToBlit.yTop;
  640.             rclToBlit.yBottom = surface->bitmap_info.cy - rclToBlit.yTop;
  641.             WinInvalidateRect (surface->hwnd_client_window,
  642.                                &rclToBlit,
  643.                                FALSE);
  644.         } else {
  645.             /* We don't know the HWND, so try to blit the pixels from here!
  646.              * Please note that it can be problematic if this is not the PM thread!
  647.              *
  648.              * It can cause internal PM stuffs to be screwed up, for some reason.
  649.              * Please always tell the HWND to the surface using the
  650.              * cairo_os2_surface_set_hwnd () API, and call cairo_os2_surface_refresh_window ()
  651.              * from your WM_PAINT, if it's possible!
  652.              */
  653.             _cairo_os2_surface_blit_pixels (surface,
  654.                                             surface->hps_client_window,
  655.                                             &rclToBlit);
  656.         }
  657.  
  658.         DosReleaseMutexSem (surface->hmtx_use_private_fields);
  659.     }
  660.     /* Also decrease Lend counter! */
  661.     DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
  662.  
  663.     if (surface->pixel_array_lend_count > 0)
  664.         surface->pixel_array_lend_count--;
  665.     DosPostEventSem (surface->hev_pixel_array_came_back);
  666.  
  667.     DosReleaseMutexSem (surface->hmtx_use_private_fields);
  668. }
  669.  
  670. static cairo_bool_t
  671. _cairo_os2_surface_get_extents (void                    *abstract_surface,
  672.                                 cairo_rectangle_int_t   *rectangle)
  673. {
  674.     cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface;
  675.  
  676.     rectangle->x = 0;
  677.     rectangle->y = 0;
  678.     rectangle->width  = surface->bitmap_info.cx;
  679.     rectangle->height = surface->bitmap_info.cy;
  680.  
  681.     return TRUE;
  682. }
  683.  
  684. /**
  685.  * cairo_os2_surface_create:
  686.  * @hps_client_window: the presentation handle to bind the surface to
  687.  * @width: the width of the surface
  688.  * @height: the height of the surface
  689.  *
  690.  * Create a Cairo surface which is bound to a given presentation space (HPS).
  691.  * The caller retains ownership of the HPS and must dispose of it after the
  692.  * the surface has been destroyed.  The surface will be created to have the
  693.  * given size. By default every change to the surface will be made visible
  694.  * immediately by blitting it into the window. This can be changed with
  695.  * cairo_os2_surface_set_manual_window_refresh().
  696.  * Note that the surface will contain garbage when created, so the pixels
  697.  * have to be initialized by hand first. You can use the Cairo functions to
  698.  * fill it with black, or use cairo_surface_mark_dirty() to fill the surface
  699.  * with pixels from the window/HPS.
  700.  *
  701.  * Return value: the newly created surface
  702.  *
  703.  * Since: 1.4
  704.  **/
  705. cairo_surface_t *
  706. cairo_os2_surface_create (HPS hps_client_window,
  707.                           int width,
  708.                           int height)
  709. {
  710.     cairo_os2_surface_t *local_os2_surface = 0;
  711.     cairo_status_t status;
  712.     int rc;
  713.  
  714.     /* Check the size of the window */
  715.     if ((width <= 0) || (height <= 0)) {
  716.         status = _cairo_error (CAIRO_STATUS_INVALID_SIZE);
  717.         goto error_exit;
  718.     }
  719.  
  720.     /* Allocate an OS/2 surface structure. */
  721.     local_os2_surface = (cairo_os2_surface_t *) malloc (sizeof (cairo_os2_surface_t));
  722.     if (!local_os2_surface) {
  723.         status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
  724.         goto error_exit;
  725.     }
  726.  
  727.     memset(local_os2_surface, 0, sizeof(cairo_os2_surface_t));
  728.  
  729.     /* Allocate resources:  mutex & event semaphores and the pixel buffer */
  730.     if (DosCreateMutexSem (NULL,
  731.                            &(local_os2_surface->hmtx_use_private_fields),
  732.                            0,
  733.                            FALSE))
  734.     {
  735.         status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
  736.         goto error_exit;
  737.     }
  738.  
  739.     if (DosCreateEventSem (NULL,
  740.                            &(local_os2_surface->hev_pixel_array_came_back),
  741.                            0,
  742.                            FALSE))
  743.     {
  744.         status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
  745.         goto error_exit;
  746.     }
  747.  
  748.     local_os2_surface->pixels = (unsigned char *) _buffer_alloc (height, width, 4);
  749.     if (!local_os2_surface->pixels) {
  750.         status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
  751.         goto error_exit;
  752.     }
  753.  
  754.     /* Create image surface from pixel array */
  755.     local_os2_surface->image_surface = (cairo_image_surface_t *)
  756.         cairo_image_surface_create_for_data (local_os2_surface->pixels,
  757.                                              CAIRO_FORMAT_ARGB32,
  758.                                              width,      /* Width */
  759.                                              height,     /* Height */
  760.                                              width * 4); /* Rowstride */
  761.     status = local_os2_surface->image_surface->base.status;
  762.     if (status)
  763.         goto error_exit;
  764.  
  765.     /* Set values for OS/2-specific data that aren't zero/NULL/FALSE.
  766.      * Note: hps_client_window may be null if this was called by
  767.      * cairo_os2_surface_create_for_window().
  768.      */
  769.     local_os2_surface->hps_client_window = hps_client_window;
  770.     local_os2_surface->blit_as_changes = TRUE;
  771.  
  772.     /* Prepare BITMAPINFO2 structure for our buffer */
  773.     local_os2_surface->bitmap_info.cbFix = sizeof (BITMAPINFOHEADER2);
  774.     local_os2_surface->bitmap_info.cx = width;
  775.     local_os2_surface->bitmap_info.cy = height;
  776.     local_os2_surface->bitmap_info.cPlanes = 1;
  777.     local_os2_surface->bitmap_info.cBitCount = 32;
  778.  
  779.     /* Initialize base surface */
  780.     _cairo_surface_init (&local_os2_surface->base,
  781.                          &cairo_os2_surface_backend,
  782.                          NULL, /* device */
  783.                          _cairo_content_from_format (CAIRO_FORMAT_ARGB32));
  784.  
  785.     /* Successful exit */
  786.     return (cairo_surface_t *)local_os2_surface;
  787.  
  788.  error_exit:
  789.  
  790.     /* This point will only be reached if an error occurred */
  791.  
  792.     if (local_os2_surface) {
  793.         if (local_os2_surface->pixels)
  794.             _buffer_free (local_os2_surface->pixels);
  795.         if (local_os2_surface->hev_pixel_array_came_back)
  796.             DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back);
  797.         if (local_os2_surface->hmtx_use_private_fields)
  798.             DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields);
  799.         free (local_os2_surface);
  800.     }
  801.  
  802.     return _cairo_surface_create_in_error (status);
  803. }
  804.  
  805. /**
  806.  * cairo_os2_surface_create_for_window:
  807.  * @hwnd_client_window: the window handle to bind the surface to
  808.  * @width: the width of the surface
  809.  * @height: the height of the surface
  810.  *
  811.  * Create a Cairo surface which is bound to a given window; the caller retains
  812.  * ownership of the window.  This is a convenience function for use with
  813.  * windows that will only be updated when cairo_os2_surface_refresh_window()
  814.  * is called (usually in response to a WM_PAINT message).  It avoids the need
  815.  * to create a persistent HPS for every window and assumes that one will be
  816.  * supplied by the caller when a cairo function needs one.  If it isn't, an
  817.  * HPS will be created on-the-fly and released before the function which needs
  818.  * it returns.
  819.  *
  820.  * Return value: the newly created surface
  821.  *
  822.  * Since: 1.10
  823.  **/
  824. cairo_surface_t *
  825. cairo_os2_surface_create_for_window (HWND hwnd_client_window,
  826.                                      int width,
  827.                                      int height)
  828. {
  829.     cairo_os2_surface_t *local_os2_surface;
  830.  
  831.     /* A window handle must be provided. */
  832.     if (!hwnd_client_window) {
  833.         return _cairo_surface_create_in_error (
  834.                                 _cairo_error (CAIRO_STATUS_NO_MEMORY));
  835.     }
  836.  
  837.     /* Create the surface. */
  838.     local_os2_surface = (cairo_os2_surface_t *)
  839.         cairo_os2_surface_create (0, width, height);
  840.  
  841.     /* If successful, save the hwnd & turn off automatic repainting. */
  842.     if (!local_os2_surface->image_surface->base.status) {
  843.         local_os2_surface->hwnd_client_window = hwnd_client_window;
  844.         local_os2_surface->blit_as_changes = FALSE;
  845.     }
  846.  
  847.     return (cairo_surface_t *)local_os2_surface;
  848. }
  849.  
  850. /**
  851.  * cairo_os2_surface_set_size:
  852.  * @surface: the cairo surface to resize
  853.  * @new_width: the new width of the surface
  854.  * @new_height: the new height of the surface
  855.  * @timeout: timeout value in milliseconds
  856.  *
  857.  * When the client window is resized, call this API to set the new size in the
  858.  * underlying surface accordingly. This function will reallocate everything,
  859.  * so you'll have to redraw everything in the surface after this call.
  860.  * The surface will contain garbage after the resizing. So the notes of
  861.  * cairo_os2_surface_create() apply here, too.
  862.  *
  863.  * The timeout value specifies how long the function should wait on other parts
  864.  * of the program to release the buffers. It is necessary, because it can happen
  865.  * that Cairo is just drawing something into the surface while we want to
  866.  * destroy and recreate it.
  867.  *
  868.  * Return value: %CAIRO_STATUS_SUCCESS if the surface could be resized,
  869.  * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface,
  870.  * %CAIRO_STATUS_INVALID_SIZE for invalid sizes
  871.  * %CAIRO_STATUS_NO_MEMORY if the new size could not be allocated, or if the
  872.  * timeout happened before all the buffers were released
  873.  *
  874.  * Since: 1.4
  875.  **/
  876. int
  877. cairo_os2_surface_set_size (cairo_surface_t *surface,
  878.                             int              new_width,
  879.                             int              new_height,
  880.                             int              timeout)
  881. {
  882.     cairo_os2_surface_t *local_os2_surface;
  883.     unsigned char *pchNewPixels;
  884.     int rc;
  885.     cairo_image_surface_t *pNewImageSurface;
  886.  
  887.     local_os2_surface = (cairo_os2_surface_t *) surface;
  888.     if ((!local_os2_surface) ||
  889.         (local_os2_surface->base.backend != &cairo_os2_surface_backend))
  890.     {
  891.         /* Invalid parameter (wrong surface)! */
  892.         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
  893.     }
  894.  
  895.     if ((new_width <= 0) ||
  896.         (new_height <= 0))
  897.     {
  898.         /* Invalid size! */
  899.         return _cairo_error (CAIRO_STATUS_INVALID_SIZE);
  900.     }
  901.  
  902.     /* Allocate memory for new stuffs */
  903.     pchNewPixels = (unsigned char *) _buffer_alloc (new_height, new_width, 4);
  904.     if (!pchNewPixels) {
  905.         /* Not enough memory for the pixels!
  906.          * Everything remains the same!
  907.          */
  908.         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
  909.     }
  910.  
  911.     /* Create image surface from new pixel array */
  912.     pNewImageSurface = (cairo_image_surface_t *)
  913.         cairo_image_surface_create_for_data (pchNewPixels,
  914.                                              CAIRO_FORMAT_ARGB32,
  915.                                              new_width,      /* Width */
  916.                                              new_height,     /* Height */
  917.                                              new_width * 4); /* Rowstride */
  918.  
  919.     if (pNewImageSurface->base.status) {
  920.         /* Could not create image surface!
  921.          * Everything remains the same!
  922.          */
  923.         _buffer_free (pchNewPixels);
  924.         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
  925.     }
  926.  
  927.     /* Okay, new memory allocated, so it's time to swap old buffers
  928.      * to new ones!
  929.      */
  930.     if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)!=NO_ERROR) {
  931.         /* Could not get mutex!
  932.          * Everything remains the same!
  933.          */
  934.         cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface);
  935.         _buffer_free (pchNewPixels);
  936.         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
  937.     }
  938.  
  939.     /* We have to make sure that we won't destroy a surface which
  940.      * is lent to some other code (Cairo is drawing into it)!
  941.      */
  942.     while (local_os2_surface->pixel_array_lend_count > 0) {
  943.         ULONG ulPostCount;
  944.         DosResetEventSem (local_os2_surface->hev_pixel_array_came_back, &ulPostCount);
  945.         DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
  946.         /* Wait for somebody to return the pixels! */
  947.         rc = DosWaitEventSem (local_os2_surface->hev_pixel_array_came_back, timeout);
  948.         if (rc != NO_ERROR) {
  949.             /* Either timeout or something wrong... Exit. */
  950.             cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface);
  951.             _buffer_free (pchNewPixels);
  952.             return _cairo_error (CAIRO_STATUS_NO_MEMORY);
  953.         }
  954.         /* Okay, grab mutex and check counter again! */
  955.         if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
  956.             != NO_ERROR)
  957.         {
  958.             /* Could not get mutex!
  959.              * Everything remains the same!
  960.              */
  961.             cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface);
  962.             _buffer_free (pchNewPixels);
  963.             return _cairo_error (CAIRO_STATUS_NO_MEMORY);
  964.         }
  965.     }
  966.  
  967.     /* Destroy old image surface */
  968.     cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface));
  969.     /* Destroy old pixel buffer */
  970.     _buffer_free (local_os2_surface->pixels);
  971.     /* Set new image surface */
  972.     local_os2_surface->image_surface = pNewImageSurface;
  973.     /* Set new pixel buffer */
  974.     local_os2_surface->pixels = pchNewPixels;
  975.     /* Change bitmap2 structure */
  976.     local_os2_surface->bitmap_info.cx = new_width;
  977.     local_os2_surface->bitmap_info.cy = new_height;
  978.  
  979.     DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
  980.     return CAIRO_STATUS_SUCCESS;
  981. }
  982.  
  983. /**
  984.  * cairo_os2_surface_refresh_window:
  985.  * @surface: the cairo surface to refresh
  986.  * @hps_begin_paint: the presentation handle of the window to refresh
  987.  * @prcl_begin_paint_rect: the rectangle to redraw
  988.  *
  989.  * This function can be used to force a repaint of a given area of the client
  990.  * window. It should usually be called from the WM_PAINT processing of the
  991.  * window procedure. However, it can be called any time a given part of the
  992.  * window has to be updated.
  993.  *
  994.  * The HPS and RECTL to be passed can be taken from the usual WinBeginPaint call
  995.  * of the window procedure, but you can also get the HPS using WinGetPS, and you
  996.  * can assemble your own update rectangle by hand.
  997.  * If hps_begin_paint is %NULL, the function will use the HPS passed into
  998.  * cairo_os2_surface_create(). If @prcl_begin_paint_rect is %NULL, the function
  999.  * will query the current window size and repaint the whole window.
  1000.  *
  1001.  * Cairo assumes that if you set the HWND to the surface using
  1002.  * cairo_os2_surface_set_hwnd(), this function will be called by the application
  1003.  * every time it gets a WM_PAINT for that HWND. If the HWND is set in the
  1004.  * surface, Cairo uses this function to handle dirty areas too.
  1005.  *
  1006.  * Since: 1.4
  1007.  **/
  1008. void
  1009. cairo_os2_surface_refresh_window (cairo_surface_t *surface,
  1010.                                   HPS              hps_begin_paint,
  1011.                                   PRECTL           prcl_begin_paint_rect)
  1012. {
  1013.     cairo_os2_surface_t *local_os2_surface;
  1014.     RECTL rclTemp;
  1015.     HPS hpsTemp = 0;
  1016.  
  1017.     local_os2_surface = (cairo_os2_surface_t *) surface;
  1018.     if ((!local_os2_surface) ||
  1019.         (local_os2_surface->base.backend != &cairo_os2_surface_backend))
  1020.     {
  1021.         /* Invalid parameter (wrong surface)! */
  1022.         return;
  1023.     }
  1024.  
  1025.     /* If an HPS wasn't provided, see if we can get one. */
  1026.     if (!hps_begin_paint) {
  1027.         hps_begin_paint = local_os2_surface->hps_client_window;
  1028.         if (!hps_begin_paint) {
  1029.             if (local_os2_surface->hwnd_client_window) {
  1030.                 hpsTemp = WinGetPS(local_os2_surface->hwnd_client_window);
  1031.                 hps_begin_paint = hpsTemp;
  1032.             }
  1033.             /* No HPS & no way to get one, so exit */
  1034.             if (!hps_begin_paint)
  1035.                 return;
  1036.         }
  1037.     }
  1038.  
  1039.     if (prcl_begin_paint_rect == NULL) {
  1040.         /* Update the whole window! */
  1041.         rclTemp.xLeft = 0;
  1042.         rclTemp.xRight = local_os2_surface->bitmap_info.cx;
  1043.         rclTemp.yTop = local_os2_surface->bitmap_info.cy;
  1044.         rclTemp.yBottom = 0;
  1045.     } else {
  1046.         /* Use the rectangle we got passed as parameter! */
  1047.         rclTemp.xLeft = prcl_begin_paint_rect->xLeft;
  1048.         rclTemp.xRight = prcl_begin_paint_rect->xRight;
  1049.         rclTemp.yTop = local_os2_surface->bitmap_info.cy - prcl_begin_paint_rect->yBottom;
  1050.         rclTemp.yBottom = local_os2_surface->bitmap_info.cy - prcl_begin_paint_rect->yTop ;
  1051.     }
  1052.  
  1053.     /* Get mutex, we'll work with the pixel array! */
  1054.     if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
  1055.         != NO_ERROR)
  1056.     {
  1057.         /* Could not get mutex! */
  1058.         if (hpsTemp)
  1059.             WinReleasePS(hpsTemp);
  1060.         return;
  1061.     }
  1062.  
  1063.     if ((local_os2_surface->dirty_area_present) &&
  1064.         (local_os2_surface->rcl_dirty_area.xLeft == rclTemp.xLeft) &&
  1065.         (local_os2_surface->rcl_dirty_area.xRight == rclTemp.xRight) &&
  1066.         (local_os2_surface->rcl_dirty_area.yTop == rclTemp.yTop) &&
  1067.         (local_os2_surface->rcl_dirty_area.yBottom == rclTemp.yBottom))
  1068.     {
  1069.         /* Aha, this call was because of a dirty area, so in this case we
  1070.          * have to blit the pixels from the screen to the surface!
  1071.          */
  1072.         local_os2_surface->dirty_area_present = FALSE;
  1073.         _cairo_os2_surface_get_pixels_from_screen (local_os2_surface,
  1074.                                                    hps_begin_paint,
  1075.                                                    &rclTemp);
  1076.     } else {
  1077.         /* Okay, we have the surface, have the HPS
  1078.          * (might be from WinBeginPaint () or from WinGetPS () )
  1079.          * Now blit there the stuffs!
  1080.          */
  1081.         _cairo_os2_surface_blit_pixels (local_os2_surface,
  1082.                                         hps_begin_paint,
  1083.                                         &rclTemp);
  1084.     }
  1085.  
  1086.     DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
  1087.  
  1088.     if (hpsTemp)
  1089.         WinReleasePS(hpsTemp);
  1090. }
  1091.  
  1092. static cairo_status_t
  1093. _cairo_os2_surface_finish (void *abstract_surface)
  1094. {
  1095.     cairo_os2_surface_t *local_os2_surface;
  1096.  
  1097.     local_os2_surface = (cairo_os2_surface_t *) abstract_surface;
  1098.     if ((!local_os2_surface) ||
  1099.         (local_os2_surface->base.backend != &cairo_os2_surface_backend))
  1100.     {
  1101.         /* Invalid parameter (wrong surface)! */
  1102.         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
  1103.     }
  1104.  
  1105.     DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
  1106.  
  1107.     /* Destroy old image surface */
  1108.     cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface));
  1109.     /* Destroy old pixel buffer */
  1110.     _buffer_free (local_os2_surface->pixels);
  1111.     DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields);
  1112.     DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back);
  1113.  
  1114.     /* The memory itself will be free'd by the cairo_surface_destroy ()
  1115.      * who called us.
  1116.      */
  1117.  
  1118.     return CAIRO_STATUS_SUCCESS;
  1119. }
  1120.  
  1121. /**
  1122.  * cairo_os2_surface_set_hwnd:
  1123.  * @surface: the cairo surface to associate with the window handle
  1124.  * @hwnd_client_window: the window handle of the client window
  1125.  *
  1126.  * Sets window handle for surface; the caller retains ownership of the window.
  1127.  * If Cairo wants to blit into the window because it is set to blit as the
  1128.  * surface changes (see cairo_os2_surface_set_manual_window_refresh()), then
  1129.  * there are two ways it can choose:
  1130.  * If it knows the HWND of the surface, then it invalidates that area, so the
  1131.  * application will get a WM_PAINT message and it can call
  1132.  * cairo_os2_surface_refresh_window() to redraw that area. Otherwise cairo itself
  1133.  * will use the HPS it got at surface creation time, and blit the pixels itself.
  1134.  * It's also a solution, but experience shows that if this happens from a non-PM
  1135.  * thread, then it can screw up PM internals.
  1136.  *
  1137.  * So, best solution is to set the HWND for the surface after the surface
  1138.  * creation, so every blit will be done from application's message processing
  1139.  * loop, which is the safest way to do.
  1140.  *
  1141.  * Since: 1.4
  1142.  **/
  1143. void
  1144. cairo_os2_surface_set_hwnd (cairo_surface_t *surface,
  1145.                             HWND             hwnd_client_window)
  1146. {
  1147.     cairo_os2_surface_t *local_os2_surface;
  1148.  
  1149.     local_os2_surface = (cairo_os2_surface_t *) surface;
  1150.     if ((!local_os2_surface) ||
  1151.         (local_os2_surface->base.backend != &cairo_os2_surface_backend))
  1152.     {
  1153.         /* Invalid parameter (wrong surface)! */
  1154.         return;
  1155.     }
  1156.  
  1157.     if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
  1158.         != NO_ERROR)
  1159.     {
  1160.         /* Could not get mutex! */
  1161.         return;
  1162.     }
  1163.  
  1164.     local_os2_surface->hwnd_client_window = hwnd_client_window;
  1165.  
  1166.     DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
  1167. }
  1168.  
  1169. /**
  1170.  * cairo_os2_surface_set_manual_window_refresh:
  1171.  * @surface: the cairo surface to set the refresh mode for
  1172.  * @manual_refresh: the switch for manual surface refresh
  1173.  *
  1174.  * This API can tell Cairo if it should show every change to this surface
  1175.  * immediately in the window or if it should be cached and will only be visible
  1176.  * once the user calls cairo_os2_surface_refresh_window() explicitly. If the
  1177.  * HWND was not set in the cairo surface, then the HPS will be used to blit the
  1178.  * graphics. Otherwise it will invalidate the given window region so the user
  1179.  * will get the WM_PAINT message to redraw that area of the window.
  1180.  *
  1181.  * So, if you're only interested in displaying the final result after several
  1182.  * drawing operations, you might get better performance if you put the surface
  1183.  * into manual refresh mode by passing a true value to this function. Then call
  1184.  * cairo_os2_surface_refresh() whenever desired.
  1185.  *
  1186.  * Since: 1.4
  1187.  **/
  1188. void
  1189. cairo_os2_surface_set_manual_window_refresh (cairo_surface_t *surface,
  1190.                                              cairo_bool_t     manual_refresh)
  1191. {
  1192.     cairo_os2_surface_t *local_os2_surface;
  1193.  
  1194.     local_os2_surface = (cairo_os2_surface_t *) surface;
  1195.     if ((!local_os2_surface) ||
  1196.         (local_os2_surface->base.backend != &cairo_os2_surface_backend))
  1197.     {
  1198.         /* Invalid parameter (wrong surface)! */
  1199.         return;
  1200.     }
  1201.  
  1202.     local_os2_surface->blit_as_changes = !manual_refresh;
  1203. }
  1204.  
  1205. /**
  1206.  * cairo_os2_surface_get_manual_window_refresh:
  1207.  * @surface: the cairo surface to query the refresh mode from
  1208.  *
  1209.  * This space left intentionally blank.
  1210.  *
  1211.  * Return value: current refresh mode of the surface (true by default)
  1212.  *
  1213.  * Since: 1.4
  1214.  **/
  1215. cairo_bool_t
  1216. cairo_os2_surface_get_manual_window_refresh (cairo_surface_t *surface)
  1217. {
  1218.     cairo_os2_surface_t *local_os2_surface;
  1219.  
  1220.     local_os2_surface = (cairo_os2_surface_t *) surface;
  1221.     if ((!local_os2_surface) ||
  1222.         (local_os2_surface->base.backend != &cairo_os2_surface_backend))
  1223.     {
  1224.         /* Invalid parameter (wrong surface)! */
  1225.         return FALSE;
  1226.     }
  1227.  
  1228.     return !(local_os2_surface->blit_as_changes);
  1229. }
  1230.  
  1231. /**
  1232.  * cairo_os2_surface_get_hps:
  1233.  * @surface: the cairo surface to be querued
  1234.  * @hps: HPS currently associated with the surface (if any)
  1235.  *
  1236.  * This API retrieves the HPS associated with the surface.
  1237.  *
  1238.  * Return value: %CAIRO_STATUS_SUCCESS if the hps could be retrieved,
  1239.  * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface,
  1240.  * %CAIRO_STATUS_NULL_POINTER if the hps argument is null
  1241.  *
  1242.  * Since: 1.10
  1243.  **/
  1244. cairo_status_t
  1245. cairo_os2_surface_get_hps (cairo_surface_t *surface,
  1246.                            HPS             *hps)
  1247. {
  1248.     cairo_os2_surface_t *local_os2_surface;
  1249.  
  1250.     local_os2_surface = (cairo_os2_surface_t *) surface;
  1251.     if ((!local_os2_surface) ||
  1252.         (local_os2_surface->base.backend != &cairo_os2_surface_backend))
  1253.     {
  1254.         /* Invalid parameter (wrong surface)! */
  1255.         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
  1256.     }
  1257.     if (!hps)
  1258.     {
  1259.         return _cairo_error (CAIRO_STATUS_NULL_POINTER);
  1260.     }
  1261.     *hps = local_os2_surface->hps_client_window;
  1262.  
  1263.     return CAIRO_STATUS_SUCCESS;
  1264. }
  1265.  
  1266. /**
  1267.  * cairo_os2_surface_set_hps:
  1268.  * @surface: the cairo surface to associate with the HPS
  1269.  * @hps: new HPS to be associated with the surface (the HPS may be null)
  1270.  *
  1271.  * This API replaces the HPS associated with the surface with a new one.
  1272.  * The caller retains ownership of the HPS and must dispose of it after
  1273.  * the surface has been destroyed or it has been replaced by another
  1274.  * call to this function.
  1275.  *
  1276.  * Return value: %CAIRO_STATUS_SUCCESS if the hps could be replaced,
  1277.  * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface,
  1278.  *
  1279.  * Since: 1.10
  1280.  **/
  1281. cairo_status_t
  1282. cairo_os2_surface_set_hps (cairo_surface_t *surface,
  1283.                            HPS              hps)
  1284. {
  1285.     cairo_os2_surface_t *local_os2_surface;
  1286.  
  1287.     local_os2_surface = (cairo_os2_surface_t *) surface;
  1288.     if ((!local_os2_surface) ||
  1289.         (local_os2_surface->base.backend != &cairo_os2_surface_backend))
  1290.     {
  1291.         /* Invalid parameter (wrong surface)! */
  1292.         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
  1293.     }
  1294.     local_os2_surface->hps_client_window = hps;
  1295.  
  1296.     return CAIRO_STATUS_SUCCESS;
  1297. }
  1298.  
  1299. static cairo_status_t
  1300. _cairo_os2_surface_mark_dirty_rectangle (void *surface,
  1301.                                          int   x,
  1302.                                          int   y,
  1303.                                          int   width,
  1304.                                          int   height)
  1305. {
  1306.     cairo_os2_surface_t *local_os2_surface;
  1307.     RECTL rclToBlit;
  1308.  
  1309.     local_os2_surface = (cairo_os2_surface_t *) surface;
  1310.     if ((!local_os2_surface) ||
  1311.         (local_os2_surface->base.backend != &cairo_os2_surface_backend))
  1312.     {
  1313.         /* Invalid parameter (wrong surface)! */
  1314.         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
  1315.     }
  1316.  
  1317.     /* Get mutex, we'll work with the pixel array! */
  1318.     if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
  1319.         != NO_ERROR)
  1320.     {
  1321.         /* Could not get mutex! */
  1322.         return CAIRO_STATUS_NO_MEMORY;
  1323.     }
  1324.  
  1325.     /* Check for defaults */
  1326.     if (width < 0)
  1327.         width = local_os2_surface->bitmap_info.cx;
  1328.     if (height < 0)
  1329.         height = local_os2_surface->bitmap_info.cy;
  1330.  
  1331.     if (local_os2_surface->hwnd_client_window) {
  1332.         /* We know the HWND, so let's invalidate the window region,
  1333.          * so the application will redraw itself, using the
  1334.          * cairo_os2_surface_refresh_window () API from its own PM thread.
  1335.          * From that function we'll note that it's not a redraw but a
  1336.          * dirty-rectangle deal stuff, so we'll handle the things from
  1337.          * there.
  1338.          *
  1339.          * This is the safe method, which should be preferred every time.
  1340.          */
  1341.         rclToBlit.xLeft = x;
  1342.         rclToBlit.xRight = x + width; /* Noninclusive */
  1343.         rclToBlit.yTop = local_os2_surface->bitmap_info.cy - (y);
  1344.         rclToBlit.yBottom = local_os2_surface->bitmap_info.cy - (y + height); /* Noninclusive */
  1345.  
  1346. #if 0
  1347.         if (local_os2_surface->dirty_area_present) {
  1348.             /* Yikes, there is already a dirty area which should be
  1349.              * cleaned up, but we'll overwrite it. Sorry.
  1350.              * TODO: Something clever should be done here.
  1351.              */
  1352.         }
  1353. #endif
  1354.  
  1355.         /* Set up dirty area reminder stuff */
  1356.         memcpy (&(local_os2_surface->rcl_dirty_area), &rclToBlit, sizeof (RECTL));
  1357.         local_os2_surface->dirty_area_present = TRUE;
  1358.  
  1359.         /* Invalidate window area */
  1360.         WinInvalidateRect (local_os2_surface->hwnd_client_window,
  1361.                            &rclToBlit,
  1362.                            FALSE);
  1363.     } else {
  1364.         /* We don't know the HWND, so try to blit the pixels from here!
  1365.          * Please note that it can be problematic if this is not the PM thread!
  1366.          *
  1367.          * It can cause internal PM stuffs to be scewed up, for some reason.
  1368.          * Please always tell the HWND to the surface using the
  1369.          * cairo_os2_surface_set_hwnd () API, and call cairo_os2_surface_refresh_window ()
  1370.          * from your WM_PAINT, if it's possible!
  1371.          */
  1372.  
  1373.         rclToBlit.xLeft = x;
  1374.         rclToBlit.xRight = x + width; /* Noninclusive */
  1375.         rclToBlit.yBottom = y;
  1376.         rclToBlit.yTop = y + height; /* Noninclusive */
  1377.         /* Now get the pixels from the screen! */
  1378.         _cairo_os2_surface_get_pixels_from_screen (local_os2_surface,
  1379.                                                    local_os2_surface->hps_client_window,
  1380.                                                    &rclToBlit);
  1381.     }
  1382.  
  1383.     DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
  1384.  
  1385.     return CAIRO_STATUS_SUCCESS;
  1386. }
  1387.  
  1388. static const cairo_surface_backend_t cairo_os2_surface_backend = {
  1389.     CAIRO_SURFACE_TYPE_OS2,
  1390.     _cairo_os2_surface_finish,
  1391.     _cairo_default_context_create,
  1392.  
  1393.     NULL, /* create_similar */
  1394.     NULL, /* create_similar_image */
  1395.     _cairo_os2_surface_map_to_image,
  1396.     _cairo_os2_surface_unmap_image,
  1397.  
  1398.     _cairo_surface_default_source,
  1399.     _cairo_os2_surface_acquire_source_image,
  1400.     _cairo_os2_surface_release_source_image,
  1401.     NULL, /* snapshot */
  1402.  
  1403.     _cairo_os2_surface_get_extents,
  1404.     NULL, /* get_font_options */
  1405.  
  1406.     NULL, /* flush */
  1407.     _cairo_os2_surface_mark_dirty_rectangle,
  1408.  
  1409.     _cairo_surface_fallback_paint,
  1410.     _cairo_surface_fallback_mask,
  1411.     _cairo_surface_fallback_fill,
  1412.     _cairo_surface_fallback_stroke,
  1413.     NULL, /* fill/stroke */
  1414.     _cairo_surface_fallback_glyphs,
  1415. };
  1416.