1 /*
2  * synergy -- mouse and keyboard sharing utility
3  * Copyright (C) 2012-2016 Symless Ltd.
4  * Copyright (C) 2004 Chris Schoeneman
5  *
6  * This package is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * found in the file LICENSE that should have accompanied this file.
9  *
10  * This package is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "platform/MSWindowsEventQueueBuffer.h"
20 
21 #include "arch/win32/ArchMiscWindows.h"
22 #include "mt/Thread.h"
23 #include "base/IEventQueue.h"
24 
25 //
26 // EventQueueTimer
27 //
28 
29 class EventQueueTimer { };
30 
31 
32 //
33 // MSWindowsEventQueueBuffer
34 //
35 
MSWindowsEventQueueBuffer(IEventQueue * events)36 MSWindowsEventQueueBuffer::MSWindowsEventQueueBuffer(IEventQueue* events) :
37     m_events(events)
38 {
39     // remember thread.  we'll be posting messages to it.
40     m_thread     = GetCurrentThreadId();
41 
42     // create a message type for custom events
43     m_userEvent  = RegisterWindowMessage("SYNERGY_USER_EVENT");
44 
45     // get message type for daemon quit
46     m_daemonQuit = ArchMiscWindows::getDaemonQuitMessage();
47 
48     // make sure this thread has a message queue
49     MSG dummy;
50     PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE);
51 }
52 
~MSWindowsEventQueueBuffer()53 MSWindowsEventQueueBuffer::~MSWindowsEventQueueBuffer()
54 {
55     // do nothing
56 }
57 
58 void
waitForEvent(double timeout)59 MSWindowsEventQueueBuffer::waitForEvent(double timeout)
60 {
61     // check if messages are available first.  if we don't do this then
62     // MsgWaitForMultipleObjects() will block even if the queue isn't
63     // empty if the messages in the queue were there before the last
64     // call to GetMessage()/PeekMessage().
65     if (HIWORD(GetQueueStatus(QS_ALLPOSTMESSAGE)) != 0) {
66         return;
67     }
68 
69     // convert timeout
70     DWORD t;
71     if (timeout < 0.0) {
72         t = INFINITE;
73     }
74     else {
75         t = (DWORD)(1000.0 * timeout);
76     }
77 
78     // wait for a message.  we cannot be interrupted by thread
79     // cancellation but that's okay because we're run in the main
80     // thread and we never cancel that thread.
81     HANDLE dummy[1];
82     MsgWaitForMultipleObjects(0, dummy, FALSE, t, QS_ALLPOSTMESSAGE);
83 }
84 
85 IEventQueueBuffer::Type
getEvent(Event & event,UInt32 & dataID)86 MSWindowsEventQueueBuffer::getEvent(Event& event, UInt32& dataID)
87 {
88     // NOTE: QS_ALLINPUT was replaced with QS_ALLPOSTMESSAGE.
89     //
90     // peek at messages first.  waiting for QS_ALLINPUT will return
91     // if a message has been sent to our window but GetMessage will
92     // dispatch that message behind our backs and block.  PeekMessage
93     // will also dispatch behind our backs but won't block.
94     if (!PeekMessage(&m_event, NULL, 0, 0, PM_NOREMOVE) &&
95         !PeekMessage(&m_event, (HWND)-1, 0, 0, PM_NOREMOVE)) {
96         return kNone;
97     }
98 
99     // BOOL.  yeah, right.
100     BOOL result = GetMessage(&m_event, NULL, 0, 0);
101     if (result == -1) {
102         return kNone;
103     }
104     else if (result == 0) {
105         event = Event(Event::kQuit);
106         return kSystem;
107     }
108     else if (m_daemonQuit != 0 && m_event.message == m_daemonQuit) {
109         event = Event(Event::kQuit);
110         return kSystem;
111     }
112     else if (m_event.message == m_userEvent) {
113         dataID = static_cast<UInt32>(m_event.wParam);
114         return kUser;
115     }
116     else {
117         event = Event(Event::kSystem,
118                             m_events->getSystemTarget(), &m_event);
119         return kSystem;
120     }
121 }
122 
123 bool
addEvent(UInt32 dataID)124 MSWindowsEventQueueBuffer::addEvent(UInt32 dataID)
125 {
126     return (PostThreadMessage(m_thread, m_userEvent,
127                             static_cast<WPARAM>(dataID), 0) != 0);
128 }
129 
130 bool
isEmpty() const131 MSWindowsEventQueueBuffer::isEmpty() const
132 {
133     return (HIWORD(GetQueueStatus(QS_ALLPOSTMESSAGE)) == 0);
134 }
135 
136 EventQueueTimer*
newTimer(double,bool) const137 MSWindowsEventQueueBuffer::newTimer(double, bool) const
138 {
139     return new EventQueueTimer;
140 }
141 
142 void
deleteTimer(EventQueueTimer * timer) const143 MSWindowsEventQueueBuffer::deleteTimer(EventQueueTimer* timer) const
144 {
145     delete timer;
146 }
147