#include <rfb/ServerCore.h>
#include "XserverDesktop.h"
+#include "vncBlockHandler.h"
#include "vncExtInit.h"
#include "vncHooks.h"
#include "vncSelection.h"
XserverDesktop* desktop;
};
-#if XORG >= 119
-extern "C" {
-/*
- * xserver NotifyFd callbacks. Note we also expect write notifies to work,
- * which only works with xserver >= 1.19.
- */
-#include "os.h"
-
-static void HandleListenFd(int fd, int xevents, void *data)
-{
- XserverDesktop *desktop = (XserverDesktop *)data;
-
- desktop->handleListenFd(fd);
-}
-
-static void HandleSocketFd(int fd, int xevents, void *data)
-{
- XserverDesktop *desktop = (XserverDesktop *)data;
-
- desktop->handleSocketFd(fd, xevents);
-}
-
-}
-#endif
XserverDesktop::XserverDesktop(int screenIndex_,
std::list<network::TcpListener*> listeners_,
if (!httpListeners.empty ())
httpServer = new FileHTTPServer(this);
-#if XORG >= 119
for (std::list<TcpListener*>::iterator i = listeners.begin();
i != listeners.end();
i++) {
- SetNotifyFd((*i)->getFd(), HandleListenFd, X_NOTIFY_READ, this);
+ vncSetNotifyFd((*i)->getFd(), screenIndex, true, false);
}
for (std::list<TcpListener*>::iterator i = httpListeners.begin();
i != httpListeners.end();
i++) {
- SetNotifyFd((*i)->getFd(), HandleListenFd, X_NOTIFY_READ, this);
+ vncSetNotifyFd((*i)->getFd(), screenIndex, true, false);
}
-#endif
}
XserverDesktop::~XserverDesktop()
{
while (!listeners.empty()) {
-#if XORG >= 119
- RemoveNotifyFd(listeners.back()->getFd());
-#endif
+ vncRemoveNotifyFd(listeners.back()->getFd());
delete listeners.back();
listeners.pop_back();
}
while (!httpListeners.empty()) {
-#if XORG >= 119
- RemoveNotifyFd(listeners.back()->getFd());
-#endif
+ vncRemoveNotifyFd(listeners.back()->getFd());
delete httpListeners.back();
httpListeners.pop_back();
}
}
}
-#if XORG >= 119
-void XserverDesktop::handleListenFd(int fd)
+void XserverDesktop::handleSocketEvent(int fd, bool read, bool write)
+{
+ try {
+ if (read) {
+ if (handleListenerEvent(fd, &listeners, server))
+ return;
+ if (handleListenerEvent(fd, &httpListeners, httpServer))
+ return;
+ }
+
+ if (handleSocketEvent(fd, server, read, write))
+ return;
+ if (handleSocketEvent(fd, httpServer, read, write))
+ return;
+
+ vlog.error("Cannot find file descriptor for socket event");
+ } catch (rdr::Exception& e) {
+ vlog.error("XserverDesktop::handleSocketEvent: %s",e.str());
+ }
+}
+
+bool XserverDesktop::handleListenerEvent(int fd,
+ std::list<TcpListener*>* sockets,
+ SocketServer* sockserv)
{
std::list<TcpListener*>::iterator i;
- SocketServer *fd_server = NULL;
- bool is_http = false;
- for (i = listeners.begin(); i != listeners.end(); i++) {
- if ((*i)->getFd() == fd) {
- fd_server = server;
+ for (i = sockets->begin(); i != sockets->end(); i++) {
+ if ((*i)->getFd() == fd)
break;
- }
- }
- if (httpServer && !fd_server) {
- for (i = httpListeners.begin(); i != httpListeners.end(); i++) {
- if ((*i)->getFd() == fd) {
- fd_server = httpServer;
- is_http = true;
- break;
- }
- }
- }
- if (!fd_server) {
- vlog.error("XserverDesktop::handleListenFd: Error cannot find fd");
- return;
}
+ if (i == sockets->end())
+ return false;
+
Socket* sock = (*i)->accept();
sock->outStream().setBlocking(false);
- vlog.debug("new %sclient, sock %d", is_http ? "http " : "", sock->getFd());
- fd_server->addSocket(sock);
- SetNotifyFd(sock->getFd(), HandleSocketFd, X_NOTIFY_READ, this);
+ vlog.debug("new client, sock %d", sock->getFd());
+ sockserv->addSocket(sock);
+
+ return true;
}
-void XserverDesktop::handleSocketFd(int fd, int xevents)
+bool XserverDesktop::handleSocketEvent(int fd,
+ SocketServer* sockserv,
+ bool read, bool write)
{
std::list<Socket*> sockets;
std::list<Socket*>::iterator i;
- SocketServer *fd_server = NULL;
- bool is_http = false;
- server->getSockets(&sockets);
+ sockserv->getSockets(&sockets);
for (i = sockets.begin(); i != sockets.end(); i++) {
- if ((*i)->getFd() == fd) {
- fd_server = server;
+ if ((*i)->getFd() == fd)
break;
- }
- }
- if (httpServer && !fd_server) {
- httpServer->getSockets(&sockets);
- for (i = sockets.begin(); i != sockets.end(); i++) {
- if ((*i)->getFd() == fd) {
- fd_server = httpServer;
- is_http = true;
- break;
- }
- }
- }
- if (!fd_server) {
- vlog.error("XserverDesktop::handleSocketFd: Error cannot find fd");
- return;
}
- if (xevents & X_NOTIFY_READ)
- fd_server->processSocketReadEvent(*i);
+ if (i == sockets.end())
+ return false;
- if (xevents & X_NOTIFY_WRITE)
- fd_server->processSocketWriteEvent(*i);
+ if (read)
+ sockserv->processSocketReadEvent(*i);
- if ((*i)->isShutdown()) {
- vlog.debug("%sclient gone, sock %d", is_http ? "http " : "", fd);
- RemoveNotifyFd(fd);
- fd_server->removeSocket(*i);
- if (!is_http)
- vncClientGone(fd);
- delete (*i);
- }
+ if (write)
+ sockserv->processSocketWriteEvent(*i);
+
+ return true;
}
void XserverDesktop::blockHandler(int* timeout)
int fd = (*i)->getFd();
if ((*i)->isShutdown()) {
vlog.debug("client gone, sock %d",fd);
+ vncRemoveNotifyFd(fd);
server->removeSocket(*i);
vncClientGone(fd);
delete (*i);
} else {
/* Update existing NotifyFD to listen for write (or not) */
- if ((*i)->outStream().bufferUsage() > 0)
- SetNotifyFd(fd, HandleSocketFd, X_NOTIFY_READ | X_NOTIFY_WRITE, this);
- else
- SetNotifyFd(fd, HandleSocketFd, X_NOTIFY_READ, this);
+ vncSetNotifyFd(fd, screenIndex, true, (*i)->outStream().bufferUsage() > 0);
}
}
if (httpServer) {
int fd = (*i)->getFd();
if ((*i)->isShutdown()) {
vlog.debug("http client gone, sock %d",fd);
+ vncRemoveNotifyFd(fd);
httpServer->removeSocket(*i);
delete (*i);
} else {
/* Update existing NotifyFD to listen for write (or not) */
- if ((*i)->outStream().bufferUsage() > 0)
- SetNotifyFd(fd, HandleSocketFd, X_NOTIFY_READ | X_NOTIFY_WRITE, this);
- else
- SetNotifyFd(fd, HandleSocketFd, X_NOTIFY_READ, this);
+ vncSetNotifyFd(fd, screenIndex, true, (*i)->outStream().bufferUsage() > 0);
}
}
}
+ // We are responsible for propagating mouse movement between clients
+ int cursorX, cursorY;
+ vncGetPointerPos(&cursorX, &cursorY);
+ cursorX -= vncGetScreenX(screenIndex);
+ cursorY -= vncGetScreenY(screenIndex);
+ if (oldCursorPos.x != cursorX || oldCursorPos.y != cursorY) {
+ oldCursorPos.x = cursorX;
+ oldCursorPos.y = cursorY;
+ server->setCursorPos(oldCursorPos);
+ }
+
+ // Trigger timers and check when the next will expire
int nextTimeout = server->checkTimeouts();
if (nextTimeout > 0 && (*timeout == -1 || nextTimeout < *timeout))
*timeout = nextTimeout;
}
}
-#else
-
-void XserverDesktop::readBlockHandler(fd_set* fds, struct timeval ** timeout)
-{
- // We don't have a good callback for when we can init input devices[1],
- // so we abuse the fact that this routine will be called first thing
- // once the dix is done initialising.
- // [1] Technically Xvnc has InitInput(), but libvnc.so has nothing.
- vncInitInputDevice();
-
- try {
- int nextTimeout;
-
- // Add all sockets we want read events for, after purging
- // any closed sockets.
- for (std::list<network::TcpListener*>::iterator i = listeners.begin();
- i != listeners.end();
- i++)
- FD_SET((*i)->getFd(), fds);
- for (std::list<network::TcpListener*>::iterator i = httpListeners.begin();
- i != httpListeners.end();
- i++)
- FD_SET((*i)->getFd(), fds);
-
- std::list<Socket*> sockets;
- std::list<Socket*>::iterator i;
- server->getSockets(&sockets);
- for (i = sockets.begin(); i != sockets.end(); i++) {
- int fd = (*i)->getFd();
- if ((*i)->isShutdown()) {
- vlog.debug("client gone, sock %d",fd);
- server->removeSocket(*i);
- vncClientGone(fd);
- delete (*i);
- } else {
- FD_SET(fd, fds);
- }
- }
- if (httpServer) {
- httpServer->getSockets(&sockets);
- for (i = sockets.begin(); i != sockets.end(); i++) {
- int fd = (*i)->getFd();
- if ((*i)->isShutdown()) {
- vlog.debug("http client gone, sock %d",fd);
- httpServer->removeSocket(*i);
- delete (*i);
- } else {
- FD_SET(fd, fds);
- }
- }
- }
-
- // Then check when the next timer will expire.
- // (this unfortunately also triggers any already expired timers)
- nextTimeout = server->checkTimeouts();
- if (nextTimeout > 0) {
- // No timeout specified? Or later timeout than we need?
- if ((*timeout == NULL) ||
- ((*timeout)->tv_sec > (nextTimeout/1000)) ||
- (((*timeout)->tv_sec == (nextTimeout/1000)) &&
- ((*timeout)->tv_usec > ((nextTimeout%1000)*1000)))) {
- dixTimeout.tv_sec = nextTimeout/1000;
- dixTimeout.tv_usec = (nextTimeout%1000)*1000;
- *timeout = &dixTimeout;
- }
- }
-
- } catch (rdr::Exception& e) {
- vlog.error("XserverDesktop::blockHandler: %s",e.str());
- }
-}
-
-void XserverDesktop::readWakeupHandler(fd_set* fds, int nfds)
-{
- try {
- // First check for file descriptors with something to do
- if (nfds >= 1) {
-
- for (std::list<network::TcpListener*>::iterator i = listeners.begin();
- i != listeners.end();
- i++) {
- if (FD_ISSET((*i)->getFd(), fds)) {
- FD_CLR((*i)->getFd(), fds);
- Socket* sock = (*i)->accept();
- sock->outStream().setBlocking(false);
- server->addSocket(sock);
- vlog.debug("new client, sock %d",sock->getFd());
- }
- }
-
- for (std::list<network::TcpListener*>::iterator i = httpListeners.begin();
- i != httpListeners.end();
- i++) {
- if (FD_ISSET((*i)->getFd(), fds)) {
- FD_CLR((*i)->getFd(), fds);
- Socket* sock = (*i)->accept();
- sock->outStream().setBlocking(false);
- httpServer->addSocket(sock);
- vlog.debug("new http client, sock %d",sock->getFd());
- }
- }
-
- std::list<Socket*> sockets;
- server->getSockets(&sockets);
- std::list<Socket*>::iterator i;
- for (i = sockets.begin(); i != sockets.end(); i++) {
- int fd = (*i)->getFd();
- if (FD_ISSET(fd, fds)) {
- FD_CLR(fd, fds);
- server->processSocketReadEvent(*i);
- }
- }
-
- if (httpServer) {
- httpServer->getSockets(&sockets);
- for (i = sockets.begin(); i != sockets.end(); i++) {
- int fd = (*i)->getFd();
- if (FD_ISSET(fd, fds)) {
- FD_CLR(fd, fds);
- httpServer->processSocketReadEvent(*i);
- }
- }
- }
-
- // We are responsible for propagating mouse movement between clients
- int cursorX, cursorY;
- vncGetPointerPos(&cursorX, &cursorY);
- cursorX -= vncGetScreenX(screenIndex);
- cursorY -= vncGetScreenY(screenIndex);
- if (oldCursorPos.x != cursorX || oldCursorPos.y != cursorY) {
- oldCursorPos.x = cursorX;
- oldCursorPos.y = cursorY;
- server->setCursorPos(oldCursorPos);
- }
- }
-
- // Then let the timers do some processing. Rescheduling is done in
- // blockHandler().
- server->checkTimeouts();
- } catch (rdr::Exception& e) {
- vlog.error("XserverDesktop::wakeupHandler: %s",e.str());
- }
-}
-
-void XserverDesktop::writeBlockHandler(fd_set* fds, struct timeval ** timeout)
-{
- try {
- std::list<Socket*> sockets;
- std::list<Socket*>::iterator i;
-
- server->getSockets(&sockets);
- for (i = sockets.begin(); i != sockets.end(); i++) {
- int fd = (*i)->getFd();
- if ((*i)->isShutdown()) {
- vlog.debug("client gone, sock %d",fd);
- server->removeSocket(*i);
- vncClientGone(fd);
- delete (*i);
- } else {
- if ((*i)->outStream().bufferUsage() > 0)
- FD_SET(fd, fds);
- }
- }
-
- if (httpServer) {
- httpServer->getSockets(&sockets);
- for (i = sockets.begin(); i != sockets.end(); i++) {
- int fd = (*i)->getFd();
- if ((*i)->isShutdown()) {
- vlog.debug("http client gone, sock %d",fd);
- httpServer->removeSocket(*i);
- delete (*i);
- } else {
- if ((*i)->outStream().bufferUsage() > 0)
- FD_SET(fd, fds);
- }
- }
- }
- } catch (rdr::Exception& e) {
- vlog.error("XserverDesktop::writeBlockHandler: %s",e.str());
- }
-}
-
-void XserverDesktop::writeWakeupHandler(fd_set* fds, int nfds)
-{
- if (nfds < 1)
- return;
-
- try {
- std::list<Socket*> sockets;
- std::list<Socket*>::iterator i;
-
- server->getSockets(&sockets);
- for (i = sockets.begin(); i != sockets.end(); i++) {
- int fd = (*i)->getFd();
- if (FD_ISSET(fd, fds)) {
- FD_CLR(fd, fds);
- server->processSocketWriteEvent(*i);
- }
- }
-
- if (httpServer) {
- httpServer->getSockets(&sockets);
- for (i = sockets.begin(); i != sockets.end(); i++) {
- int fd = (*i)->getFd();
- if (FD_ISSET(fd, fds)) {
- FD_CLR(fd, fds);
- httpServer->processSocketWriteEvent(*i);
- }
- }
- }
- } catch (rdr::Exception& e) {
- vlog.error("XserverDesktop::writeWakeupHandler: %s",e.str());
- }
-}
-
-#endif
-
void XserverDesktop::addClient(Socket* sock, bool reverse)
{
vlog.debug("new client, sock %d reverse %d",sock->getFd(),reverse);
server->addSocket(sock, reverse);
-#if XORG >= 119
- SetNotifyFd(sock->getFd(), HandleSocketFd, X_NOTIFY_READ, this);
-#endif
+ vncSetNotifyFd(sock->getFd(), screenIndex, true, false);
}
void XserverDesktop::disconnectClients()
#include <X11/Xpoll.h>
+#include "os.h"
#include "dix.h"
#include "scrnintstr.h"
#include "xorg-version.h"
#if XORG >= 119
+static void vncBlockHandler(void* data, void* timeout);
+static void vncSocketNotify(int fd, int xevents, void *data);
+#else
+static void vncBlockHandler(void * data, OSTimePtr t, void * readmask);
+static void vncWakeupHandler(void * data, int nfds, void * readmask);
-static void vncBlockHandler(void* data, void* timeout)
-{
- vncCallBlockHandlers(timeout);
-}
+struct vncFdEntry {
+ int fd;
+ int read, write;
+ int scrIdx;
+ struct vncFdEntry* next;
+};
+
+static struct vncFdEntry* fdsHead = NULL;
+#endif
void vncRegisterBlockHandlers(void)
{
if (!RegisterBlockAndWakeupHandlers(vncBlockHandler,
- (ServerWakeupHandlerProcPtr)NoopDDA, 0))
+#if XORG >= 119
+ (ServerWakeupHandlerProcPtr)NoopDDA,
+#else
+ vncWakeupHandler,
+#endif
+ 0))
FatalError("RegisterBlockAndWakeupHandlers() failed\n");
}
+void vncSetNotifyFd(int fd, int scrIdx, int read, int write)
+{
+#if XORG >= 119
+ int mask = (read ? X_NOTIFY_READ : 0) | (write ? X_NOTIFY_WRITE : 0);
+ SetNotifyFd(fd, vncSocketNotify, mask, (void*)scrIdx);
#else
+ static struct vncFdEntry* entry;
+
+ entry = fdsHead;
+ while (entry) {
+ if (entry->fd == fd) {
+ assert(entry->scrIdx == scrIdx);
+ entry->read = read;
+ entry->write = write;
+ return;
+ }
+ entry = entry->next;
+ }
-static void vncBlockHandler(void * data, OSTimePtr t, void * readmask);
-static void vncWakeupHandler(void * data, int nfds, void * readmask);
-void vncWriteBlockHandler(fd_set *fds);
-void vncWriteWakeupHandler(int nfds, fd_set *fds);
+ entry = malloc(sizeof(struct vncFdEntry));
+ memset(entry, 0, sizeof(struct vncFdEntry));
-void vncRegisterBlockHandlers(void)
+ entry->fd = fd;
+ entry->scrIdx = scrIdx;
+ entry->read = read;
+ entry->write = write;
+
+ entry->next = fdsHead;
+ fdsHead = entry;
+#endif
+}
+
+void vncRemoveNotifyFd(int fd)
{
- if (!RegisterBlockAndWakeupHandlers(vncBlockHandler, vncWakeupHandler, 0))
- FatalError("RegisterBlockAndWakeupHandlers() failed\n");
+#if XORG >= 119
+ RemoveNotifyFd(fd);
+#else
+ static struct vncFdEntry** prev;
+ static struct vncFdEntry* entry;
+
+ prev = &fdsHead;
+ entry = fdsHead;
+ while (entry) {
+ if (entry->fd == fd) {
+ *prev = entry->next;
+ return;
+ }
+ prev = &entry->next;
+ entry = entry->next;
+ }
+
+ assert(FALSE);
+#endif
}
+#if XORG >= 119
+static void vncSocketNotify(int fd, int xevents, void *data)
+{
+ int scrIdx;
+
+ scrIdx = (int)data;
+ vncHandleSocketEvent(fd, scrIdx,
+ xevents & X_NOTIFY_READ,
+ xevents & X_NOTIFY_WRITE);
+}
+#endif
+
+#if XORG < 119
static void vncWriteBlockHandlerFallback(OSTimePtr timeout);
static void vncWriteWakeupHandlerFallback(void);
+void vncWriteBlockHandler(fd_set *fds);
+void vncWriteWakeupHandler(int nfds, fd_set *fds);
+#endif
//
-// vncBlockHandler - called just before the X server goes into select(). Call
-// on to the block handler for each desktop. Then check whether any of the
-// selections have changed, and if so, notify any interested X clients.
+// vncBlockHandler - called just before the X server goes into poll().
+//
+// For older versions of X this also allows us to register file
+// descriptors that we want read events on.
//
-static void vncBlockHandler(void * data, OSTimePtr timeout, void * readmask)
+#if XORG >= 119
+static void vncBlockHandler(void* data, void* timeout)
+#else
+static void vncBlockHandler(void * data, OSTimePtr t, void * readmask)
+#endif
{
- fd_set* fds = (fd_set*)readmask;
+#if XORG < 119
+ int _timeout;
+ int* timeout = &_timeout;
+ static struct timeval tv;
+
+ fd_set* fds;
+ static struct vncFdEntry* entry;
+
+ if (*t == NULL)
+ _timeout = -1;
+ else
+ _timeout = (*t)->tv_sec * 1000 + (*t)->tv_usec / 1000;
+#endif
+
+ vncCallBlockHandlers(timeout);
- vncWriteBlockHandlerFallback(timeout);
+#if XORG < 119
+ if (_timeout != -1) {
+ tv.tv_sec= _timeout / 1000;
+ tv.tv_usec = (_timeout % 1000) * 1000;
+ *t = &tv;
+ }
- vncCallReadBlockHandlers(fds, timeout);
+ fds = (fd_set*)readmask;
+ entry = fdsHead;
+ while (entry) {
+ if (entry->read)
+ FD_SET(entry->fd, fds);
+ entry = entry->next;
+ }
+
+ vncWriteBlockHandlerFallback(t);
+#endif
}
+#if XORG < 119
static void vncWakeupHandler(void * data, int nfds, void * readmask)
{
fd_set* fds = (fd_set*)readmask;
- vncCallReadWakeupHandlers(fds, nfds);
+ static struct vncFdEntry* entry;
+
+ entry = fdsHead;
+ while (entry) {
+ if (entry->read && FD_ISSET(entry->fd, fds))
+ vncHandleSocketEvent(entry->fd, entry->scrIdx, TRUE, FALSE);
+ entry = entry->next;
+ }
vncWriteWakeupHandlerFallback();
}
+#endif
//
-// vncWriteBlockHandler - extra hack to be able to get the main select loop
-// to monitor writeable fds and not just readable. This requirers a modified
-// Xorg and might therefore not be called. When it is called though, it will
-// do so before vncBlockHandler (and vncWriteWakeupHandler called after
-// vncWakeupHandler).
+// vncWriteBlockHandler - extra hack to be able to get old versions of the X
+// server to monitor writeable fds and not just readable. This requirers a
+// modified Xorg and might therefore not be called.
//
+#if XORG < 119
static Bool needFallback = TRUE;
static fd_set fallbackFds;
static struct timeval tw;
void vncWriteBlockHandler(fd_set *fds)
{
- struct timeval *dummy;
+ static struct vncFdEntry* entry;
needFallback = FALSE;
- dummy = NULL;
- vncCallWriteBlockHandlers(fds, &dummy);
+ entry = fdsHead;
+ while (entry) {
+ if (entry->write)
+ FD_SET(entry->fd, fds);
+ entry = entry->next;
+ }
}
void vncWriteWakeupHandler(int nfds, fd_set *fds)
{
- vncCallWriteWakeupHandlers(fds, nfds);
+ static struct vncFdEntry* entry;
+
+ entry = fdsHead;
+ while (entry) {
+ if (entry->write && FD_ISSET(entry->fd, fds))
+ vncHandleSocketEvent(entry->fd, entry->scrIdx, FALSE, TRUE);
+ entry = entry->next;
+ }
}
static void vncWriteBlockHandlerFallback(OSTimePtr timeout)
vncWriteWakeupHandler(ret, &fallbackFds);
}
-
#endif