xref: /haiku/src/servers/app/EventStream.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
1 /*
2  * Copyright 2005, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Axel Dörfler, axeld@pinc-software.de
7  */
8 
9 
10 #include "EventStream.h"
11 
12 #include <InputServerTypes.h>
13 #include <ServerProtocol.h>
14 #include <shared_cursor_area.h>
15 
16 #include <AppMisc.h>
17 
18 #include <new>
19 #include <stdio.h>
20 #include <string.h>
21 
22 
23 EventStream::EventStream()
24 {
25 }
26 
27 
28 EventStream::~EventStream()
29 {
30 }
31 
32 
33 bool
34 EventStream::SupportsCursorThread() const
35 {
36 	return false;
37 }
38 
39 
40 status_t
41 EventStream::GetNextCursorPosition(BPoint& where, bigtime_t timeout)
42 {
43 	return B_ERROR;
44 }
45 
46 
47 //	#pragma mark -
48 
49 
50 InputServerStream::InputServerStream(BMessenger& messenger)
51 	:
52 	fInputServer(messenger),
53 	fPort(-1),
54 	fQuitting(false),
55 	fLatestMouseMoved(NULL)
56 {
57 	BMessage message(IS_ACQUIRE_INPUT);
58 	message.AddInt32("remote team", BPrivate::current_team());
59 
60 	fCursorArea = create_area("shared cursor", (void **)&fCursorBuffer, B_ANY_ADDRESS,
61 		B_PAGE_SIZE, B_LAZY_LOCK, B_READ_AREA | B_WRITE_AREA);
62 	if (fCursorArea >= B_OK)
63 		message.AddInt32("cursor area", fCursorArea);
64 
65 	BMessage reply;
66 	if (messenger.SendMessage(&message, &reply) != B_OK)
67 		return;
68 
69 	if (reply.FindInt32("event port", &fPort) != B_OK)
70 		fPort = -1;
71 	if (reply.FindInt32("cursor semaphore", &fCursorSemaphore) != B_OK)
72 		fCursorSemaphore = -1;
73 }
74 
75 
76 #if TEST_MODE
77 InputServerStream::InputServerStream()
78 	:
79 	fQuitting(false),
80 	fCursorSemaphore(-1),
81 	fLatestMouseMoved(NULL)
82 {
83 	fPort = find_port(SERVER_INPUT_PORT);
84 }
85 #endif
86 
87 
88 InputServerStream::~InputServerStream()
89 {
90 	delete_area(fCursorArea);
91 }
92 
93 
94 bool
95 InputServerStream::IsValid()
96 {
97 	port_info portInfo;
98 	if (fPort < B_OK || get_port_info(fPort, &portInfo) != B_OK)
99 		return false;
100 
101 	return true;
102 }
103 
104 
105 void
106 InputServerStream::SendQuit()
107 {
108 	fQuitting = true;
109 	write_port(fPort, 'quit', NULL, 0);
110 	release_sem(fCursorSemaphore);
111 }
112 
113 
114 void
115 InputServerStream::UpdateScreenBounds(BRect bounds)
116 {
117 	BMessage update(IS_SCREEN_BOUNDS_UPDATED);
118 	update.AddRect("screen_bounds", bounds);
119 
120 	fInputServer.SendMessage(&update);
121 }
122 
123 
124 bool
125 InputServerStream::GetNextEvent(BMessage** _event)
126 {
127 	while (fEvents.IsEmpty()) {
128 		// wait for new events
129 		BMessage* event;
130 		status_t status = _MessageFromPort(&event);
131 		if (status == B_OK) {
132 			if (event->what == B_MOUSE_MOVED)
133 				fLatestMouseMoved = event;
134 
135 			fEvents.AddMessage(event);
136 		} else if (status == B_BAD_PORT_ID) {
137 			// our port got deleted - the input_server must have died
138 			fPort = -1;
139 			return false;
140 		}
141 
142 		int32 count = port_count(fPort);
143 		if (count > 0) {
144 			// empty port queue completely while we're at it
145 			for (int32 i = 0; i < count; i++) {
146 				if (_MessageFromPort(&event, 0) == B_OK) {
147 					if (event->what == B_MOUSE_MOVED)
148 						fLatestMouseMoved = event;
149 					fEvents.AddMessage(event);
150 				}
151 			}
152 		}
153 	}
154 
155 	// there are items in our list, so just work through them
156 
157 	*_event = fEvents.NextMessage();
158 	return true;
159 }
160 
161 
162 status_t
163 InputServerStream::GetNextCursorPosition(BPoint &where, bigtime_t timeout)
164 {
165 	status_t status;
166 
167 	do {
168 		status = acquire_sem_etc(fCursorSemaphore, 1, B_RELATIVE_TIMEOUT,
169 			timeout);
170 	} while (status == B_INTERRUPTED);
171 
172 	if (status == B_TIMED_OUT)
173 		return status;
174 
175 	if (status == B_BAD_SEM_ID) {
176 		// the semaphore is no longer valid - the input_server must have died
177 		fCursorSemaphore = -1;
178 		return B_ERROR;
179 	}
180 
181 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
182 	uint32 pos = atomic_get((int32*)&fCursorBuffer->pos);
183 #else
184 	uint32 pos = fCursorBuffer->pos;
185 #endif
186 
187 	where.x = pos >> 16UL;
188 	where.y = pos & 0xffff;
189 
190 	atomic_and(&fCursorBuffer->read, 0);
191 		// this tells the input_server that we've read the
192 		// cursor position and want to be notified if updated
193 
194 	if (fQuitting) {
195 		fQuitting = false;
196 		return B_ERROR;
197 	}
198 
199 	return B_OK;
200 }
201 
202 
203 status_t
204 InputServerStream::InsertEvent(BMessage* event)
205 {
206 	fEvents.AddMessage(event);
207 	status_t status = write_port_etc(fPort, 'insm', NULL, 0, B_RELATIVE_TIMEOUT,
208 		0);
209 	if (status == B_BAD_PORT_ID)
210 		return status;
211 
212 	// If the port is full, we obviously don't care to report this, as we
213 	// already placed our message.
214 	return B_OK;
215 }
216 
217 
218 BMessage*
219 InputServerStream::PeekLatestMouseMoved()
220 {
221 	return fLatestMouseMoved;
222 }
223 
224 
225 status_t
226 InputServerStream::_MessageFromPort(BMessage** _message, bigtime_t timeout)
227 {
228 	uint8 *buffer = NULL;
229 	ssize_t bufferSize;
230 
231 	// read message from port
232 
233 	do {
234 		bufferSize = port_buffer_size_etc(fPort, B_RELATIVE_TIMEOUT, timeout);
235 	} while (bufferSize == B_INTERRUPTED);
236 
237 	if (bufferSize < B_OK)
238 		return bufferSize;
239 
240 	if (bufferSize > 0) {
241 		buffer = new (std::nothrow) uint8[bufferSize];
242 		if (buffer == NULL)
243 			return B_NO_MEMORY;
244 	}
245 
246 	int32 code;
247 	bufferSize = read_port_etc(fPort, &code, buffer, bufferSize,
248 		B_RELATIVE_TIMEOUT, 0);
249 	if (bufferSize < B_OK) {
250 		delete[] buffer;
251 		return bufferSize;
252 	}
253 
254 	if (code == 'quit') {
255 		// this will cause GetNextEvent() to return false
256 		return B_BAD_PORT_ID;
257 	}
258 	if (code == 'insm') {
259 		// a message has been inserted into our queue
260 		return B_INTERRUPTED;
261 	}
262 
263 	// we have the message, now let's unflatten it
264 
265 	BMessage* message = new BMessage(code);
266 	if (message == NULL)
267 		return B_NO_MEMORY;
268 
269 	if (buffer == NULL) {
270 		*_message = message;
271 		return B_OK;
272 	}
273 
274 	status_t status = message->Unflatten((const char*)buffer);
275 	delete[] buffer;
276 
277 	if (status != B_OK) {
278 		printf("Unflatten event failed: %s, port message code was: %" B_PRId32
279 			" - %c%c%c%c\n", strerror(status), code, (int8)(code >> 24),
280 			(int8)(code >> 16), (int8)(code >> 8), (int8)code);
281 		delete message;
282 		return status;
283 	}
284 
285 	*_message = message;
286 	return B_OK;
287 }
288 
289