2
* \file miniglx_events.c
3
* \brief Mini GLX client/server communication functions.
4
* \author Keith Whitwell
6
* The Mini GLX interface is a subset of the GLX interface, plus a
7
* minimal set of Xlib functions. This file adds interfaces to
8
* arbitrate a single cliprect between multiple direct rendering
11
* A fairly complete client/server non-blocking communication
12
* mechanism. Probably overkill given that none of our messages
13
* currently exceed 1 byte in length and take place over the
14
* relatively benign channel provided by a Unix domain socket.
18
* Mesa 3-D graphics library
21
* Copyright (C) 1999-2003 Brian Paul All Rights Reserved.
23
* Permission is hereby granted, free of charge, to any person obtaining a
24
* copy of this software and associated documentation files (the "Software"),
25
* to deal in the Software without restriction, including without limitation
26
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
27
* and/or sell copies of the Software, and to permit persons to whom the
28
* Software is furnished to do so, subject to the following conditions:
30
* The above copyright notice and this permission notice shall be included
31
* in all copies or substantial portions of the Software.
33
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
34
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
36
* BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
37
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
38
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
52
#include <sys/ioctl.h>
55
#include <sys/types.h>
57
#include <sys/socket.h>
68
#define MINIGLX_FIFO_NAME "/tmp/miniglx.fifo"
71
* \brief Allocate an XEvent structure on the event queue.
73
* \param dpy the display handle.
75
* \return Pointer to the queued event structure or NULL on failure.
78
* If there is space on the XEvent queue, return a pointer
79
* to the next free event and increment the eventqueue tail value.
80
* Otherwise return null.
82
static XEvent *queue_event( Display *dpy )
84
int incr = (dpy->eventqueue.tail + 1) & MINIGLX_EVENT_QUEUE_MASK;
85
if (incr == dpy->eventqueue.head) {
89
XEvent *ev = &dpy->eventqueue.queue[dpy->eventqueue.tail];
90
dpy->eventqueue.tail = incr;
96
* \brief Dequeue an XEvent and copy it into provided storage.
98
* \param dpy the display handle.
99
* \param event_return pointer to copy the queued event to.
101
* \return True or False depending on success.
104
* If there is a queued XEvent on the queue, copy it to the provided
105
* pointer and increment the eventqueue head value. Otherwise return
108
static int dequeue_event( Display *dpy, XEvent *event_return )
110
if (dpy->eventqueue.tail == dpy->eventqueue.head) {
114
*event_return = dpy->eventqueue.queue[dpy->eventqueue.head];
115
dpy->eventqueue.head += 1;
116
dpy->eventqueue.head &= MINIGLX_EVENT_QUEUE_MASK;
122
* \brief Shutdown a socket connection.
124
* \param dpy the display handle.
125
* \param i the index in dpy->fd of the socket connection.
128
* Shutdown and close the file descriptor. If this is the special
129
* connection in fd[0], issue an error message and exit - there's been
130
* some sort of failure somewhere. Otherwise, let the application
131
* know about whats happened by issuing a DestroyNotify event.
133
static void shut_fd( Display *dpy, int i )
135
if (dpy->fd[i].fd < 0)
138
shutdown (dpy->fd[i].fd, SHUT_RDWR);
139
close (dpy->fd[i].fd);
141
dpy->fd[i].readbuf_count = 0;
142
dpy->fd[i].writebuf_count = 0;
145
fprintf(stderr, "server connection lost\n");
149
/* Pass this to the application as a DestroyNotify event.
151
XEvent *er = queue_event(dpy);
153
er->xdestroywindow.type = DestroyNotify;
154
er->xdestroywindow.serial = 0;
155
er->xdestroywindow.send_event = 0;
156
er->xdestroywindow.display = dpy;
157
er->xdestroywindow.window = (Window)i;
159
drmGetLock(dpy->driverContext.drmFD, 1, 0);
160
drmUnlock(dpy->driverContext.drmFD, 1);
165
* \brief Send a message to a socket connection.
167
* \param dpy the display handle.
168
* \param i the index in dpy->fd of the socket connection.
169
* \param msg the message to send.
170
* \param sz the size of the message
173
* Copy the message to the write buffer for the nominated connection.
174
* This will be actually sent to that file descriptor from
175
* __miniglx_Select().
177
int send_msg( Display *dpy, int i,
178
const void *msg, size_t sz )
180
int cnt = dpy->fd[i].writebuf_count;
181
if (MINIGLX_BUF_SIZE - cnt < sz) {
182
fprintf(stderr, "client %d: writebuf overflow\n", i);
186
memcpy( dpy->fd[i].writebuf + cnt, msg, sz ); cnt += sz;
187
dpy->fd[i].writebuf_count = cnt;
192
* \brief Send a message to a socket connection.
194
* \param dpy the display handle.
195
* \param i the index in dpy->fd of the socket connection.
196
* \param msg the message to send.
199
* Use send_msg() to send a one-byte message to a socket.
201
int send_char_msg( Display *dpy, int i, char msg )
203
return send_msg( dpy, i, &msg, sizeof(char));
208
* \brief Block and receive a message from a socket connection.
210
* \param dpy the display handle.
211
* \param connection the index in dpy->fd of the socket connection.
212
* \param msg storage for the received message.
213
* \param msg_size the number of bytes to read.
216
* Block and read from the connection's file descriptor
217
* until msg_size bytes have been received.
219
* Only called from welcome_message_part().
221
int blocking_read( Display *dpy, int connection,
222
char *msg, size_t msg_size )
226
for (i = 0 ; i < msg_size ; i += r) {
227
r = read(dpy->fd[connection].fd, msg + i, msg_size - i);
229
fprintf(stderr, "blocking_read: %d %s\n", r, strerror(errno));
230
shut_fd(dpy,connection);
239
* \brief Send/receive a part of the welcome message.
241
* \param dpy the display handle.
242
* \param i the index in dpy->fd of the socket connection.
243
* \param msg storage for the sent/received message.
244
* \param sz the number of bytes to write/read.
246
* \return True on success, or False on failure.
248
* This function is called by welcome_message_part(), to either send or receive
249
* (via blocking_read()) part of the welcome message, according to whether
250
* Display::IsClient is set.
252
* Each part of the welcome message on the wire consists of a count and then the
253
* actual message data with that number of bytes.
255
static int welcome_message_part( Display *dpy, int i, void **msg, int sz )
259
if (!blocking_read( dpy, i, (char *)&sz, sizeof(sz))) return False;
260
if (!*msg) *msg = malloc(sz);
261
if (!*msg) return False;
262
if (!blocking_read( dpy, i, *msg, sz )) return False;
266
if (!send_msg( dpy, i, &sz, sizeof(sz))) return False;
267
if (!send_msg( dpy, i, *msg, sz )) return False;
274
* \brief Send/receive the welcome message.
276
* \param dpy the display handle.
277
* \param i the index in dpy->fd of the socket connection.
279
* \return True on success, or False on failure.
281
* Using welcome_message_part(), sends/receives the client ID, the client
282
* configuration details in DRIDriverContext::shared, and the driver private
283
* message in DRIDriverContext::driverClientMsg.
285
static int welcome_message( Display *dpy, int i )
287
void *tmp = &dpy->driverContext.shared;
288
int *clientid = dpy->IsClient ? &dpy->clientID : &i;
291
if (!welcome_message_part( dpy, i, (void **)&clientid, sizeof(*clientid)))
294
if (!welcome_message_part( dpy, i, &tmp, sizeof(dpy->driverContext.shared)))
297
size=welcome_message_part( dpy, i,
298
(void **)&dpy->driverContext.driverClientMsg,
299
dpy->driverContext.driverClientMsgSize );
305
dpy->driverContext.driverClientMsgSize = size;
312
* \brief Handle a new client connection.
314
* \param dpy the display handle.
316
* \return True on success or False on failure.
318
* Accepts the connection, sets it in non-blocking operation, and finds a free
319
* slot in Display::fd for it.
321
static int handle_new_client( Display *dpy )
323
struct sockaddr_un client_address;
324
unsigned int l = sizeof(client_address);
327
r = accept(dpy->fd[0].fd, (struct sockaddr *) &client_address, &l);
334
if (fcntl(r, F_SETFL, O_NONBLOCK) != 0) {
341
/* Some rough & ready adaption of the XEvent semantics.
343
for (i = 1 ; i < dpy->nrFds ; i++) {
344
if (dpy->fd[i].fd < 0) {
345
XEvent *er = queue_event(dpy);
352
er->xcreatewindow.type = CreateNotify;
353
er->xcreatewindow.serial = 0;
354
er->xcreatewindow.send_event = 0;
355
er->xcreatewindow.display = dpy;
356
er->xcreatewindow.window = (Window)i; /* fd slot == window, now? */
358
/* Send the driver client message - this is expected as the
359
* first message on a new connection. The recpient already
360
* knows the size of the message.
362
welcome_message( dpy, i );
368
fprintf(stderr, "[miniglx] %s: Max nr clients exceeded\n", __FUNCTION__);
374
* This routine "puffs out" the very basic communications between
375
* client and server to full-sized X Events that can be handled by the
378
* \param dpy the display handle.
379
* \param i the index in dpy->fd of the socket connection.
381
* \return True on success or False on failure.
384
* Interprets the message (see msg) into a XEvent and advances the file FIFO
388
handle_fifo_read( Display *dpy, int i )
393
while (dpy->fd[i].readbuf_count) {
394
char id = dpy->fd[i].readbuf[0];
400
/* The server has called XMapWindow on a client window */
402
er = queue_event(dpy);
403
if (!er) return False;
404
er->xmap.type = MapNotify;
406
er->xmap.send_event = False;
407
er->xmap.display = dpy;
408
er->xmap.event = dpy->TheWindow;
409
er->xmap.window = dpy->TheWindow;
410
er->xmap.override_redirect = False;
411
if (dpy->driver->notifyFocus)
412
dpy->driver->notifyFocus( 1 );
415
/* The server has called XMapWindow on a client window */
417
er = queue_event(dpy);
418
if (!er) return False;
419
er->xexpose.type = Expose;
420
er->xexpose.serial = 0;
421
er->xexpose.send_event = False;
422
er->xexpose.display = dpy;
423
er->xexpose.window = dpy->TheWindow;
424
if (dpy->rotateMode) {
425
er->xexpose.x = dpy->TheWindow->y;
426
er->xexpose.y = dpy->TheWindow->x;
427
er->xexpose.width = dpy->TheWindow->h;
428
er->xexpose.height = dpy->TheWindow->w;
431
er->xexpose.x = dpy->TheWindow->x;
432
er->xexpose.y = dpy->TheWindow->y;
433
er->xexpose.width = dpy->TheWindow->w;
434
er->xexpose.height = dpy->TheWindow->h;
436
er->xexpose.count = 0;
439
/* The server has called 'XUnmapWindow' on a client
442
case _YouveLostFocus:
443
er = queue_event(dpy);
444
if (!er) return False;
445
er->xunmap.type = UnmapNotify;
446
er->xunmap.serial = 0;
447
er->xunmap.send_event = False;
448
er->xunmap.display = dpy;
449
er->xunmap.event = dpy->TheWindow;
450
er->xunmap.window = dpy->TheWindow;
451
er->xunmap.from_configure = False;
452
if (dpy->driver->notifyFocus)
453
dpy->driver->notifyFocus( 0 );
457
dpy->authorized = True;
461
fprintf(stderr, "Client received unhandled message type %d\n", id);
462
shut_fd(dpy, i); /* Actually shuts down the client */
468
/* Lets the server know that the client is ready to render
469
* (having called 'XMapWindow' locally).
472
er = queue_event(dpy);
473
if (!er) return False;
474
er->xmaprequest.type = MapRequest;
475
er->xmaprequest.serial = 0;
476
er->xmaprequest.send_event = False;
477
er->xmaprequest.display = dpy;
478
er->xmaprequest.parent = 0;
479
er->xmaprequest.window = (Window)i;
482
/* Both _YouveLostFocus and _IDontWantFocus generate unmap
483
* events. The idea is that _YouveLostFocus lets the client
484
* know that it has had focus revoked by the server, whereas
485
* _IDontWantFocus lets the server know that the client has
486
* unmapped its own window.
488
case _IDontWantFocus:
489
er = queue_event(dpy);
490
if (!er) return False;
491
er->xunmap.type = UnmapNotify;
492
er->xunmap.serial = 0;
493
er->xunmap.send_event = False;
494
er->xunmap.display = dpy;
495
er->xunmap.event = (Window)i;
496
er->xunmap.window = (Window)i;
497
er->xunmap.from_configure = False;
501
/* is full message here yet? */
502
if (dpy->fd[i].readbuf_count < count + sizeof(magic)) {
506
memcpy(&magic, dpy->fd[i].readbuf + count, sizeof(magic));
507
fprintf(stderr, "Authorize - magic %d\n", magic);
509
err = drmAuthMagic(dpy->driverContext.drmFD, magic);
510
count += sizeof(magic);
512
send_char_msg( dpy, i, _Authorize );
516
fprintf(stderr, "Server received unhandled message type %d\n", id);
517
shut_fd(dpy, i); /* Generates DestroyNotify event */
522
dpy->fd[i].readbuf_count -= count;
524
if (dpy->fd[i].readbuf_count) {
525
memmove(dpy->fd[i].readbuf,
526
dpy->fd[i].readbuf + count,
527
dpy->fd[i].readbuf_count);
537
* \param dpy display handle.
539
* The VT switches is detected by comparing Display::haveVT and
540
* Display::hwActive. When loosing the VT the hardware lock is acquired, the
541
* hardware is shutdown via a call to DRIDriverRec::shutdownHardware(), and the
542
* VT released. When acquiring the VT back the hardware state is restored via a
543
* call to DRIDriverRec::restoreHardware() and the hardware lock released.
545
static void __driHandleVtSignals( Display *dpy )
547
dpy->vtSignalFlag = 0;
549
fprintf(stderr, "%s: haveVT %d hwActive %d\n", __FUNCTION__,
550
dpy->haveVT, dpy->hwActive);
552
if (!dpy->haveVT && dpy->hwActive) {
553
/* Need to get lock and shutdown hardware */
554
DRM_LIGHT_LOCK( dpy->driverContext.drmFD,
555
dpy->driverContext.pSAREA,
556
dpy->driverContext.serverContext );
557
dpy->driver->shutdownHardware( &dpy->driverContext );
559
/* Can now give up control of the VT */
560
ioctl( dpy->ConsoleFD, VT_RELDISP, 1 );
563
else if (dpy->haveVT && !dpy->hwActive) {
564
/* Get VT (wait??) */
565
ioctl( dpy->ConsoleFD, VT_RELDISP, VT_ACTIVATE );
567
/* restore HW state, release lock */
568
dpy->driver->restoreHardware( &dpy->driverContext );
569
DRM_UNLOCK( dpy->driverContext.drmFD,
570
dpy->driverContext.pSAREA,
571
dpy->driverContext.serverContext );
578
#define max(x,y) ((x) > (y) ? (x) : (y))
581
* Logic for the select() call.
583
* \param dpy display handle.
584
* \param n highest fd in any set plus one.
585
* \param rfds fd set to be watched for reading, or NULL to create one.
586
* \param wfds fd set to be watched for writing, or NULL to create one.
587
* \param xfds fd set to be watched for exceptions or error, or NULL to create one.
588
* \param tv timeout value, or NULL for no timeout.
590
* \return number of file descriptors contained in the sets, or a negative number on failure.
593
* This all looks pretty complex, but is necessary especially on the
594
* server side to prevent a poorly-behaved client from causing the
595
* server to block in a read or write and hence not service the other
599
* See select_tut in the Linux manual pages for more discussion.
602
* Creates and initializes the file descriptor sets by inspecting Display::fd
603
* if these aren't passed in the function call. Calls select() and fulfill the
604
* demands by trying to fill MiniGLXConnection::readbuf and draining
605
* MiniGLXConnection::writebuf.
606
* The server fd[0] is handled specially for new connections, by calling
607
* handle_new_client().
611
__miniglx_Select( Display *dpy, int n, fd_set *rfds, fd_set *wfds, fd_set *xfds,
616
fd_set my_rfds, my_wfds;
617
struct timeval my_tv;
629
/* Don't block if there are events queued. Review this if the
630
* flush in XMapWindow is changed to blocking. (Test case:
633
if (dpy->eventqueue.head != dpy->eventqueue.tail) {
634
my_tv.tv_sec = my_tv.tv_usec = 0;
638
for (i = 0 ; i < dpy->nrFds; i++) {
639
if (dpy->fd[i].fd < 0)
642
if (dpy->fd[i].writebuf_count)
643
FD_SET(dpy->fd[i].fd, wfds);
645
if (dpy->fd[i].readbuf_count < MINIGLX_BUF_SIZE)
646
FD_SET(dpy->fd[i].fd, rfds);
648
n = max(n, dpy->fd[i].fd + 1);
651
if (dpy->vtSignalFlag)
652
__driHandleVtSignals( dpy );
654
retval = select( n, rfds, wfds, xfds, tv );
656
if (dpy->vtSignalFlag) {
658
__driHandleVtSignals( dpy );
668
/* Handle server fd[0] specially on the server - accept new client
671
if (!dpy->IsClient) {
672
if (FD_ISSET(dpy->fd[0].fd, rfds)) {
673
FD_CLR(dpy->fd[0].fd, rfds);
674
handle_new_client( dpy );
678
/* Otherwise, try and fill readbuffer and drain writebuffer:
680
for (i = 0 ; i < dpy->nrFds ; i++) {
681
if (dpy->fd[i].fd < 0)
684
/* If there aren't any event slots left, don't examine
685
* any more file events. This will prevent lost events.
687
if (dpy->eventqueue.head ==
688
((dpy->eventqueue.tail + 1) & MINIGLX_EVENT_QUEUE_MASK)) {
689
fprintf(stderr, "leaving event loop as event queue is full\n");
693
if (FD_ISSET(dpy->fd[i].fd, wfds)) {
694
int r = write(dpy->fd[i].fd,
696
dpy->fd[i].writebuf_count);
701
dpy->fd[i].writebuf_count -= r;
702
if (dpy->fd[i].writebuf_count) {
703
memmove(dpy->fd[i].writebuf,
704
dpy->fd[i].writebuf + r,
705
dpy->fd[i].writebuf_count);
710
if (FD_ISSET(dpy->fd[i].fd, rfds)) {
711
int r = read(dpy->fd[i].fd,
712
dpy->fd[i].readbuf + dpy->fd[i].readbuf_count,
713
MINIGLX_BUF_SIZE - dpy->fd[i].readbuf_count);
718
dpy->fd[i].readbuf_count += r;
720
handle_fifo_read( dpy, i );
729
* \brief Handle socket events.
731
* \param dpy the display handle.
732
* \param nonblock whether to return immediately or wait for an event.
734
* \return True on success, False on failure. Aborts on critical error.
737
* This function is the select() main loop.
739
int handle_fd_events( Display *dpy, int nonblock )
742
struct timeval tv = {0, 0};
743
int r = __miniglx_Select( dpy, 0, 0, 0, 0, nonblock ? &tv : 0 );
746
if (errno == EINTR || errno == EAGAIN)
754
* Initializes the connections.
756
* \param dpy the display handle.
758
* \return True on success or False on failure.
760
* Allocates and initializes the Display::fd array and create a Unix socket on
761
* the first entry. For a server binds the socket to a filename and listen for
762
* connections. For a client connects to the server and waits for a welcome
763
* message. Sets the socket in nonblocking mode.
765
int __miniglx_open_connections( Display *dpy )
767
struct sockaddr_un sa;
770
dpy->nrFds = dpy->IsClient ? 1 : MINIGLX_MAX_SERVER_FDS;
771
dpy->fd = calloc(1, dpy->nrFds * sizeof(struct MiniGLXConnection));
775
for (i = 0 ; i < dpy->nrFds ; i++)
778
if (!dpy->IsClient) {
779
if (unlink(MINIGLX_FIFO_NAME) != 0 && errno != ENOENT) {
780
perror("unlink " MINIGLX_FIFO_NAME);
786
/* Create a Unix socket -- Note this is *not* a network connection!
788
dpy->fd[0].fd = socket(PF_UNIX, SOCK_STREAM, 0);
789
if (dpy->fd[0].fd < 0) {
790
perror("socket " MINIGLX_FIFO_NAME);
794
memset(&sa, 0, sizeof(sa));
795
sa.sun_family = AF_UNIX;
796
strcpy(sa.sun_path, MINIGLX_FIFO_NAME);
801
if (connect(dpy->fd[0].fd, (struct sockaddr *)&sa, sizeof(sa)) != 0) {
807
/* Wait for configuration messages from the server.
809
welcome_message( dpy, 0 );
812
mode_t tmp = umask( 0000 ); /* open to everybody ? */
814
/* Bind socket to our filename
816
if (bind(dpy->fd[0].fd, (struct sockaddr *)&sa, sizeof(sa)) != 0) {
824
/* Listen for connections
826
if (listen(dpy->fd[0].fd, 5) != 0) {
833
if (fcntl(dpy->fd[0].fd, F_SETFL, O_NONBLOCK) != 0) {
845
* Frees the connections initialized by __miniglx_open_connections().
847
* \param dpy the display handle.
849
void __miniglx_close_connections( Display *dpy )
853
for (i = 0 ; i < dpy->nrFds ; i++) {
854
if (dpy->fd[i].fd >= 0) {
855
shutdown (dpy->fd[i].fd, SHUT_RDWR);
856
close (dpy->fd[i].fd);
866
* Set a drawable flag.
868
* \param dpy the display handle.
869
* \param w drawable (window).
872
* Sets the specified drawable flag in the SAREA and increment its stamp while
873
* holding the light hardware lock.
875
static void set_drawable_flag( Display *dpy, int w, int flag )
877
if (dpy->driverContext.pSAREA) {
879
DRM_LIGHT_LOCK( dpy->driverContext.drmFD,
880
dpy->driverContext.pSAREA,
881
dpy->driverContext.serverContext );
883
dpy->driverContext.pSAREA->drawableTable[w].stamp++;
884
dpy->driverContext.pSAREA->drawableTable[w].flags = flag;
887
DRM_UNLOCK( dpy->driverContext.drmFD,
888
dpy->driverContext.pSAREA,
889
dpy->driverContext.serverContext );
898
* \param dpy the display handle as returned by XOpenDisplay().
899
* \param w the window handle.
901
* If called by a client, sends a request for focus to the server. If
902
* called by the server, will generate a MapNotify and Expose event at
907
XMapWindow( Display *dpy, Window w )
910
send_char_msg( dpy, 0, _CanIHaveFocus );
912
set_drawable_flag( dpy, (int)w, 1 );
913
send_char_msg( dpy, (int)w, _YouveGotFocus );
914
send_char_msg( dpy, (int)w, _RepaintPlease );
917
handle_fd_events( dpy, 0 ); /* flush write queue */
921
* \brief Unmap Window.
923
* \param dpy the display handle as returned by XOpenDisplay().
924
* \param w the window handle.
926
* Called from the client: Lets the server know that the window won't
927
* be updated anymore.
929
* Called from the server: Tells the specified client that it no longer
933
XUnmapWindow( Display *dpy, Window w )
936
send_char_msg( dpy, 0, _IDontWantFocus );
940
set_drawable_flag( dpy, (int)w, 0 );
941
send_char_msg( dpy, (int)w, _YouveLostFocus );
943
handle_fd_events( dpy, 0 ); /* flush write queue */
948
* \brief Block and wait for next X event.
950
* \param dpy the display handle as returned by XOpenDisplay().
951
* \param event_return a pointer to an XEvent structure for the returned data.
953
* Wait until there is a new XEvent pending.
955
int XNextEvent(Display *dpy, XEvent *event_return)
958
if ( dpy->eventqueue.head != dpy->eventqueue.tail )
959
return dequeue_event( dpy, event_return );
961
handle_fd_events( dpy, 0 );
966
* \brief Non-blocking check for next X event.
968
* \param dpy the display handle as returned by XOpenDisplay().
969
* \param event_mask ignored.
970
* \param event_return a pointer to an XEvent structure for the returned data.
972
* Check if there is a new XEvent pending. Note that event_mask is
973
* ignored and any pending event will be returned.
975
Bool XCheckMaskEvent(Display *dpy, long event_mask, XEvent *event_return)
977
if ( dpy->eventqueue.head != dpy->eventqueue.tail )
978
return dequeue_event( dpy, event_return );
980
handle_fd_events( dpy, 1 );
982
return dequeue_event( dpy, event_return );