| // Copyright 2018 The Cobalt Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "starboard/shared/wayland/dev_input.h" |
| |
| #include <linux/input.h> |
| #include <string.h> |
| |
| #include "starboard/common/log.h" |
| #include "starboard/input.h" |
| #include "starboard/key.h" |
| #include "starboard/memory.h" |
| #include "starboard/shared/starboard/application.h" |
| #include "starboard/shared/wayland/application_wayland.h" |
| |
| namespace starboard { |
| namespace shared { |
| namespace wayland { |
| |
| namespace { |
| |
| // YouTube Technical Requirement 2018 (2016/11/1 - Initial draft) |
| // 9.5 The device MUST dispatch the following key events, as appropriate: |
| // * Window.keydown |
| // * After a key is held down for 500ms, the Window.keydown event |
| // MUST repeat every 50ms until a user stops holding the key down. |
| // * Window.keyup |
| const SbTime kKeyHoldTime = 500 * kSbTimeMillisecond; |
| const SbTime kKeyRepeatTime = 50 * kSbTimeMillisecond; |
| |
| void SeatHandleCapabilities(void* data, |
| struct wl_seat* seat, |
| unsigned int caps) { |
| DevInput* dev_input = reinterpret_cast<DevInput*>(data); |
| SB_DCHECK(dev_input); |
| dev_input->OnSeatCapabilitiesChanged(seat, caps); |
| } |
| |
| const struct wl_seat_listener seat_listener = { |
| &SeatHandleCapabilities, |
| }; |
| |
| #define KEY_INFO_BUTTON 0xbc |
| |
| // Converts an input_event code into an SbKey. |
| SbKey KeyCodeToSbKey(uint16_t code) { |
| switch (code) { |
| case KEY_BACKSPACE: |
| return kSbKeyBack; |
| case KEY_DELETE: |
| return kSbKeyDelete; |
| case KEY_TAB: |
| return kSbKeyTab; |
| case KEY_LINEFEED: |
| case KEY_ENTER: |
| case KEY_KPENTER: |
| return kSbKeyReturn; |
| case KEY_CLEAR: |
| return kSbKeyClear; |
| case KEY_SPACE: |
| return kSbKeySpace; |
| case KEY_HOME: |
| return kSbKeyHome; |
| case KEY_END: |
| return kSbKeyEnd; |
| case KEY_PAGEUP: |
| return kSbKeyPrior; |
| case KEY_PAGEDOWN: |
| return kSbKeyNext; |
| case KEY_LEFT: |
| return kSbKeyLeft; |
| case KEY_RIGHT: |
| return kSbKeyRight; |
| case KEY_DOWN: |
| return kSbKeyDown; |
| case KEY_UP: |
| return kSbKeyUp; |
| case KEY_ESC: |
| return kSbKeyEscape; |
| case KEY_KATAKANA: |
| case KEY_HIRAGANA: |
| case KEY_KATAKANAHIRAGANA: |
| return kSbKeyKana; |
| case KEY_HANGEUL: |
| return kSbKeyHangul; |
| case KEY_HANJA: |
| return kSbKeyHanja; |
| case KEY_HENKAN: |
| return kSbKeyConvert; |
| case KEY_MUHENKAN: |
| return kSbKeyNonconvert; |
| case KEY_ZENKAKUHANKAKU: |
| return kSbKeyDbeDbcschar; |
| case KEY_A: |
| return kSbKeyA; |
| case KEY_B: |
| return kSbKeyB; |
| case KEY_C: |
| return kSbKeyC; |
| case KEY_D: |
| return kSbKeyD; |
| case KEY_E: |
| return kSbKeyE; |
| case KEY_F: |
| return kSbKeyF; |
| case KEY_G: |
| return kSbKeyG; |
| case KEY_H: |
| return kSbKeyH; |
| case KEY_I: |
| return kSbKeyI; |
| case KEY_J: |
| return kSbKeyJ; |
| case KEY_K: |
| return kSbKeyK; |
| case KEY_L: |
| return kSbKeyL; |
| case KEY_M: |
| return kSbKeyM; |
| case KEY_N: |
| return kSbKeyN; |
| case KEY_O: |
| return kSbKeyO; |
| case KEY_P: |
| return kSbKeyP; |
| case KEY_Q: |
| return kSbKeyQ; |
| case KEY_R: |
| return kSbKeyR; |
| case KEY_S: |
| return kSbKeyS; |
| case KEY_T: |
| return kSbKeyT; |
| case KEY_U: |
| return kSbKeyU; |
| case KEY_V: |
| return kSbKeyV; |
| case KEY_W: |
| return kSbKeyW; |
| case KEY_X: |
| return kSbKeyX; |
| case KEY_Y: |
| return kSbKeyY; |
| case KEY_Z: |
| return kSbKeyZ; |
| |
| case KEY_0: |
| return kSbKey0; |
| case KEY_1: |
| return kSbKey1; |
| case KEY_2: |
| return kSbKey2; |
| case KEY_3: |
| return kSbKey3; |
| case KEY_4: |
| return kSbKey4; |
| case KEY_5: |
| return kSbKey5; |
| case KEY_6: |
| return kSbKey6; |
| case KEY_7: |
| return kSbKey7; |
| case KEY_8: |
| return kSbKey8; |
| case KEY_9: |
| return kSbKey9; |
| |
| case KEY_NUMERIC_0: |
| case KEY_NUMERIC_1: |
| case KEY_NUMERIC_2: |
| case KEY_NUMERIC_3: |
| case KEY_NUMERIC_4: |
| case KEY_NUMERIC_5: |
| case KEY_NUMERIC_6: |
| case KEY_NUMERIC_7: |
| case KEY_NUMERIC_8: |
| case KEY_NUMERIC_9: |
| return static_cast<SbKey>(kSbKey0 + (code - KEY_NUMERIC_0)); |
| |
| case KEY_KP0: |
| return kSbKeyNumpad0; |
| case KEY_KP1: |
| return kSbKeyNumpad1; |
| case KEY_KP2: |
| return kSbKeyNumpad2; |
| case KEY_KP3: |
| return kSbKeyNumpad3; |
| case KEY_KP4: |
| return kSbKeyNumpad4; |
| case KEY_KP5: |
| return kSbKeyNumpad5; |
| case KEY_KP6: |
| return kSbKeyNumpad6; |
| case KEY_KP7: |
| return kSbKeyNumpad7; |
| case KEY_KP8: |
| return kSbKeyNumpad8; |
| case KEY_KP9: |
| return kSbKeyNumpad9; |
| |
| case KEY_KPASTERISK: |
| return kSbKeyMultiply; |
| case KEY_KPDOT: |
| return kSbKeyDecimal; |
| case KEY_KPSLASH: |
| return kSbKeyDivide; |
| case KEY_KPPLUS: |
| case KEY_EQUAL: |
| return kSbKeyOemPlus; |
| case KEY_COMMA: |
| return kSbKeyOemComma; |
| case KEY_KPMINUS: |
| case KEY_MINUS: |
| return kSbKeyOemMinus; |
| case KEY_DOT: |
| return kSbKeyOemPeriod; |
| case KEY_SEMICOLON: |
| return kSbKeyOem1; |
| case KEY_SLASH: |
| return kSbKeyOem2; |
| case KEY_GRAVE: |
| return kSbKeyOem3; |
| case KEY_LEFTBRACE: |
| return kSbKeyOem4; |
| case KEY_BACKSLASH: |
| return kSbKeyOem5; |
| case KEY_RIGHTBRACE: |
| return kSbKeyOem6; |
| case KEY_APOSTROPHE: |
| return kSbKeyOem7; |
| case KEY_LEFTSHIFT: |
| case KEY_RIGHTSHIFT: |
| return kSbKeyShift; |
| case KEY_LEFTCTRL: |
| case KEY_RIGHTCTRL: |
| return kSbKeyControl; |
| case KEY_LEFTMETA: |
| case KEY_RIGHTMETA: |
| case KEY_LEFTALT: |
| case KEY_RIGHTALT: |
| return kSbKeyMenu; |
| case KEY_PAUSE: |
| return kSbKeyPause; |
| case KEY_CAPSLOCK: |
| return kSbKeyCapital; |
| case KEY_NUMLOCK: |
| return kSbKeyNumlock; |
| case KEY_SCROLLLOCK: |
| return kSbKeyScroll; |
| case KEY_SELECT: |
| return kSbKeySelect; |
| case KEY_PRINT: |
| return kSbKeyPrint; |
| case KEY_INSERT: |
| return kSbKeyInsert; |
| case KEY_HELP: |
| return kSbKeyHelp; |
| case KEY_MENU: |
| return kSbKeyApps; |
| case KEY_FN_F1: |
| case KEY_FN_F2: |
| case KEY_FN_F3: |
| case KEY_FN_F4: |
| case KEY_FN_F5: |
| case KEY_FN_F6: |
| case KEY_FN_F7: |
| case KEY_FN_F8: |
| case KEY_FN_F9: |
| case KEY_FN_F10: |
| case KEY_FN_F11: |
| case KEY_FN_F12: |
| return static_cast<SbKey>(kSbKeyF1 + (code - KEY_FN_F1)); |
| |
| // For supporting multimedia buttons on a USB keyboard. |
| case KEY_BACK: |
| return kSbKeyBrowserBack; |
| case KEY_FORWARD: |
| return kSbKeyBrowserForward; |
| case KEY_REFRESH: |
| return kSbKeyBrowserRefresh; |
| case KEY_STOP: |
| return kSbKeyBrowserStop; |
| case KEY_SEARCH: |
| return kSbKeyBrowserSearch; |
| case KEY_FAVORITES: |
| return kSbKeyBrowserFavorites; |
| case KEY_HOMEPAGE: |
| return kSbKeyBrowserHome; |
| case KEY_MUTE: |
| return kSbKeyVolumeMute; |
| case KEY_VOLUMEDOWN: |
| return kSbKeyVolumeDown; |
| case KEY_VOLUMEUP: |
| return kSbKeyVolumeUp; |
| case KEY_NEXTSONG: |
| return kSbKeyMediaNextTrack; |
| case KEY_PREVIOUSSONG: |
| return kSbKeyMediaPrevTrack; |
| case KEY_STOPCD: |
| return kSbKeyMediaStop; |
| case KEY_PLAYPAUSE: |
| return kSbKeyMediaPlayPause; |
| case KEY_MAIL: |
| return kSbKeyMediaLaunchMail; |
| case KEY_CALC: |
| return kSbKeyMediaLaunchApp2; |
| case KEY_WLAN: |
| return kSbKeyWlan; |
| case KEY_POWER: |
| return kSbKeyPower; |
| case KEY_BRIGHTNESSDOWN: |
| return kSbKeyBrightnessDown; |
| case KEY_BRIGHTNESSUP: |
| return kSbKeyBrightnessUp; |
| |
| case KEY_INFO_BUTTON: |
| return kSbKeyF1; |
| } |
| SB_DLOG(WARNING) << "Unknown code: 0x" << std::hex << code; |
| return kSbKeyUnknown; |
| } // NOLINT(readability/fn_size) |
| |
| // Get a SbKeyLocation from an input_event.code. |
| SbKeyLocation KeyCodeToSbKeyLocation(uint16_t code) { |
| switch (code) { |
| case KEY_LEFTALT: |
| case KEY_LEFTCTRL: |
| case KEY_LEFTMETA: |
| case KEY_LEFTSHIFT: |
| return kSbKeyLocationLeft; |
| case KEY_RIGHTALT: |
| case KEY_RIGHTCTRL: |
| case KEY_RIGHTMETA: |
| case KEY_RIGHTSHIFT: |
| return kSbKeyLocationRight; |
| } |
| |
| return kSbKeyLocationUnspecified; |
| } |
| |
| void KeyboardHandleKeyMap(void* data, |
| struct wl_keyboard* keyboard, |
| uint32_t format, |
| int fd, |
| uint32_t size) { |
| DevInput* dev_input = reinterpret_cast<DevInput*>(data); |
| SB_DCHECK(dev_input); |
| dev_input->OnKeyboardHandleKeyMap(keyboard, format, fd, size); |
| } |
| |
| void KeyboardHandleEnter(void* data, |
| struct wl_keyboard* keyboard, |
| uint32_t serial, |
| struct wl_surface* surface, |
| struct wl_array* keys) { |
| DevInput* dev_input = reinterpret_cast<DevInput*>(data); |
| SB_DCHECK(dev_input); |
| dev_input->OnKeyboardHandleEnter(keyboard, serial, surface, keys); |
| } |
| |
| void KeyboardHandleLeave(void* data, |
| struct wl_keyboard* keyboard, |
| uint32_t serial, |
| struct wl_surface* surface) { |
| DevInput* dev_input = reinterpret_cast<DevInput*>(data); |
| SB_DCHECK(dev_input); |
| dev_input->OnKeyboardHandleLeave(keyboard, serial, surface); |
| } |
| |
| void KeyboardHandleKey(void* data, |
| struct wl_keyboard* keyboard, |
| uint32_t serial, |
| uint32_t time, |
| uint32_t key, |
| uint32_t state) { |
| DevInput* dev_input = reinterpret_cast<DevInput*>(data); |
| SB_DCHECK(dev_input); |
| dev_input->OnKeyboardHandleKey(keyboard, serial, time, key, state); |
| } |
| |
| void KeyboardHandleModifiers(void* data, |
| struct wl_keyboard* keyboard, |
| uint32_t serial, |
| uint32_t mods_depressed, |
| uint32_t mods_latched, |
| uint32_t mods_locked, |
| uint32_t group) { |
| DevInput* dev_input = reinterpret_cast<DevInput*>(data); |
| SB_DCHECK(dev_input); |
| dev_input->OnKeyboardHandleModifiers(keyboard, serial, mods_depressed, |
| mods_latched, mods_locked, group); |
| } |
| |
| const struct wl_keyboard_listener keyboard_listener = { |
| &KeyboardHandleKeyMap, &KeyboardHandleEnter, &KeyboardHandleLeave, |
| &KeyboardHandleKey, &KeyboardHandleModifiers, |
| }; |
| |
| } // namespace |
| |
| DevInput::DevInput() |
| : wl_seat_(NULL), |
| wl_keyboard_(NULL), |
| key_repeat_event_id_(kSbEventIdInvalid), |
| key_repeat_interval_(kKeyHoldTime), |
| key_modifiers_(0), |
| window_(kSbWindowInvalid) {} |
| |
| bool DevInput::OnGlobalObjectAvailable(struct wl_registry* registry, |
| uint32_t name, |
| const char* interface, |
| uint32_t version) { |
| if (strcmp(interface, "wl_seat") == 0) { |
| wl_seat_ = static_cast<wl_seat*>( |
| wl_registry_bind(registry, name, &wl_seat_interface, 1)); |
| SB_DCHECK(wl_seat_); |
| wl_seat_add_listener(wl_seat_, &seat_listener, this); |
| return true; |
| } |
| return false; |
| } |
| |
| void DevInput::OnSeatCapabilitiesChanged(struct wl_seat* seat, |
| unsigned int caps) { |
| SB_DLOG(INFO) << "[Key] seat_handle_capabilities caps: " << caps; |
| if (caps & WL_SEAT_CAPABILITY_KEYBOARD) { |
| wl_keyboard_ = static_cast<wl_keyboard*>(wl_seat_get_keyboard(seat)); |
| if (wl_keyboard_) |
| wl_keyboard_add_listener(wl_keyboard_, &keyboard_listener, this); |
| } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD)) { |
| if (wl_keyboard_) |
| wl_keyboard_destroy(wl_keyboard_); |
| wl_keyboard_ = NULL; |
| } |
| } |
| |
| void DevInput::OnKeyboardHandleKeyMap(struct wl_keyboard* keyboard, |
| uint32_t format, |
| int fd, |
| uint32_t size) { |
| SB_DLOG(INFO) << "[Key] Keyboard keymap"; |
| } |
| |
| void DevInput::OnKeyboardHandleEnter(struct wl_keyboard* keyboard, |
| uint32_t serial, |
| struct wl_surface* surface, |
| struct wl_array* keys) { |
| SB_DLOG(INFO) << "[Key] Keyboard gained focus"; |
| } |
| |
| void DevInput::OnKeyboardHandleLeave(struct wl_keyboard* keyboard, |
| uint32_t serial, |
| struct wl_surface* surface) { |
| SB_DLOG(INFO) << "[Key] Keyboard lost focus"; |
| DeleteRepeatKey(); |
| } |
| |
| void DevInput::OnKeyboardHandleModifiers(struct wl_keyboard* keyboard, |
| uint32_t serial, |
| uint32_t mods_depressed, |
| uint32_t mods_latched, |
| uint32_t mods_locked, |
| uint32_t group) { |
| // Convert to SbKeyModifiers. |
| unsigned int modifiers = kSbKeyModifiersNone; |
| |
| if (mods_depressed & 1) |
| modifiers |= kSbKeyModifiersShift; |
| if (mods_depressed & 4) |
| modifiers |= kSbKeyModifiersCtrl; |
| if (mods_depressed & 8) |
| modifiers |= kSbKeyModifiersAlt; |
| |
| SB_DLOG(INFO) << "[Key] Modifiers depressed " << mods_depressed |
| << ", latched " << mods_latched << ", locked " << mods_locked |
| << ", group " << group << ", key modifiers " << modifiers; |
| |
| key_modifiers_ = modifiers; |
| } |
| |
| void DevInput::OnKeyboardHandleKey(struct wl_keyboard* keyboard, |
| uint32_t serial, |
| uint32_t time, |
| uint32_t key, |
| uint32_t state) { |
| bool repeatable = |
| (key == KEY_LEFT || key == KEY_RIGHT || key == KEY_UP || key == KEY_DOWN); |
| SB_DLOG(INFO) << "[Key] Key :" << key << ", state:" << state << " repeatable " |
| << repeatable << " key_repeat_key_ " << key_repeat_key_ |
| << " key_repeat_state_ " << key_repeat_state_; |
| if (state && repeatable && key == key_repeat_key_ && key_repeat_state_) |
| return; |
| |
| if (repeatable) { |
| CreateKey(key, state, true); |
| } else { |
| CreateKey(key, state, false); |
| } |
| } |
| |
| void DevInput::CreateRepeatKey() { |
| if (!key_repeat_state_) { |
| return; |
| } |
| |
| if (key_repeat_interval_) { |
| key_repeat_interval_ = kKeyRepeatTime; |
| } |
| |
| CreateKey(key_repeat_key_, key_repeat_state_, true); |
| } |
| |
| void DevInput::CreateKey(int key, int state, bool is_repeat) { |
| SbInputData* data = new SbInputData(); |
| SbMemorySet(data, 0, sizeof(*data)); |
| data->timestamp = SbTimeGetMonotonicNow(); |
| data->window = window_; |
| data->type = (state == 0 ? kSbInputEventTypeUnpress : kSbInputEventTypePress); |
| data->device_type = kSbInputDeviceTypeRemote; |
| data->device_id = 1; // kKeyboardDeviceId; |
| data->key = KeyCodeToSbKey(key); |
| data->key_location = KeyCodeToSbKeyLocation(key); |
| data->key_modifiers = key_modifiers_; |
| ApplicationWayland::Get()->InjectInputEvent(data); |
| |
| DeleteRepeatKey(); |
| |
| if (is_repeat && state) { |
| key_repeat_key_ = key; |
| key_repeat_state_ = state; |
| key_repeat_event_id_ = SbEventSchedule( |
| [](void* data) { |
| DevInput* dev_input = reinterpret_cast<DevInput*>(data); |
| dev_input->CreateRepeatKey(); |
| }, |
| this, key_repeat_interval_); |
| } else { |
| key_repeat_interval_ = kKeyHoldTime; |
| } |
| } |
| |
| void DevInput::DeleteRepeatKey() { |
| key_repeat_state_ = 0; |
| if (key_repeat_event_id_ != kSbEventIdInvalid) { |
| SbEventCancel(key_repeat_event_id_); |
| key_repeat_event_id_ = kSbEventIdInvalid; |
| } |
| } |
| |
| } // namespace wayland |
| } // namespace shared |
| } // namespace starboard |