blob: cc5a6b18485c5eb12f626d774521249b9526c90e [file] [log] [blame]
// 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