From 189537b8b9892f404871823f3040e7280c137653 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 27 May 2002 18:30:13 +0000 Subject: [PATCH] removed getEventMask() from primary screen. added a class to CXWindowsUtil that installs/uninstalls an X error hander. using that in primary screen, clipboard, and util to ensure that certain errors don't kill the app. --- server/CXWindowsPrimaryScreen.cpp | 20 ++++---- server/CXWindowsPrimaryScreen.h | 2 +- synergy/CXWindowsClipboard.cpp | 19 +++++++- synergy/CXWindowsScreen.h | 3 -- synergy/CXWindowsUtil.cpp | 79 +++++++++++++++++++++++++++++-- synergy/CXWindowsUtil.h | 30 ++++++++++++ 6 files changed, 133 insertions(+), 20 deletions(-) diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 149b1835..2ba6cfee 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -440,6 +440,7 @@ void CXWindowsPrimaryScreen::onOpenDisplay() CWDontPropagate | CWEventMask | CWOverrideRedirect | CWCursor, &attr); + log((CLOG_DEBUG "window is 0x%08x", m_window)); // start watching for events on other windows selectEvents(display, getRoot()); @@ -469,18 +470,17 @@ void CXWindowsPrimaryScreen::onLostClipboard( m_server->grabClipboard(id); } -long CXWindowsPrimaryScreen::getEventMask(Window w) const +void CXWindowsPrimaryScreen::selectEvents( + Display* display, Window w) const { - if (w == m_window) - return PointerMotionMask |// PointerMotionHintMask | - ButtonPressMask | ButtonReleaseMask | - KeyPressMask | KeyReleaseMask | - KeymapStateMask; - else - return PointerMotionMask | SubstructureNotifyMask; + // ignore errors while we adjust event masks + CXWindowsUtil::CErrorLock lock; + + // adjust event masks + doSelectEvents(display, w); } -void CXWindowsPrimaryScreen::selectEvents( +void CXWindowsPrimaryScreen::doSelectEvents( Display* display, Window w) const { // we want to track the mouse everywhere on the display. to achieve @@ -500,7 +500,7 @@ void CXWindowsPrimaryScreen::selectEvents( unsigned int nc; if (XQueryTree(display, w, &rw, &pw, &cw, &nc)) { for (unsigned int i = 0; i < nc; ++i) - selectEvents(display, cw[i]); + doSelectEvents(display, cw[i]); XFree(cw); } } diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index b3fc98b6..e57f00b4 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -34,10 +34,10 @@ protected: createClipboard(ClipboardID); virtual void onCloseDisplay(); virtual void onLostClipboard(ClipboardID); - virtual long getEventMask(Window) const; private: void selectEvents(Display*, Window) const; + void doSelectEvents(Display*, Window) const; void warpCursorNoLock(Display*, SInt32 xAbsolute, SInt32 yAbsolute); diff --git a/synergy/CXWindowsClipboard.cpp b/synergy/CXWindowsClipboard.cpp index 0ec423b7..09b713e7 100644 --- a/synergy/CXWindowsClipboard.cpp +++ b/synergy/CXWindowsClipboard.cpp @@ -190,6 +190,7 @@ bool CXWindowsClipboard::destroyRequest( // destroy all replies for this window clearReplies(index->second); + m_replies.erase(index); // note -- we don't stop watching the window for events because // we're called in response to the window being destroyed. @@ -758,11 +759,24 @@ void CXWindowsClipboard::insertReply(CReply* reply) // want events in case the window is destroyed or any of its // properties change. if (newWindow) { + // note errors while we adjust event masks + bool error = false; + CXWindowsUtil::CErrorLock lock(&error); + + // get and save the current event mask XWindowAttributes attr; XGetWindowAttributes(m_display, reply->m_requestor, &attr); + m_eventMasks[reply->m_requestor] = attr.your_event_mask; + + // add the events we want XSelectInput(m_display, reply->m_requestor, attr.your_event_mask | StructureNotifyMask | PropertyChangeMask); - m_eventMasks[reply->m_requestor] = attr.your_event_mask; + + // if we failed then the window has already been destroyed + if (error) { + m_replies.erase(reply->m_requestor); + delete reply; + } } } @@ -799,6 +813,7 @@ void CXWindowsClipboard::pushReplies( // if there are no more replies in the list then remove the list // and stop watching the requestor for events. if (replies.empty()) { + CXWindowsUtil::CErrorLock lock; Window requestor = mapIndex->first; XSelectInput(m_display, requestor, m_eventMasks[requestor]); m_replies.erase(mapIndex); @@ -871,6 +886,7 @@ bool CXWindowsClipboard::sendReply(CReply* reply) log((CLOG_DEBUG1 "clipboard: sending failure to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); reply->m_done = true; if (reply->m_property != None) { + CXWindowsUtil::CErrorLock lock; XDeleteProperty(m_display, reply->m_requestor, reply->m_property); } @@ -958,6 +974,7 @@ void CXWindowsClipboard::sendNotify( event.xselection.target = target; event.xselection.property = property; event.xselection.time = time; + CXWindowsUtil::CErrorLock lock; XSendEvent(m_display, requestor, False, 0, &event); } diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h index c97be519..b2a675ed 100644 --- a/synergy/CXWindowsScreen.h +++ b/synergy/CXWindowsScreen.h @@ -76,9 +76,6 @@ protected: // called when a clipboard is lost virtual void onLostClipboard(ClipboardID) = 0; - // get the X event mask required by the subclass for the given window - virtual long getEventMask(Window) const = 0; - private: // internal event processing bool processEvent(XEvent*); diff --git a/synergy/CXWindowsUtil.cpp b/synergy/CXWindowsUtil.cpp index 86b4677a..ffc6a9f5 100644 --- a/synergy/CXWindowsUtil.cpp +++ b/synergy/CXWindowsUtil.cpp @@ -1,8 +1,13 @@ #include "CXWindowsUtil.h" #include "CLog.h" #include "CThread.h" +#include #include +// +// CXWindowsUtil +// + bool CXWindowsUtil::getWindowProperty( Display* display, Window window, Atom property, @@ -15,6 +20,9 @@ bool CXWindowsUtil::getWindowProperty( Atom actualType; int actualDatumSize; + // ignore errors. XGetWindowProperty() will report failure. + CXWindowsUtil::CErrorLock lock; + // read the property const long length = XMaxRequestSize(display); long offset = 0; @@ -82,13 +90,14 @@ bool CXWindowsUtil::setWindowProperty( const void* vdata, UInt32 size, Atom type, SInt32 format) { -// FIXME -- must catch Alloc errors (using XSetErrorHandler()) and -// report failure to caller. - const UInt32 length = 4 * XMaxRequestSize(display); const unsigned char* data = reinterpret_cast(vdata); const UInt32 datumSize = static_cast(format / 8); + // save errors + bool error = false; + CXWindowsUtil::CErrorLock lock(&error); + // how much data to send in first chunk? UInt32 chunkSize = size; if (chunkSize > length) @@ -102,7 +111,7 @@ bool CXWindowsUtil::setWindowProperty( // append remaining chunks data += chunkSize; size -= chunkSize; - while (size > 0) { + while (!error && size > 0) { chunkSize = size; if (chunkSize > length) chunkSize = length; @@ -113,7 +122,7 @@ bool CXWindowsUtil::setWindowProperty( size -= chunkSize; } - return true; + return !error; } Time CXWindowsUtil::getCurrentTime( @@ -167,3 +176,63 @@ Bool CXWindowsUtil::propertyNotifyPredicate( xevent->xproperty.atom == filter->m_property && xevent->xproperty.state == PropertyNewValue) ? True : False; } + + +// +// CXWindowsUtil::CErrorLock +// + +CXWindowsUtil::CErrorLock* CXWindowsUtil::CErrorLock::s_top = NULL; + +CXWindowsUtil::CErrorLock::CErrorLock() +{ + install(&CXWindowsUtil::CErrorLock::ignoreHandler, NULL); +} + +CXWindowsUtil::CErrorLock::CErrorLock(bool* flag) +{ + install(&CXWindowsUtil::CErrorLock::saveHandler, flag); +} + +CXWindowsUtil::CErrorLock::CErrorLock(ErrorHandler handler, void* data) +{ + install(handler, data); +} + +CXWindowsUtil::CErrorLock::~CErrorLock() +{ + XSetErrorHandler(m_oldXHandler); + s_top = m_next; +} + +void CXWindowsUtil::CErrorLock::install( + ErrorHandler handler, void* data) +{ + m_handler = handler; + m_userData = data; + m_oldXHandler = XSetErrorHandler( + &CXWindowsUtil::CErrorLock::internalHandler); + m_next = s_top; + s_top = this; +} + +int CXWindowsUtil::CErrorLock::internalHandler( + Display* display, XErrorEvent* event) +{ + if (s_top != NULL && s_top->m_handler != NULL) { + s_top->m_handler(display, event, s_top->m_userData); + } + return 0; +} + +void CXWindowsUtil::CErrorLock::ignoreHandler( + Display*, XErrorEvent*, void*) +{ + // do nothing +} + +void CXWindowsUtil::CErrorLock::saveHandler( + Display*, XErrorEvent*, void* flag) +{ + *reinterpret_cast(flag) = true; +} diff --git a/synergy/CXWindowsUtil.h b/synergy/CXWindowsUtil.h index 07bd2a97..82414dd5 100644 --- a/synergy/CXWindowsUtil.h +++ b/synergy/CXWindowsUtil.h @@ -17,6 +17,36 @@ public: Atom type, SInt32 format); static Time getCurrentTime(Display*, Window); + // class to set an X error handler in the c'tor and restore the + // previous error handler in the d'tor. a lock should only + // be installed while the display is locked by the thread. + // + // CErrorLock() ignores errors + // CErrorLock(bool* flag) sets *flag to true if any error occurs + class CErrorLock { + public: + typedef void (*ErrorHandler)(Display*, XErrorEvent*, void* userData); + CErrorLock(); + CErrorLock(bool* errorFlag); + CErrorLock(ErrorHandler, void* userData); + ~CErrorLock(); + + private: + void install(ErrorHandler, void*); + static int internalHandler(Display*, XErrorEvent*); + static void ignoreHandler(Display*, XErrorEvent*, void*); + static void saveHandler(Display*, XErrorEvent*, void*); + + private: + typedef int (*XErrorHandler)(Display*, XErrorEvent*); + + ErrorHandler m_handler; + void* m_userData; + XErrorHandler m_oldXHandler; + CErrorLock* m_next; + static CErrorLock* s_top; + }; + private: class CPropertyNotifyPredicateInfo { public: