From 128ddaa3e3ee40b5c8f74b1bdd816bacdd00f272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Wed, 16 Nov 2005 17:25:58 +0000 Subject: [PATCH] Some work on the input_server: * Rewrote event handling: instead of writing every single device message to the event port, they are just queued in a list, and the event loop is only notified if necessary (ie. if a notification has been sent already, new events are just queued up until the input_server found the time to process them). * This also fixed a big memory leak: every message processed by EnqueueDeviceMessage() (IOW every key or mouse event) was leaked! * no longer abuses gInputMethodListLocker to lock the method event queue (it now uses the standard event queue lock). * removed the completely superfluous, weird and decelerating event caching mechanism * tried to find a better distribution of work between _SanitizeEvents(), _MethodizeEvents(), and _DispatchEvents(). * HandleSetMousePosition() now only does what it's supposed to do (this currently causes the mouse to jump at the start, though). * now uses the "Message4" for message sending if available. * fixed "kb_mouse_settings.h" to include all headers it needs. * some more cleanup. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@14966 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/input/kb_mouse_settings.h | 35 +- src/servers/input/BottomlineWindow.cpp | 98 ++- src/servers/input/BottomlineWindow.h | 56 +- src/servers/input/InputServer.cpp | 814 +++++++++++----------- src/servers/input/InputServer.h | 51 +- src/servers/input/Jamfile | 5 +- 6 files changed, 530 insertions(+), 529 deletions(-) diff --git a/headers/private/input/kb_mouse_settings.h b/headers/private/input/kb_mouse_settings.h index 513120875e..2bb40c4b95 100644 --- a/headers/private/input/kb_mouse_settings.h +++ b/headers/private/input/kb_mouse_settings.h @@ -1,41 +1,34 @@ -// -// kb_mouse_settings.h -// - - +/* + * Copyright 2001-2005, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ #ifndef _KB_MOUSE_SETTINGS_H #define _KB_MOUSE_SETTINGS_H -#include -#ifdef __cplusplus -extern "C" { -#endif +#include + typedef struct { - bigtime_t key_repeat_delay; - int32 key_repeat_rate; + bigtime_t key_repeat_delay; + int32 key_repeat_rate; } kb_settings; #define kb_settings_file "Keyboard_settings" typedef struct { - bool enabled; - int32 accel_factor; - int32 speed; + bool enabled; + int32 accel_factor; + int32 speed; } mouse_accel; typedef struct { int32 type; mouse_map map; - mouse_accel accel; - bigtime_t click_speed; + mouse_accel accel; + bigtime_t click_speed; } mouse_settings; #define mouse_settings_file "Mouse_settings" -#ifdef __cplusplus -} -#endif - -#endif +#endif /* _KB_MOUSE_SETTINGS_H */ diff --git a/src/servers/input/BottomlineWindow.cpp b/src/servers/input/BottomlineWindow.cpp index 2bf7670240..27df0b5446 100644 --- a/src/servers/input/BottomlineWindow.cpp +++ b/src/servers/input/BottomlineWindow.cpp @@ -1,34 +1,30 @@ -// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ -// -// Copyright (c) 2004, Haiku -// -// This software is part of the Haiku distribution and is covered -// by the Haiku license. -// -// -// File: BottomlineWindow.cpp -// Author: Jérôme Duval -// Description: Input server bottomline window -// Created : January 24, 2005 -// -// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ +/* + * Copyright 2004-2005, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jérôme Duval + */ + #include "BottomlineWindow.h" -#include "InputServer.h" #include "WindowPrivate.h" -BottomlineWindow::BottomlineWindow(const BFont *font) - : BWindow(BRect(0,0,350,16), "", - kLeftTitledWindowLook, - B_FLOATING_ALL_WINDOW_FEEL, - B_NOT_V_RESIZABLE | B_NOT_CLOSABLE | - B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE | B_AVOID_FOCUS | - B_WILL_ACCEPT_FIRST_CLICK) +#include + + +BottomlineWindow::BottomlineWindow() + : BWindow(BRect(0, 0, 350, 16), "", + kLeftTitledWindowLook, + B_FLOATING_ALL_WINDOW_FEEL, + B_NOT_V_RESIZABLE | B_NOT_CLOSABLE | B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE + | B_AVOID_FOCUS | B_WILL_ACCEPT_FIRST_CLICK) { BRect textRect = Bounds(); textRect.OffsetTo(B_ORIGIN); textRect.InsetBy(2,2); - fTextView = new BTextView(Bounds(), "", textRect, font, NULL, B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS); + fTextView = new BTextView(Bounds(), "", textRect, be_plain_font, + NULL, B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS); AddChild(fTextView); fTextView->SetText(""); @@ -73,45 +69,43 @@ BottomlineWindow::QuitRequested() void -BottomlineWindow::HandleInputMethodEvent(BMessage *msg, BList *list) +BottomlineWindow::HandleInputMethodEvent(BMessage* event, EventList& newEvents) { CALLED(); - PostMessage(msg, fTextView); + PostMessage(event, fTextView); - int32 opcode = -1; - const char* string = NULL; - - msg->FindInt32("be:opcode", &opcode); - msg->FindString("be:string", &string); + const char* string; + bool confirmed; + int32 opcode; + if (event->FindInt32("be:opcode", &opcode) != B_OK + || opcode != B_INPUT_METHOD_CHANGED + || event->FindBool("be:confirmed", &confirmed) != B_OK + || !confirmed + || event->FindString("be:string", &string) != B_OK) + return; SERIAL_PRINT(("IME : %i, %s\n", opcode, string)); - if (opcode != B_INPUT_METHOD_CHANGED) - return; - - bool confirmed = false; - if ((msg->FindBool("be:confirmed", &confirmed) != B_OK) - || !confirmed) - return; - SERIAL_PRINT(("IME : confirmed\n")); - - if (string != NULL) { - int32 offset = 0; - int32 length = strlen(string); - int32 next_offset = 0; - while (offset < length) { - for (++next_offset; (string[next_offset] & 0xC0) == 0x80; ++next_offset) - ; + int32 length = strlen(string); + int32 offset = 0; + int32 nextOffset = 0; - BMessage *newmsg = new BMessage(B_KEY_DOWN); - newmsg->AddInt32("key", 0); - newmsg->AddInt64("when", system_time()); - newmsg->AddData("bytes", B_STRING_TYPE, string + offset, next_offset - offset); - list->AddItem(newmsg); - offset = next_offset; + while (offset < length) { + // this is supposed to go to the next UTF-8 character + for (++nextOffset; (string[nextOffset] & 0xC0) == 0x80; ++nextOffset) + ; + + BMessage *newEvent = new BMessage(B_KEY_DOWN); + if (newEvent != NULL) { + newEvent->AddInt32("key", 0); + newEvent->AddInt64("when", system_time()); + newEvent->AddData("bytes", B_STRING_TYPE, string + offset, nextOffset - offset); + newEvents.AddItem(newEvent); } + + offset = nextOffset; } } diff --git a/src/servers/input/BottomlineWindow.h b/src/servers/input/BottomlineWindow.h index 2f0f390097..ef6093fc34 100644 --- a/src/servers/input/BottomlineWindow.h +++ b/src/servers/input/BottomlineWindow.h @@ -1,38 +1,34 @@ -// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ -// -// Copyright (c) 2005, Haiku -// -// This software is part of the Haiku distribution and is covered -// by the Haiku license. -// -// -// File: TMWindow.h -// Author: Jérôme Duval -// Description: Input server bottomline window -// Created : January 24, 2005 -// -// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ +/* + * Copyright 2004-2005, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jérôme Duval + */ +#ifndef BOTTOMLINE_WINDOW_H +#define BOTTOMLINE_WINDOW_H -#ifndef BOTTOMLINEWINDOW_H -#define BOTTOMLINEWINDOW_H + +#include "InputServer.h" #include -#include #include -class BottomlineWindow : public BWindow -{ -public: - BottomlineWindow(const BFont *font); - ~BottomlineWindow(); - - void MessageReceived(BMessage *msg); - virtual bool QuitRequested(); +class BTextView; - void HandleInputMethodEvent(BMessage *msg, BList *list); -//private: - BTextView *fTextView; + +class BottomlineWindow : public BWindow { + public: + BottomlineWindow(); + virtual ~BottomlineWindow(); + + virtual void MessageReceived(BMessage* message); + virtual bool QuitRequested(); + + void HandleInputMethodEvent(BMessage* event, EventList& newEvents); + + private: + BTextView *fTextView; }; - -#endif //BOTTOMLINEWINDOW_H +#endif // BOTTOMLINE_WINDOW_H diff --git a/src/servers/input/InputServer.cpp b/src/servers/input/InputServer.cpp index 1a1badd878..b0a7d7473c 100644 --- a/src/servers/input/InputServer.cpp +++ b/src/servers/input/InputServer.cpp @@ -6,10 +6,12 @@ #include "InputServer.h" #include "InputServerTypes.h" +#include "BottomlineWindow.h" #include "kb_mouse_driver.h" #include "MethodReplicant.h" #include +#include #include #include @@ -38,11 +40,11 @@ // received from app_server when screen res changed, but could be sent to too. -#ifdef COMPILE_FOR_R5 -extern "C" status_t _kget_safemode_option_(const char *parameter, +#ifdef HAIKU_TARGET_PLATFORM_HAIKU +extern "C" status_t _kern_get_safemode_option(const char *parameter, char *buffer, size_t *_bufferSize); #else -extern "C" status_t _kern_get_safemode_option(const char *parameter, +extern "C" status_t _kget_safemode_option_(const char *parameter, char *buffer, size_t *_bufferSize); #endif @@ -140,8 +142,8 @@ InputServer::InputServer() fInputDeviceListLocker("input server device list"), fChars(NULL), fScreen(B_MAIN_SCREEN_ID), - fBLWindow(NULL), - fIMAware(false), + fInputMethodWindow(NULL), + fInputMethodAware(false), fCursorSem(-1), fAppServerPort(-1), fCursorArea(-1) @@ -154,15 +156,15 @@ InputServer::InputServer() CALLED(); gInputServer = this; - EventLoop(); + _StartEventLoop(); char parameter[32]; size_t parameterLength = sizeof(parameter); -#ifdef COMPILE_FOR_R5 - if (_kget_safemode_option_(B_SAFEMODE_SAFE_MODE, parameter, ¶meterLength) == B_OK) -#else +#ifdef HAIKU_TARGET_PLATFORM_HAIKU if (_kern_get_safemode_option(B_SAFEMODE_SAFE_MODE, parameter, ¶meterLength) == B_OK) +#else + if (_kget_safemode_option_(B_SAFEMODE_SAFE_MODE, parameter, ¶meterLength) == B_OK) #endif { if (!strcasecmp(parameter, "enabled") || !strcasecmp(parameter, "on") @@ -760,79 +762,25 @@ InputServer::HandleGetSetMouseSpeed(BMessage* message, BMessage* reply) status_t -InputServer::HandleSetMousePosition(BMessage* message, BMessage* outbound) +InputServer::HandleSetMousePosition(BMessage* message, BMessage* reply) { CALLED(); - // this assumes that both supplied pointers are identical + BPoint where; + if (message->FindPoint("where", &where) != B_OK) + return B_BAD_VALUE; - ASSERT(outbound == message); + // create a new event for this and enqueue it to the event list just like any other - int32 xValue, yValue; + BMessage* event = new BMessage(B_MOUSE_MOVED); + if (event == NULL) + return B_NO_MEMORY; - switch (message->what) { - case B_MOUSE_MOVED: - case B_MOUSE_DOWN: - case B_MOUSE_UP: - case FAST_MOUSE_MOVED: - // get point and button from msg - if ((outbound->FindInt32("x", &xValue) == B_OK) - && (outbound->FindInt32("y", &yValue) == B_OK)) { - fMousePos.x += xValue; - fMousePos.y -= yValue; - fMousePos.ConstrainTo(fFrame); - outbound->RemoveName("x"); - outbound->RemoveName("y"); - outbound->AddPoint("where", fMousePos); - outbound->AddInt32("modifiers", fKeyInfo.modifiers); - PRINT(("new position : %f, %f, %ld, %ld\n", fMousePos.x, fMousePos.y, xValue, yValue)); - } else if (outbound->FindPoint("where", &fMousePos) == B_OK) { - outbound->RemoveName("where"); - fMousePos.ConstrainTo(fFrame); - outbound->AddPoint("where", fMousePos); - outbound->AddInt32("modifiers", fKeyInfo.modifiers); - PRINT(("new position : %f, %f\n", fMousePos.x, fMousePos.y)); - } - - if (fCursorBuffer) { -#ifndef HAIKU_TARGET_PLATFORM_HAIKU - fCursorBuffer[0] = (0x3 << 30L) - | ((uint32)fMousePos.x & 0x7fff) << 15 - | ((uint32)fMousePos.y & 0x7fff); - - PRINT(("released cursorSem with value : %08lx\n", fCursorBuffer[0])); - release_sem(fCursorSem); -#else - atomic_set((int32*)&fCursorBuffer->pos, (uint32)fMousePos.x << 16UL - | ((uint32)fMousePos.y & 0xffff)); - if (atomic_or(&fCursorBuffer->read, 1) == 0) - release_sem_etc(fCursorSem, 0, B_RELEASE_ALL); -#endif - } - break; - - // some Key Down and up codes .. - case B_KEY_DOWN: - case B_KEY_UP: - case B_UNMAPPED_KEY_DOWN: - case B_UNMAPPED_KEY_UP: - case B_MODIFIERS_CHANGED: - { - ssize_t size = 0; - uint8 *data = NULL; - outbound->FindInt32("modifiers", (int32*)&fKeyInfo.modifiers); - if (outbound->FindData("states", B_UINT8_TYPE, - (const void**)&data, &size) == B_OK - && size == (ssize_t)sizeof(fKeyInfo.key_states)) { - PRINT(("updated keyinfo\n")); - memcpy(fKeyInfo.key_states, data, size); - } - PRINT(("keyinfo %ld %ld\n", size, (ssize_t)sizeof(fKeyInfo.key_states))); - break; - } - - default: - break; + event->AddPoint("where", where); + event->AddBool("be:set_mouse", true); + if (EnqueueDeviceMessage(event) != B_OK) { + delete event; + return B_NO_MEMORY; } return B_OK; @@ -938,34 +886,41 @@ InputServer::HandleFocusUnfocusIMAwareView(BMessage* message, if (message->what == IS_FOCUS_IM_AWARE_VIEW) { PRINT(("HandleFocusUnfocusIMAwareView : entering\n")); - fIMAware = true; + fInputMethodAware = true; } else { PRINT(("HandleFocusUnfocusIMAwareView : leaving\n")); - fIMAware = false; + fInputMethodAware = false; } return B_OK; } +/** Enqueues the message into the event queue. + * The message must only be deleted in case this method returns an error. + */ + status_t InputServer::EnqueueDeviceMessage(BMessage* message) { CALLED(); - // TODO: message is not deleted??? - // TODO: why use ports for in-house messaging??? + BAutolock _(fEventQueueLock); + if (!fEventQueue.AddItem(message)) + return B_NO_MEMORY; - ssize_t length = message->FlattenedSize(); - char buffer[length]; - status_t err; - if ((err = message->Flatten(buffer,length)) < B_OK) - return err; - - return write_port(fEventLooperPort, 0, buffer, length); + if (fEventQueue.CountItems() == 1) { + // notify event loop only if we haven't already done so + write_port_etc(fEventLooperPort, 1, NULL, 0, B_RELATIVE_TIMEOUT, 0); + } + return B_OK; } +/** Enqueues the message into the method queue. + * The message must only be deleted in case this method returns an error. + */ + status_t InputServer::EnqueueMethodMessage(BMessage* message) { @@ -981,27 +936,14 @@ InputServer::EnqueueMethodMessage(BMessage* message) } #endif - LockMethodQueue(); - fMethodQueue.AddItem(message); - UnlockMethodQueue(); + BAutolock _(fEventQueueLock); + if (!fMethodQueue.AddItem(message)) + return B_NO_MEMORY; - write_port_etc(fEventLooperPort, 0, NULL, 0, B_RELATIVE_TIMEOUT, 0); - return B_OK; -} - - -status_t -InputServer::UnlockMethodQueue() -{ - gInputMethodListLocker.Unlock(); - return B_OK; -} - - -status_t -InputServer::LockMethodQueue() -{ - gInputMethodListLocker.Lock(); + if (fMethodQueue.CountItems() == 1) { + // notify event loop only if we haven't already done so + write_port_etc(fEventLooperPort, 0, NULL, 0, B_RELATIVE_TIMEOUT, 0); + } return B_OK; } @@ -1009,7 +951,7 @@ InputServer::LockMethodQueue() status_t InputServer::SetNextMethod(bool direction) { - LockMethodQueue(); + gInputMethodListLocker.Lock(); int32 index = gInputMethodList.IndexOf(fActiveMethod); index += (direction ? 1 : -1); @@ -1026,7 +968,7 @@ InputServer::SetNextMethod(bool direction) SetActiveMethod(method); - UnlockMethodQueue(); + gInputMethodListLocker.Unlock(); return B_OK; } @@ -1059,31 +1001,6 @@ InputServer::SetMethodReplicant(const BMessenger* messenger) } -status_t -InputServer::EventLoop() -{ - CALLED(); - fEventLooperPort = create_port(100, "input server event loop"); - if (fEventLooperPort < 0) { - PRINTERR(("InputServer: create_port error: (0x%x) %s\n", - fEventLooperPort, strerror(fEventLooperPort))); - return fEventLooperPort; - } - - thread_id thread = spawn_thread(_PortWatcher, "_input_server_event_loop_", - B_REAL_TIME_DISPLAY_PRIORITY + 3, this); - if (thread < B_OK || resume_thread(thread) < B_OK) { - if (thread >= B_OK) - kill_thread(thread); - delete_port(fEventLooperPort); - fEventLooperPort = -1; - return thread < B_OK ? thread : B_ERROR; - } - - return B_OK; -} - - bool InputServer::EventLoopRunning() { @@ -1091,241 +1008,6 @@ InputServer::EventLoopRunning() } -bool -InputServer::DispatchEvents(BList* eventList) -{ - CALLED(); - - CacheEvents(eventList); - - if (fEventsCache.CountItems() > 0) { - BMessage* event; - - for (int32 i = 0; NULL != (event = (BMessage *)fEventsCache.ItemAt(i)); i++) { - // now we must send each event to the app_server - if (event->what == B_INPUT_METHOD_EVENT && !fIMAware) { - SERIAL_PRINT(("IME received\n")); - int32 opcode = -1; - event->FindInt32("be:opcode", &opcode); - BList events; - if (!fBLWindow && opcode == B_INPUT_METHOD_STARTED) - fBLWindow = new BottomlineWindow(be_plain_font); - - if (fBLWindow) { - fBLWindow->HandleInputMethodEvent(event, &events); - - if (!events.IsEmpty()) { - fBLWindow->PostMessage(B_QUIT_REQUESTED); - fBLWindow = NULL; - - fEventsCache.AddList(&events); - } - } - } else - DispatchEvent(event); - - delete event; - } - - fEventsCache.MakeEmpty(); - } - - return true; -} - - -status_t -InputServer::DispatchEvent(BMessage* message) -{ - CALLED(); - - switch (message->what) { - case B_KEY_DOWN: - case B_KEY_UP: - case B_UNMAPPED_KEY_UP: - case B_UNMAPPED_KEY_DOWN: { - uint32 modifiers; - ssize_t size; - uint8 *data; - if (message->FindData("states", B_UINT8_TYPE, (const void**)&data, &size) != B_OK) - message->AddData("states", B_UINT8_TYPE, fKeyInfo.key_states, 16); - if (message->FindInt32("modifiers", (int32*)&modifiers) != B_OK) - message->AddInt32("modifiers", fKeyInfo.modifiers); - break; - } - } - - ssize_t length = message->FlattenedSize(); - char buffer[length]; - status_t err; - if ((err = message->Flatten(buffer,length)) < B_OK) - return err; - - if (fAppServerPort > 0) - write_port(fAppServerPort, 0, buffer, length); - - return B_OK; -} - - -bool -InputServer::CacheEvents(BList* eventsToCache) -{ - CALLED(); - - SanitizeEvents(eventsToCache); - MethodizeEvents(eventsToCache, true); - FilterEvents(eventsToCache); - - fEventsCache.AddList(eventsToCache); - eventsToCache->MakeEmpty(); - - return true; -} - - -const BList* -InputServer::GetNextEvents(BList *) -{ - return NULL; -} - - -/** This method applies all defined filters to each event in the - * supplied list. The supplied list is modified to reflect the - * output of the filters. - * The method returns true if the filters were applied to all - * events without error and false otherwise. - */ - -bool -InputServer::FilterEvents(BList* eventsToFilter) -{ - CALLED(); - - if (NULL == eventsToFilter) - return false; - - BInputServerFilter* currentFilter; - int32 filterIndex = 0; - - while ((currentFilter = (BInputServerFilter*)gInputFilterList.ItemAt(filterIndex)) != NULL) { - // Apply the current filter to all available event messages. - int32 eventIndex = 0; - BMessage *currentEvent; - while ((currentEvent = (BMessage*)eventsToFilter->ItemAt(eventIndex)) != NULL) { - // Storage for new event messages generated by the filter. - BList outList; - - // Apply the current filter to the current event message. - PRINT(("InputServer::FilterEvents Filter called\n")); - filter_result result = currentFilter->Filter(currentEvent, &outList); - - if (result == B_SKIP_MESSAGE) { - // Use the result in out_list (if any); ignore current message. - eventsToFilter->RemoveItem(eventIndex); - eventsToFilter->AddList(&outList, eventIndex); - eventIndex += outList.CountItems(); - - // NOTE: eventsToFilter now owns outList's items. - } else { - // Free resources associated with items in outList. - void* outItem; - for (int32 i = 0; (outItem = outList.ItemAt(i)) != NULL; i++) { - delete (BMessage*)outItem; - } - } - if (result == B_DISPATCH_MESSAGE) { - // Use the result in current_message; ignore outList. - eventIndex++; - } else { - // Error - return false; - } - } - - filterIndex++; - } - - return true; -} - - -bool -InputServer::SanitizeEvents(BList* events) -{ - CALLED(); - int32 index = 0; - BMessage *event; - PRINT(("SanitizeEvents: %ld \n", events->CountItems())); - while ((event = (BMessage*)events->ItemAt(index)) != NULL) { - PRINT(("SanitizeEvents: what:%lx\n", event->what)); - switch (event->what) { - case B_KEY_DOWN: - case B_UNMAPPED_KEY_DOWN: - // we scan for Alt+Space key down events which means we change to next input method - // Note : Shift+Alt+Space key allows to change to the previous input method - - PRINT(("SanitizeEvents: %lx, %lx\n", fKeyInfo.modifiers, - fKeyInfo.key_states[KEY_Spacebar >> 3])); - - if ((fKeyInfo.modifiers & B_COMMAND_KEY) != 0 - && (fKeyInfo.key_states[KEY_Spacebar >> 3] - & (1 << (7 - (KEY_Spacebar % 8))))) { - SetNextMethod(!fKeyInfo.modifiers & B_SHIFT_KEY); - - // this event isn't sent to the user - events->RemoveItem(index); - delete event; - continue; - } - break; - } - - index++; - } - - return true; -} - - -bool -InputServer::MethodizeEvents(BList* events, bool) -{ - CALLED(); - - if (fActiveMethod) { - BList newList; - newList.AddList(&fMethodQueue); - fMethodQueue.MakeEmpty(); - - for (int32 i=0; iCountItems(); i++) { - BMessage *item = (BMessage *)events->ItemAt(i); - BList filterList; - filter_result result = fActiveMethod->Filter(item, &filterList); - switch (result) { - case B_SKIP_MESSAGE: - delete item; - case B_DISPATCH_MESSAGE: - if (filterList.CountItems() > 0) { - newList.AddList(&filterList); - delete item; - } else - newList.AddItem(item); - } - - newList.AddList(&fMethodQueue); - fMethodQueue.MakeEmpty(); - } - - events->MakeEmpty(); - events->AddList(&newList); - } - - return true; -} - - /** The fInputDeviceListLocker must be locked when calling this function */ InputDeviceListItem* @@ -1571,20 +1253,45 @@ InputServer::SafeMode() } -int32 -InputServer::_PortWatcher(void* arg) +status_t +InputServer::_StartEventLoop() { - InputServer* self = (InputServer*)arg; - self->_WatchPort(); + CALLED(); + fEventLooperPort = create_port(100, "input server events"); + if (fEventLooperPort < 0) { + PRINTERR(("InputServer: create_port error: (0x%x) %s\n", + fEventLooperPort, strerror(fEventLooperPort))); + return fEventLooperPort; + } + + thread_id thread = spawn_thread(_EventLooper, "_input_server_event_loop_", + B_REAL_TIME_DISPLAY_PRIORITY + 3, this); + if (thread < B_OK || resume_thread(thread) < B_OK) { + if (thread >= B_OK) + kill_thread(thread); + delete_port(fEventLooperPort); + fEventLooperPort = -1; + return thread < B_OK ? thread : B_ERROR; + } return B_OK; } -void -InputServer::_WatchPort() +status_t +InputServer::_EventLooper(void* arg) { - while (true) { + InputServer* self = (InputServer*)arg; + self->_EventLoop(); + + return B_OK; +} + + +void +InputServer::_EventLoop() +{ + while (true) { // Block until we find the size of the next message ssize_t length = port_buffer_size(fEventLooperPort); if (length < B_OK) { @@ -1606,40 +1313,353 @@ InputServer::_WatchPort() continue; } - if (length == 0) { - BList list; - DispatchEvents(&list); - continue; + EventList events; + if (fEventQueueLock.Lock()) { + // move the items to our own list to block the event queue as short as possible + events.AddList(&fEventQueue); + fEventQueue.MakeEmpty(); + fEventQueueLock.Unlock(); } - BMessage* event = new BMessage; + if (length > 0) { + BMessage* event = new BMessage; - if ((err = event->Unflatten(buffer)) < 0) { - PRINTERR(("[InputServer] Unflatten() error: (0x%lx) %s\n", err, strerror(err))); - delete event; - continue; + if ((err = event->Unflatten(buffer)) < 0) { + PRINTERR(("[InputServer] Unflatten() error: (0x%lx) %s\n", err, strerror(err))); + delete event; + } + + events.AddItem(event); } // This is where the message should be processed. - // here we test for a message coming from app_server, screen resolution change could have happened - if (event->what == FAST_MOUSE_MOVED) { - BRect frame = fScreen.Frame(); - if (frame != fFrame) { - fMousePos.x = fMousePos.x * frame.Width() / fFrame.Width(); - fMousePos.y = fMousePos.y * frame.Height() / fFrame.Height(); - fFrame = frame; + if (_SanitizeEvents(events) + && _MethodizeEvents(events) + && _FilterEvents(events)) + _DispatchEvents(events); + } +} + + +/** Frees events from unwanted fields, adds missing fields, and removes + * unwanted events altogether. + * As a side effect, it will also update the internal mouse states. + */ + +bool +InputServer::_SanitizeEvents(EventList& events) +{ + CALLED(); + int32 index = 0; + BMessage *event; + + while ((event = (BMessage*)events.ItemAt(index)) != NULL) { + switch (event->what) { + case FAST_MOUSE_MOVED: + { + // here we test for a message coming from app_server, + // screen resolution change could have happened + BRect frame = fScreen.Frame(); + if (frame != fFrame) { + fMousePos.x = fMousePos.x * frame.Width() / fFrame.Width(); + fMousePos.y = fMousePos.y * frame.Height() / fFrame.Height(); + fFrame = frame; + } else { + // TODO: discard event + } + + event->what = B_MOUSE_MOVED; + event->AddPoint("where", fMousePos); + // supposed to fall through } + case B_MOUSE_MOVED: + case B_MOUSE_DOWN: + { + int32 buttons; + if (event->FindInt32("buttons", &buttons) != B_OK) + event->AddInt32("buttons", 0); + + // supposed to fall through + } + case B_MOUSE_UP: + { + BPoint where; + int32 x, y; + + if (event->FindInt32("x", &x) == B_OK + && event->FindInt32("y", &y) == B_OK) { + fMousePos.x += x; + fMousePos.y -= y; + fMousePos.ConstrainTo(fFrame); + + event->RemoveName("x"); + event->RemoveName("y"); + event->AddPoint("where", fMousePos); + + PRINT(("new position: %f, %f, %ld, %ld\n", + fMousePos.x, fMousePos.y, xValue, yValue)); + } else if (event->FindPoint("where", &where) == B_OK) { + fMousePos = where; + fMousePos.ConstrainTo(fFrame); + + event->ReplacePoint("where", fMousePos); + PRINT(("new position : %f, %f\n", fMousePos.x, fMousePos.y)); + } + + event->AddInt32("modifiers", fKeyInfo.modifiers); + break; + } + case B_KEY_DOWN: + case B_UNMAPPED_KEY_DOWN: + if (fActiveMethod == NULL) + break; + + // we scan for Alt+Space key down events which means we change + // to next input method + // (pressing "shift" will let us switch to the previous method) + + PRINT(("SanitizeEvents: %lx, %lx\n", fKeyInfo.modifiers, + fKeyInfo.key_states[KEY_Spacebar >> 3])); + + if ((fKeyInfo.modifiers & B_COMMAND_KEY) != 0 + && (fKeyInfo.key_states[KEY_Spacebar >> 3] + & (1 << (7 - (KEY_Spacebar % 8))))) { + SetNextMethod(!fKeyInfo.modifiers & B_SHIFT_KEY); + + // this event isn't sent to the user + events.RemoveItemAt(index); + delete event; + continue; + } + break; } - HandleSetMousePosition(event, event); + index++; + } - BList list; - list.AddItem(event); - DispatchEvents(&list); + return true; +} - PRINT(("Event written to port\n")); + +/** Applies the filters of the active input method to the + * incoming events. It will also move the events in the method + * queue to the event list. + */ + +bool +InputServer::_MethodizeEvents(EventList& events) +{ + CALLED(); + + if (fActiveMethod == NULL) + return true; + + int32 count = events.CountItems(); + for (int32 i = 0; i < count;) { + _FilterEvent(fActiveMethod, events, i, count); } + + if (!fInputMethodAware) { + // special handling for non-input-method-aware views + for (int32 i = 0; i < count; i++) { + BMessage* event = events.ItemAt(i); + + if (event->what != B_INPUT_METHOD_EVENT) + continue; + + SERIAL_PRINT(("IME received\n")); + + int32 opcode; + if (fInputMethodWindow == NULL + && event->FindInt32("be:opcode", &opcode) == B_OK + && opcode == B_INPUT_METHOD_STARTED) + fInputMethodWindow = new (nothrow) BottomlineWindow(); + + if (fInputMethodWindow != NULL) { + EventList newEvents; + fInputMethodWindow->HandleInputMethodEvent(event, newEvents); + + if (!newEvents.IsEmpty()) { + fInputMethodWindow->PostMessage(B_QUIT_REQUESTED); + fInputMethodWindow = NULL; + + // replace event with new events (but don't scan them again + // for input method messages) + events.RemoveItemAt(i); + delete event; + + events.AddList(&newEvents, i); + i += newEvents.CountItems() - 1; + count = events.CountItems(); + } + } + } + } + + // move the method events into the event queue - they are not + // "methodized" either + BAutolock _(fEventQueueLock); + events.AddList(&fMethodQueue); + fMethodQueue.MakeEmpty(); + + return events.CountItems() > 0; +} + + +/** This method applies all defined filters to each event in the + * supplied list. The supplied list is modified to reflect the + * output of the filters. + * The method returns true if the filters were applied to all + * events without error and false otherwise. + */ + +bool +InputServer::_FilterEvents(EventList& events) +{ + CALLED(); + BAutolock _(gInputFilterListLocker); + + int32 count = gInputFilterList.CountItems(); + int32 eventCount = events.CountItems(); + + for (int32 i = 0; i < count; i++) { + BInputServerFilter* filter = (BInputServerFilter*)gInputFilterList.ItemAt(i); + + // Apply the current filter to all available event messages. + + for (int32 eventIndex = 0; eventIndex < eventCount;) { + _FilterEvent(filter, events, eventIndex, eventCount); + } + } + + return eventCount != 0; +} + + +void +InputServer::_DispatchEvents(EventList& events) +{ + CALLED(); + + int32 count = events.CountItems(); + + for (int32 i = 0; i < count; i++) { + BMessage* event = events.ItemAt(i); + + // now we must send each event to the app_server + _DispatchEvent(event); + delete event; + } + + events.MakeEmpty(); +} + + +/** Applies the given filter to the event list. + * For your convenience, it also alters the \a index and \a count arguments + * ready for the next call to this method. + */ + +void +InputServer::_FilterEvent(BInputServerFilter* filter, EventList& events, + int32& index, int32& count) +{ + BMessage* event = events.ItemAt(index); + + BList newEvents; + filter_result result = filter->Filter(event, &newEvents); + + if (result == B_SKIP_MESSAGE || newEvents.CountItems() > 0) { + // we no longer need the current event + events.RemoveItemAt(index); + delete event; + + if (result == B_DISPATCH_MESSAGE) { + // add the new events - but don't methodize them again + events.AsBList()->AddList(&newEvents, index); + index += newEvents.CountItems(); + count = events.CountItems(); + } else + count--; + } else + index++; +} + + +status_t +InputServer::_DispatchEvent(BMessage* event) +{ + CALLED(); + + switch (event->what) { + case B_MOUSE_MOVED: + case B_MOUSE_DOWN: + case B_MOUSE_UP: + if (fCursorBuffer) { +#ifndef HAIKU_TARGET_PLATFORM_HAIKU + fCursorBuffer[0] = (0x3 << 30L) + | ((uint32)fMousePos.x & 0x7fff) << 15 + | ((uint32)fMousePos.y & 0x7fff); + + PRINT(("released cursorSem with value : %08lx\n", fCursorBuffer[0])); + release_sem(fCursorSem); +#else + atomic_set((int32*)&fCursorBuffer->pos, (uint32)fMousePos.x << 16UL + | ((uint32)fMousePos.y & 0xffff)); + if (atomic_or(&fCursorBuffer->read, 1) == 0) + release_sem_etc(fCursorSem, 0, B_RELEASE_ALL); +#endif + } + break; + + case B_KEY_DOWN: + case B_KEY_UP: + case B_UNMAPPED_KEY_DOWN: + case B_UNMAPPED_KEY_UP: + case B_MODIFIERS_CHANGED: + { + // update or add modifiers + uint32 modifiers; + if (event->FindInt32("modifiers", (int32*)&modifiers) == B_OK) + fKeyInfo.modifiers = modifiers; + else + event->AddInt32("modifiers", fKeyInfo.modifiers); + + // update or add key states + const uint8 *data; + ssize_t size; + if (event->FindData("states", B_UINT8_TYPE, + (const void**)&data, &size) == B_OK) { + PRINT(("updated keyinfo\n")); + if (size == sizeof(fKeyInfo.key_states)) + memcpy(fKeyInfo.key_states, data, size); + } else { + event->AddData("states", B_UINT8_TYPE, fKeyInfo.key_states, + sizeof(fKeyInfo.key_states)); + } + + PRINT(("keyinfo %ld %ld\n", size, (ssize_t)sizeof(fKeyInfo.key_states))); + break; + } + + default: + break; + } + +#if defined(HAIKU_TARGET_PLATFORM_HAIKU) && defined(USING_MESSAGE4) + BMessenger reply; + BMessage::Private messagePrivate(event); + return messagePrivate.SendMessage(fAppServerPort, 0, true, 0, false, reply); +#else + ssize_t length = event->FlattenedSize(); + char buffer[length]; + status_t err; + if ((err = event->Flatten(buffer,length)) < B_OK) + return err; + + return write_port(fAppServerPort, 0, buffer, length); +#endif } diff --git a/src/servers/input/InputServer.h b/src/servers/input/InputServer.h index 49c64c4498..d4d69ae9f1 100644 --- a/src/servers/input/InputServer.h +++ b/src/servers/input/InputServer.h @@ -7,7 +7,6 @@ #include "AddOnManager.h" -#include "BottomlineWindow.h" #include "DeviceManager.h" #include "KeyboardSettings.h" #include "MouseSettings.h" @@ -23,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +35,10 @@ #define INPUTSERVER_SIGNATURE "application/x-vnd.Be-input_server" // use this when target should replace R5 input_server +typedef BObjectList EventList; + +class BottomlineWindow; + class InputDeviceListItem { public: InputDeviceListItem(BInputServerDevice& serverDevice, input_device_ref& device); @@ -87,14 +91,12 @@ class _BMethodAddOn_ { BMessenger fMessenger; }; - class KeymapMethod : public BInputServerMethod { public: KeymapMethod(); ~KeymapMethod(); }; - class InputServer : public BApplication { public: InputServer(); @@ -125,23 +127,12 @@ class InputServer : public BApplication { status_t EnqueueDeviceMessage(BMessage* message); status_t EnqueueMethodMessage(BMessage* message); - status_t UnlockMethodQueue(); - status_t LockMethodQueue(); status_t SetNextMethod(bool direction); void SetActiveMethod(BInputServerMethod* method); const BMessenger* MethodReplicant(); void SetMethodReplicant(const BMessenger *replicant); - status_t EventLoop(); bool EventLoopRunning(); - bool DispatchEvents(BList* eventList); - status_t DispatchEvent(BMessage* event); - bool CacheEvents(BList* eventList); - const BList* GetNextEvents(BList*); - bool FilterEvents(BList* eventList); - bool SanitizeEvents(BList* eventList); - bool MethodizeEvents(BList* eventList, bool); - status_t GetDeviceInfo(const char* name, input_device_type *_type, bool *_isRunning = NULL); status_t UnregisterDevices(BInputServerDevice& serverDevice, @@ -180,13 +171,22 @@ class InputServer : public BApplication { status_t _LoadSystemKeymap(); void _InitKeyboardMouseStates(); + status_t _StartEventLoop(); + void _EventLoop(); + static status_t _EventLooper(void *arg); + + bool _SanitizeEvents(EventList& events); + bool _MethodizeEvents(EventList& events); + bool _FilterEvents(EventList& events); + void _DispatchEvents(EventList& events); + + void _FilterEvent(BInputServerFilter* filter, EventList& events, + int32& index, int32& count); + status_t _DispatchEvent(BMessage* event); + status_t _AcquireInput(BMessage& message, BMessage& reply); void _ReleaseInput(BMessage* message); - void _WatchPort(); - - static int32 _PortWatcher(void *arg); -// static bool doStartStopDevice(void*, void*); InputDeviceListItem* _FindInputDeviceListItem(BInputServerDevice& device); private: @@ -199,8 +199,8 @@ class InputServer : public BApplication { BList fInputDeviceList; BLocker fInputDeviceListLocker; - KeyboardSettings fKeyboardSettings; - MouseSettings fMouseSettings; + KeyboardSettings fKeyboardSettings; + MouseSettings fMouseSettings; BPoint fMousePos; // current mouse position key_info fKeyInfo; // current key info @@ -212,16 +212,17 @@ class InputServer : public BApplication { AddOnManager* fAddOnManager; - BList fEventsCache; - BScreen fScreen; BRect fFrame; + BLocker fEventQueueLock; + EventList fEventQueue; + BInputServerMethod* fActiveMethod; - BList fMethodQueue; + EventList fMethodQueue; const BMessenger* fReplicantMessenger; - BottomlineWindow* fBLWindow; - bool fIMAware; + BottomlineWindow* fInputMethodWindow; + bool fInputMethodAware; sem_id fCursorSem; port_id fAppServerPort; diff --git a/src/servers/input/Jamfile b/src/servers/input/Jamfile index 48c44d184a..4a99259bf1 100644 --- a/src/servers/input/Jamfile +++ b/src/servers/input/Jamfile @@ -32,10 +32,7 @@ SEARCH on US-International.keymap AddResources input_server : input_server.rdef ; -UsePrivateHeaders app ; -UsePrivateHeaders input ; -UsePrivateHeaders interface ; -UsePrivateHeaders storage ; +UsePrivateHeaders app input interface shared storage ; SubDirC++Flags -DADD_ON_STABLE_SECONDS=1 ; # for AddOnMonitorHandler.cpp