⛏️ index : haiku.git

/*
 * Copyright 2005, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Axel DΓΆrfler, axeld@pinc-software.de
 */


#include "EventStream.h"

#include <InputServerTypes.h>
#include <ServerProtocol.h>
#include <shared_cursor_area.h>

#include <AppMisc.h>
#include <AutoDeleter.h>

#include <new>
#include <stdio.h>
#include <string.h>


EventStream::EventStream()
{
}


EventStream::~EventStream()
{
}


bool
EventStream::SupportsCursorThread() const
{
	return false;
}


status_t
EventStream::GetNextCursorPosition(BPoint& where, bigtime_t timeout)
{
	return B_ERROR;
}


//	#pragma mark -


InputServerStream::InputServerStream(BMessenger& messenger)
	:
	fInputServer(messenger),
	fPort(-1),
	fQuitting(false),
	fLatestMouseMoved(NULL)
{
	BMessage message(IS_ACQUIRE_INPUT);
	message.AddInt32("remote team", BPrivate::current_team());

	fCursorArea = create_area("shared cursor", (void **)&fCursorBuffer, B_ANY_ADDRESS,
		B_PAGE_SIZE, B_LAZY_LOCK, B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA);
	if (fCursorArea >= B_OK)
		message.AddInt32("cursor area", fCursorArea);

	BMessage reply;
	if (messenger.SendMessage(&message, &reply) != B_OK)
		return;

	if (reply.FindInt32("event port", &fPort) != B_OK)
		fPort = -1;
	if (reply.FindInt32("cursor semaphore", &fCursorSemaphore) != B_OK)
		fCursorSemaphore = -1;
}


#if TEST_MODE
InputServerStream::InputServerStream()
	:
	fQuitting(false),
	fCursorSemaphore(-1),
	fLatestMouseMoved(NULL)
{
	fPort = find_port(SERVER_INPUT_PORT);
}
#endif


InputServerStream::~InputServerStream()
{
	delete_area(fCursorArea);
}


bool
InputServerStream::IsValid()
{
	port_info portInfo;
	if (fPort < B_OK || get_port_info(fPort, &portInfo) != B_OK)
		return false;

	return true;
}


void
InputServerStream::SendQuit()
{
	fQuitting = true;
	write_port(fPort, 'quit', NULL, 0);
	release_sem(fCursorSemaphore);
}


void
InputServerStream::UpdateScreenBounds(BRect bounds)
{
	BMessage update(IS_SCREEN_BOUNDS_UPDATED);
	update.AddRect("screen_bounds", bounds);

	fInputServer.SendMessage(&update);
}


bool
InputServerStream::GetNextEvent(BMessage** _event)
{
	while (fEvents.IsEmpty()) {
		// wait for new events
		BMessage* event;
		status_t status = _MessageFromPort(&event);
		if (status == B_OK) {
			if (event->what == B_MOUSE_MOVED)
				fLatestMouseMoved = event;

			fEvents.AddMessage(event);
		} else if (status == B_BAD_PORT_ID) {
			// our port got deleted - the input_server must have died
			fPort = -1;
			return false;
		}

		int32 count = port_count(fPort);
		if (count > 0) {
			// empty port queue completely while we're at it
			for (int32 i = 0; i < count; i++) {
				if (_MessageFromPort(&event, 0) == B_OK) {
					if (event->what == B_MOUSE_MOVED)
						fLatestMouseMoved = event;
					fEvents.AddMessage(event);
				}
			}
		}
	}

	// there are items in our list, so just work through them

	*_event = fEvents.NextMessage();
	return true;
}


status_t
InputServerStream::GetNextCursorPosition(BPoint &where, bigtime_t timeout)
{
	status_t status;

	do {
		status = acquire_sem_etc(fCursorSemaphore, 1, B_RELATIVE_TIMEOUT,
			timeout);
	} while (status == B_INTERRUPTED);

	if (status == B_TIMED_OUT)
		return status;

	if (status == B_BAD_SEM_ID) {
		// the semaphore is no longer valid - the input_server must have died
		fCursorSemaphore = -1;
		return B_ERROR;
	}

#ifdef HAIKU_TARGET_PLATFORM_HAIKU
	uint32 pos = atomic_get((int32*)&fCursorBuffer->pos);
#else
	uint32 pos = fCursorBuffer->pos;
#endif

	where.x = pos >> 16UL;
	where.y = pos & 0xffff;

	atomic_and(&fCursorBuffer->read, 0);
		// this tells the input_server that we've read the
		// cursor position and want to be notified if updated

	if (fQuitting) {
		fQuitting = false;
		return B_ERROR;
	}

	return B_OK;
}


status_t
InputServerStream::InsertEvent(BMessage* event)
{
	fEvents.AddMessage(event);
	status_t status = write_port_etc(fPort, 'insm', NULL, 0, B_RELATIVE_TIMEOUT,
		0);
	if (status == B_BAD_PORT_ID)
		return status;

	// If the port is full, we obviously don't care to report this, as we
	// already placed our message.
	return B_OK;
}


BMessage*
InputServerStream::PeekLatestMouseMoved()
{
	return fLatestMouseMoved;
}


status_t
InputServerStream::_MessageFromPort(BMessage** _message, bigtime_t timeout)
{
	uint8 *buffer = NULL;
	ssize_t bufferSize;

	// read message from port

	do {
		bufferSize = port_buffer_size_etc(fPort, B_RELATIVE_TIMEOUT, timeout);
	} while (bufferSize == B_INTERRUPTED);

	if (bufferSize < B_OK)
		return bufferSize;

	if (bufferSize > 0) {
		buffer = new (std::nothrow) uint8[bufferSize];
		if (buffer == NULL)
			return B_NO_MEMORY;
	}

	int32 code;
	bufferSize = read_port_etc(fPort, &code, buffer, bufferSize,
		B_RELATIVE_TIMEOUT, 0);
	if (bufferSize < B_OK) {
		delete[] buffer;
		return bufferSize;
	}

	if (code == 'quit') {
		// this will cause GetNextEvent() to return false
		return B_BAD_PORT_ID;
	}
	if (code == 'insm') {
		// a message has been inserted into our queue
		return B_INTERRUPTED;
	}

	// we have the message, now let's unflatten it

	ObjectDeleter<BMessage> message(new BMessage(code));
	if (!message.IsSet())
		return B_NO_MEMORY;

	if (buffer == NULL) {
		*_message = message.Detach();
		return B_OK;
	}

	status_t status = message->Unflatten((const char*)buffer);
	delete[] buffer;

	if (status != B_OK) {
		printf("Unflatten event failed: %s, port message code was: %" B_PRId32
			" - %c%c%c%c\n", strerror(status), code, (int8)(code >> 24),
			(int8)(code >> 16), (int8)(code >> 8), (int8)code);
		return status;
	}

	*_message = message.Detach();
	return B_OK;
}