/*****************************************************************
 * OS Depedent input routines:
 *
 *  WaitForSomething
 *
 *****************************************************************/

#include "Xos.h"			/* for strings, fcntl, time */

#define _CDECL
#define _NEAR
#include <errno.h>
#include <stdio.h>
#include "X.h"
#include "misc.h"
#include "opaque.h"

#include <signal.h>
#include "dixstruct.h"
#include "osdep.h"
#include "generic.h"
#include "funcs.h"

extern OsCommPtr oneClient;
extern u_long AllClients, ClosingClients, WellKnownSockets;
extern u_long InputSockets;
u_long sleepcount;
extern int screenIsSaved;
extern int minpriority;
extern u_char input_ready;
#ifdef X_PRINT_PORT
extern long PrinterSockets;
#endif
#ifdef XDMCP
extern int xdmcpSocket;
#endif

extern Bool NewOutputPending;
extern Bool AnyClientsWriteBlocked;
extern OsCommPtr MyPrivate[NPORTS];

extern long TimeSinceLastInputEvent();
extern void  mouseBlockHandler(void);
extern void client_poll(void);

static void FlushData(OsCommPtr oc);

/*****************
 * WaitForSomething:
 *     Make the server suspend until there is
 *	1. data from clients or
 *	2. input events available or
 *	3. ddx notices something of interest (graphics
 *	   queue ready, etc.) or
 *	4. clients that have buffered replies/events are ready
 *
 *     If the time between INPUT events is
 *     greater than ScreenSaverTime, the display is turned off (or
 *     saved, depending on the hardware).  So, WaitForSomething()
 *     has to handle this also (that's why the select() has a timeout.
 *     For more info on ClientsWithInput, see ReadRequestFromClient().
 *     pClientsReady is a mask, the bits set are 
 *     indices into the o.s. depedent table of available clients.
 *     (In this case, there is no table -- the index is the socket
 *     file descriptor.)  
 *****************/

static long timeTilFrob = 0;		/* while screen saving */
#ifdef X_PRINT_PORT
static int print_open;
#endif

