2 * (C) Copyright IBM Corporation 2004
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * on the rights to use, copy, modify, merge, publish, distribute, sub
9 * license, and/or sell copies of the Software, and to permit persons to whom
10 * the Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
19 * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
27 * Implementation of pbuffer related functions.
29 * \author Ian Romanick <idr@us.ibm.com>
33 #include "glxclient.h"
34 #include <X11/extensions/extutil.h>
35 #include <X11/extensions/Xext.h>
38 #include "glxextensions.h"
40 #ifdef GLX_USE_APPLEGL
42 #include "apple_glx_drawable.h"
43 #include "glx_error.h"
46 #define WARN_ONCE_GLX_1_3(a, b) { \
47 static int warned=1; \
49 warn_GLX_1_3((a), b ); \
55 * Emit a warning when clients use GLX 1.3 functions on pre-1.3 systems.
58 warn_GLX_1_3(Display
* dpy
, const char *function_name
)
60 __GLXdisplayPrivate
*priv
= __glXInitialize(dpy
);
62 if (priv
->minorVersion
< 3) {
64 "WARNING: Application calling GLX 1.3 function \"%s\" "
65 "when GLX 1.3 is not supported! This is an application bug!\n",
70 #ifndef GLX_USE_APPLEGL
72 * Change a drawable's attribute.
74 * This function is used to implement \c glXSelectEvent and
75 * \c glXSelectEventSGIX.
78 * This function dynamically determines whether to use the SGIX_pbuffer
79 * version of the protocol or the GLX 1.3 version of the protocol.
82 * This function needs to be modified to work with direct-rendering drivers.
85 ChangeDrawableAttribute(Display
* dpy
, GLXDrawable drawable
,
86 const CARD32
* attribs
, size_t num_attribs
)
88 __GLXdisplayPrivate
*priv
= __glXInitialize(dpy
);
92 if ((dpy
== NULL
) || (drawable
== 0)) {
96 opcode
= __glXSetupForCommand(dpy
);
102 if ((priv
->majorVersion
> 1) || (priv
->minorVersion
>= 3)) {
103 xGLXChangeDrawableAttributesReq
*req
;
105 GetReqExtra(GLXChangeDrawableAttributes
, 8 + (8 * num_attribs
), req
);
106 output
= (CARD32
*) (req
+ 1);
108 req
->reqType
= opcode
;
109 req
->glxCode
= X_GLXChangeDrawableAttributes
;
110 req
->drawable
= drawable
;
111 req
->numAttribs
= (CARD32
) num_attribs
;
114 xGLXVendorPrivateWithReplyReq
*vpreq
;
116 GetReqExtra(GLXVendorPrivateWithReply
, 4 + (8 * num_attribs
), vpreq
);
117 output
= (CARD32
*) (vpreq
+ 1);
119 vpreq
->reqType
= opcode
;
120 vpreq
->glxCode
= X_GLXVendorPrivateWithReply
;
121 vpreq
->vendorCode
= X_GLXvop_ChangeDrawableAttributesSGIX
;
123 output
[0] = (CARD32
) drawable
;
127 (void) memcpy(output
, attribs
, sizeof(CARD32
) * 2 * num_attribs
);
139 * This function is used to implement \c glXDestroyPbuffer and
140 * \c glXDestroyGLXPbufferSGIX.
143 * This function dynamically determines whether to use the SGIX_pbuffer
144 * version of the protocol or the GLX 1.3 version of the protocol.
147 * This function needs to be modified to work with direct-rendering drivers.
150 DestroyPbuffer(Display
* dpy
, GLXDrawable drawable
)
152 __GLXdisplayPrivate
*priv
= __glXInitialize(dpy
);
155 if ((dpy
== NULL
) || (drawable
== 0)) {
159 opcode
= __glXSetupForCommand(dpy
);
165 if ((priv
->majorVersion
> 1) || (priv
->minorVersion
>= 3)) {
166 xGLXDestroyPbufferReq
*req
;
168 GetReq(GLXDestroyPbuffer
, req
);
169 req
->reqType
= opcode
;
170 req
->glxCode
= X_GLXDestroyPbuffer
;
171 req
->pbuffer
= (GLXPbuffer
) drawable
;
174 xGLXVendorPrivateWithReplyReq
*vpreq
;
177 GetReqExtra(GLXVendorPrivateWithReply
, 4, vpreq
);
178 data
= (CARD32
*) (vpreq
+ 1);
180 data
[0] = (CARD32
) drawable
;
182 vpreq
->reqType
= opcode
;
183 vpreq
->glxCode
= X_GLXVendorPrivateWithReply
;
184 vpreq
->vendorCode
= X_GLXvop_DestroyGLXPbufferSGIX
;
194 #ifdef GLX_DIRECT_RENDERING
196 determineTextureTarget(const int *attribs
, int numAttribs
)
201 for (i
= 0; i
< numAttribs
; i
++) {
202 if (attribs
[2 * i
] == GLX_TEXTURE_TARGET_EXT
) {
203 switch (attribs
[2 * i
+ 1]) {
204 case GLX_TEXTURE_2D_EXT
:
205 target
= GL_TEXTURE_2D
;
207 case GLX_TEXTURE_RECTANGLE_EXT
:
208 target
= GL_TEXTURE_RECTANGLE_ARB
;
219 determineTextureFormat(const int *attribs
, int numAttribs
)
223 for (i
= 0; i
< numAttribs
; i
++) {
224 if (attribs
[2 * i
] == GLX_TEXTURE_FORMAT_EXT
)
225 return attribs
[2 * i
+ 1];
233 * Get a drawable's attribute.
235 * This function is used to implement \c glXGetSelectedEvent and
236 * \c glXGetSelectedEventSGIX.
239 * This function dynamically determines whether to use the SGIX_pbuffer
240 * version of the protocol or the GLX 1.3 version of the protocol.
243 * The number of attributes returned is likely to be small, probably less than
244 * 10. Given that, this routine should try to use an array on the stack to
245 * capture the reply rather than always calling Xmalloc.
248 * This function needs to be modified to work with direct-rendering drivers.
251 GetDrawableAttribute(Display
* dpy
, GLXDrawable drawable
,
252 int attribute
, unsigned int *value
)
254 __GLXdisplayPrivate
*priv
;
255 xGLXGetDrawableAttributesReply reply
;
260 unsigned int num_attributes
;
261 GLboolean use_glx_1_3
;
263 if ((dpy
== NULL
) || (drawable
== 0)) {
267 priv
= __glXInitialize(dpy
);
268 use_glx_1_3
= ((priv
->majorVersion
> 1) || (priv
->minorVersion
>= 3));
273 opcode
= __glXSetupForCommand(dpy
);
280 xGLXGetDrawableAttributesReq
*req
;
282 GetReqExtra(GLXGetDrawableAttributes
, 4, req
);
283 req
->reqType
= opcode
;
284 req
->glxCode
= X_GLXGetDrawableAttributes
;
285 req
->drawable
= drawable
;
288 xGLXVendorPrivateWithReplyReq
*vpreq
;
290 GetReqExtra(GLXVendorPrivateWithReply
, 4, vpreq
);
291 data
= (CARD32
*) (vpreq
+ 1);
292 data
[0] = (CARD32
) drawable
;
294 vpreq
->reqType
= opcode
;
295 vpreq
->glxCode
= X_GLXVendorPrivateWithReply
;
296 vpreq
->vendorCode
= X_GLXvop_GetDrawableAttributesSGIX
;
299 _XReply(dpy
, (xReply
*) & reply
, 0, False
);
301 if (reply
.type
== X_Error
) {
307 length
= reply
.length
;
309 num_attributes
= (use_glx_1_3
) ? reply
.numAttribs
: length
/ 2;
310 data
= (CARD32
*) Xmalloc(length
* sizeof(CARD32
));
312 /* Throw data on the floor */
313 _XEatData(dpy
, length
);
316 _XRead(dpy
, (char *) data
, length
* sizeof(CARD32
));
318 /* Search the set of returned attributes for the attribute requested by
321 for (i
= 0; i
< num_attributes
; i
++) {
322 if (data
[i
* 2] == attribute
) {
323 *value
= data
[(i
* 2) + 1];
328 #if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL)
330 __GLXDRIdrawable
*pdraw
= GetGLXDRIDrawable(dpy
, drawable
, NULL
);
332 if (pdraw
!= NULL
&& !pdraw
->textureTarget
)
333 pdraw
->textureTarget
=
334 determineTextureTarget((const int *) data
, num_attributes
);
335 if (pdraw
!= NULL
&& !pdraw
->textureFormat
)
336 pdraw
->textureFormat
=
337 determineTextureFormat((const int *) data
, num_attributes
);
352 * Create a non-pbuffer GLX drawable.
355 * This function needs to be modified to work with direct-rendering drivers.
358 CreateDrawable(Display
* dpy
, const __GLcontextModes
* fbconfig
,
359 Drawable drawable
, const int *attrib_list
, CARD8 glxCode
)
361 xGLXCreateWindowReq
*req
;
368 while (attrib_list
[i
* 2] != None
)
372 opcode
= __glXSetupForCommand(dpy
);
377 GetReqExtra(GLXCreateWindow
, 8 * i
, req
);
378 data
= (CARD32
*) (req
+ 1);
380 req
->reqType
= opcode
;
381 req
->glxCode
= glxCode
;
382 req
->screen
= (CARD32
) fbconfig
->screen
;
383 req
->fbconfig
= fbconfig
->fbconfigID
;
384 req
->window
= (CARD32
) drawable
;
385 req
->glxwindow
= (GLXWindow
) XAllocID(dpy
);
386 req
->numAttribs
= (CARD32
) i
;
389 memcpy(data
, attrib_list
, 8 * i
);
394 #if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL)
396 /* FIXME: Maybe delay __DRIdrawable creation until the drawable
397 * is actually bound to a context... */
399 __GLXdisplayPrivate
*const priv
= __glXInitialize(dpy
);
400 __GLXDRIdrawable
*pdraw
;
401 __GLXscreenConfigs
*psc
;
403 psc
= &priv
->screenConfigs
[fbconfig
->screen
];
404 if (psc
->driScreen
== NULL
)
406 pdraw
= psc
->driScreen
->createDrawable(psc
, drawable
,
407 req
->glxwindow
, fbconfig
);
409 fprintf(stderr
, "failed to create drawable\n");
413 if (__glxHashInsert(psc
->drawHash
, req
->glxwindow
, pdraw
)) {
414 (*pdraw
->destroyDrawable
) (pdraw
);
415 return None
; /* FIXME: Check what we're supposed to do here... */
418 pdraw
->textureTarget
= determineTextureTarget(attrib_list
, i
);
419 pdraw
->textureFormat
= determineTextureFormat(attrib_list
, i
);
423 return (GLXDrawable
) req
->glxwindow
;
428 * Destroy a non-pbuffer GLX drawable.
431 * This function needs to be modified to work with direct-rendering drivers.
434 DestroyDrawable(Display
* dpy
, GLXDrawable drawable
, CARD32 glxCode
)
436 xGLXDestroyPbufferReq
*req
;
439 if ((dpy
== NULL
) || (drawable
== 0)) {
444 opcode
= __glXSetupForCommand(dpy
);
450 GetReqExtra(GLXDestroyPbuffer
, 4, req
);
451 req
->reqType
= opcode
;
452 req
->glxCode
= glxCode
;
453 req
->pbuffer
= (GLXPbuffer
) drawable
;
458 #if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL)
461 __GLXdisplayPrivate
*const priv
= __glXInitialize(dpy
);
462 __GLXDRIdrawable
*pdraw
= GetGLXDRIDrawable(dpy
, drawable
, &screen
);
463 __GLXscreenConfigs
*psc
= &priv
->screenConfigs
[screen
];
466 (*pdraw
->destroyDrawable
) (pdraw
);
467 __glxHashDelete(psc
->drawHash
, drawable
);
479 * This function is used to implement \c glXCreatePbuffer and
480 * \c glXCreateGLXPbufferSGIX.
483 * This function dynamically determines whether to use the SGIX_pbuffer
484 * version of the protocol or the GLX 1.3 version of the protocol.
487 * This function needs to be modified to work with direct-rendering drivers.
490 CreatePbuffer(Display
* dpy
, const __GLcontextModes
* fbconfig
,
491 unsigned int width
, unsigned int height
,
492 const int *attrib_list
, GLboolean size_in_attribs
)
494 __GLXdisplayPrivate
*priv
= __glXInitialize(dpy
);
502 while (attrib_list
[i
* 2])
506 opcode
= __glXSetupForCommand(dpy
);
513 if ((priv
->majorVersion
> 1) || (priv
->minorVersion
>= 3)) {
514 xGLXCreatePbufferReq
*req
;
515 unsigned int extra
= (size_in_attribs
) ? 0 : 2;
517 GetReqExtra(GLXCreatePbuffer
, (8 * (i
+ extra
)), req
);
518 data
= (CARD32
*) (req
+ 1);
520 req
->reqType
= opcode
;
521 req
->glxCode
= X_GLXCreatePbuffer
;
522 req
->screen
= (CARD32
) fbconfig
->screen
;
523 req
->fbconfig
= fbconfig
->fbconfigID
;
524 req
->pbuffer
= (GLXPbuffer
) id
;
525 req
->numAttribs
= (CARD32
) (i
+ extra
);
527 if (!size_in_attribs
) {
528 data
[(2 * i
) + 0] = GLX_PBUFFER_WIDTH
;
529 data
[(2 * i
) + 1] = width
;
530 data
[(2 * i
) + 2] = GLX_PBUFFER_HEIGHT
;
531 data
[(2 * i
) + 3] = height
;
536 xGLXVendorPrivateReq
*vpreq
;
538 GetReqExtra(GLXVendorPrivate
, 20 + (8 * i
), vpreq
);
539 data
= (CARD32
*) (vpreq
+ 1);
541 vpreq
->reqType
= opcode
;
542 vpreq
->glxCode
= X_GLXVendorPrivate
;
543 vpreq
->vendorCode
= X_GLXvop_CreateGLXPbufferSGIX
;
545 data
[0] = (CARD32
) fbconfig
->screen
;
546 data
[1] = (CARD32
) fbconfig
->fbconfigID
;
547 data
[2] = (CARD32
) id
;
548 data
[3] = (CARD32
) width
;
549 data
[4] = (CARD32
) height
;
553 (void) memcpy(data
, attrib_list
, sizeof(CARD32
) * 2 * i
);
562 * Create a new pbuffer.
564 PUBLIC GLXPbufferSGIX
565 glXCreateGLXPbufferSGIX(Display
* dpy
, GLXFBConfigSGIX config
,
566 unsigned int width
, unsigned int height
,
569 return (GLXPbufferSGIX
) CreatePbuffer(dpy
, (__GLcontextModes
*) config
,
571 attrib_list
, GL_FALSE
);
574 #endif /* GLX_USE_APPLEGL */
577 * Create a new pbuffer.
580 glXCreatePbuffer(Display
* dpy
, GLXFBConfig config
, const int *attrib_list
)
582 int i
, width
, height
;
583 #ifdef GLX_USE_APPLEGL
591 WARN_ONCE_GLX_1_3(dpy
, __func__
);
593 #ifdef GLX_USE_APPLEGL
594 for (i
= 0; attrib_list
[i
]; ++i
) {
595 switch (attrib_list
[i
]) {
596 case GLX_PBUFFER_WIDTH
:
597 width
= attrib_list
[i
+ 1];
601 case GLX_PBUFFER_HEIGHT
:
602 height
= attrib_list
[i
+ 1];
606 case GLX_LARGEST_PBUFFER
:
607 /* This is a hint we should probably handle, but how? */
611 case GLX_PRESERVED_CONTENTS
:
612 /* The contents are always preserved with AppleSGLX with CGL. */
621 if (apple_glx_pbuffer_create(dpy
, config
, width
, height
, &errorcode
,
624 * apple_glx_pbuffer_create only sets the errorcode to core X11
627 __glXSendError(dpy
, errorcode
, 0, X_GLXCreatePbuffer
, true);
634 for (i
= 0; attrib_list
[i
* 2]; i
++) {
635 switch (attrib_list
[i
* 2]) {
636 case GLX_PBUFFER_WIDTH
:
637 width
= attrib_list
[i
* 2 + 1];
639 case GLX_PBUFFER_HEIGHT
:
640 height
= attrib_list
[i
* 2 + 1];
645 return (GLXPbuffer
) CreatePbuffer(dpy
, (__GLcontextModes
*) config
,
646 width
, height
, attrib_list
, GL_TRUE
);
652 * Destroy an existing pbuffer.
655 glXDestroyPbuffer(Display
* dpy
, GLXPbuffer pbuf
)
657 #ifdef GLX_USE_APPLEGL
658 if (apple_glx_pbuffer_destroy(dpy
, pbuf
)) {
659 __glXSendError(dpy
, GLXBadPbuffer
, pbuf
, X_GLXDestroyPbuffer
, false);
662 DestroyPbuffer(dpy
, pbuf
);
668 * Query an attribute of a drawable.
671 glXQueryDrawable(Display
* dpy
, GLXDrawable drawable
,
672 int attribute
, unsigned int *value
)
674 WARN_ONCE_GLX_1_3(dpy
, __func__
);
675 #ifdef GLX_USE_APPLEGL
678 unsigned int width
, height
, bd
, depth
;
680 if (apple_glx_pixmap_query(drawable
, attribute
, value
))
683 if (apple_glx_pbuffer_query(drawable
, attribute
, value
))
687 * The OpenGL spec states that we should report GLXBadDrawable if
688 * the drawable is invalid, however doing so would require that we
689 * use XSetErrorHandler(), which is known to not be thread safe.
690 * If we use a round-trip call to validate the drawable, there could
691 * be a race, so instead we just opt in favor of letting the
692 * XGetGeometry request fail with a GetGeometry request X error
693 * rather than GLXBadDrawable, in what is hoped to be a rare
694 * case of an invalid drawable. In practice most and possibly all
695 * X11 apps using GLX shouldn't notice a difference.
698 (dpy
, drawable
, &root
, &x
, &y
, &width
, &height
, &bd
, &depth
)) {
710 GetDrawableAttribute(dpy
, drawable
, attribute
, value
);
715 #ifndef GLX_USE_APPLEGL
717 * Query an attribute of a pbuffer.
720 glXQueryGLXPbufferSGIX(Display
* dpy
, GLXPbufferSGIX drawable
,
721 int attribute
, unsigned int *value
)
723 return GetDrawableAttribute(dpy
, drawable
, attribute
, value
);
728 * Select the event mask for a drawable.
731 glXSelectEvent(Display
* dpy
, GLXDrawable drawable
, unsigned long mask
)
733 #ifdef GLX_USE_APPLEGL
734 XWindowAttributes xwattr
;
736 if (apple_glx_pbuffer_set_event_mask(drawable
, mask
))
740 * The spec allows a window, but currently there are no valid
741 * events for a window, so do nothing.
743 if (XGetWindowAttributes(dpy
, drawable
, &xwattr
))
745 /* The drawable seems to be invalid. Report an error. */
747 __glXSendError(dpy
, GLXBadDrawable
, drawable
,
748 X_GLXChangeDrawableAttributes
, false);
752 attribs
[0] = (CARD32
) GLX_EVENT_MASK
;
753 attribs
[1] = (CARD32
) mask
;
755 ChangeDrawableAttribute(dpy
, drawable
, attribs
, 1);
761 * Get the selected event mask for a drawable.
764 glXGetSelectedEvent(Display
* dpy
, GLXDrawable drawable
, unsigned long *mask
)
766 #ifdef GLX_USE_APPLEGL
767 XWindowAttributes xwattr
;
769 if (apple_glx_pbuffer_get_event_mask(drawable
, mask
))
773 * The spec allows a window, but currently there are no valid
774 * events for a window, so do nothing, but set the mask to 0.
776 if (XGetWindowAttributes(dpy
, drawable
, &xwattr
)) {
777 /* The window is valid, so set the mask to 0. */
781 /* The drawable seems to be invalid. Report an error. */
783 __glXSendError(dpy
, GLXBadDrawable
, drawable
, X_GLXGetDrawableAttributes
,
789 /* The non-sense with value is required because on LP64 platforms
790 * sizeof(unsigned int) != sizeof(unsigned long). On little-endian
791 * we could just type-cast the pointer, but why?
794 GetDrawableAttribute(dpy
, drawable
, GLX_EVENT_MASK_SGIX
, &value
);
801 glXCreatePixmap(Display
* dpy
, GLXFBConfig config
, Pixmap pixmap
,
802 const int *attrib_list
)
804 WARN_ONCE_GLX_1_3(dpy
, __func__
);
806 #ifdef GLX_USE_APPLEGL
807 const __GLcontextModes
*modes
= (const __GLcontextModes
*) config
;
809 if (apple_glx_pixmap_create(dpy
, modes
->screen
, pixmap
, modes
))
814 return CreateDrawable(dpy
, (__GLcontextModes
*) config
,
815 (Drawable
) pixmap
, attrib_list
, X_GLXCreatePixmap
);
821 glXCreateWindow(Display
* dpy
, GLXFBConfig config
, Window win
,
822 const int *attrib_list
)
824 WARN_ONCE_GLX_1_3(dpy
, __func__
);
825 #ifdef GLX_USE_APPLEGL
826 XWindowAttributes xwattr
;
827 XVisualInfo
*visinfo
;
829 (void) attrib_list
; /*unused according to GLX 1.4 */
831 XGetWindowAttributes(dpy
, win
, &xwattr
);
833 visinfo
= glXGetVisualFromFBConfig(dpy
, config
);
835 if (NULL
== visinfo
) {
836 __glXSendError(dpy
, GLXBadFBConfig
, 0, X_GLXCreateWindow
, false);
840 if (visinfo
->visualid
!= XVisualIDFromVisual(xwattr
.visual
)) {
841 __glXSendError(dpy
, BadMatch
, 0, X_GLXCreateWindow
, true);
849 return CreateDrawable(dpy
, (__GLcontextModes
*) config
,
850 (Drawable
) win
, attrib_list
, X_GLXCreateWindow
);
856 glXDestroyPixmap(Display
* dpy
, GLXPixmap pixmap
)
858 WARN_ONCE_GLX_1_3(dpy
, __func__
);
859 #ifdef GLX_USE_APPLEGL
860 if (apple_glx_pixmap_destroy(dpy
, pixmap
))
861 __glXSendError(dpy
, GLXBadPixmap
, pixmap
, X_GLXDestroyPixmap
, false);
863 DestroyDrawable(dpy
, (GLXDrawable
) pixmap
, X_GLXDestroyPixmap
);
869 glXDestroyWindow(Display
* dpy
, GLXWindow win
)
871 WARN_ONCE_GLX_1_3(dpy
, __func__
);
872 #ifndef GLX_USE_APPLEGL
873 DestroyDrawable(dpy
, (GLXDrawable
) win
, X_GLXDestroyWindow
);
877 #ifndef GLX_USE_APPLEGL
879 GLX_ALIAS_VOID(glXDestroyGLXPbufferSGIX
,
880 (Display
* dpy
, GLXPbufferSGIX pbuf
),
881 (dpy
, pbuf
), glXDestroyPbuffer
)
884 GLX_ALIAS_VOID(glXSelectEventSGIX
,
885 (Display
* dpy
, GLXDrawable drawable
,
886 unsigned long mask
), (dpy
, drawable
, mask
), glXSelectEvent
)
889 GLX_ALIAS_VOID(glXGetSelectedEventSGIX
,
890 (Display
* dpy
, GLXDrawable drawable
,
891 unsigned long *mask
), (dpy
, drawable
, mask
),