/* * Copyright 2002-2013 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. */ #include "InputServer.h" #include "InputServerTypes.h" #include "BottomlineWindow.h" #include "MethodReplicant.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "SystemKeymap.h" // this is an automatically generated file #include using std::nothrow; // Global InputServer member variables. InputServer* gInputServer; BList InputServer::gInputFilterList; BLocker InputServer::gInputFilterListLocker("is_filter_queue_sem"); BList InputServer::gInputMethodList; BLocker InputServer::gInputMethodListLocker("is_method_queue_sem"); KeymapMethod InputServer::gKeymapMethod; extern "C" _EXPORT BView* instantiate_deskbar_item(); // #pragma mark - InputDeviceListItem InputDeviceListItem::InputDeviceListItem(BInputServerDevice& serverDevice, const input_device_ref& device) : fServerDevice(&serverDevice), fDevice(), fRunning(false) { fDevice.name = strdup(device.name); fDevice.type = device.type; fDevice.cookie = device.cookie; } InputDeviceListItem::~InputDeviceListItem() { free(fDevice.name); } void InputDeviceListItem::Start() { PRINT((" Starting: %s\n", fDevice.name)); status_t err = fServerDevice->Start(fDevice.name, fDevice.cookie); if (err != B_OK) { PRINTERR((" error: %s (%" B_PRIx32 ")\n", strerror(err), err)); } fRunning = err == B_OK; } void InputDeviceListItem::Stop() { PRINT((" Stopping: %s\n", fDevice.name)); fServerDevice->Stop(fDevice.name, fDevice.cookie); fRunning = false; } void InputDeviceListItem::Control(uint32 code, BMessage* message) { fServerDevice->Control(fDevice.name, fDevice.cookie, code, message); } bool InputDeviceListItem::HasName(const char* name) const { if (name == NULL) return false; return !strcmp(name, fDevice.name); } bool InputDeviceListItem::HasType(input_device_type type) const { return type == fDevice.type; } bool InputDeviceListItem::Matches(const char* name, input_device_type type) const { if (name != NULL) return HasName(name); return HasType(type); } // #pragma mark - InputServer::InputServer() : BApplication(INPUTSERVER_SIGNATURE), fKeyboardID(0), fInputDeviceListLocker("input server device list"), fKeyboardSettings(), fMouseSettings(), fChars(NULL), fScreen(B_MAIN_SCREEN_ID), fEventQueueLock("input server event queue"), fReplicantMessenger(NULL), fInputMethodWindow(NULL), fInputMethodAware(false), fCursorSem(-1), fAppServerPort(-1), fAppServerTeam(-1), fCursorArea(-1) { CALLED(); gInputServer = this; set_thread_priority(find_thread(NULL), B_URGENT_DISPLAY_PRIORITY); // elevate priority for client interaction _StartEventLoop(); _InitKeyboardMouseStates(); fAddOnManager = new(std::nothrow) ::AddOnManager(); if (fAddOnManager != NULL) { // We need to Run() the AddOnManager looper after having loaded // the initial add-ons, otherwise we may deadlock when the looper // thread for some reason already receives node monitor notifications // while we are still locked ourselves and are executing LoadState() // at the same time (which may lock the add-on looper thread). // NOTE: At first sight this may look like we may loose node monitor // notifications while the thread is not yet running, but in fact those // message should just pile up and be processed later. fAddOnManager->LoadState(); fAddOnManager->Run(); } BMessenger messenger(this); BRoster().StartWatching(messenger, B_REQUEST_LAUNCHED); } InputServer::~InputServer() { CALLED(); if (fAddOnManager->Lock()) fAddOnManager->Quit(); _ReleaseInput(NULL); } void InputServer::ArgvReceived(int32 argc, char** argv) { CALLED(); if (argc == 2 && strcmp(argv[1], "-q") == 0) { PRINT(("InputServer::ArgvReceived - Restarting ...\n")); PostMessage(B_QUIT_REQUESTED); } } void InputServer::_InitKeyboardMouseStates() { CALLED(); // This is where we determine the screen resolution from the app_server and // find the center of the screen // fMousePos is then set to the center of the screen. fFrame = fScreen.Frame(); if (fFrame == BRect(0, 0, 0, 0)) fFrame = BRect(0, 0, 799, 599); fMousePos = BPoint((int32)((fFrame.right + 1) / 2), (int32)((fFrame.bottom + 1) / 2)); memset(&fKeyInfo, 0, sizeof(fKeyInfo)); if (_LoadKeymap() != B_OK) _LoadSystemKeymap(); BMessage msg(B_MOUSE_MOVED); HandleSetMousePosition(&msg, &msg); fActiveMethod = &gKeymapMethod; } status_t InputServer::_LoadKeymap() { BPath path; if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) return B_BAD_VALUE; path.Append("Key_map"); status_t err; BFile file(path.Path(), B_READ_ONLY); if ((err = file.InitCheck()) != B_OK) return err; if (file.Read(&fKeys, sizeof(fKeys)) < (ssize_t)sizeof(fKeys)) return B_BAD_VALUE; for (uint32 i = 0; i < sizeof(fKeys) / 4; i++) ((uint32*)&fKeys)[i] = B_BENDIAN_TO_HOST_INT32(((uint32*)&fKeys)[i]); if (file.Read(&fCharsSize, sizeof(uint32)) < (ssize_t)sizeof(uint32)) return B_BAD_VALUE; fCharsSize = B_BENDIAN_TO_HOST_INT32(fCharsSize); if (fCharsSize <= 0) return B_BAD_VALUE; delete[] fChars; fChars = new (nothrow) char[fCharsSize]; if (fChars == NULL) return B_NO_MEMORY; if (file.Read(fChars, fCharsSize) != (signed)fCharsSize) return B_BAD_VALUE; return B_OK; } status_t InputServer::_LoadSystemKeymap() { delete[] fChars; fKeys = kSystemKeymap; fCharsSize = kSystemKeyCharsSize; fChars = new (nothrow) char[fCharsSize]; if (fChars == NULL) return B_NO_MEMORY; memcpy(fChars, kSystemKeyChars, fCharsSize); // TODO: why are we doing this? return _SaveKeymap(true); } status_t InputServer::_SaveKeymap(bool isDefault) { // we save this keymap to file BPath path; if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) return B_BAD_VALUE; path.Append("Key_map"); BFile file; status_t err = file.SetTo(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); if (err != B_OK) { PRINTERR(("error %s\n", strerror(err))); return err; } for (uint32 i = 0; i < sizeof(fKeys) / sizeof(uint32); i++) { ((uint32*)&fKeys)[i] = B_HOST_TO_BENDIAN_INT32(((uint32*)&fKeys)[i]); } if ((err = file.Write(&fKeys, sizeof(fKeys))) < (ssize_t)sizeof(fKeys)) return err; for (uint32 i = 0; i < sizeof(fKeys) / sizeof(uint32); i++) { ((uint32*)&fKeys)[i] = B_BENDIAN_TO_HOST_INT32(((uint32*)&fKeys)[i]); } uint32 size = B_HOST_TO_BENDIAN_INT32(fCharsSize); if ((err = file.Write(&size, sizeof(uint32))) < (ssize_t)sizeof(uint32)) return B_BAD_VALUE; if ((err = file.Write(fChars, fCharsSize)) < (ssize_t)fCharsSize) return err; // don't bother reporting an error if this fails, since this isn't fatal // the keymap will still be functional, and will just be identified as (Current) in prefs instead of its // actual name if (isDefault) { const BString systemKeymapName(kSystemKeymapName); file.WriteAttrString("keymap:name", &systemKeymapName); } return B_OK; } bool InputServer::QuitRequested() { CALLED(); if (!BApplication::QuitRequested()) return false; PostMessage(SYSTEM_SHUTTING_DOWN); bool shutdown = false; CurrentMessage()->FindBool("_shutdown_", &shutdown); // Don't actually quit when the system is being shutdown if (shutdown) { return false; } else { fAddOnManager->SaveState(); delete_port(fEventLooperPort); // the event looper thread will exit after this fEventLooperPort = -1; return true; } } void InputServer::ReadyToRun() { CALLED(); // say hello to the app_server BPrivate::AppServerLink link; link.StartMessage(AS_REGISTER_INPUT_SERVER); link.Flush(); } status_t InputServer::_AcquireInput(BMessage& message, BMessage& reply) { // TODO: it currently just gets everything we have area_id area; if (message.FindInt32("cursor area", &area) == B_OK) { // try to clone the area fCursorBuffer = NULL; fCursorSem = create_sem(0, "cursor semaphore"); if (fCursorSem >= B_OK) { fCursorArea = clone_area("input server cursor", (void**)&fCursorBuffer, B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, area); } } if (message.FindInt32("remote team", &fAppServerTeam) != B_OK) fAppServerTeam = -1; fAppServerPort = create_port(200, "input server target"); if (fAppServerPort < B_OK) { _ReleaseInput(&message); return fAppServerPort; } reply.AddBool("has keyboard", true); reply.AddBool("has mouse", true); reply.AddInt32("event port", fAppServerPort); if (fCursorBuffer != NULL) { // cursor shared buffer is supported reply.AddInt32("cursor semaphore", fCursorSem); } return B_OK; } void InputServer::_ReleaseInput(BMessage* /*message*/) { if (fCursorBuffer != NULL) { fCursorBuffer = NULL; delete_sem(fCursorSem); delete_area(fCursorArea); fCursorSem = -1; fCursorArea = -1; } delete_port(fAppServerPort); } void InputServer::MessageReceived(BMessage* message) { CALLED(); BMessage reply; status_t status = B_OK; PRINT(("%s what:%c%c%c%c\n", __PRETTY_FUNCTION__, (char)(message->what >> 24), (char)(message->what >> 16), (char)(message->what >> 8), (char)message->what)); switch (message->what) { case IS_SET_METHOD: HandleSetMethod(message); break; case IS_GET_MOUSE_TYPE: status = HandleGetSetMouseType(message, &reply); break; case IS_SET_MOUSE_TYPE: status = HandleGetSetMouseType(message, &reply); break; case IS_GET_MOUSE_ACCELERATION: status = HandleGetSetMouseAcceleration(message, &reply); break; case IS_SET_MOUSE_ACCELERATION: status = HandleGetSetMouseAcceleration(message, &reply); break; case IS_GET_KEY_REPEAT_DELAY: status = HandleGetSetKeyRepeatDelay(message, &reply); break; case IS_SET_KEY_REPEAT_DELAY: status = HandleGetSetKeyRepeatDelay(message, &reply); break; case IS_GET_KEY_INFO: status = HandleGetKeyInfo(message, &reply); break; case IS_GET_MODIFIERS: status = HandleGetModifiers(message, &reply); break; case IS_GET_MODIFIER_KEY: status = HandleGetModifierKey(message, &reply); break; case IS_SET_MODIFIER_KEY: status = HandleSetModifierKey(message, &reply); break; case IS_SET_KEYBOARD_LOCKS: status = HandleSetKeyboardLocks(message, &reply); break; case IS_GET_MOUSE_SPEED: status = HandleGetSetMouseSpeed(message, &reply); break; case IS_SET_MOUSE_SPEED: status = HandleGetSetMouseSpeed(message, &reply); break; case IS_SET_MOUSE_POSITION: status = HandleSetMousePosition(message, &reply); break; case IS_GET_MOUSE_MAP: status = HandleGetSetMouseMap(message, &reply); break; case IS_SET_MOUSE_MAP: status = HandleGetSetMouseMap(message, &reply); break; case IS_GET_KEYBOARD_ID: status = HandleGetSetKeyboardID(message, &reply); break; case IS_SET_KEYBOARD_ID: status = HandleGetSetKeyboardID(message, &reply); break; case IS_GET_CLICK_SPEED: status = HandleGetSetClickSpeed(message, &reply); break; case IS_SET_CLICK_SPEED: status = HandleGetSetClickSpeed(message, &reply); break; case IS_GET_KEY_REPEAT_RATE: status = HandleGetSetKeyRepeatRate(message, &reply); break; case IS_SET_KEY_REPEAT_RATE: status = HandleGetSetKeyRepeatRate(message, &reply); break; case IS_GET_KEY_MAP: status = HandleGetSetKeyMap(message, &reply); break; case IS_RESTORE_KEY_MAP: status = HandleGetSetKeyMap(message, &reply); break; case IS_FOCUS_IM_AWARE_VIEW: status = HandleFocusUnfocusIMAwareView(message, &reply); break; case IS_UNFOCUS_IM_AWARE_VIEW: status = HandleFocusUnfocusIMAwareView(message, &reply); break; // app_server communication case IS_ACQUIRE_INPUT: status = _AcquireInput(*message, reply); break; case IS_RELEASE_INPUT: _ReleaseInput(message); return; case IS_SCREEN_BOUNDS_UPDATED: { // This is what the R5 app_server sends us when the screen // configuration changes BRect frame; if (message->FindRect("screen_bounds", &frame) != B_OK) frame = fScreen.Frame(); if (frame == fFrame) break; BPoint pos(fMousePos.x * frame.Width() / fFrame.Width(), fMousePos.y * frame.Height() / fFrame.Height()); fFrame = frame; BMessage set; set.AddPoint("where", pos); HandleSetMousePosition(&set, NULL); break; } // device looper related case IS_FIND_DEVICES: case IS_WATCH_DEVICES: case IS_IS_DEVICE_RUNNING: case IS_START_DEVICE: case IS_STOP_DEVICE: case IS_CONTROL_DEVICES: case SYSTEM_SHUTTING_DOWN: case IS_METHOD_REGISTER: fAddOnManager->PostMessage(message); return; case IS_SAVE_SETTINGS: fKeyboardSettings.Save(); fMouseSettings.SaveSettings(); return; case IS_SAVE_KEYMAP: _SaveKeymap(); return; case B_SOME_APP_LAUNCHED: { // The message contains a be:signature with the app signature // TODO: what's this for? return; } case kMsgAppServerRestarted: { BApplication::MessageReceived(message); BPrivate::AppServerLink link; link.StartMessage(AS_REGISTER_INPUT_SERVER); link.Flush(); return; } default: return; } reply.AddInt32("status", status); message->SendReply(&reply); } void InputServer::HandleSetMethod(BMessage* message) { CALLED(); int32 cookie; if (message->FindInt32("cookie", &cookie) != B_OK) return; if (cookie == gKeymapMethod.fOwner->Cookie()) { SetActiveMethod(&gKeymapMethod); } else { BAutolock lock(InputServer::gInputMethodListLocker); for (int32 i = 0; i < gInputMethodList.CountItems(); i++) { BInputServerMethod* method = (BInputServerMethod*)InputServer::gInputMethodList.ItemAt(i); if (method->fOwner->Cookie() == cookie) { PRINT(("%s cookie %" B_PRId32 "\n", __PRETTY_FUNCTION__, cookie)); SetActiveMethod(method); break; } } } } status_t InputServer::HandleGetSetMouseType(BMessage* message, BMessage* reply) { BString mouseName; MouseSettings* settings = NULL; if (message->FindString("mouse_name", &mouseName) == B_OK) { settings = fMouseSettings.GetMouseSettings(mouseName); if (settings == NULL) return B_NAME_NOT_FOUND; } int32 type; if (message->FindInt32("mouse_type", &type) == B_OK) { if (settings != NULL) settings->SetMouseType(type); else { // TODO if no mouse_name was specified, apply the setting to // all mouses return B_NOT_SUPPORTED; } be_app_messenger.SendMessage(IS_SAVE_SETTINGS); BMessage msg(IS_CONTROL_DEVICES); msg.AddInt32("type", B_POINTING_DEVICE); msg.AddInt32("code", B_MOUSE_TYPE_CHANGED); return fAddOnManager->PostMessage(&msg); } if (settings != NULL) { return reply->AddInt32("mouse_type", settings->MouseType()); } else { // TODO return type of the "first" mouse? return B_NOT_SUPPORTED; } } status_t InputServer::HandleGetSetMouseAcceleration(BMessage* message, BMessage* reply) { BString mouseName; MouseSettings* settings = NULL; if (message->FindString("mouse_name", &mouseName) == B_OK) { settings = fMouseSettings.GetMouseSettings(mouseName); if (settings == NULL) return B_NAME_NOT_FOUND; } int32 factor; if (message->FindInt32("speed", &factor) == B_OK) { if (settings != NULL) settings->SetAccelerationFactor(factor); else { // TODO if no mouse_name was specified, apply the setting to // all mouses return B_NOT_SUPPORTED; } be_app_messenger.SendMessage(IS_SAVE_SETTINGS); BMessage msg(IS_CONTROL_DEVICES); msg.AddInt32("type", B_POINTING_DEVICE); msg.AddInt32("code", B_MOUSE_ACCELERATION_CHANGED); return fAddOnManager->PostMessage(&msg); } if (settings != NULL) return reply->AddInt32("speed", settings->AccelerationFactor()); else { // TODO return type of the "first" mouse? return B_NOT_SUPPORTED; } } status_t InputServer::HandleGetSetKeyRepeatDelay(BMessage* message, BMessage* reply) { bigtime_t delay; if (message->FindInt64("delay", &delay) == B_OK) { fKeyboardSettings.SetKeyboardRepeatDelay(delay); be_app_messenger.SendMessage(IS_SAVE_SETTINGS); BMessage msg(IS_CONTROL_DEVICES); msg.AddInt32("type", B_KEYBOARD_DEVICE); msg.AddInt32("code", B_KEY_REPEAT_DELAY_CHANGED); return fAddOnManager->PostMessage(&msg); } return reply->AddInt64("delay", fKeyboardSettings.KeyboardRepeatDelay()); } status_t InputServer::HandleGetKeyInfo(BMessage* message, BMessage* reply) { return reply->AddData("key_info", B_ANY_TYPE, &fKeyInfo, sizeof(fKeyInfo)); } status_t InputServer::HandleGetModifiers(BMessage* message, BMessage* reply) { return reply->AddInt32("modifiers", fKeyInfo.modifiers); } status_t InputServer::HandleGetModifierKey(BMessage* message, BMessage* reply) { int32 modifier; if (message->FindInt32("modifier", &modifier) == B_OK) { switch (modifier) { case B_CAPS_LOCK: return reply->AddInt32("key", fKeys.caps_key); case B_NUM_LOCK: return reply->AddInt32("key", fKeys.num_key); case B_SCROLL_LOCK: return reply->AddInt32("key", fKeys.scroll_key); case B_LEFT_SHIFT_KEY: return reply->AddInt32("key", fKeys.left_shift_key); case B_RIGHT_SHIFT_KEY: return reply->AddInt32("key", fKeys.right_shift_key); case B_LEFT_COMMAND_KEY: return reply->AddInt32("key", fKeys.left_command_key); case B_RIGHT_COMMAND_KEY: return reply->AddInt32("key", fKeys.right_command_key); case B_LEFT_CONTROL_KEY: return reply->AddInt32("key", fKeys.left_control_key); case B_RIGHT_CONTROL_KEY: return reply->AddInt32("key", fKeys.right_control_key); case B_LEFT_OPTION_KEY: return reply->AddInt32("key", fKeys.left_option_key); case B_RIGHT_OPTION_KEY: return reply->AddInt32("key", fKeys.right_option_key); case B_MENU_KEY: return reply->AddInt32("key", fKeys.menu_key); } } return B_ERROR; } status_t InputServer::HandleSetModifierKey(BMessage* message, BMessage* reply) { int32 modifier, key; if (message->FindInt32("modifier", &modifier) == B_OK && message->FindInt32("key", &key) == B_OK) { switch (modifier) { case B_CAPS_LOCK: fKeys.caps_key = key; break; case B_NUM_LOCK: fKeys.num_key = key; break; case B_SCROLL_LOCK: fKeys.scroll_key = key; break; case B_LEFT_SHIFT_KEY: fKeys.left_shift_key = key; break; case B_RIGHT_SHIFT_KEY: fKeys.right_shift_key = key; break; case B_LEFT_COMMAND_KEY: fKeys.left_command_key = key; break; case B_RIGHT_COMMAND_KEY: fKeys.right_command_key = key; break; case B_LEFT_CONTROL_KEY: fKeys.left_control_key = key; break; case B_RIGHT_CONTROL_KEY: fKeys.right_control_key = key; break; case B_LEFT_OPTION_KEY: fKeys.left_option_key = key; break; case B_RIGHT_OPTION_KEY: fKeys.right_option_key = key; break; case B_MENU_KEY: fKeys.menu_key = key; break; default: return B_ERROR; } // TODO: unmap the key ? be_app_messenger.SendMessage(IS_SAVE_KEYMAP); BMessage msg(IS_CONTROL_DEVICES); msg.AddInt32("type", B_KEYBOARD_DEVICE); msg.AddInt32("code", B_KEY_MAP_CHANGED); return fAddOnManager->PostMessage(&msg); } return B_ERROR; } status_t InputServer::HandleSetKeyboardLocks(BMessage* message, BMessage* reply) { if (message->FindInt32("locks", (int32*)&fKeys.lock_settings) == B_OK) { be_app_messenger.SendMessage(IS_SAVE_KEYMAP); BMessage msg(IS_CONTROL_DEVICES); msg.AddInt32("type", B_KEYBOARD_DEVICE); msg.AddInt32("code", B_KEY_LOCKS_CHANGED); return fAddOnManager->PostMessage(&msg); } return B_ERROR; } // #pragma mark - Mouse settings /** This method does all possible efforts to return some settings. * * The settings will be created if they do not exist. If a mouse name is * specified, the settings for that mouse are created and used. Otherwise, * default settings are returned. */ MouseSettings* InputServer::_GetSettingsForMouse(BString mouseName) { // If no mouse name is specified, use the first one found in settings if (mouseName == "") { std::map::iterator itr = fMouseSettingsObject.begin(); if (itr != fMouseSettingsObject.end()) return itr->second; } // If a mouse name is specified or there are no settings yet, get or create // some return fMouseSettings.AddMouseSettings(mouseName); } status_t InputServer::HandleGetSetMouseSpeed(BMessage* message, BMessage* reply) { BString mouseName; message->FindString("mouse_name", &mouseName); MouseSettings* settings = _GetSettingsForMouse(mouseName); if (settings == NULL) return B_NO_MEMORY; int32 speed; if (message->FindInt32("speed", &speed) == B_OK) { settings->SetMouseSpeed(speed); be_app_messenger.SendMessage(IS_SAVE_SETTINGS); BMessage msg(IS_CONTROL_DEVICES); msg.AddInt32("type", B_POINTING_DEVICE); msg.AddInt32("code", B_MOUSE_SPEED_CHANGED); return fAddOnManager->PostMessage(&msg); } return reply->AddInt32("speed", settings->MouseSpeed()); } status_t InputServer::HandleGetSetMouseMap(BMessage* message, BMessage* reply) { BString mouseName; message->FindString("mouse_name", &mouseName); MouseSettings* settings = _GetSettingsForMouse(mouseName); if (settings == NULL) return B_NO_MEMORY; mouse_map *map; ssize_t size; if (message->FindData("mousemap", B_RAW_TYPE, (const void**)&map, &size) == B_OK) { settings->SetMapping(*map); be_app_messenger.SendMessage(IS_SAVE_SETTINGS); BMessage msg(IS_CONTROL_DEVICES); msg.AddInt32("type", B_POINTING_DEVICE); msg.AddInt32("code", B_MOUSE_MAP_CHANGED); return fAddOnManager->PostMessage(&msg); } mouse_map getmap; settings->Mapping(getmap); return reply->AddData("mousemap", B_RAW_TYPE, &getmap, sizeof(mouse_map)); } status_t InputServer::HandleGetSetClickSpeed(BMessage* message, BMessage* reply) { BString mouseName; message->FindString("mouse_name", &mouseName); MouseSettings* settings = _GetSettingsForMouse(mouseName); if (settings == NULL) return B_NO_MEMORY; bigtime_t clickSpeed; if (message->FindInt64("speed", &clickSpeed) == B_OK) { settings->SetClickSpeed(clickSpeed); be_app_messenger.SendMessage(IS_SAVE_SETTINGS); BMessage msg(IS_CONTROL_DEVICES); msg.AddInt32("type", B_POINTING_DEVICE); msg.AddInt32("code", B_CLICK_SPEED_CHANGED); return fAddOnManager->PostMessage(&msg); } return reply->AddInt64("speed", settings->ClickSpeed()); } status_t InputServer::HandleSetMousePosition(BMessage* message, BMessage* reply) { CALLED(); BPoint where; if (message->FindPoint("where", &where) != B_OK) return B_BAD_VALUE; // create a new event for this and enqueue it to the event list just like any other BMessage* event = new BMessage(B_MOUSE_MOVED); if (event == NULL) return B_NO_MEMORY; event->AddPoint("where", where); event->AddBool("be:set_mouse", true); if (EnqueueDeviceMessage(event) != B_OK) { delete event; return B_NO_MEMORY; } return B_OK; } // #pragma mark - Keyboard settings status_t InputServer::HandleGetSetKeyboardID(BMessage* message, BMessage* reply) { int16 id; if (message->FindInt16("id", &id) == B_OK) { fKeyboardID = (uint16)id; return B_OK; } return reply->AddInt16("id", fKeyboardID); } status_t InputServer::HandleGetSetKeyRepeatRate(BMessage* message, BMessage* reply) { int32 keyRepeatRate; if (message->FindInt32("rate", &keyRepeatRate) == B_OK) { fKeyboardSettings.SetKeyboardRepeatRate(keyRepeatRate); be_app_messenger.SendMessage(IS_SAVE_SETTINGS); BMessage msg(IS_CONTROL_DEVICES); msg.AddInt32("type", B_KEYBOARD_DEVICE); msg.AddInt32("code", B_KEY_REPEAT_RATE_CHANGED); return fAddOnManager->PostMessage(&msg); } return reply->AddInt32("rate", fKeyboardSettings.KeyboardRepeatRate()); } status_t InputServer::HandleGetSetKeyMap(BMessage* message, BMessage* reply) { CALLED(); status_t status; if (message->what == IS_GET_KEY_MAP) { status = reply->AddData("keymap", B_ANY_TYPE, &fKeys, sizeof(fKeys)); if (status == B_OK) status = reply->AddData("key_buffer", B_ANY_TYPE, fChars, fCharsSize); return status; } status = _LoadKeymap(); if (status != B_OK) { status = _LoadSystemKeymap(); if (status != B_OK) return status; } BMessage msg(IS_CONTROL_DEVICES); msg.AddInt32("type", B_KEYBOARD_DEVICE); msg.AddInt32("code", B_KEY_MAP_CHANGED); status = fAddOnManager->PostMessage(&msg); if (status == B_OK) { BMessage appMsg(B_KEY_MAP_LOADED); be_roster->Broadcast(&appMsg); } return status; } status_t InputServer::HandleFocusUnfocusIMAwareView(BMessage* message, BMessage* reply) { CALLED(); BMessenger messenger; status_t status = message->FindMessenger("view", &messenger); if (status != B_OK) return status; // check if current view is ours if (message->what == IS_FOCUS_IM_AWARE_VIEW) { PRINT(("HandleFocusUnfocusIMAwareView : entering\n")); fInputMethodAware = true; } else { PRINT(("HandleFocusUnfocusIMAwareView : leaving\n")); 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(); BAutolock _(fEventQueueLock); if (!fEventQueue.AddItem(message)) return B_NO_MEMORY; 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) { CALLED(); PRINT(("%s what:%c%c%c%c\n", __PRETTY_FUNCTION__, (char)(message->what >> 24), (char)(message->what >> 16), (char)(message->what >> 8), (char)message->what)); #ifdef DEBUG if (message->what == 'IMEV') { int32 code; message->FindInt32("be:opcode", &code); PRINT(("%s be:opcode %" B_PRId32 "\n", __PRETTY_FUNCTION__, code)); } #endif BAutolock _(fEventQueueLock); if (!fMethodQueue.AddItem(message)) return B_NO_MEMORY; 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; } status_t InputServer::SetNextMethod(bool direction) { gInputMethodListLocker.Lock(); int32 index = gInputMethodList.IndexOf(fActiveMethod); int32 oldIndex = index; index += (direction ? 1 : -1); if (index < -1) index = gInputMethodList.CountItems() - 1; if (index >= gInputMethodList.CountItems()) index = -1; if (index == oldIndex) return B_BAD_INDEX; BInputServerMethod *method = &gKeymapMethod; if (index != -1) method = (BInputServerMethod *)gInputMethodList.ItemAt(index); SetActiveMethod(method); gInputMethodListLocker.Unlock(); return B_OK; } void InputServer::SetActiveMethod(BInputServerMethod* method) { CALLED(); if (fActiveMethod) fActiveMethod->fOwner->MethodActivated(false); fActiveMethod = method; if (fActiveMethod) fActiveMethod->fOwner->MethodActivated(true); } const BMessenger* InputServer::MethodReplicant() { return fReplicantMessenger; } void InputServer::SetMethodReplicant(const BMessenger* messenger) { fReplicantMessenger = messenger; } bool InputServer::EventLoopRunning() { return fEventLooperPort >= B_OK; } status_t InputServer::GetDeviceInfo(const char* name, input_device_type *_type, bool *_isRunning) { BAutolock lock(fInputDeviceListLocker); for (int32 i = fInputDeviceList.CountItems() - 1; i >= 0; i--) { InputDeviceListItem* item = (InputDeviceListItem*)fInputDeviceList.ItemAt(i); if (item->HasName(name)) { if (_type) *_type = item->Type(); if (_isRunning) *_isRunning = item->Running(); return B_OK; } } return B_NAME_NOT_FOUND; } status_t InputServer::GetDeviceInfos(BMessage *msg) { CALLED(); BAutolock lock(fInputDeviceListLocker); for (int32 i = fInputDeviceList.CountItems() - 1; i >= 0; i--) { InputDeviceListItem* item = (InputDeviceListItem*)fInputDeviceList.ItemAt(i); msg->AddString("device", item->Name()); msg->AddInt32("type", item->Type()); } return B_OK; } status_t InputServer::UnregisterDevices(BInputServerDevice& serverDevice, input_device_ref **devices) { CALLED(); BAutolock lock(fInputDeviceListLocker); if (devices != NULL) { // remove the devices as specified only input_device_ref *device = NULL; for (int32 i = 0; (device = devices[i]) != NULL; i++) { for (int32 j = fInputDeviceList.CountItems() - 1; j >= 0; j--) { InputDeviceListItem* item = (InputDeviceListItem*)fInputDeviceList.ItemAt(j); if (item->ServerDevice() == &serverDevice && item->HasName(device->name)) { item->Stop(); if (fInputDeviceList.RemoveItem(j)) { BMessage message(IS_NOTIFY_DEVICE); message.AddBool("added", false); message.AddString("name", item->Name()); message.AddInt32("type", item->Type()); fAddOnManager->PostMessage(&message); delete item; } break; } } } } else { // remove all devices from this BInputServerObject for (int32 i = fInputDeviceList.CountItems() - 1; i >= 0; i--) { InputDeviceListItem* item = (InputDeviceListItem*)fInputDeviceList.ItemAt(i); if (item->ServerDevice() == &serverDevice) { item->Stop(); if (fInputDeviceList.RemoveItem(i)) delete item; } } } return B_OK; } status_t InputServer::RegisterDevices(BInputServerDevice& serverDevice, input_device_ref** devices) { if (devices == NULL) return B_BAD_VALUE; BAutolock lock(fInputDeviceListLocker); input_device_ref *device = NULL; for (int32 i = 0; (device = devices[i]) != NULL; i++) { if (device->type != B_POINTING_DEVICE && device->type != B_KEYBOARD_DEVICE && device->type != B_UNDEFINED_DEVICE) continue; // find existing input server device bool found = false; for (int32 j = fInputDeviceList.CountItems() - 1; j >= 0; j--) { InputDeviceListItem* item = (InputDeviceListItem*)fInputDeviceList.ItemAt(j); if (item->HasName(device->name)) { debug_printf("InputServer::RegisterDevices() device_ref already exists: %s\n", device->name); PRINT(("RegisterDevices found %s\n", device->name)); found = true; break; } } if (!found) { PRINT(("RegisterDevices not found %s\n", device->name)); InputDeviceListItem* item = new (nothrow) InputDeviceListItem(serverDevice, *device); if (item != NULL && fInputDeviceList.AddItem(item)) { item->Start(); BMessage message(IS_NOTIFY_DEVICE); message.AddBool("added", true); message.AddString("name", item->Name()); message.AddInt32("type", item->Type()); fAddOnManager->PostMessage(&message); } else { delete item; return B_NO_MEMORY; } } } return B_OK; } status_t InputServer::StartStopDevices(const char* name, input_device_type type, bool doStart) { CALLED(); BAutolock lock(fInputDeviceListLocker); for (int32 i = fInputDeviceList.CountItems() - 1; i >= 0; i--) { InputDeviceListItem* item = (InputDeviceListItem*)fInputDeviceList.ItemAt(i); if (!item) continue; if (item->Matches(name, type)) { if (doStart == item->Running()) { if (name) return B_OK; else continue; } if (doStart) item->Start(); else item->Stop(); BMessage message(IS_NOTIFY_DEVICE); message.AddBool("started", doStart); message.AddString("name", item->Name()); message.AddInt32("type", item->Type()); fAddOnManager->PostMessage(&message); if (name) return B_OK; } } if (name) { // item not found return B_ERROR; } return B_OK; } status_t InputServer::StartStopDevices(BInputServerDevice& serverDevice, bool doStart) { CALLED(); BAutolock lock(fInputDeviceListLocker); for (int32 i = fInputDeviceList.CountItems() - 1; i >= 0; i--) { InputDeviceListItem* item = (InputDeviceListItem*)fInputDeviceList.ItemAt(i); if (item->ServerDevice() != &serverDevice) continue; if (doStart == item->Running()) continue; if (doStart) item->Start(); else item->Stop(); BMessage message(IS_NOTIFY_DEVICE); message.AddBool("started", doStart); message.AddString("name", item->Name()); message.AddInt32("type", item->Type()); fAddOnManager->PostMessage(&message); } return B_OK; } status_t InputServer::ControlDevices(const char* name, input_device_type type, uint32 code, BMessage* message) { CALLED(); BAutolock lock(fInputDeviceListLocker); for (int32 i = fInputDeviceList.CountItems() - 1; i >= 0; i--) { InputDeviceListItem* item = (InputDeviceListItem*)fInputDeviceList.ItemAt(i); if (!item) continue; if (item->Matches(name, type)) { item->Control(code, message); if (name) return B_OK; } } if (name) return B_ERROR; return B_OK; } bool InputServer::SafeMode() { char parameter[32]; size_t parameterLength = sizeof(parameter); if (_kern_get_safemode_option(B_SAFEMODE_SAFE_MODE, parameter, ¶meterLength) == B_OK) { if (!strcasecmp(parameter, "enabled") || !strcasecmp(parameter, "on") || !strcasecmp(parameter, "true") || !strcasecmp(parameter, "yes") || !strcasecmp(parameter, "enable") || !strcmp(parameter, "1")) { return true; } } if (_kern_get_safemode_option(B_SAFEMODE_DISABLE_USER_ADD_ONS, parameter, ¶meterLength) == B_OK) { if (!strcasecmp(parameter, "enabled") || !strcasecmp(parameter, "on") || !strcasecmp(parameter, "true") || !strcasecmp(parameter, "yes") || !strcasecmp(parameter, "enable") || !strcmp(parameter, "1")) { return true; } } return false; } status_t InputServer::_StartEventLoop() { CALLED(); fEventLooperPort = create_port(100, "input server events"); if (fEventLooperPort < 0) { PRINTERR(("InputServer: create_port error: (0x%" B_PRIx32 ") %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; } status_t InputServer::_EventLooper(void* arg) { 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) { PRINT(("[Event Looper] port gone, exiting.\n")); return; } PRINT(("[Event Looper] BMessage Size = %lu\n", length)); char buffer[length]; int32 code; status_t err = read_port(fEventLooperPort, &code, buffer, length); if (err != length) { if (err >= 0) { PRINTERR(("InputServer: failed to read full packet " "(read %" B_PRIu32 " of %lu)\n", err, length)); } else { PRINTERR(("InputServer: read_port error: (0x%" B_PRIx32 ") %s\n", err, strerror(err))); } 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(); } if (length > 0) { BMessage* event = new BMessage; if ((err = event->Unflatten(buffer)) < 0) { PRINTERR(("[InputServer] Unflatten() error: (0x%" B_PRIx32 ") %s\n", err, strerror(err))); delete event; continue; } events.AddItem(event); } // This is where the message should be processed. if (_SanitizeEvents(events) && _MethodizeEvents(events) && _FilterEvents(events)) { _UpdateMouseAndKeys(events); _DispatchEvents(events); } } } /*! Updates the internal mouse position and keyboard info. */ void InputServer::_UpdateMouseAndKeys(EventList& events) { for (int32 index = 0;BMessage* event = (BMessage*)events.ItemAt(index); index++) { switch (event->what) { case B_MOUSE_DOWN: case B_MOUSE_UP: case B_MOUSE_MOVED: event->FindPoint("where", &fMousePos); break; case B_KEY_DOWN: case B_UNMAPPED_KEY_DOWN: // update modifiers uint32 modifiers; if (event->FindInt32("modifiers", (int32*)&modifiers) == B_OK) fKeyInfo.modifiers = modifiers; // update 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); } 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) // If there is only one input method, SetNextMethod will return // B_BAD_INDEX and the event will be forwarded to the user. PRINT(("SanitizeEvents: %" B_PRIx32 ", %x\n", fKeyInfo.modifiers, fKeyInfo.key_states[KEY_Spacebar >> 3])); uint8 byte; if (event->FindInt8("byte", (int8*)&byte) < B_OK) byte = 0; if ((((fKeyInfo.modifiers & B_COMMAND_KEY) != 0 && byte == ' ') || byte == B_HANKAKU_ZENKAKU) && SetNextMethod((fKeyInfo.modifiers & B_SHIFT_KEY) == 0) == B_OK) { // this event isn't sent to the user events.RemoveItemAt(index); delete event; continue; } break; } } } /*! Frees events from unwanted fields, adds missing fields, and removes unwanted events altogether. */ bool InputServer::_SanitizeEvents(EventList& events) { CALLED(); for (int32 index = 0; BMessage* event = (BMessage*)events.ItemAt(index); index++) { switch (event->what) { 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; float absX, absY; if (event->FindInt32("x", &x) == B_OK && event->FindInt32("y", &y) == B_OK) { where.x = fMousePos.x + x; where.y = fMousePos.y - y; event->RemoveName("x"); event->RemoveName("y"); event->AddInt32("be:delta_x", x); event->AddInt32("be:delta_y", y); PRINT(("new position: %f, %f, %" B_PRId32 ", %" B_PRId32 "\n", where.x, where.y, x, y)); } else if (event->FindFloat("x", &absX) == B_OK && event->FindFloat("y", &absY) == B_OK) { // The device gives us absolute screen coords in range 0..1; // convert them to absolute screen position // (the message is supposed to contain the original // absolute coordinates as "be:tablet_x/y"). where.x = absX * fFrame.Width(); where.y = absY * fFrame.Height(); event->RemoveName("x"); event->RemoveName("y"); PRINT(("new position : %f, %f\n", where.x, where.y)); } else if (event->FindPoint("where", &where) == B_OK) { PRINT(("new position : %f, %f\n", where.x, where.y)); } // Constrain and filter the mouse coords and add the final // point to the message. where.x = roundf(where.x); where.y = roundf(where.y); where.ConstrainTo(fFrame); if (event->ReplacePoint("where", where) != B_OK) event->AddPoint("where", where); if (!event->HasInt64("when")) event->AddInt64("when", system_time()); event->AddInt32("modifiers", fKeyInfo.modifiers); break; } case B_KEY_DOWN: case B_UNMAPPED_KEY_DOWN: // add modifiers if (!event->HasInt32("modifiers")) event->AddInt32("modifiers", fKeyInfo.modifiers); // add key states if (!event->HasData("states", B_UINT8_TYPE)) { event->AddData("states", B_UINT8_TYPE, fKeyInfo.key_states, sizeof(fKeyInfo.key_states)); } break; } } return true; } /*! 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); } { // move the method events into the event queue - they are not // "methodized" either BAutolock _(fEventQueueLock); events.AddList(&fMethodQueue); fMethodQueue.MakeEmpty(); } if (!fInputMethodAware) { // special handling for non-input-method-aware views int32 newCount = events.CountItems(); // we may add new events in this loop that don't need to be checked again for (int32 i = 0; i < newCount; i++) { BMessage* event = events.ItemAt(i); if (event->what != B_INPUT_METHOD_EVENT) continue; SERIAL_PRINT(("IME received\n")); bool removeEvent = true; int32 opcode; if (event->FindInt32("be:opcode", &opcode) == B_OK) { bool inlineOnly; if (event->FindBool("be:inline_only", &inlineOnly) != B_OK) inlineOnly = false; if (inlineOnly) { BMessage translated; bool confirmed; if (opcode == B_INPUT_METHOD_CHANGED && event->FindBool("be:confirmed", &confirmed) == B_OK && confirmed && event->FindMessage("be:translated", &translated) == B_OK) { // translate event for the non-aware view *event = translated; removeEvent = false; } } else { if (fInputMethodWindow == NULL && opcode == B_INPUT_METHOD_STARTED) fInputMethodWindow = new (nothrow) BottomlineWindow(); if (fInputMethodWindow != NULL) { EventList newEvents; fInputMethodWindow->HandleInputMethodEvent(event, newEvents); if (!newEvents.IsEmpty()) { events.AddList(&newEvents); opcode = B_INPUT_METHOD_STOPPED; } if (opcode == B_INPUT_METHOD_STOPPED) { fInputMethodWindow->PostMessage(B_QUIT_REQUESTED); fInputMethodWindow = NULL; } } } } if (removeEvent) { // the inline/bottom window has eaten the event events.RemoveItemAt(i--); delete event; newCount--; } } } 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) { EventList addedEvents; EventList::Private(&addedEvents).AsBList()->AddList(&newEvents); _SanitizeEvents(addedEvents); // add the new events - but don't methodize them again events.AddList(&addedEvents, 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) { atomic_set((int32*)&fCursorBuffer->pos, (uint32)fMousePos.x << 16UL | ((uint32)fMousePos.y & 0xffff)); if (atomic_or(&fCursorBuffer->read, 1) == 0) release_sem(fCursorSem); } 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)); } break; } default: break; } BMessenger reply; BMessage::Private messagePrivate(event); return messagePrivate.SendMessage(fAppServerPort, fAppServerTeam, 0, 0, false, reply); } // #pragma mark - extern "C" void RegisterDevices(input_device_ref** devices) { CALLED(); } BView * instantiate_deskbar_item() { return new MethodReplicant(INPUTSERVER_SIGNATURE); } // #pragma mark - int main(int /*argc*/, char** /*argv*/) { InputServer *inputServer = new InputServer; inputServer->Run(); delete inputServer; return 0; }