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