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