|
|
@@ -41,6 +41,7 @@ |
|
|
|
#include <rfb/ServerCore.h> |
|
|
|
|
|
|
|
#include "XserverDesktop.h" |
|
|
|
#include "vncBlockHandler.h" |
|
|
|
#include "vncExtInit.h" |
|
|
|
#include "vncHooks.h" |
|
|
|
#include "vncSelection.h" |
|
|
@@ -90,30 +91,6 @@ public: |
|
|
|
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_, |
|
|
@@ -136,34 +113,28 @@ XserverDesktop::XserverDesktop(int screenIndex_, |
|
|
|
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(); |
|
|
|
} |
|
|
@@ -433,83 +404,72 @@ void XserverDesktop::add_copied(const rfb::Region &dest, const rfb::Point &delta |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#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) |
|
|
@@ -528,15 +488,13 @@ 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) { |
|
|
@@ -545,18 +503,28 @@ void XserverDesktop::blockHandler(int* timeout) |
|
|
|
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; |
|
|
@@ -565,231 +533,11 @@ void XserverDesktop::blockHandler(int* timeout) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#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() |