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