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