added better handling of X server disconnecting unexpectedly.
the apps still exit but they do it in a mostly controlled manner. in particular, the server threads except the one processing primary screen events will terminate gracefully. this will be important should the server ever allow HTTP clients to rewrite the configuration file. note that X makes it effectively impossible to continue once the X server disconnects. even if it didn't it would be difficult for synergy to recover. users will have to add synergy to the X display manager's startup script if they expect the server to be restarted. alternatively, we could add code to fork synergy at startup; the child would do the normal work while the parent would simply wait for the child to exit and restart it.
This commit is contained in:
parent
ddbb465540
commit
1cbdaee31b
|
@ -10,6 +10,7 @@
|
||||||
#include "CThread.h"
|
#include "CThread.h"
|
||||||
#include "CTimerThread.h"
|
#include "CTimerThread.h"
|
||||||
#include "TMethodJob.h"
|
#include "TMethodJob.h"
|
||||||
|
#include "XScreen.h"
|
||||||
#include "XSynergy.h"
|
#include "XSynergy.h"
|
||||||
#include "XThread.h"
|
#include "XThread.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
@ -51,7 +52,16 @@ void CClient::run(const CNetworkAddress& serverAddress)
|
||||||
log((CLOG_NOTE "starting client"));
|
log((CLOG_NOTE "starting client"));
|
||||||
|
|
||||||
// connect to secondary screen
|
// connect to secondary screen
|
||||||
openSecondaryScreen();
|
while (m_screen == NULL) {
|
||||||
|
try {
|
||||||
|
openSecondaryScreen();
|
||||||
|
}
|
||||||
|
catch (XScreenOpenFailure&) {
|
||||||
|
// can't open screen yet. wait a few seconds to retry.
|
||||||
|
log((CLOG_INFO "failed to open screen. waiting to retry."));
|
||||||
|
CThread::sleep(3.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// start server interactions
|
// start server interactions
|
||||||
m_serverAddress = &serverAddress;
|
m_serverAddress = &serverAddress;
|
||||||
|
@ -88,7 +98,9 @@ void CClient::run(const CNetworkAddress& serverAddress)
|
||||||
thread->wait();
|
thread->wait();
|
||||||
delete thread;
|
delete thread;
|
||||||
}
|
}
|
||||||
closeSecondaryScreen();
|
if (m_screen != NULL) {
|
||||||
|
closeSecondaryScreen();
|
||||||
|
}
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
catch (...) {
|
catch (...) {
|
||||||
|
@ -101,7 +113,9 @@ void CClient::run(const CNetworkAddress& serverAddress)
|
||||||
thread->wait();
|
thread->wait();
|
||||||
delete thread;
|
delete thread;
|
||||||
}
|
}
|
||||||
closeSecondaryScreen();
|
if (m_screen != NULL) {
|
||||||
|
closeSecondaryScreen();
|
||||||
|
}
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -290,12 +290,11 @@ void CXWindowsSecondaryScreen::getClipboard(
|
||||||
getDisplayClipboard(id, clipboard);
|
getDisplayClipboard(id, clipboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CXWindowsSecondaryScreen::onOpenDisplay()
|
void CXWindowsSecondaryScreen::onOpenDisplay(
|
||||||
|
Display* display)
|
||||||
{
|
{
|
||||||
assert(m_window == None);
|
assert(m_window == None);
|
||||||
|
|
||||||
CDisplayLock display(this);
|
|
||||||
|
|
||||||
// create the cursor hiding window. this window is used to hide the
|
// create the cursor hiding window. this window is used to hide the
|
||||||
// cursor when it's not on the screen. the window is hidden as soon
|
// cursor when it's not on the screen. the window is hidden as soon
|
||||||
// as the cursor enters the screen or the display's real cursor is
|
// as the cursor enters the screen or the display's real cursor is
|
||||||
|
@ -326,16 +325,18 @@ CXWindowsClipboard* CXWindowsSecondaryScreen::createClipboard(
|
||||||
return new CXWindowsClipboard(display, m_window, id);
|
return new CXWindowsClipboard(display, m_window, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CXWindowsSecondaryScreen::onCloseDisplay()
|
void CXWindowsSecondaryScreen::onCloseDisplay(
|
||||||
|
Display* display)
|
||||||
{
|
{
|
||||||
assert(m_window != None);
|
assert(m_window != None);
|
||||||
|
|
||||||
// no longer impervious to server grabs
|
if (display != NULL) {
|
||||||
CDisplayLock display(this);
|
// no longer impervious to server grabs
|
||||||
XTestGrabControl(display, False);
|
XTestGrabControl(display, False);
|
||||||
|
|
||||||
// destroy window
|
// destroy window
|
||||||
XDestroyWindow(display, m_window);
|
XDestroyWindow(display, m_window);
|
||||||
|
}
|
||||||
m_window = None;
|
m_window = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,10 +35,10 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// CXWindowsScreen overrides
|
// CXWindowsScreen overrides
|
||||||
virtual void onOpenDisplay();
|
virtual void onOpenDisplay(Display*);
|
||||||
virtual CXWindowsClipboard*
|
virtual CXWindowsClipboard*
|
||||||
createClipboard(ClipboardID);
|
createClipboard(ClipboardID);
|
||||||
virtual void onCloseDisplay();
|
virtual void onCloseDisplay(Display*);
|
||||||
virtual void onLostClipboard(ClipboardID);
|
virtual void onLostClipboard(ClipboardID);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "CStopwatch.h"
|
#include "CStopwatch.h"
|
||||||
#include "CFunctionJob.h"
|
#include "CFunctionJob.h"
|
||||||
#include "TMethodJob.h"
|
#include "TMethodJob.h"
|
||||||
|
#include "XScreen.h"
|
||||||
#include "XSocket.h"
|
#include "XSocket.h"
|
||||||
#include "XSynergy.h"
|
#include "XSynergy.h"
|
||||||
#include "XThread.h"
|
#include "XThread.h"
|
||||||
|
@ -46,7 +47,8 @@ else { wait(0); exit(1); }
|
||||||
|
|
||||||
const SInt32 CServer::s_httpMaxSimultaneousRequests = 3;
|
const SInt32 CServer::s_httpMaxSimultaneousRequests = 3;
|
||||||
|
|
||||||
CServer::CServer() : m_primary(NULL),
|
CServer::CServer() : m_cleanupSize(&m_mutex, 0),
|
||||||
|
m_primary(NULL),
|
||||||
m_active(NULL),
|
m_active(NULL),
|
||||||
m_primaryInfo(NULL),
|
m_primaryInfo(NULL),
|
||||||
m_seqNum(0),
|
m_seqNum(0),
|
||||||
|
@ -69,7 +71,16 @@ void CServer::run()
|
||||||
log((CLOG_NOTE "starting server"));
|
log((CLOG_NOTE "starting server"));
|
||||||
|
|
||||||
// connect to primary screen
|
// connect to primary screen
|
||||||
openPrimaryScreen();
|
while (m_primary == NULL) {
|
||||||
|
try {
|
||||||
|
openPrimaryScreen();
|
||||||
|
}
|
||||||
|
catch (XScreenOpenFailure&) {
|
||||||
|
// can't open screen yet. wait a few seconds to retry.
|
||||||
|
log((CLOG_INFO "failed to open screen. waiting to retry."));
|
||||||
|
CThread::sleep(3.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// start listening for HTTP requests
|
// start listening for HTTP requests
|
||||||
m_httpServer = new CHTTPServer(this);
|
m_httpServer = new CHTTPServer(this);
|
||||||
|
@ -84,9 +95,9 @@ void CServer::run()
|
||||||
|
|
||||||
// clean up
|
// clean up
|
||||||
log((CLOG_NOTE "stopping server"));
|
log((CLOG_NOTE "stopping server"));
|
||||||
|
cleanupThreads();
|
||||||
delete m_httpServer;
|
delete m_httpServer;
|
||||||
m_httpServer = NULL;
|
m_httpServer = NULL;
|
||||||
cleanupThreads();
|
|
||||||
closePrimaryScreen();
|
closePrimaryScreen();
|
||||||
}
|
}
|
||||||
catch (XBase& e) {
|
catch (XBase& e) {
|
||||||
|
@ -94,18 +105,20 @@ void CServer::run()
|
||||||
|
|
||||||
// clean up
|
// clean up
|
||||||
log((CLOG_NOTE "stopping server"));
|
log((CLOG_NOTE "stopping server"));
|
||||||
|
cleanupThreads();
|
||||||
delete m_httpServer;
|
delete m_httpServer;
|
||||||
m_httpServer = NULL;
|
m_httpServer = NULL;
|
||||||
cleanupThreads();
|
|
||||||
closePrimaryScreen();
|
closePrimaryScreen();
|
||||||
}
|
}
|
||||||
catch (XThread&) {
|
catch (XThread&) {
|
||||||
// clean up
|
// clean up
|
||||||
log((CLOG_NOTE "stopping server"));
|
log((CLOG_NOTE "stopping server"));
|
||||||
|
cleanupThreads();
|
||||||
delete m_httpServer;
|
delete m_httpServer;
|
||||||
m_httpServer = NULL;
|
m_httpServer = NULL;
|
||||||
cleanupThreads();
|
if (m_primary != NULL) {
|
||||||
closePrimaryScreen();
|
closePrimaryScreen();
|
||||||
|
}
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
catch (...) {
|
catch (...) {
|
||||||
|
@ -113,10 +126,12 @@ void CServer::run()
|
||||||
|
|
||||||
// clean up
|
// clean up
|
||||||
log((CLOG_NOTE "stopping server"));
|
log((CLOG_NOTE "stopping server"));
|
||||||
|
cleanupThreads();
|
||||||
delete m_httpServer;
|
delete m_httpServer;
|
||||||
m_httpServer = NULL;
|
m_httpServer = NULL;
|
||||||
cleanupThreads();
|
if (m_primary != NULL) {
|
||||||
closePrimaryScreen();
|
closePrimaryScreen();
|
||||||
|
}
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,6 +141,19 @@ void CServer::quit()
|
||||||
m_primary->stop();
|
m_primary->stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CServer::shutdown()
|
||||||
|
{
|
||||||
|
// stop all running threads but don't wait too long since some
|
||||||
|
// threads may be unable to proceed until this thread returns.
|
||||||
|
cleanupThreads(3.0);
|
||||||
|
|
||||||
|
// done with the HTTP server
|
||||||
|
delete m_httpServer;
|
||||||
|
m_httpServer = NULL;
|
||||||
|
|
||||||
|
// note -- we do not attempt to close down the primary screen
|
||||||
|
}
|
||||||
|
|
||||||
bool CServer::setConfig(const CConfig& config)
|
bool CServer::setConfig(const CConfig& config)
|
||||||
{
|
{
|
||||||
typedef std::vector<CThread> CThreads;
|
typedef std::vector<CThread> CThreads;
|
||||||
|
@ -1289,19 +1317,29 @@ void CServer::openPrimaryScreen()
|
||||||
// reset sequence number
|
// reset sequence number
|
||||||
m_seqNum = 0;
|
m_seqNum = 0;
|
||||||
|
|
||||||
// add connection
|
try {
|
||||||
m_active = addConnection(CString("primary"/* FIXME */), NULL);
|
// add connection
|
||||||
m_primaryInfo = m_active;
|
m_active = addConnection(CString("primary"/* FIXME */), NULL);
|
||||||
|
m_primaryInfo = m_active;
|
||||||
|
|
||||||
// open screen
|
// open screen
|
||||||
log((CLOG_DEBUG1 "creating primary screen"));
|
log((CLOG_DEBUG1 "creating primary screen"));
|
||||||
#if defined(CONFIG_PLATFORM_WIN32)
|
#if defined(CONFIG_PLATFORM_WIN32)
|
||||||
m_primary = new CMSWindowsPrimaryScreen;
|
m_primary = new CMSWindowsPrimaryScreen;
|
||||||
#elif defined(CONFIG_PLATFORM_UNIX)
|
#elif defined(CONFIG_PLATFORM_UNIX)
|
||||||
m_primary = new CXWindowsPrimaryScreen;
|
m_primary = new CXWindowsPrimaryScreen;
|
||||||
#endif
|
#endif
|
||||||
log((CLOG_DEBUG1 "opening primary screen"));
|
log((CLOG_DEBUG1 "opening primary screen"));
|
||||||
m_primary->open(this);
|
m_primary->open(this);
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
delete m_primary;
|
||||||
|
removeConnection(CString("primary"/* FIXME */));
|
||||||
|
m_primary = NULL;
|
||||||
|
m_primaryInfo = NULL;
|
||||||
|
m_active = NULL;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
// set the clipboard owner to the primary screen and then get the
|
// set the clipboard owner to the primary screen and then get the
|
||||||
// current clipboard data.
|
// current clipboard data.
|
||||||
|
@ -1340,6 +1378,7 @@ void CServer::addCleanupThread(const CThread& thread)
|
||||||
{
|
{
|
||||||
CLock lock(&m_mutex);
|
CLock lock(&m_mutex);
|
||||||
m_cleanupList.insert(m_cleanupList.begin(), new CThread(thread));
|
m_cleanupList.insert(m_cleanupList.begin(), new CThread(thread));
|
||||||
|
m_cleanupSize = m_cleanupSize + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CServer::removeCleanupThread(const CThread& thread)
|
void CServer::removeCleanupThread(const CThread& thread)
|
||||||
|
@ -1350,30 +1389,52 @@ void CServer::removeCleanupThread(const CThread& thread)
|
||||||
if (**index == thread) {
|
if (**index == thread) {
|
||||||
CThread* thread = *index;
|
CThread* thread = *index;
|
||||||
m_cleanupList.erase(index);
|
m_cleanupList.erase(index);
|
||||||
|
m_cleanupSize = m_cleanupSize - 1;
|
||||||
|
if (m_cleanupSize == 0) {
|
||||||
|
m_cleanupSize.broadcast();
|
||||||
|
}
|
||||||
delete thread;
|
delete thread;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CServer::cleanupThreads()
|
void CServer::cleanupThreads(double timeout)
|
||||||
{
|
{
|
||||||
log((CLOG_DEBUG1 "cleaning up threads"));
|
log((CLOG_DEBUG1 "cleaning up threads"));
|
||||||
m_mutex.lock();
|
|
||||||
while (m_cleanupList.begin() != m_cleanupList.end()) {
|
|
||||||
// get the next thread and cancel it
|
|
||||||
CThread* thread = m_cleanupList.front();
|
|
||||||
thread->cancel();
|
|
||||||
|
|
||||||
// wait for thread to finish with cleanup list unlocked. the
|
// first cancel every thread except the current one (with mutex
|
||||||
// thread will remove itself from the cleanup list.
|
// locked so the cleanup list won't change).
|
||||||
m_mutex.unlock();
|
CLock lock(&m_mutex);
|
||||||
thread->wait();
|
CThread current(CThread::getCurrentThread());
|
||||||
m_mutex.lock();
|
SInt32 minCount = 0;
|
||||||
|
for (CThreadList::iterator index = m_cleanupList.begin();
|
||||||
|
index != m_cleanupList.end(); ++index) {
|
||||||
|
CThread* thread = *index;
|
||||||
|
if (thread != ¤t) {
|
||||||
|
thread->cancel();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
minCount = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME -- delete remaining threads from list
|
// now wait for the threads (with mutex unlocked as each thread
|
||||||
m_mutex.unlock();
|
// will remove itself from the list)
|
||||||
|
CStopwatch timer(true);
|
||||||
|
while (m_cleanupSize > minCount) {
|
||||||
|
m_cleanupSize.wait(timer, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete remaining threads
|
||||||
|
for (CThreadList::iterator index = m_cleanupList.begin();
|
||||||
|
index != m_cleanupList.end(); ++index) {
|
||||||
|
CThread* thread = *index;
|
||||||
|
delete thread;
|
||||||
|
}
|
||||||
|
m_cleanupList.clear();
|
||||||
|
m_cleanupSize = 0;
|
||||||
|
|
||||||
log((CLOG_DEBUG1 "cleaned up threads"));
|
log((CLOG_DEBUG1 "cleaned up threads"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,11 @@ public:
|
||||||
// tell server to exit gracefully
|
// tell server to exit gracefully
|
||||||
void quit();
|
void quit();
|
||||||
|
|
||||||
|
// tell the server to shutdown. this is called in an emergency
|
||||||
|
// when we need to tell the server that we cannot continue. the
|
||||||
|
// server will attempt to clean up.
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
// update screen map. returns true iff the new configuration was
|
// update screen map. returns true iff the new configuration was
|
||||||
// accepted.
|
// accepted.
|
||||||
bool setConfig(const CConfig&);
|
bool setConfig(const CConfig&);
|
||||||
|
@ -173,7 +178,7 @@ private:
|
||||||
void updatePrimaryClipboard(ClipboardID);
|
void updatePrimaryClipboard(ClipboardID);
|
||||||
|
|
||||||
// cancel running threads
|
// cancel running threads
|
||||||
void cleanupThreads();
|
void cleanupThreads(double timeout = -1.0);
|
||||||
|
|
||||||
// thread method to accept incoming client connections
|
// thread method to accept incoming client connections
|
||||||
void acceptClients(void*);
|
void acceptClients(void*);
|
||||||
|
@ -220,6 +225,7 @@ private:
|
||||||
ISecurityFactory* m_securityFactory;
|
ISecurityFactory* m_securityFactory;
|
||||||
|
|
||||||
CThreadList m_cleanupList;
|
CThreadList m_cleanupList;
|
||||||
|
CCondVar<SInt32> m_cleanupSize;
|
||||||
|
|
||||||
IPrimaryScreen* m_primary;
|
IPrimaryScreen* m_primary;
|
||||||
CScreenList m_screens;
|
CScreenList m_screens;
|
||||||
|
|
|
@ -179,6 +179,7 @@ void CXWindowsPrimaryScreen::run()
|
||||||
|
|
||||||
void CXWindowsPrimaryScreen::stop()
|
void CXWindowsPrimaryScreen::stop()
|
||||||
{
|
{
|
||||||
|
CDisplayLock display(this);
|
||||||
doStop();
|
doStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,12 +188,12 @@ void CXWindowsPrimaryScreen::open(CServer* server)
|
||||||
assert(m_server == NULL);
|
assert(m_server == NULL);
|
||||||
assert(server != NULL);
|
assert(server != NULL);
|
||||||
|
|
||||||
// set the server
|
|
||||||
m_server = server;
|
|
||||||
|
|
||||||
// open the display
|
// open the display
|
||||||
openDisplay();
|
openDisplay();
|
||||||
|
|
||||||
|
// set the server
|
||||||
|
m_server = server;
|
||||||
|
|
||||||
// check for peculiarities
|
// check for peculiarities
|
||||||
// FIXME -- may have to get these from some database
|
// FIXME -- may have to get these from some database
|
||||||
m_numLockHalfDuplex = false;
|
m_numLockHalfDuplex = false;
|
||||||
|
@ -419,12 +420,10 @@ bool CXWindowsPrimaryScreen::isLockedToScreen() const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CXWindowsPrimaryScreen::onOpenDisplay()
|
void CXWindowsPrimaryScreen::onOpenDisplay(Display* display)
|
||||||
{
|
{
|
||||||
assert(m_window == None);
|
assert(m_window == None);
|
||||||
|
|
||||||
CDisplayLock display(this);
|
|
||||||
|
|
||||||
// get size of screen
|
// get size of screen
|
||||||
SInt32 w, h;
|
SInt32 w, h;
|
||||||
getScreenSize(&w, &h);
|
getScreenSize(&w, &h);
|
||||||
|
@ -458,16 +457,25 @@ CXWindowsClipboard* CXWindowsPrimaryScreen::createClipboard(
|
||||||
return new CXWindowsClipboard(display, m_window, id);
|
return new CXWindowsClipboard(display, m_window, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CXWindowsPrimaryScreen::onCloseDisplay()
|
void CXWindowsPrimaryScreen::onCloseDisplay(Display* display)
|
||||||
{
|
{
|
||||||
assert(m_window != None);
|
assert(m_window != None);
|
||||||
|
|
||||||
// destroy window
|
// destroy window
|
||||||
CDisplayLock display(this);
|
if (display != NULL) {
|
||||||
XDestroyWindow(display, m_window);
|
XDestroyWindow(display, m_window);
|
||||||
|
}
|
||||||
m_window = None;
|
m_window = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CXWindowsPrimaryScreen::onUnexpectedClose()
|
||||||
|
{
|
||||||
|
// tell server to shutdown
|
||||||
|
if (m_server != NULL) {
|
||||||
|
m_server->shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CXWindowsPrimaryScreen::onLostClipboard(
|
void CXWindowsPrimaryScreen::onLostClipboard(
|
||||||
ClipboardID id)
|
ClipboardID id)
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,10 +30,11 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// CXWindowsScreen overrides
|
// CXWindowsScreen overrides
|
||||||
virtual void onOpenDisplay();
|
virtual void onOpenDisplay(Display*);
|
||||||
virtual CXWindowsClipboard*
|
virtual CXWindowsClipboard*
|
||||||
createClipboard(ClipboardID);
|
createClipboard(ClipboardID);
|
||||||
virtual void onCloseDisplay();
|
virtual void onCloseDisplay(Display*);
|
||||||
|
virtual void onUnexpectedClose();
|
||||||
virtual void onLostClipboard(ClipboardID);
|
virtual void onLostClipboard(ClipboardID);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
#include "CLog.h"
|
#include "CLog.h"
|
||||||
#include "CString.h"
|
#include "CString.h"
|
||||||
#include "CThread.h"
|
#include "CThread.h"
|
||||||
|
#include "XScreen.h"
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
@ -13,29 +15,38 @@
|
||||||
// CXWindowsScreen
|
// CXWindowsScreen
|
||||||
//
|
//
|
||||||
|
|
||||||
|
CXWindowsScreen* CXWindowsScreen::s_screen = NULL;
|
||||||
|
|
||||||
CXWindowsScreen::CXWindowsScreen() :
|
CXWindowsScreen::CXWindowsScreen() :
|
||||||
m_display(NULL),
|
m_display(NULL),
|
||||||
m_root(None),
|
m_root(None),
|
||||||
m_w(0), m_h(0),
|
m_w(0), m_h(0),
|
||||||
m_stop(false)
|
m_stop(false)
|
||||||
{
|
{
|
||||||
// do nothing
|
assert(s_screen == NULL);
|
||||||
|
s_screen = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
CXWindowsScreen::~CXWindowsScreen()
|
CXWindowsScreen::~CXWindowsScreen()
|
||||||
{
|
{
|
||||||
|
assert(s_screen != NULL);
|
||||||
assert(m_display == NULL);
|
assert(m_display == NULL);
|
||||||
|
|
||||||
|
s_screen = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CXWindowsScreen::openDisplay()
|
void CXWindowsScreen::openDisplay()
|
||||||
{
|
{
|
||||||
assert(m_display == NULL);
|
assert(m_display == NULL);
|
||||||
|
|
||||||
|
// set the X I/O error handler so we catch the display disconnecting
|
||||||
|
XSetIOErrorHandler(&CXWindowsScreen::ioErrorHandler);
|
||||||
|
|
||||||
// open the display
|
// open the display
|
||||||
log((CLOG_DEBUG "XOpenDisplay(%s)", "NULL"));
|
log((CLOG_DEBUG "XOpenDisplay(%s)", "NULL"));
|
||||||
m_display = XOpenDisplay(NULL); // FIXME -- allow non-default
|
m_display = XOpenDisplay(NULL); // FIXME -- allow non-default
|
||||||
if (m_display == NULL)
|
if (m_display == NULL)
|
||||||
throw int(5); // FIXME -- make exception for this
|
throw XScreenOpenFailure();
|
||||||
|
|
||||||
// get default screen
|
// get default screen
|
||||||
m_screen = DefaultScreen(m_display);
|
m_screen = DefaultScreen(m_display);
|
||||||
|
@ -50,7 +61,7 @@ void CXWindowsScreen::openDisplay()
|
||||||
m_root = RootWindow(m_display, m_screen);
|
m_root = RootWindow(m_display, m_screen);
|
||||||
|
|
||||||
// let subclass prep display
|
// let subclass prep display
|
||||||
onOpenDisplay();
|
onOpenDisplay(m_display);
|
||||||
|
|
||||||
// initialize clipboards
|
// initialize clipboards
|
||||||
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
|
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
|
||||||
|
@ -60,10 +71,10 @@ void CXWindowsScreen::openDisplay()
|
||||||
|
|
||||||
void CXWindowsScreen::closeDisplay()
|
void CXWindowsScreen::closeDisplay()
|
||||||
{
|
{
|
||||||
assert(m_display != NULL);
|
CLock lock(&m_mutex);
|
||||||
|
|
||||||
// let subclass close down display
|
// let subclass close down display
|
||||||
onCloseDisplay();
|
onCloseDisplay(m_display);
|
||||||
|
|
||||||
// destroy clipboards
|
// destroy clipboards
|
||||||
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
|
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
|
||||||
|
@ -71,9 +82,12 @@ void CXWindowsScreen::closeDisplay()
|
||||||
}
|
}
|
||||||
|
|
||||||
// close the display
|
// close the display
|
||||||
XCloseDisplay(m_display);
|
if (m_display != NULL) {
|
||||||
m_display = NULL;
|
XCloseDisplay(m_display);
|
||||||
log((CLOG_DEBUG "closed display"));
|
m_display = NULL;
|
||||||
|
log((CLOG_DEBUG "closed display"));
|
||||||
|
}
|
||||||
|
XSetIOErrorHandler(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
int CXWindowsScreen::getScreen() const
|
int CXWindowsScreen::getScreen() const
|
||||||
|
@ -164,7 +178,7 @@ bool CXWindowsScreen::getEvent(XEvent* xevent) const
|
||||||
|
|
||||||
void CXWindowsScreen::doStop()
|
void CXWindowsScreen::doStop()
|
||||||
{
|
{
|
||||||
CLock lock(&m_mutex);
|
// caller must have locked display
|
||||||
m_stop = true;
|
m_stop = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,6 +193,11 @@ ClipboardID CXWindowsScreen::getClipboardID(Atom selection) const
|
||||||
return kClipboardEnd;
|
return kClipboardEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CXWindowsScreen::onUnexpectedClose()
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
bool CXWindowsScreen::processEvent(XEvent* xevent)
|
bool CXWindowsScreen::processEvent(XEvent* xevent)
|
||||||
{
|
{
|
||||||
switch (xevent->type) {
|
switch (xevent->type) {
|
||||||
|
@ -326,6 +345,21 @@ void CXWindowsScreen::destroyClipboardRequest(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CXWindowsScreen::ioErrorHandler(Display*)
|
||||||
|
{
|
||||||
|
// the display has disconnected, probably because X is shutting
|
||||||
|
// down. X forces us to exit at this point. that's arguably
|
||||||
|
// a flaw in X but, realistically, it's difficult to gracefully
|
||||||
|
// handle not having a Display* anymore. we'll simply log the
|
||||||
|
// error, notify the subclass (which must not use the display
|
||||||
|
// so we set it to NULL), and exit.
|
||||||
|
log((CLOG_WARN "X display has unexpectedly disconnected"));
|
||||||
|
s_screen->m_display = NULL;
|
||||||
|
s_screen->onUnexpectedClose();
|
||||||
|
log((CLOG_CRIT "quiting due to X display disconnection"));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// CXWindowsScreen::CDisplayLock
|
// CXWindowsScreen::CDisplayLock
|
||||||
|
|
|
@ -52,7 +52,8 @@ protected:
|
||||||
// wait for and get the next X event. cancellable.
|
// wait for and get the next X event. cancellable.
|
||||||
bool getEvent(XEvent*) const;
|
bool getEvent(XEvent*) const;
|
||||||
|
|
||||||
// cause getEvent() to return false immediately and forever after
|
// cause getEvent() to return false immediately and forever after.
|
||||||
|
// the caller must have locked the display.
|
||||||
void doStop();
|
void doStop();
|
||||||
|
|
||||||
// set the contents of the clipboard (i.e. primary selection)
|
// set the contents of the clipboard (i.e. primary selection)
|
||||||
|
@ -63,15 +64,21 @@ protected:
|
||||||
bool getDisplayClipboard(ClipboardID,
|
bool getDisplayClipboard(ClipboardID,
|
||||||
IClipboard* clipboard) const;
|
IClipboard* clipboard) const;
|
||||||
|
|
||||||
// called by openDisplay() to allow subclasses to prepare the display
|
// called by openDisplay() to allow subclasses to prepare the display.
|
||||||
virtual void onOpenDisplay() = 0;
|
// the display is locked and passed to the subclass.
|
||||||
|
virtual void onOpenDisplay(Display*) = 0;
|
||||||
|
|
||||||
// called by openDisplay() after onOpenDisplay() to create each clipboard
|
// called by openDisplay() after onOpenDisplay() to create each clipboard
|
||||||
virtual CXWindowsClipboard*
|
virtual CXWindowsClipboard*
|
||||||
createClipboard(ClipboardID) = 0;
|
createClipboard(ClipboardID) = 0;
|
||||||
|
|
||||||
// called by closeDisplay() to
|
// called by closeDisplay() to allow subclasses to clean up the display.
|
||||||
virtual void onCloseDisplay() = 0;
|
// the display is locked and passed to the subclass. note that the
|
||||||
|
// display may be NULL if the display has unexpectedly disconnected.
|
||||||
|
virtual void onCloseDisplay(Display*) = 0;
|
||||||
|
|
||||||
|
// called if the display is unexpectedly closing. default does nothing.
|
||||||
|
virtual void onUnexpectedClose();
|
||||||
|
|
||||||
// called when a clipboard is lost
|
// called when a clipboard is lost
|
||||||
virtual void onLostClipboard(ClipboardID) = 0;
|
virtual void onLostClipboard(ClipboardID) = 0;
|
||||||
|
@ -91,6 +98,9 @@ private:
|
||||||
// terminate a selection request
|
// terminate a selection request
|
||||||
void destroyClipboardRequest(Window window);
|
void destroyClipboardRequest(Window window);
|
||||||
|
|
||||||
|
// X I/O error handler
|
||||||
|
static int ioErrorHandler(Display*);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Display* m_display;
|
Display* m_display;
|
||||||
int m_screen;
|
int m_screen;
|
||||||
|
@ -103,6 +113,10 @@ private:
|
||||||
|
|
||||||
// X is not thread safe
|
// X is not thread safe
|
||||||
CMutex m_mutex;
|
CMutex m_mutex;
|
||||||
|
|
||||||
|
// pointer to (singleton) screen. this is only needed by
|
||||||
|
// ioErrorHandler().
|
||||||
|
static CXWindowsScreen* s_screen;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -24,6 +24,7 @@ CXXFILES = \
|
||||||
CXWindowsClipboard.cpp \
|
CXWindowsClipboard.cpp \
|
||||||
CXWindowsScreen.cpp \
|
CXWindowsScreen.cpp \
|
||||||
CXWindowsUtil.cpp \
|
CXWindowsUtil.cpp \
|
||||||
|
XScreen.cpp \
|
||||||
XSynergy.cpp \
|
XSynergy.cpp \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
#include "XScreen.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// XScreenOpenFailure
|
||||||
|
//
|
||||||
|
|
||||||
|
CString XScreenOpenFailure::getWhat() const throw()
|
||||||
|
{
|
||||||
|
return "XScreenOpenFailure";
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
#ifndef XSCREEN_H
|
||||||
|
#define XSCREEN_H
|
||||||
|
|
||||||
|
#include "XBase.h"
|
||||||
|
|
||||||
|
class XScreen : public XBase { };
|
||||||
|
|
||||||
|
// screen cannot be opened
|
||||||
|
class XScreenOpenFailure : public XScreen {
|
||||||
|
protected:
|
||||||
|
virtual CString getWhat() const throw();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -115,6 +115,10 @@ SOURCE=.\CTCPSocketFactory.cpp
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\XScreen.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=.\XSynergy.cpp
|
SOURCE=.\XSynergy.cpp
|
||||||
# End Source File
|
# End Source File
|
||||||
# End Group
|
# End Group
|
||||||
|
@ -187,6 +191,10 @@ SOURCE=.\ProtocolTypes.h
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\XScreen.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=.\XSynergy.h
|
SOURCE=.\XSynergy.h
|
||||||
# End Source File
|
# End Source File
|
||||||
# End Group
|
# End Group
|
||||||
|
|
Loading…
Reference in New Issue