int
WaitForSomething(pClientsReady)
    int *pClientsReady;
{
    int i;
    long timeout;
    u_long timeouttime;
    OsCommPtr oc;
    Bool done, first;
    int nready = 0;
    u_long iflags, oflags;

    /* We need a while loop here to handle 
       crashed connections and the screen saver timeout */
    for (;;)
    {
	    if (dispatchException)
		return nready;

            if (ScreenSaverTime)
	    {
                timeout = ScreenSaverTime - TimeSinceLastInputEvent();
	        if (timeout <= 0) /* may be forced by AutoResetServer() */
	        {
		    long timeSinceSave;

		    timeSinceSave = -timeout;
	            if ((timeSinceSave >= timeTilFrob) && (timeTilFrob >= 0))
                    {
		        SaveScreens(SCREEN_SAVER_ON, ScreenSaverActive);
			if (ScreenSaverInterval)
			    /* round up to the next ScreenSaverInterval */
			    timeTilFrob = ScreenSaverInterval *
				    ((timeSinceSave + ScreenSaverInterval) /
					    ScreenSaverInterval);
			else
			    timeTilFrob = -1;
		    }
    	            timeout = timeTilFrob - timeSinceSave;
		    if (timeTilFrob < 0)
			timeout = 0;
    	        }
 		else
 		{
		    if (timeout > ScreenSaverTime)
		        timeout = ScreenSaverTime;
	            timeTilFrob = 0;
		}
	    }
            else {
                timeout = 0;
		if (screenIsSaved == SCREEN_SAVER_ON)
			SaveScreens(SCREEN_SAVER_OFF, ScreenSaverReset);
	    }
	    for (done = FALSE, first = TRUE; !done;) {
		client_poll();
		iflags = InputSockets | WellKnownSockets;
		oflags = WellKnownSockets;
		if (net_select(32, &iflags, &oflags) < 0) {
		    /*
		     * Help!  What do we do in this case?  The error is
		     * probably EBADFD, in which case we should try a read
		     * and then zero that FD.
		     */
		    if (neterrno != NETERR_BADFD)
			net_perror("net_select");
		    oflags &= ~WellKnownSockets;
		    iflags &= ~(WellKnownSockets | 1 << xdmcpSocket);
		}
		/* Why the OR?  Because some PC transports set iflags
		 * when a socket is ready to be accepted (Novell, TCPOpen),
		 * some set oflags (PC/TCP, B&W), and some we have to
		 * do manually in the .NET overlay (SNTCP, PC-NFS).
		 */
		if ((oflags|iflags) & WellKnownSockets)
		    EstablishNewConnections((iflags|oflags) & WellKnownSockets);

#ifdef X_PRINT_PORT
		if (print_open) {
		    if (iflags & PrinterSockets);
			print_open = PrintIt();
		} else {
		    if (oflags & PrinterSockets);
			print_open = PrintOpen();
		}
#endif
#ifdef XDMCP
		if (iflags & (1L << xdmcpSocket))
			XdmcpReceivePacket();
#endif
		iflags &= AllClients;
		minpriority = MAXPRIORITY;
		for (i = 0, oflags = 1; i < NPORTS; i++, oflags <<= 1) {
		    oc = MyPrivate[i];
		    if (!oc)
			continue;
		    if (oflags & ClosingClients) {
			if ((oflags & iflags) || (oc->flags & OS_CLOSED) == 0)
				FlushData(oc);
			if ((oc->flags & (OS_FCLOSED|OS_CLOSED)) ==
			  (OS_FCLOSED|OS_CLOSED)) {
			    ClosingClients &= ~oflags;
			    AllClients &= ~oflags;
			    net_release(i);
			    xfree(oc);
			    MyPrivate[i] = 0;
			}
		    }
		    else if ((!oneClient || oc == oneClient) &&
			((oc->flags & OS_MORE) || (iflags & oflags))) {
			if (oc->priority < minpriority)
			    minpriority = oc->priority;
			pClientsReady[nready++] = oc->client;
			done = TRUE;
		    } else {
			if (oc->priority > 0)
			    oc->priority--;
		    }
		}
		if (AnyClientsWriteBlocked)
		{

		    AnyClientsWriteBlocked = FALSE;
		    for (i = 0; i < NPORTS; i++) {

			oc = MyPrivate[i];
			if (!oc)
			    continue;
			if (!(oc->flags & (OS_BLOCKED|OS_OUTPUT)))
			    continue;
			FlushClient(oc);
		    }
		}
		if (input_ready)
			done = TRUE;
		if (first && !done) {
		    XdmcpBlockHandler(&timeout);
		    mouseBlockHandler();
		    if (timeout) {
			_disable();
			timeouttime = timeout + *(u_long *)0x46c * 55;
			_enable();
		    }
		    first = FALSE;
		}
		if (!done && timeout) {
		    _disable();
		    if (*(u_long *)0x46c * 55 >= timeouttime)
			    done = TRUE;
		    _enable();
		}
	    }
	    XdmcpWakeupHandler();
	    if (input_ready || nready)
		    break;
    }
    return nready;
}

/* Flush data from any closing clients.  Free the descriptor when the
 * client has sent the EOF.
 */
static void
FlushData(oc)
OsCommPtr	oc;
{
    int fd = oc->fd;
    int i;

    if ((oc->flags & OS_FCLOSED) == 0) {
	i = net_drain(fd);
	if (i == 0 || i < 0 && neterrno != NETERR_WOULD_BLOCK) {
	    oc->flags |= OS_FCLOSED;
	    InputSockets &= ~(1L << fd);
	}
    }
    if ((oc->flags & OS_CLOSED) == 0)
	if (net_eof(fd) == 0 || neterrno != NETERR_WOULD_BLOCK)
	    oc->flags |= OS_CLOSED;
}
