• Main Page
  • Classes
  • Files
  • File List

CXWindowsEventQueueBuffer.cpp

00001 /*
00002  * synergy -- mouse and keyboard sharing utility
00003  * Copyright (C) 2012 Bolton Software Ltd.
00004  * Copyright (C) 2004 Chris Schoeneman
00005  * 
00006  * This package is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * found in the file COPYING that should have accompanied this file.
00009  * 
00010  * This package is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00017  */
00018 
00019 #include "CXWindowsEventQueueBuffer.h"
00020 #include "CLock.h"
00021 #include "CThread.h"
00022 #include "CEvent.h"
00023 #include "IEventQueue.h"
00024 #include <fcntl.h>
00025 #if HAVE_UNISTD_H
00026 #   include <unistd.h>
00027 #endif
00028 #if HAVE_POLL
00029 #   include <poll.h>
00030 #else
00031 #   if HAVE_SYS_SELECT_H
00032 #       include <sys/select.h>
00033 #   endif
00034 #   if HAVE_SYS_TIME_H
00035 #       include <sys/time.h>
00036 #   endif
00037 #   if HAVE_SYS_TYPES_H
00038 #       include <sys/types.h>
00039 #   endif
00040 #endif
00041 
00042 //
00043 // CEventQueueTimer
00044 //
00045 
00046 class CEventQueueTimer { };
00047 
00048 
00049 //
00050 // CXWindowsEventQueueBuffer
00051 //
00052 
00053 CXWindowsEventQueueBuffer::CXWindowsEventQueueBuffer(
00054                 Display* display, Window window) :
00055     m_display(display),
00056     m_window(window),
00057     m_waiting(false)
00058 {
00059     assert(m_display != NULL);
00060     assert(m_window  != None);
00061 
00062     m_userEvent = XInternAtom(m_display, "SYNERGY_USER_EVENT", False);
00063     // set up for pipe hack
00064     int result = pipe(m_pipefd);
00065     assert(result == 0);
00066 
00067     int pipeflags;
00068     pipeflags = fcntl(m_pipefd[0], F_GETFL);
00069     fcntl(m_pipefd[0], F_SETFL, pipeflags | O_NONBLOCK);
00070     pipeflags = fcntl(m_pipefd[1], F_GETFL);
00071     fcntl(m_pipefd[1], F_SETFL, pipeflags | O_NONBLOCK);
00072 }
00073 
00074 CXWindowsEventQueueBuffer::~CXWindowsEventQueueBuffer()
00075 {
00076     // release pipe hack resources
00077     close(m_pipefd[0]);
00078     close(m_pipefd[1]);
00079 }
00080 
00081 void
00082 CXWindowsEventQueueBuffer::waitForEvent(double dtimeout)
00083 {
00084     CThread::testCancel();
00085 
00086     // clear out the pipe in preparation for waiting.
00087 
00088     char buf[16];
00089     ssize_t read_response = read(m_pipefd[0], buf, 15);
00090     
00091     // with linux automake, warnings are treated as errors by default
00092     if (read_response < 0)
00093     {
00094         // todo: handle read response
00095     }
00096 
00097     {
00098         CLock lock(&m_mutex);
00099         // we're now waiting for events
00100         m_waiting = true;
00101 
00102         // push out pending events
00103         flush();
00104     }
00105     // calling flush may have queued up a new event.
00106     if (!CXWindowsEventQueueBuffer::isEmpty()) {
00107         CThread::testCancel();
00108         return;
00109     }
00110 
00111     // use poll() to wait for a message from the X server or for timeout.
00112     // this is a good deal more efficient than polling and sleeping.
00113 #if HAVE_POLL
00114     struct pollfd pfds[2];
00115     pfds[0].fd     = ConnectionNumber(m_display);
00116     pfds[0].events = POLLIN;
00117     pfds[1].fd     = m_pipefd[0];
00118     pfds[1].events = POLLIN;
00119     int timeout    = (dtimeout < 0.0) ? -1 :
00120                         static_cast<int>(1000.0 * dtimeout);
00121     int remaining  =  timeout;
00122     int retval     =  0;
00123 #else
00124     struct timeval timeout;
00125     struct timeval* timeoutPtr;
00126     if (dtimeout < 0.0) {
00127         timeoutPtr = NULL;
00128     }
00129     else {
00130         timeout.tv_sec  = static_cast<int>(dtimeout);
00131         timeout.tv_usec = static_cast<int>(1.0e+6 *
00132                                 (dtimeout - timeout.tv_sec));
00133         timeoutPtr      = &timeout;
00134     }
00135 
00136     // initialize file descriptor sets
00137     fd_set rfds;
00138     FD_ZERO(&rfds);
00139     FD_SET(ConnectionNumber(m_display), &rfds);
00140     FD_SET(m_pipefd[0], &rfds);
00141     int nfds;
00142     if (ConnectionNumber(m_display) > m_pipefd[0]) {
00143         nfds = ConnectionNumber(m_display) + 1;
00144     }
00145     else {
00146         nfds = m_pipefd[0] + 1;
00147     }
00148 #endif
00149     // It's possible that the X server has queued events locally
00150     // in xlib's event buffer and not pushed on to the fd. Hence we
00151     // can't simply monitor the fd as we may never be woken up.
00152     // ie addEvent calls flush, XFlush may not send via the fd hence
00153     // there is an event waiting to be sent but we must exit the poll
00154     // before it can.
00155     // Instead we poll for a brief period of time (so if events
00156     // queued locally in the xlib buffer can be processed)
00157     // and continue doing this until timeout is reached.
00158     // The human eye can notice 60hz (ansi) which is 16ms, however
00159     // we want to give the cpu a chance s owe up this to 25ms
00160 #define TIMEOUT_DELAY 25
00161 
00162     while( ((dtimeout < 0.0) || (remaining > 0)) && QLength(m_display)==0 && retval==0){
00163 #if HAVE_POLL
00164     retval = poll(pfds, 2, TIMEOUT_DELAY); //16ms = 60hz, but we make it > to play nicely with the cpu
00165     if (pfds[1].revents & POLLIN) {
00166         ssize_t read_response = read(m_pipefd[0], buf, 15);
00167         
00168         // with linux automake, warnings are treated as errors by default
00169         if (read_response < 0)
00170         {
00171             // todo: handle read response
00172         }
00173 
00174     }
00175 #else
00176     retval = select(nfds,
00177                         SELECT_TYPE_ARG234 &rfds,
00178                         SELECT_TYPE_ARG234 NULL,
00179                         SELECT_TYPE_ARG234 NULL,
00180                         SELECT_TYPE_ARG5   TIMEOUT_DELAY);
00181     if (FD_SET(m_pipefd[0], &rfds)) {
00182         read(m_pipefd[0], buf, 15);
00183     }
00184 #endif
00185         remaining-=TIMEOUT_DELAY;
00186     }
00187 
00188     {
00189         // we're no longer waiting for events
00190         CLock lock(&m_mutex);
00191         m_waiting = false;
00192     }
00193 
00194     CThread::testCancel();
00195 }
00196 
00197 IEventQueueBuffer::Type
00198 CXWindowsEventQueueBuffer::getEvent(CEvent& event, UInt32& dataID)
00199 {
00200     CLock lock(&m_mutex);
00201 
00202     // push out pending events
00203     flush();
00204 
00205     // get next event
00206     XNextEvent(m_display, &m_event);
00207 
00208     // process event
00209     if (m_event.xany.type == ClientMessage &&
00210         m_event.xclient.message_type == m_userEvent) {
00211         dataID = static_cast<UInt32>(m_event.xclient.data.l[0]);
00212         return kUser;
00213     }
00214     else {
00215         event = CEvent(CEvent::kSystem,
00216                             IEventQueue::getSystemTarget(), &m_event);
00217         return kSystem;
00218     }
00219 }
00220 
00221 bool
00222 CXWindowsEventQueueBuffer::addEvent(UInt32 dataID)
00223 {
00224     // prepare a message
00225     XEvent xevent;
00226     xevent.xclient.type         = ClientMessage;
00227     xevent.xclient.window       = m_window;
00228     xevent.xclient.message_type = m_userEvent;
00229     xevent.xclient.format       = 32;
00230     xevent.xclient.data.l[0]    = static_cast<long>(dataID);
00231 
00232     // save the message
00233     CLock lock(&m_mutex);
00234     m_postedEvents.push_back(xevent);
00235 
00236     // if we're currently waiting for an event then send saved events to
00237     // the X server now.  if we're not waiting then some other thread
00238     // might be using the display connection so we can't safely use it
00239     // too.
00240     if (m_waiting) {
00241         flush();
00242         // Send a character through the round-trip pipe to wake a thread
00243         // that is waiting for a ConnectionNumber() socket to be readable.
00244         // The flush call can read incoming data from the socket and put
00245         // it in Xlib's input buffer.  That sneaks it past the other thread.
00246         ssize_t write_response = write(m_pipefd[1], "!", 1);
00247 
00248         // with linux automake, warnings are treated as errors by default
00249         if (write_response < 0)
00250         {
00251             // todo: handle read response
00252         }
00253     }
00254 
00255     return true;
00256 }
00257 
00258 bool
00259 CXWindowsEventQueueBuffer::isEmpty() const
00260 {
00261     CLock lock(&m_mutex);
00262     return (XPending(m_display) == 0 );
00263 }
00264 
00265 CEventQueueTimer*
00266 CXWindowsEventQueueBuffer::newTimer(double, bool) const
00267 {
00268     return new CEventQueueTimer;
00269 }
00270 
00271 void
00272 CXWindowsEventQueueBuffer::deleteTimer(CEventQueueTimer* timer) const
00273 {
00274     delete timer;
00275 }
00276 
00277 void
00278 CXWindowsEventQueueBuffer::flush()
00279 {
00280     // note -- m_mutex must be locked on entry
00281 
00282     // flush the posted event list to the X server
00283     for (size_t i = 0; i < m_postedEvents.size(); ++i) {
00284         XSendEvent(m_display, m_window, False, 0, &m_postedEvents[i]);
00285     }
00286     XFlush(m_display);
00287     m_postedEvents.clear();
00288 }

Generated on Sun May 19 2013 00:00:05 for Synergy by  doxygen 1.7.1