root/src/servers/app/drawing/interface/remote/RemoteEventStream.cpp
/*
 * Copyright 2009, Haiku, Inc.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Michael Lotz <mmlr@mlotz.ch>
 */

#include "RemoteEventStream.h"

#include "RemoteMessage.h"
#include "StreamingRingBuffer.h"

#include <Autolock.h>

#include <new>


RemoteEventStream::RemoteEventStream()
        :
        fEventList(10),
        fEventListLocker("remote event list"),
        fEventNotification(-1),
        fWaitingOnEvent(false),
        fLatestMouseMovedEvent(NULL),
        fMousePosition(0, 0),
        fMouseButtons(0),
        fModifiers(0)
{
        fEventNotification = create_sem(0, "remote event notification");
}


RemoteEventStream::~RemoteEventStream()
{
        delete_sem(fEventNotification);
}


void
RemoteEventStream::UpdateScreenBounds(BRect bounds)
{
}


bool
RemoteEventStream::GetNextEvent(BMessage** _event)
{
        BAutolock lock(fEventListLocker);
        while (fEventList.CountItems() == 0) {
                fWaitingOnEvent = true;
                lock.Unlock();

                status_t result;
                do {
                        result = acquire_sem(fEventNotification);
                } while (result == B_INTERRUPTED);

                lock.Lock();
                if (!lock.IsLocked())
                        return false;
        }

        *_event = fEventList.RemoveItemAt(0);
        return true;
}


status_t
RemoteEventStream::InsertEvent(BMessage* event)
{
        BAutolock lock(fEventListLocker);
        if (!lock.IsLocked())
                return B_ERROR;

        if (!fEventList.AddItem(event))
                return B_ERROR;

        if (event->what == B_MOUSE_MOVED)
                fLatestMouseMovedEvent = event;

        return B_OK;
}


BMessage*
RemoteEventStream::PeekLatestMouseMoved()
{
        return fLatestMouseMovedEvent;
}


bool
RemoteEventStream::EventReceived(RemoteMessage& message)
{
        uint16 code = message.Code();
        uint32 what = 0;
        switch (code) {
                case RP_MOUSE_MOVED:
                        what = B_MOUSE_MOVED;
                        break;
                case RP_MOUSE_DOWN:
                        what = B_MOUSE_DOWN;
                        break;
                case RP_MOUSE_UP:
                        what = B_MOUSE_UP;
                        break;
                case RP_MOUSE_WHEEL_CHANGED:
                        what = B_MOUSE_WHEEL_CHANGED;
                        break;
                case RP_KEY_DOWN:
                        what = B_KEY_DOWN;
                        break;
                case RP_KEY_UP:
                        what = B_KEY_UP;
                        break;
                case RP_MODIFIERS_CHANGED:
                        what = B_MODIFIERS_CHANGED;
                        break;
        }

        if (what == 0)
                return false;

        BMessage* event = new BMessage(what);
        if (event == NULL)
                return false;

        event->AddInt64("when", system_time());

        switch (code) {
                case RP_MOUSE_MOVED:
                case RP_MOUSE_DOWN:
                case RP_MOUSE_UP:
                {
                        message.Read(fMousePosition);
                        if (code != RP_MOUSE_MOVED)
                                message.Read(fMouseButtons);

                        event->AddPoint("where", fMousePosition);
                        event->AddInt32("buttons", fMouseButtons);
                        event->AddInt32("modifiers", fModifiers);

                        if (code == RP_MOUSE_DOWN) {
                                int32 clicks;
                                if (message.Read(clicks) == B_OK)
                                        event->AddInt32("clicks", clicks);
                        }

                        if (code == RP_MOUSE_MOVED)
                                fLatestMouseMovedEvent = event;
                        break;
                }

                case RP_MOUSE_WHEEL_CHANGED:
                {
                        float xDelta, yDelta;
                        message.Read(xDelta);
                        message.Read(yDelta);
                        event->AddFloat("be:wheel_delta_x", xDelta);
                        event->AddFloat("be:wheel_delta_y", yDelta);
                        break;
                }

                case RP_KEY_DOWN:
                case RP_KEY_UP:
                {
                        int32 numBytes;
                        if (message.Read(numBytes) != B_OK)
                                break;

                        if (numBytes > 1000)
                                break;

                        char* bytes = (char*)malloc(numBytes + 1);
                        if (bytes == NULL)
                                break;

                        if (message.ReadList(bytes, numBytes) != B_OK) {
                                free(bytes);
                                break;
                        }

                        for (int32 i = 0; i < numBytes; i++)
                                event->AddInt8("byte", (int8)bytes[i]);

                        bytes[numBytes] = 0;
                        event->AddData("bytes", B_STRING_TYPE, bytes, numBytes + 1, false);
                        event->AddInt32("modifiers", fModifiers);

                        int32 rawChar;
                        if (message.Read(rawChar) == B_OK)
                                event->AddInt32("raw_char", rawChar);

                        int32 key;
                        if (message.Read(key) == B_OK)
                                event->AddInt32("key", key);

                        free(bytes);
                        break;
                }

                case RP_MODIFIERS_CHANGED:
                {
                        event->AddInt32("be:old_modifiers", fModifiers);
                        message.Read(fModifiers);
                        event->AddInt32("modifiers", fModifiers);
                        break;
                }
        }

        BAutolock lock(fEventListLocker);
        fEventList.AddItem(event);
        if (fWaitingOnEvent) {
                fWaitingOnEvent = false;
                lock.Unlock();
                release_sem(fEventNotification);
        }

        return true;
}