summaryrefslogtreecommitdiff
path: root/src/glx/mini/miniglx_events.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/glx/mini/miniglx_events.c')
-rw-r--r--src/glx/mini/miniglx_events.c962
1 files changed, 962 insertions, 0 deletions
diff --git a/src/glx/mini/miniglx_events.c b/src/glx/mini/miniglx_events.c
new file mode 100644
index 0000000000..d367dba395
--- /dev/null
+++ b/src/glx/mini/miniglx_events.c
@@ -0,0 +1,962 @@
+/**
+ * \file miniglx_events.c
+ * \brief Mini GLX client/server communication functions.
+ * \author Keith Whitwell
+ *
+ * The Mini GLX interface is a subset of the GLX interface, plus a
+ * minimal set of Xlib functions. This file adds interfaces to
+ * arbitrate a single cliprect between multiple direct rendering
+ * clients.
+ *
+ * A fairly complete client/server non-blocking communication
+ * mechanism. Probably overkill given that none of our messages
+ * currently exceed 1 byte in length and take place over the
+ * relatively benign channel provided by a Unix domain socket.
+ */
+
+/*
+ * Mesa 3-D graphics library
+ * Version: 5.0
+ *
+ * Copyright (C) 1999-2003 Brian Paul All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* $Id: miniglx_events.c,v 1.1 2003/08/22 20:11:43 brianp Exp $ */
+
+
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+
+#include <linux/kd.h>
+#include <linux/vt.h>
+
+#include "miniglxP.h"
+
+#include "xf86drm.h"
+#include "sarea.h"
+
+#define MINIGLX_FIFO_NAME "/tmp/miniglx.fifo"
+
+/** Character messages. */
+enum msgs {
+ _CanIHaveFocus,
+ _IDontWantFocus,
+ _YouveGotFocus,
+ _YouveLostFocus,
+ _RepaintPlease,
+};
+
+/**
+ * \brief Allocate an XEvent structure on the event queue.
+ *
+ * \param dpy the display handle.
+ *
+ * \return Pointer to the queued event structure or NULL on failure.
+ *
+ * \internal
+ * If there is space on the XEvent queue, return a pointer
+ * to the next free event and increment the eventqueue tail value.
+ * Otherwise return null.
+ */
+static XEvent *queue_event( Display *dpy )
+{
+ int incr = (dpy->eventqueue.tail + 1) & MINIGLX_EVENT_QUEUE_MASK;
+ if (incr == dpy->eventqueue.head) {
+ return 0;
+ }
+ else {
+ XEvent *ev = &dpy->eventqueue.queue[dpy->eventqueue.tail];
+ dpy->eventqueue.tail = incr;
+ return ev;
+ }
+}
+
+/**
+ * \brief Dequeue an XEvent and copy it into provided storage.
+ *
+ * \param dpy the display handle.
+ * \param event_return pointer to copy the queued event to.
+ *
+ * \return True or False depending on success.
+ *
+ * \internal
+ * If there is a queued XEvent on the queue, copy it to the provided
+ * pointer and increment the eventqueue head value. Otherwise return
+ * null.
+ */
+static int dequeue_event( Display *dpy, XEvent *event_return )
+{
+ if (dpy->eventqueue.tail == dpy->eventqueue.head) {
+ return False;
+ }
+ else {
+ *event_return = dpy->eventqueue.queue[dpy->eventqueue.head];
+ dpy->eventqueue.head += 1;
+ dpy->eventqueue.head &= MINIGLX_EVENT_QUEUE_MASK;
+ return True;
+ }
+}
+
+/**
+ * \brief Shutdown a socket connection.
+ *
+ * \param dpy the display handle.
+ * \param i the index in dpy->fd of the socket connection.
+ *
+ * \internal
+ * Shutdown and close the file descriptor. If this is the special
+ * connection in fd[0], issue an error message and exit - there's been
+ * some sort of failure somewhere. Otherwise, let the application
+ * know about whats happened by issuing a DestroyNotify event.
+ */
+static void shut_fd( Display *dpy, int i )
+{
+ if (dpy->fd[i].fd < 0)
+ return;
+
+ shutdown (dpy->fd[i].fd, SHUT_RDWR);
+ close (dpy->fd[i].fd);
+ dpy->fd[i].fd = -1;
+ dpy->fd[i].readbuf_count = 0;
+ dpy->fd[i].writebuf_count = 0;
+
+ if (i == 0) {
+ fprintf(stderr, "server connection lost\n");
+ exit(1);
+ }
+ else {
+ /* Pass this to the application as a DestroyNotify event.
+ */
+ XEvent *er = queue_event(dpy);
+ if (!er) return;
+ er->xdestroywindow.type = DestroyNotify;
+ er->xdestroywindow.serial = 0;
+ er->xdestroywindow.send_event = 0;
+ er->xdestroywindow.display = dpy;
+ er->xdestroywindow.window = (Window)i;
+ }
+}
+
+/**
+ * \brief Send a message to a socket connection.
+ *
+ * \param dpy the display handle.
+ * \param i the index in dpy->fd of the socket connection.
+ * \param msg the message to send.
+ * \param sz the size of the message
+ *
+ * \internal
+ * Copy the message to the write buffer for the nominated connection.
+ * This will be actually sent to that file descriptor from
+ * __miniglx_Select().
+ */
+static int send_msg( Display *dpy, int i,
+ const void *msg, size_t sz )
+{
+ int cnt = dpy->fd[i].writebuf_count;
+ if (MINIGLX_BUF_SIZE - cnt < sz) {
+ fprintf(stderr, "client %d: writebuf overflow\n", i);
+ return False;
+ }
+
+ memcpy( dpy->fd[i].writebuf + cnt, msg, sz ); cnt += sz;
+ dpy->fd[i].writebuf_count = cnt;
+ return True;
+}
+
+/**
+ * \brief Send a message to a socket connection.
+ *
+ * \param dpy the display handle.
+ * \param i the index in dpy->fd of the socket connection.
+ * \param msg the message to send.
+ *
+ * \internal
+ * Use send_msg() to send a one-byte message to a socket.
+ */
+static int send_char_msg( Display *dpy, int i, char msg )
+{
+ return send_msg( dpy, i, &msg, sizeof(char));
+}
+
+
+/**
+ * \brief Block and receive a message from a socket connection.
+ *
+ * \param dpy the display handle.
+ * \param connection the index in dpy->fd of the socket connection.
+ * \param msg storage for the received message.
+ * \param msg_size the number of bytes to read.
+ *
+ * \internal
+ * Block and read from the connection's file descriptor
+ * until msg_size bytes have been received.
+ *
+ * Only called from welcome_message_part().
+ */
+static int blocking_read( Display *dpy, int connection,
+ char *msg, size_t msg_size )
+{
+ int i, r;
+
+ for (i = 0 ; i < msg_size ; i += r) {
+ r = read(dpy->fd[connection].fd, msg + i, msg_size - i);
+ if (r < 1) {
+ fprintf(stderr, "blocking_read: %d %s\n", r, strerror(errno));
+ shut_fd(dpy,connection);
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/**
+ * \brief Send/receive a part of the welcome message.
+ *
+ * \param dpy the display handle.
+ * \param i the index in dpy->fd of the socket connection.
+ * \param msg storage for the sent/received message.
+ * \param sz the number of bytes to write/read.
+ *
+ * \return True on success, or False on failure.
+ *
+ * This function is called by welcome_message_part(), to either send or receive
+ * (via blocking_read()) part of the welcome message, according to whether
+ * Display::IsClient is set.
+ *
+ * Each part of the welcome message on the wire consists of a count and then the
+ * actual message data with that number of bytes.
+ */
+static int welcome_message_part( Display *dpy, int i, void **msg, int sz )
+{
+ if (dpy->IsClient) {
+ int sz;
+ if (!blocking_read( dpy, i, (char *)&sz, sizeof(sz))) return False;
+ if (!*msg) *msg = malloc(sz);
+ if (!*msg) return False;
+ if (!blocking_read( dpy, i, *msg, sz )) return False;
+ }
+ else {
+ if (!send_msg( dpy, i, &sz, sizeof(sz))) return False;
+ if (!send_msg( dpy, i, *msg, sz )) return False;
+ }
+
+ return True;
+}
+
+/**
+ * \brief Send/receive the welcome message.
+ *
+ * \param dpy the display handle.
+ * \param i the index in dpy->fd of the socket connection.
+ *
+ * \return True on success, or False on failure.
+ *
+ * Using welcome_message_part(), sends/receives the client ID, the client
+ * configuration details in DRIDriverContext::shared, and the driver private
+ * message in DRIDriverContext::driverClientMsg.
+ */
+static int welcome_message( Display *dpy, int i )
+{
+ void *tmp = &dpy->driverContext.shared;
+ int *clientid = dpy->IsClient ? &dpy->clientID : &i;
+
+ if (!welcome_message_part( dpy, i, (void **)&clientid, sizeof(*clientid)))
+ return False;
+
+ if (!welcome_message_part( dpy, i, &tmp, sizeof(dpy->driverContext.shared)))
+ return False;
+
+ if (!welcome_message_part( dpy, i,
+ (void **)&dpy->driverContext.driverClientMsg,
+ dpy->driverContext.driverClientMsgSize ))
+ return False;
+
+ return True;
+}
+
+
+/**
+ * \brief Handle a new client connection.
+ *
+ * \param dpy the display handle.
+ *
+ * \return True on success or False on failure.
+ *
+ * Accepts the connection, sets it in non-blocking operation, and finds a free
+ * slot in Display::fd for it.
+ */
+static int handle_new_client( Display *dpy )
+{
+ struct sockaddr_un client_address;
+ unsigned int l = sizeof(client_address);
+ int r, i;
+
+ r = accept(dpy->fd[0].fd, (struct sockaddr *) &client_address, &l);
+ if (r < 0) {
+ perror ("accept()");
+ shut_fd(dpy,0);
+ return False;
+ }
+
+ if (fcntl(r, F_SETFL, O_NONBLOCK) != 0) {
+ perror("fcntl");
+ close(r);
+ return False;
+ }
+
+
+ /* Some rough & ready adaption of the XEvent semantics.
+ */
+ for (i = 1 ; i < dpy->nrFds ; i++) {
+ if (dpy->fd[i].fd < 0) {
+ XEvent *er = queue_event(dpy);
+ if (!er) {
+ close(r);
+ return False;
+ }
+
+ dpy->fd[i].fd = r;
+ er->xcreatewindow.type = CreateNotify;
+ er->xcreatewindow.serial = 0;
+ er->xcreatewindow.send_event = 0;
+ er->xcreatewindow.display = dpy;
+ er->xcreatewindow.window = (Window)i; /* fd slot == window, now? */
+
+ /* Send the driver client message - this is expected as the
+ * first message on a new connection. The recpient already
+ * knows the size of the message.
+ */
+ welcome_message( dpy, i );
+ return True;
+ }
+ }
+
+
+ fprintf(stderr, "[miniglx] %s: Max nr clients exceeded\n", __FUNCTION__);
+ close(r);
+ return False;
+}
+
+/**
+ * This routine "puffs out" the very basic communications between
+ * client and server to full-sized X Events that can be handled by the
+ * application.
+ *
+ * \param dpy the display handle.
+ * \param i the index in dpy->fd of the socket connection.
+ *
+ * \return True on success or False on failure.
+ *
+ * \internal
+ * Interprets the message (see msg) into a XEvent and advances the file FIFO
+ * buffer.
+ */
+static int
+handle_fifo_read( Display *dpy, int i )
+{
+ while (dpy->fd[i].readbuf_count) {
+ char id = dpy->fd[i].readbuf[0];
+ XEvent *er;
+ int count = 1;
+
+ if (dpy->IsClient) {
+ switch (id) {
+ /* The server has called XMapWindow on a client window */
+ case _YouveGotFocus:
+ er = queue_event(dpy);
+ if (!er) return False;
+ er->xmap.type = MapNotify;
+ er->xmap.serial = 0;
+ er->xmap.send_event = False;
+ er->xmap.display = dpy;
+ er->xmap.event = dpy->TheWindow;
+ er->xmap.window = dpy->TheWindow;
+ er->xmap.override_redirect = False;
+ if (dpy->driver->notifyFocus)
+ dpy->driver->notifyFocus( 1 );
+ break;
+
+ /* The server has called XMapWindow on a client window */
+ case _RepaintPlease:
+ er = queue_event(dpy);
+ if (!er) return False;
+ er->xexpose.type = Expose;
+ er->xexpose.serial = 0;
+ er->xexpose.send_event = False;
+ er->xexpose.display = dpy;
+ er->xexpose.window = dpy->TheWindow;
+ if (dpy->rotateMode) {
+ er->xexpose.x = dpy->TheWindow->y;
+ er->xexpose.y = dpy->TheWindow->x;
+ er->xexpose.width = dpy->TheWindow->h;
+ er->xexpose.height = dpy->TheWindow->w;
+ }
+ else {
+ er->xexpose.x = dpy->TheWindow->x;
+ er->xexpose.y = dpy->TheWindow->y;
+ er->xexpose.width = dpy->TheWindow->w;
+ er->xexpose.height = dpy->TheWindow->h;
+ }
+ er->xexpose.count = 0;
+ break;
+
+ /* The server has called 'XUnmapWindow' on a client
+ * window.
+ */
+ case _YouveLostFocus:
+ er = queue_event(dpy);
+ if (!er) return False;
+ er->xunmap.type = UnmapNotify;
+ er->xunmap.serial = 0;
+ er->xunmap.send_event = False;
+ er->xunmap.display = dpy;
+ er->xunmap.event = dpy->TheWindow;
+ er->xunmap.window = dpy->TheWindow;
+ er->xunmap.from_configure = False;
+ if (dpy->driver->notifyFocus)
+ dpy->driver->notifyFocus( 0 );
+ break;
+
+ default:
+ fprintf(stderr, "Client received unhandled message type %d\n", id);
+ shut_fd(dpy, i); /* Actually shuts down the client */
+ return False;
+ }
+ }
+ else {
+ switch (id) {
+ /* Lets the server know that the client is ready to render
+ * (having called 'XMapWindow' locally).
+ */
+ case _CanIHaveFocus:
+ er = queue_event(dpy);
+ if (!er) return False;
+ er->xmaprequest.type = MapRequest;
+ er->xmaprequest.serial = 0;
+ er->xmaprequest.send_event = False;
+ er->xmaprequest.display = dpy;
+ er->xmaprequest.parent = 0;
+ er->xmaprequest.window = (Window)i;
+ break;
+
+ /* Both _YouveLostFocus and _IDontWantFocus generate unmap
+ * events. The idea is that _YouveLostFocus lets the client
+ * know that it has had focus revoked by the server, whereas
+ * _IDontWantFocus lets the server know that the client has
+ * unmapped its own window.
+ */
+ case _IDontWantFocus:
+ er = queue_event(dpy);
+ if (!er) return False;
+ er->xunmap.type = UnmapNotify;
+ er->xunmap.serial = 0;
+ er->xunmap.send_event = False;
+ er->xunmap.display = dpy;
+ er->xunmap.event = (Window)i;
+ er->xunmap.window = (Window)i;
+ er->xunmap.from_configure = False;
+ break;
+
+ default:
+ fprintf(stderr, "Server received unhandled message type %d\n", id);
+ shut_fd(dpy, i); /* Generates DestroyNotify event */
+ return False;
+ }
+ }
+
+ dpy->fd[i].readbuf_count -= count;
+
+ if (dpy->fd[i].readbuf_count) {
+ memmove(dpy->fd[i].readbuf,
+ dpy->fd[i].readbuf + count,
+ dpy->fd[i].readbuf_count);
+ }
+ }
+
+ return True;
+}
+
+/**
+ * Handle a VT signal
+ *
+ * \param dpy display handle.
+ *
+ * The VT switches is detected by comparing Display::haveVT and
+ * Display::hwActive. When loosing the VT the hardware lock is acquired, the
+ * hardware is shutdown via a call to DRIDriverRec::shutdownHardware(), and the
+ * VT released. When acquiring the VT back the hardware state is restored via a
+ * call to DRIDriverRec::restoreHardware() and the hardware lock released.
+ */
+static void __driHandleVtSignals( Display *dpy )
+{
+ dpy->vtSignalFlag = 0;
+
+ fprintf(stderr, "%s: haveVT %d hwActive %d\n", __FUNCTION__,
+ dpy->haveVT, dpy->hwActive);
+
+ if (!dpy->haveVT && dpy->hwActive) {
+ /* Need to get lock and shutdown hardware */
+ DRM_LIGHT_LOCK( dpy->driverContext.drmFD,
+ dpy->driverContext.pSAREA,
+ dpy->driverContext.serverContext );
+ dpy->driver->shutdownHardware( &dpy->driverContext );
+
+ /* Can now give up control of the VT */
+ ioctl( dpy->ConsoleFD, VT_RELDISP, 1 );
+ dpy->hwActive = 0;
+ }
+ else if (dpy->haveVT && !dpy->hwActive) {
+ /* Get VT (wait??) */
+ ioctl( dpy->ConsoleFD, VT_RELDISP, VT_ACTIVATE );
+
+ /* restore HW state, release lock */
+ dpy->driver->restoreHardware( &dpy->driverContext );
+ DRM_UNLOCK( dpy->driverContext.drmFD,
+ dpy->driverContext.pSAREA,
+ dpy->driverContext.serverContext );
+ dpy->hwActive = 1;
+ }
+}
+
+
+#undef max
+#define max(x,y) ((x) > (y) ? (x) : (y))
+
+/**
+ * Logic for the select() call.
+ *
+ * \param dpy display handle.
+ * \param n highest fd in any set plus one.
+ * \param rfds fd set to be watched for reading, or NULL to create one.
+ * \param wfds fd set to be watched for writing, or NULL to create one.
+ * \param xfds fd set to be watched for exceptions or error, or NULL to create one.
+ * \param tv timeout value, or NULL for no timeout.
+ *
+ * \return number of file descriptors contained in the sets, or a negative number on failure.
+ *
+ * \note
+ * This all looks pretty complex, but is necessary especially on the
+ * server side to prevent a poorly-behaved client from causing the
+ * server to block in a read or write and hence not service the other
+ * clients.
+ *
+ * \sa
+ * See select_tut in the Linux manual pages for more discussion.
+ *
+ * \internal
+ * Creates and initializes the file descriptor sets by inspecting Display::fd
+ * if these aren't passed in the function call. Calls select() and fulfill the
+ * demands by trying to fill MiniGLXConnection::readbuf and draining
+ * MiniGLXConnection::writebuf.
+ * The server fd[0] is handled specially for new connections, by calling
+ * handle_new_client().
+ *
+ */
+int
+__miniglx_Select( Display *dpy, int n, fd_set *rfds, fd_set *wfds, fd_set *xfds,
+ struct timeval *tv )
+{
+ int i;
+ int retval;
+ fd_set my_rfds, my_wfds;
+ struct timeval my_tv;
+
+ if (!rfds) {
+ rfds = &my_rfds;
+ FD_ZERO(rfds);
+ }
+
+ if (!wfds) {
+ wfds = &my_wfds;
+ FD_ZERO(wfds);
+ }
+
+ /* Don't block if there are events queued. Review this if the
+ * flush in XMapWindow is changed to blocking. (Test case:
+ * miniglxtest).
+ */
+ if (dpy->eventqueue.head != dpy->eventqueue.tail) {
+ my_tv.tv_sec = my_tv.tv_usec = 0;
+ tv = &my_tv;
+ }
+
+ for (i = 0 ; i < dpy->nrFds; i++) {
+ if (dpy->fd[i].fd < 0)
+ continue;
+
+ if (dpy->fd[i].writebuf_count)
+ FD_SET(dpy->fd[i].fd, wfds);
+
+ if (dpy->fd[i].readbuf_count < MINIGLX_BUF_SIZE)
+ FD_SET(dpy->fd[i].fd, rfds);
+
+ n = max(n, dpy->fd[i].fd + 1);
+ }
+
+ if (dpy->vtSignalFlag)
+ __driHandleVtSignals( dpy );
+
+ retval = select( n, rfds, wfds, xfds, tv );
+
+ if (dpy->vtSignalFlag) {
+ int tmp = errno;
+ __driHandleVtSignals( dpy );
+ errno = tmp;
+ }
+
+ if (retval < 0) {
+ FD_ZERO(rfds);
+ FD_ZERO(wfds);
+ return retval;
+ }
+
+ /* Handle server fd[0] specially on the server - accept new client
+ * connections.
+ */
+ if (!dpy->IsClient) {
+ if (FD_ISSET(dpy->fd[0].fd, rfds)) {
+ FD_CLR(dpy->fd[0].fd, rfds);
+ handle_new_client( dpy );
+ }
+ }
+
+ /* Otherwise, try and fill readbuffer and drain writebuffer:
+ */
+ for (i = 0 ; i < dpy->nrFds ; i++) {
+ if (dpy->fd[i].fd < 0)
+ continue;
+
+ /* If there aren't any event slots left, don't examine
+ * any more file events. This will prevent lost events.
+ */
+ if (dpy->eventqueue.head ==
+ ((dpy->eventqueue.tail + 1) & MINIGLX_EVENT_QUEUE_MASK)) {
+ fprintf(stderr, "leaving event loop as event queue is full\n");
+ return retval;
+ }
+
+ if (FD_ISSET(dpy->fd[i].fd, wfds)) {
+ int r = write(dpy->fd[i].fd,
+ dpy->fd[i].writebuf,
+ dpy->fd[i].writebuf_count);
+
+ if (r < 1)
+ shut_fd(dpy,i);
+ else {
+ dpy->fd[i].writebuf_count -= r;
+ if (dpy->fd[i].writebuf_count) {
+ memmove(dpy->fd[i].writebuf,
+ dpy->fd[i].writebuf + r,
+ dpy->fd[i].writebuf_count);
+ }
+ }
+ }
+
+ if (FD_ISSET(dpy->fd[i].fd, rfds)) {
+ int r = read(dpy->fd[i].fd,
+ dpy->fd[i].readbuf + dpy->fd[i].readbuf_count,
+ MINIGLX_BUF_SIZE - dpy->fd[i].readbuf_count);
+
+ if (r < 1)
+ shut_fd(dpy,i);
+ else {
+ dpy->fd[i].readbuf_count += r;
+
+ handle_fifo_read( dpy, i );
+ }
+ }
+ }
+
+ return retval;
+}
+
+/**
+ * \brief Handle socket events.
+ *
+ * \param dpy the display handle.
+ * \param nonblock whether to return immediately or wait for an event.
+ *
+ * \return True on success, False on failure. Aborts on critical error.
+ *
+ * \internal
+ * This function is the select() main loop.
+ */
+static int handle_fd_events( Display *dpy, int nonblock )
+{
+ while (1) {
+ struct timeval tv = {0, 0};
+ int r = __miniglx_Select( dpy, 0, 0, 0, 0, nonblock ? &tv : 0 );
+ if (r >= 0)
+ return True;
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ perror ("select()");
+ exit (1);
+ }
+}
+
+/**
+ * Initializes the connections.
+ *
+ * \param dpy the display handle.
+ *
+ * \return True on success or False on failure.
+ *
+ * Allocates and initializes the Display::fd array and create a Unix socket on
+ * the first entry. For a server binds the socket to a filename and listen for
+ * connections. For a client connects to the server and waits for a welcome
+ * message. Sets the socket in nonblocking mode.
+ */
+int __miniglx_open_connections( Display *dpy )
+{
+ struct sockaddr_un sa;
+ int i;
+
+ dpy->nrFds = dpy->IsClient ? 1 : MINIGLX_MAX_SERVER_FDS;
+ dpy->fd = calloc(1, dpy->nrFds * sizeof(struct MiniGLXConnection));
+ if (!dpy->fd)
+ return False;
+
+ for (i = 0 ; i < dpy->nrFds ; i++)
+ dpy->fd[i].fd = -1;
+
+ if (!dpy->IsClient) {
+ if (unlink(MINIGLX_FIFO_NAME) != 0 && errno != ENOENT) {
+ perror("unlink " MINIGLX_FIFO_NAME);
+ return False;
+ }
+
+ }
+
+ /* Create a Unix socket -- Note this is *not* a network connection!
+ */
+ dpy->fd[0].fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (dpy->fd[0].fd < 0) {
+ perror("socket " MINIGLX_FIFO_NAME);
+ return False;
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sun_family = AF_UNIX;
+ strcpy(sa.sun_path, MINIGLX_FIFO_NAME);
+
+ if (dpy->IsClient) {
+ /* Connect to server
+ */
+ if (connect(dpy->fd[0].fd, (struct sockaddr *)&sa, sizeof(sa)) != 0) {
+ perror("connect");
+ shut_fd(dpy,0);
+ return False;
+ }
+
+ /* Wait for configuration messages from the server.
+ */
+ welcome_message( dpy, 0 );
+ }
+ else {
+ mode_t tmp = umask( 0000 ); /* open to everybody ? */
+
+ /* Bind socket to our filename
+ */
+ if (bind(dpy->fd[0].fd, (struct sockaddr *)&sa, sizeof(sa)) != 0) {
+ perror("bind");
+ shut_fd(dpy,0);
+ return False;
+ }
+
+ umask( tmp );
+
+ /* Listen for connections
+ */
+ if (listen(dpy->fd[0].fd, 5) != 0) {
+ perror("listen");
+ shut_fd(dpy,0);
+ return False;
+ }
+ }
+
+ if (fcntl(dpy->fd[0].fd, F_SETFL, O_NONBLOCK) != 0) {
+ perror("fcntl");
+ shut_fd(dpy,0);
+ return False;
+ }
+
+
+ return True;
+}
+
+
+/**
+ * Frees the connections initialized by __miniglx_open_connections().
+ *
+ * \param dpy the display handle.
+ */
+void __miniglx_close_connections( Display *dpy )
+{
+ int i;
+
+ for (i = 0 ; i < dpy->nrFds ; i++) {
+ if (dpy->fd[i].fd >= 0) {
+ shutdown (dpy->fd[i].fd, SHUT_RDWR);
+ close (dpy->fd[i].fd);
+ }
+ }
+
+ dpy->nrFds = 0;
+ free(dpy->fd);
+}
+
+
+/**
+ * Set a drawable flag.
+ *
+ * \param dpy the display handle.
+ * \param w drawable (window).
+ * \param flag flag.
+ *
+ * Sets the specified drawable flag in the SAREA and increment its stamp while
+ * holding the light hardware lock.
+ */
+static void set_drawable_flag( Display *dpy, int w, int flag )
+{
+ if (dpy->driverContext.pSAREA) {
+ if (dpy->hwActive)
+ DRM_LIGHT_LOCK( dpy->driverContext.drmFD,
+ dpy->driverContext.pSAREA,
+ dpy->driverContext.serverContext );
+
+ dpy->driverContext.pSAREA->drawableTable[w].stamp++;
+ dpy->driverContext.pSAREA->drawableTable[w].flags = flag;
+
+ if (dpy->hwActive)
+ DRM_UNLOCK( dpy->driverContext.drmFD,
+ dpy->driverContext.pSAREA,
+ dpy->driverContext.serverContext );
+ }
+}
+
+
+
+/**
+ * \brief Map Window.
+ *
+ * \param dpy the display handle as returned by XOpenDisplay().
+ * \param w the window handle.
+ *
+ * If called by a client, sends a request for focus to the server. If
+ * called by the server, will generate a MapNotify and Expose event at
+ * the client.
+ *
+ */
+void
+XMapWindow( Display *dpy, Window w )
+{
+ if (dpy->IsClient)
+ send_char_msg( dpy, 0, _CanIHaveFocus );
+ else {
+ set_drawable_flag( dpy, (int)w, 1 );
+ send_char_msg( dpy, (int)w, _YouveGotFocus );
+ send_char_msg( dpy, (int)w, _RepaintPlease );
+ dpy->TheWindow = w;
+ }
+ handle_fd_events( dpy, 0 ); /* flush write queue */
+}
+
+/**
+ * \brief Unmap Window.
+ *
+ * \param dpy the display handle as returned by XOpenDisplay().
+ * \param w the window handle.
+ *
+ * Called from the client: Lets the server know that the window won't
+ * be updated anymore.
+ *
+ * Called from the server: Tells the specified client that it no longer
+ * holds the focus.
+ */
+void
+XUnmapWindow( Display *dpy, Window w )
+{
+ if (dpy->IsClient) {
+ send_char_msg( dpy, 0, _IDontWantFocus );
+ }
+ else {
+ dpy->TheWindow = 0;
+ set_drawable_flag( dpy, (int)w, 0 );
+ send_char_msg( dpy, (int)w, _YouveLostFocus );
+ }
+ handle_fd_events( dpy, 0 ); /* flush write queue */
+}
+
+
+/**
+ * \brief Block and wait for next X event.
+ *
+ * \param dpy the display handle as returned by XOpenDisplay().
+ * \param event_return a pointer to an XEvent structure for the returned data.
+ *
+ * Wait until there is a new XEvent pending.
+ */
+int XNextEvent(Display *dpy, XEvent *event_return)
+{
+ for (;;) {
+ if ( dpy->eventqueue.head != dpy->eventqueue.tail )
+ return dequeue_event( dpy, event_return );
+
+ handle_fd_events( dpy, 0 );
+ }
+}
+
+/**
+ * \brief Non-blocking check for next X event.
+ *
+ * \param dpy the display handle as returned by XOpenDisplay().
+ * \param event_mask ignored.
+ * \param event_return a pointer to an XEvent structure for the returned data.
+ *
+ * Check if there is a new XEvent pending. Note that event_mask is
+ * ignored and any pending event will be returned.
+ */
+Bool XCheckMaskEvent(Display *dpy, long event_mask, XEvent *event_return)
+{
+ if ( dpy->eventqueue.head != dpy->eventqueue.tail )
+ return dequeue_event( dpy, event_return );
+
+ handle_fd_events( dpy, 1 );
+
+ return dequeue_event( dpy, event_return );
+}