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
DisableFPUException(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
cairo_os2_init(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
cairo_os2_fini(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  */
_buffer_alloc(size_t a,size_t b,const unsigned int size)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 = _cairo_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  */
_buffer_free(void * buffer)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
LibMain(unsigned hmod,unsigned termination)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
_cairo_os2_surface_blit_pixels(cairo_os2_surface_t * surface,HPS hps_begin_paint,PRECTL prcl_begin_paint_rect)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
_cairo_os2_surface_get_pixels_from_screen(cairo_os2_surface_t * surface,HPS hps_begin_paint,PRECTL prcl_begin_paint_rect)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
_cairo_os2_surface_acquire_source_image(void * abstract_surface,cairo_image_surface_t ** image_out,void ** image_extra)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
_cairo_os2_surface_release_source_image(void * abstract_surface,cairo_image_surface_t * image,void * image_extra)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 *
_cairo_os2_surface_map_to_image(void * abstract_surface,const cairo_rectangle_int_t * extents)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
_cairo_os2_surface_unmap_image(void * abstract_surface,cairo_image_surface_t * image)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
_cairo_os2_surface_get_extents(void * abstract_surface,cairo_rectangle_int_t * rectangle)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 *
cairo_os2_surface_create(HPS hps_client_window,int width,int height)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 *) _cairo_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 			 FALSE); /* is_vector */
785 
786     /* Successful exit */
787     return (cairo_surface_t *)local_os2_surface;
788 
789  error_exit:
790 
791     /* This point will only be reached if an error occurred */
792 
793     if (local_os2_surface) {
794         if (local_os2_surface->pixels)
795             _buffer_free (local_os2_surface->pixels);
796         if (local_os2_surface->hev_pixel_array_came_back)
797             DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back);
798         if (local_os2_surface->hmtx_use_private_fields)
799             DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields);
800         free (local_os2_surface);
801     }
802 
803     return _cairo_surface_create_in_error (status);
804 }
805 
806 /**
807  * cairo_os2_surface_create_for_window:
808  * @hwnd_client_window: the window handle to bind the surface to
809  * @width: the width of the surface
810  * @height: the height of the surface
811  *
812  * Create a Cairo surface which is bound to a given window; the caller retains
813  * ownership of the window.  This is a convenience function for use with
814  * windows that will only be updated when cairo_os2_surface_refresh_window()
815  * is called (usually in response to a WM_PAINT message).  It avoids the need
816  * to create a persistent HPS for every window and assumes that one will be
817  * supplied by the caller when a cairo function needs one.  If it isn't, an
818  * HPS will be created on-the-fly and released before the function which needs
819  * it returns.
820  *
821  * Return value: the newly created surface
822  *
823  * Since: 1.10
824  **/
825 cairo_surface_t *
cairo_os2_surface_create_for_window(HWND hwnd_client_window,int width,int height)826 cairo_os2_surface_create_for_window (HWND hwnd_client_window,
827                                      int width,
828                                      int height)
829 {
830     cairo_os2_surface_t *local_os2_surface;
831 
832     /* A window handle must be provided. */
833     if (!hwnd_client_window) {
834         return _cairo_surface_create_in_error (
835                                 _cairo_error (CAIRO_STATUS_NO_MEMORY));
836     }
837 
838     /* Create the surface. */
839     local_os2_surface = (cairo_os2_surface_t *)
840         cairo_os2_surface_create (0, width, height);
841 
842     /* If successful, save the hwnd & turn off automatic repainting. */
843     if (!local_os2_surface->image_surface->base.status) {
844         local_os2_surface->hwnd_client_window = hwnd_client_window;
845         local_os2_surface->blit_as_changes = FALSE;
846     }
847 
848     return (cairo_surface_t *)local_os2_surface;
849 }
850 
851 /**
852  * cairo_os2_surface_set_size:
853  * @surface: the cairo surface to resize
854  * @new_width: the new width of the surface
855  * @new_height: the new height of the surface
856  * @timeout: timeout value in milliseconds
857  *
858  * When the client window is resized, call this API to set the new size in the
859  * underlying surface accordingly. This function will reallocate everything,
860  * so you'll have to redraw everything in the surface after this call.
861  * The surface will contain garbage after the resizing. So the notes of
862  * cairo_os2_surface_create() apply here, too.
863  *
864  * The timeout value specifies how long the function should wait on other parts
865  * of the program to release the buffers. It is necessary, because it can happen
866  * that Cairo is just drawing something into the surface while we want to
867  * destroy and recreate it.
868  *
869  * Return value: %CAIRO_STATUS_SUCCESS if the surface could be resized,
870  * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface,
871  * %CAIRO_STATUS_INVALID_SIZE for invalid sizes
872  * %CAIRO_STATUS_NO_MEMORY if the new size could not be allocated, or if the
873  * timeout happened before all the buffers were released
874  *
875  * Since: 1.4
876  **/
877 int
cairo_os2_surface_set_size(cairo_surface_t * surface,int new_width,int new_height,int timeout)878 cairo_os2_surface_set_size (cairo_surface_t *surface,
879                             int              new_width,
880                             int              new_height,
881                             int              timeout)
882 {
883     cairo_os2_surface_t *local_os2_surface;
884     unsigned char *pchNewPixels;
885     int rc;
886     cairo_image_surface_t *pNewImageSurface;
887 
888     local_os2_surface = (cairo_os2_surface_t *) surface;
889     if ((!local_os2_surface) ||
890         (local_os2_surface->base.backend != &cairo_os2_surface_backend))
891     {
892         /* Invalid parameter (wrong surface)! */
893         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
894     }
895 
896     if ((new_width <= 0) ||
897         (new_height <= 0))
898     {
899         /* Invalid size! */
900         return _cairo_error (CAIRO_STATUS_INVALID_SIZE);
901     }
902 
903     /* Allocate memory for new stuffs */
904     pchNewPixels = (unsigned char *) _buffer_alloc (new_height, new_width, 4);
905     if (!pchNewPixels) {
906         /* Not enough memory for the pixels!
907          * Everything remains the same!
908          */
909         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
910     }
911 
912     /* Create image surface from new pixel array */
913     pNewImageSurface = (cairo_image_surface_t *)
914         cairo_image_surface_create_for_data (pchNewPixels,
915                                              CAIRO_FORMAT_ARGB32,
916                                              new_width,      /* Width */
917                                              new_height,     /* Height */
918                                              new_width * 4); /* Rowstride */
919 
920     if (pNewImageSurface->base.status) {
921         /* Could not create image surface!
922          * Everything remains the same!
923          */
924         _buffer_free (pchNewPixels);
925         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
926     }
927 
928     /* Okay, new memory allocated, so it's time to swap old buffers
929      * to new ones!
930      */
931     if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)!=NO_ERROR) {
932         /* Could not get mutex!
933          * Everything remains the same!
934          */
935         cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface);
936         _buffer_free (pchNewPixels);
937         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
938     }
939 
940     /* We have to make sure that we won't destroy a surface which
941      * is lent to some other code (Cairo is drawing into it)!
942      */
943     while (local_os2_surface->pixel_array_lend_count > 0) {
944         ULONG ulPostCount;
945         DosResetEventSem (local_os2_surface->hev_pixel_array_came_back, &ulPostCount);
946         DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
947         /* Wait for somebody to return the pixels! */
948         rc = DosWaitEventSem (local_os2_surface->hev_pixel_array_came_back, timeout);
949         if (rc != NO_ERROR) {
950             /* Either timeout or something wrong... Exit. */
951             cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface);
952             _buffer_free (pchNewPixels);
953             return _cairo_error (CAIRO_STATUS_NO_MEMORY);
954         }
955         /* Okay, grab mutex and check counter again! */
956         if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
957             != NO_ERROR)
958         {
959             /* Could not get mutex!
960              * Everything remains the same!
961              */
962             cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface);
963             _buffer_free (pchNewPixels);
964             return _cairo_error (CAIRO_STATUS_NO_MEMORY);
965         }
966     }
967 
968     /* Destroy old image surface */
969     cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface));
970     /* Destroy old pixel buffer */
971     _buffer_free (local_os2_surface->pixels);
972     /* Set new image surface */
973     local_os2_surface->image_surface = pNewImageSurface;
974     /* Set new pixel buffer */
975     local_os2_surface->pixels = pchNewPixels;
976     /* Change bitmap2 structure */
977     local_os2_surface->bitmap_info.cx = new_width;
978     local_os2_surface->bitmap_info.cy = new_height;
979 
980     DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
981     return CAIRO_STATUS_SUCCESS;
982 }
983 
984 /**
985  * cairo_os2_surface_refresh_window:
986  * @surface: the cairo surface to refresh
987  * @hps_begin_paint: the presentation handle of the window to refresh
988  * @prcl_begin_paint_rect: the rectangle to redraw
989  *
990  * This function can be used to force a repaint of a given area of the client
991  * window. It should usually be called from the WM_PAINT processing of the
992  * window procedure. However, it can be called any time a given part of the
993  * window has to be updated.
994  *
995  * The HPS and RECTL to be passed can be taken from the usual WinBeginPaint call
996  * of the window procedure, but you can also get the HPS using WinGetPS, and you
997  * can assemble your own update rectangle by hand.
998  * If hps_begin_paint is %NULL, the function will use the HPS passed into
999  * cairo_os2_surface_create(). If @prcl_begin_paint_rect is %NULL, the function
1000  * will query the current window size and repaint the whole window.
1001  *
1002  * Cairo assumes that if you set the HWND to the surface using
1003  * cairo_os2_surface_set_hwnd(), this function will be called by the application
1004  * every time it gets a WM_PAINT for that HWND. If the HWND is set in the
1005  * surface, Cairo uses this function to handle dirty areas too.
1006  *
1007  * Since: 1.4
1008  **/
1009 void
cairo_os2_surface_refresh_window(cairo_surface_t * surface,HPS hps_begin_paint,PRECTL prcl_begin_paint_rect)1010 cairo_os2_surface_refresh_window (cairo_surface_t *surface,
1011                                   HPS              hps_begin_paint,
1012                                   PRECTL           prcl_begin_paint_rect)
1013 {
1014     cairo_os2_surface_t *local_os2_surface;
1015     RECTL rclTemp;
1016     HPS hpsTemp = 0;
1017 
1018     local_os2_surface = (cairo_os2_surface_t *) surface;
1019     if ((!local_os2_surface) ||
1020         (local_os2_surface->base.backend != &cairo_os2_surface_backend))
1021     {
1022         /* Invalid parameter (wrong surface)! */
1023         return;
1024     }
1025 
1026     /* If an HPS wasn't provided, see if we can get one. */
1027     if (!hps_begin_paint) {
1028         hps_begin_paint = local_os2_surface->hps_client_window;
1029         if (!hps_begin_paint) {
1030             if (local_os2_surface->hwnd_client_window) {
1031                 hpsTemp = WinGetPS(local_os2_surface->hwnd_client_window);
1032                 hps_begin_paint = hpsTemp;
1033             }
1034             /* No HPS & no way to get one, so exit */
1035             if (!hps_begin_paint)
1036                 return;
1037         }
1038     }
1039 
1040     if (prcl_begin_paint_rect == NULL) {
1041         /* Update the whole window! */
1042         rclTemp.xLeft = 0;
1043         rclTemp.xRight = local_os2_surface->bitmap_info.cx;
1044         rclTemp.yTop = local_os2_surface->bitmap_info.cy;
1045         rclTemp.yBottom = 0;
1046     } else {
1047         /* Use the rectangle we got passed as parameter! */
1048         rclTemp.xLeft = prcl_begin_paint_rect->xLeft;
1049         rclTemp.xRight = prcl_begin_paint_rect->xRight;
1050         rclTemp.yTop = local_os2_surface->bitmap_info.cy - prcl_begin_paint_rect->yBottom;
1051         rclTemp.yBottom = local_os2_surface->bitmap_info.cy - prcl_begin_paint_rect->yTop ;
1052     }
1053 
1054     /* Get mutex, we'll work with the pixel array! */
1055     if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
1056         != NO_ERROR)
1057     {
1058         /* Could not get mutex! */
1059         if (hpsTemp)
1060             WinReleasePS(hpsTemp);
1061         return;
1062     }
1063 
1064     if ((local_os2_surface->dirty_area_present) &&
1065         (local_os2_surface->rcl_dirty_area.xLeft == rclTemp.xLeft) &&
1066         (local_os2_surface->rcl_dirty_area.xRight == rclTemp.xRight) &&
1067         (local_os2_surface->rcl_dirty_area.yTop == rclTemp.yTop) &&
1068         (local_os2_surface->rcl_dirty_area.yBottom == rclTemp.yBottom))
1069     {
1070         /* Aha, this call was because of a dirty area, so in this case we
1071          * have to blit the pixels from the screen to the surface!
1072          */
1073         local_os2_surface->dirty_area_present = FALSE;
1074         _cairo_os2_surface_get_pixels_from_screen (local_os2_surface,
1075                                                    hps_begin_paint,
1076                                                    &rclTemp);
1077     } else {
1078         /* Okay, we have the surface, have the HPS
1079          * (might be from WinBeginPaint () or from WinGetPS () )
1080          * Now blit there the stuffs!
1081          */
1082         _cairo_os2_surface_blit_pixels (local_os2_surface,
1083                                         hps_begin_paint,
1084                                         &rclTemp);
1085     }
1086 
1087     DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
1088 
1089     if (hpsTemp)
1090         WinReleasePS(hpsTemp);
1091 }
1092 
1093 static cairo_status_t
_cairo_os2_surface_finish(void * abstract_surface)1094 _cairo_os2_surface_finish (void *abstract_surface)
1095 {
1096     cairo_os2_surface_t *local_os2_surface;
1097 
1098     local_os2_surface = (cairo_os2_surface_t *) abstract_surface;
1099     if ((!local_os2_surface) ||
1100         (local_os2_surface->base.backend != &cairo_os2_surface_backend))
1101     {
1102         /* Invalid parameter (wrong surface)! */
1103         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1104     }
1105 
1106     DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
1107 
1108     /* Destroy old image surface */
1109     cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface));
1110     /* Destroy old pixel buffer */
1111     _buffer_free (local_os2_surface->pixels);
1112     DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields);
1113     DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back);
1114 
1115     /* The memory itself will be free'd by the cairo_surface_destroy ()
1116      * who called us.
1117      */
1118 
1119     return CAIRO_STATUS_SUCCESS;
1120 }
1121 
1122 /**
1123  * cairo_os2_surface_set_hwnd:
1124  * @surface: the cairo surface to associate with the window handle
1125  * @hwnd_client_window: the window handle of the client window
1126  *
1127  * Sets window handle for surface; the caller retains ownership of the window.
1128  * If Cairo wants to blit into the window because it is set to blit as the
1129  * surface changes (see cairo_os2_surface_set_manual_window_refresh()), then
1130  * there are two ways it can choose:
1131  * If it knows the HWND of the surface, then it invalidates that area, so the
1132  * application will get a WM_PAINT message and it can call
1133  * cairo_os2_surface_refresh_window() to redraw that area. Otherwise cairo itself
1134  * will use the HPS it got at surface creation time, and blit the pixels itself.
1135  * It's also a solution, but experience shows that if this happens from a non-PM
1136  * thread, then it can screw up PM internals.
1137  *
1138  * So, best solution is to set the HWND for the surface after the surface
1139  * creation, so every blit will be done from application's message processing
1140  * loop, which is the safest way to do.
1141  *
1142  * Since: 1.4
1143  **/
1144 void
cairo_os2_surface_set_hwnd(cairo_surface_t * surface,HWND hwnd_client_window)1145 cairo_os2_surface_set_hwnd (cairo_surface_t *surface,
1146                             HWND             hwnd_client_window)
1147 {
1148     cairo_os2_surface_t *local_os2_surface;
1149 
1150     local_os2_surface = (cairo_os2_surface_t *) surface;
1151     if ((!local_os2_surface) ||
1152         (local_os2_surface->base.backend != &cairo_os2_surface_backend))
1153     {
1154         /* Invalid parameter (wrong surface)! */
1155         return;
1156     }
1157 
1158     if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
1159         != NO_ERROR)
1160     {
1161         /* Could not get mutex! */
1162         return;
1163     }
1164 
1165     local_os2_surface->hwnd_client_window = hwnd_client_window;
1166 
1167     DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
1168 }
1169 
1170 /**
1171  * cairo_os2_surface_set_manual_window_refresh:
1172  * @surface: the cairo surface to set the refresh mode for
1173  * @manual_refresh: the switch for manual surface refresh
1174  *
1175  * This API can tell Cairo if it should show every change to this surface
1176  * immediately in the window or if it should be cached and will only be visible
1177  * once the user calls cairo_os2_surface_refresh_window() explicitly. If the
1178  * HWND was not set in the cairo surface, then the HPS will be used to blit the
1179  * graphics. Otherwise it will invalidate the given window region so the user
1180  * will get the WM_PAINT message to redraw that area of the window.
1181  *
1182  * So, if you're only interested in displaying the final result after several
1183  * drawing operations, you might get better performance if you put the surface
1184  * into manual refresh mode by passing a true value to this function. Then call
1185  * cairo_os2_surface_refresh() whenever desired.
1186  *
1187  * Since: 1.4
1188  **/
1189 void
cairo_os2_surface_set_manual_window_refresh(cairo_surface_t * surface,cairo_bool_t manual_refresh)1190 cairo_os2_surface_set_manual_window_refresh (cairo_surface_t *surface,
1191                                              cairo_bool_t     manual_refresh)
1192 {
1193     cairo_os2_surface_t *local_os2_surface;
1194 
1195     local_os2_surface = (cairo_os2_surface_t *) surface;
1196     if ((!local_os2_surface) ||
1197         (local_os2_surface->base.backend != &cairo_os2_surface_backend))
1198     {
1199         /* Invalid parameter (wrong surface)! */
1200         return;
1201     }
1202 
1203     local_os2_surface->blit_as_changes = !manual_refresh;
1204 }
1205 
1206 /**
1207  * cairo_os2_surface_get_manual_window_refresh:
1208  * @surface: the cairo surface to query the refresh mode from
1209  *
1210  * This space left intentionally blank.
1211  *
1212  * Return value: current refresh mode of the surface (true by default)
1213  *
1214  * Since: 1.4
1215  **/
1216 cairo_bool_t
cairo_os2_surface_get_manual_window_refresh(cairo_surface_t * surface)1217 cairo_os2_surface_get_manual_window_refresh (cairo_surface_t *surface)
1218 {
1219     cairo_os2_surface_t *local_os2_surface;
1220 
1221     local_os2_surface = (cairo_os2_surface_t *) surface;
1222     if ((!local_os2_surface) ||
1223         (local_os2_surface->base.backend != &cairo_os2_surface_backend))
1224     {
1225         /* Invalid parameter (wrong surface)! */
1226         return FALSE;
1227     }
1228 
1229     return !(local_os2_surface->blit_as_changes);
1230 }
1231 
1232 /**
1233  * cairo_os2_surface_get_hps:
1234  * @surface: the cairo surface to be querued
1235  * @hps: HPS currently associated with the surface (if any)
1236  *
1237  * This API retrieves the HPS associated with the surface.
1238  *
1239  * Return value: %CAIRO_STATUS_SUCCESS if the hps could be retrieved,
1240  * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface,
1241  * %CAIRO_STATUS_NULL_POINTER if the hps argument is null
1242  *
1243  * Since: 1.10
1244  **/
1245 cairo_status_t
cairo_os2_surface_get_hps(cairo_surface_t * surface,HPS * hps)1246 cairo_os2_surface_get_hps (cairo_surface_t *surface,
1247                            HPS             *hps)
1248 {
1249     cairo_os2_surface_t *local_os2_surface;
1250 
1251     local_os2_surface = (cairo_os2_surface_t *) surface;
1252     if ((!local_os2_surface) ||
1253         (local_os2_surface->base.backend != &cairo_os2_surface_backend))
1254     {
1255         /* Invalid parameter (wrong surface)! */
1256         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1257     }
1258     if (!hps)
1259     {
1260         return _cairo_error (CAIRO_STATUS_NULL_POINTER);
1261     }
1262     *hps = local_os2_surface->hps_client_window;
1263 
1264     return CAIRO_STATUS_SUCCESS;
1265 }
1266 
1267 /**
1268  * cairo_os2_surface_set_hps:
1269  * @surface: the cairo surface to associate with the HPS
1270  * @hps: new HPS to be associated with the surface (the HPS may be null)
1271  *
1272  * This API replaces the HPS associated with the surface with a new one.
1273  * The caller retains ownership of the HPS and must dispose of it after
1274  * the surface has been destroyed or it has been replaced by another
1275  * call to this function.
1276  *
1277  * Return value: %CAIRO_STATUS_SUCCESS if the hps could be replaced,
1278  * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface,
1279  *
1280  * Since: 1.10
1281  **/
1282 cairo_status_t
cairo_os2_surface_set_hps(cairo_surface_t * surface,HPS hps)1283 cairo_os2_surface_set_hps (cairo_surface_t *surface,
1284                            HPS              hps)
1285 {
1286     cairo_os2_surface_t *local_os2_surface;
1287 
1288     local_os2_surface = (cairo_os2_surface_t *) surface;
1289     if ((!local_os2_surface) ||
1290         (local_os2_surface->base.backend != &cairo_os2_surface_backend))
1291     {
1292         /* Invalid parameter (wrong surface)! */
1293         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1294     }
1295     local_os2_surface->hps_client_window = hps;
1296 
1297     return CAIRO_STATUS_SUCCESS;
1298 }
1299 
1300 static cairo_status_t
_cairo_os2_surface_mark_dirty_rectangle(void * surface,int x,int y,int width,int height)1301 _cairo_os2_surface_mark_dirty_rectangle (void *surface,
1302                                          int   x,
1303                                          int   y,
1304                                          int   width,
1305                                          int   height)
1306 {
1307     cairo_os2_surface_t *local_os2_surface;
1308     RECTL rclToBlit;
1309 
1310     local_os2_surface = (cairo_os2_surface_t *) surface;
1311     if ((!local_os2_surface) ||
1312         (local_os2_surface->base.backend != &cairo_os2_surface_backend))
1313     {
1314         /* Invalid parameter (wrong surface)! */
1315         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1316     }
1317 
1318     /* Get mutex, we'll work with the pixel array! */
1319     if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
1320         != NO_ERROR)
1321     {
1322         /* Could not get mutex! */
1323         return CAIRO_STATUS_NO_MEMORY;
1324     }
1325 
1326     /* Check for defaults */
1327     if (width < 0)
1328         width = local_os2_surface->bitmap_info.cx;
1329     if (height < 0)
1330         height = local_os2_surface->bitmap_info.cy;
1331 
1332     if (local_os2_surface->hwnd_client_window) {
1333         /* We know the HWND, so let's invalidate the window region,
1334          * so the application will redraw itself, using the
1335          * cairo_os2_surface_refresh_window () API from its own PM thread.
1336          * From that function we'll note that it's not a redraw but a
1337          * dirty-rectangle deal stuff, so we'll handle the things from
1338          * there.
1339          *
1340          * This is the safe method, which should be preferred every time.
1341          */
1342         rclToBlit.xLeft = x;
1343         rclToBlit.xRight = x + width; /* Noninclusive */
1344         rclToBlit.yTop = local_os2_surface->bitmap_info.cy - (y);
1345         rclToBlit.yBottom = local_os2_surface->bitmap_info.cy - (y + height); /* Noninclusive */
1346 
1347 #if 0
1348         if (local_os2_surface->dirty_area_present) {
1349             /* Yikes, there is already a dirty area which should be
1350              * cleaned up, but we'll overwrite it. Sorry.
1351              * TODO: Something clever should be done here.
1352              */
1353         }
1354 #endif
1355 
1356         /* Set up dirty area reminder stuff */
1357         memcpy (&(local_os2_surface->rcl_dirty_area), &rclToBlit, sizeof (RECTL));
1358         local_os2_surface->dirty_area_present = TRUE;
1359 
1360         /* Invalidate window area */
1361         WinInvalidateRect (local_os2_surface->hwnd_client_window,
1362                            &rclToBlit,
1363                            FALSE);
1364     } else {
1365         /* We don't know the HWND, so try to blit the pixels from here!
1366          * Please note that it can be problematic if this is not the PM thread!
1367          *
1368          * It can cause internal PM stuffs to be scewed up, for some reason.
1369          * Please always tell the HWND to the surface using the
1370          * cairo_os2_surface_set_hwnd () API, and call cairo_os2_surface_refresh_window ()
1371          * from your WM_PAINT, if it's possible!
1372          */
1373 
1374         rclToBlit.xLeft = x;
1375         rclToBlit.xRight = x + width; /* Noninclusive */
1376         rclToBlit.yBottom = y;
1377         rclToBlit.yTop = y + height; /* Noninclusive */
1378         /* Now get the pixels from the screen! */
1379         _cairo_os2_surface_get_pixels_from_screen (local_os2_surface,
1380                                                    local_os2_surface->hps_client_window,
1381                                                    &rclToBlit);
1382     }
1383 
1384     DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
1385 
1386     return CAIRO_STATUS_SUCCESS;
1387 }
1388 
1389 static const cairo_surface_backend_t cairo_os2_surface_backend = {
1390     CAIRO_SURFACE_TYPE_OS2,
1391     _cairo_os2_surface_finish,
1392     _cairo_default_context_create,
1393 
1394     NULL, /* create_similar */
1395     NULL, /* create_similar_image */
1396     _cairo_os2_surface_map_to_image,
1397     _cairo_os2_surface_unmap_image,
1398 
1399     _cairo_surface_default_source,
1400     _cairo_os2_surface_acquire_source_image,
1401     _cairo_os2_surface_release_source_image,
1402     NULL, /* snapshot */
1403 
1404     _cairo_os2_surface_get_extents,
1405     NULL, /* get_font_options */
1406 
1407     NULL, /* flush */
1408     _cairo_os2_surface_mark_dirty_rectangle,
1409 
1410     _cairo_surface_fallback_paint,
1411     _cairo_surface_fallback_mask,
1412     _cairo_surface_fallback_fill,
1413     _cairo_surface_fallback_stroke,
1414     NULL, /* fill/stroke */
1415     _cairo_surface_fallback_glyphs,
1416 };
1417