Main Page | Class Hierarchy | Alphabetical List | Class List | File List | Class Members

CXWindowsEventQueueBuffer.cpp

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

Generated on Fri Nov 6 00:21:14 2009 for synergy-plus by  doxygen 1.3.9.1