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
24EventStream::EventStream()
25{
26}
27
28
29EventStream::~EventStream()
30{
31}
32
33
34bool
35EventStream::SupportsCursorThread() const
36{
37	return false;
38}
39
40
41status_t
42EventStream::GetNextCursorPosition(BPoint& where, bigtime_t timeout)
43{
44	return B_ERROR;
45}
46
47
48//	#pragma mark -
49
50
51InputServerStream::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
78InputServerStream::InputServerStream()
79	:
80	fQuitting(false),
81	fCursorSemaphore(-1),
82	fLatestMouseMoved(NULL)
83{
84	fPort = find_port(SERVER_INPUT_PORT);
85}
86#endif
87
88
89InputServerStream::~InputServerStream()
90{
91	delete_area(fCursorArea);
92}
93
94
95bool
96InputServerStream::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
106void
107InputServerStream::SendQuit()
108{
109	fQuitting = true;
110	write_port(fPort, 'quit', NULL, 0);
111	release_sem(fCursorSemaphore);
112}
113
114
115void
116InputServerStream::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
125bool
126InputServerStream::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
163status_t
164InputServerStream::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
204status_t
205InputServerStream::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
219BMessage*
220InputServerStream::PeekLatestMouseMoved()
221{
222	return fLatestMouseMoved;
223}
224
225
226status_t
227InputServerStream::_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