blob: a48e49d045af9b4d2d127cf7796ef32150f30536 [file]
//
// Copyright 2020 Comcast Cable Communications Management, LLC
//
// 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.
//
// SPDX-License-Identifier: Apache-2.0
//
// Copyright 2016 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 "third_party/starboard/rdk/shared/ess_input.h"
#include "starboard/common/log.h"
#include "starboard/event.h"
#include "starboard/input.h"
#include "starboard/key.h"
#include "third_party/starboard/rdk/shared/application_rdk.h"
#include "third_party/starboard/rdk/shared/linux_key_mapping.h"
#include "third_party/starboard/rdk/shared/log_override.h"
#include <linux/input.h>
#include <cstring>
namespace third_party {
namespace starboard {
namespace rdk {
namespace shared {
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
constexpr SbTime kKeyHoldTime = 500 * kSbTimeMillisecond;
constexpr SbTime kKeyRepeatTime = 50 * kSbTimeMillisecond;
// 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_REWIND:
return kSbKeyMediaRewind;
case KEY_FASTFORWARD:
return kSbKeyMediaFastForward;
case KEY_RED:
return kSbKeyRed;
case KEY_GREEN:
return kSbKeyGreen;
case KEY_YELLOW:
return kSbKeyYellow;
case KEY_BLUE:
return kSbKeyBlue;
case KEY_CHANNELUP:
return kSbKeyChannelUp;
case KEY_CHANNELDOWN:
return kSbKeyChannelDown;
case KEY_INFO:
return kSbKeyInfo;
//[sky] mapping of the YouTube partner button
case KEY_KPLEFTPAREN:
return kSbKeyLaunchThisApplication;
case KEY_F8:
return kSbKeyMicrophone;
case KEY_UNKNOWN:
break;
}
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;
}
SbKeyModifiers KeyCodeToSbKeyModifiers(uint16_t code) {
switch (code) {
case KEY_LEFTCTRL:
case KEY_RIGHTCTRL:
return kSbKeyModifiersCtrl;
case KEY_LEFTSHIFT:
case KEY_RIGHTSHIFT:
return kSbKeyModifiersShift;
case KEY_LEFTALT:
case KEY_RIGHTALT:
return kSbKeyModifiersAlt;
case KEY_LEFTMETA:
case KEY_RIGHTMETA:
return kSbKeyModifiersMeta;
}
return kSbKeyModifiersNone;
}
} // namespace
EssInput::EssInput() : key_repeat_interval_(kKeyHoldTime) {
}
EssInput::~EssInput() {
DeleteRepeatKey();
}
void EssInput::CreateKey(unsigned int key, SbInputEventType type, unsigned int modifiers, bool repeatable) {
SbKey sb_key = KeyCodeToSbKey(key);
if (sb_key == kSbKeyUnknown) {
DeleteRepeatKey();
return;
}
SbInputData* data = new SbInputData();
memset(data, 0, sizeof(*data));
data->type = type;
data->device_type = kSbInputDeviceTypeRemote;
data->device_id = 1; // kKeyboardDeviceId;
data->key = sb_key;
data->key_location = KeyCodeToSbKeyLocation(key);
data->key_modifiers = modifiers;
Application::Get()->InjectInputEvent(data);
DeleteRepeatKey();
if (repeatable && type == kSbInputEventTypePress) {
key_repeat_key_ = key;
key_repeat_state_ = 1;
key_repeat_modifiers_ = modifiers;
key_repeat_event_id_ = SbEventSchedule(
[](void* data) {
EssInput* ess_input = reinterpret_cast<EssInput*>(data);
ess_input->CreateRepeatKey();
},
this, key_repeat_interval_);
} else {
key_repeat_interval_ = kKeyHoldTime;
}
}
void EssInput::CreateRepeatKey() {
if (!key_repeat_state_) {
return;
}
if (key_repeat_interval_) {
key_repeat_interval_ = kKeyRepeatTime;
}
CreateKey(key_repeat_key_, kSbInputEventTypePress, key_repeat_modifiers_, true);
}
void EssInput::DeleteRepeatKey() {
key_repeat_state_ = 0;
if (key_repeat_event_id_ != kSbEventIdInvalid) {
SbEventCancel(key_repeat_event_id_);
key_repeat_event_id_ = kSbEventIdInvalid;
}
}
bool EssInput::UpdateModifiers(unsigned int key, SbInputEventType type) {
SbKeyModifiers modifiers = KeyCodeToSbKeyModifiers(key);
if (modifiers != kSbKeyModifiersNone) {
if (type == kSbInputEventTypePress)
key_modifiers_ |= modifiers;
else
key_modifiers_ &= ~modifiers;
return true;
}
return false;
}
void EssInput::OnKeyboardKey(unsigned int key, SbInputEventType type) {
if (UpdateModifiers(key, type))
return;
unsigned int modifiers = key_modifiers_;
LinuxKeyMapping::MapKeyCodeAndModifiers(key, modifiers);
bool repeatable =
(key == KEY_LEFT || key == KEY_RIGHT || key == KEY_UP || key == KEY_DOWN ||
key == KEY_FASTFORWARD || key == KEY_REWIND) && (modifiers == 0);
if (type == kSbInputEventTypePress && repeatable && key == key_repeat_key_ && key_repeat_state_)
return;
static bool enable_key_debug = !!getenv("COBALT_ENABLE_KEY_DEBUG");
if (enable_key_debug) {
SB_LOG(INFO) << "OnKeyboardKey, key code = " << key
<< ", type = " << type
<< ", modifiers = " << modifiers
<< ", repeatable = " << repeatable;
}
if (repeatable) {
CreateKey(key, type, modifiers, true);
} else {
CreateKey(key, type, modifiers, false);
}
}
void EssInput::OnKeyPressed(unsigned int key) {
OnKeyboardKey(key, kSbInputEventTypePress);
}
void EssInput::OnKeyReleased(unsigned int key) {
OnKeyboardKey(key, kSbInputEventTypeUnpress);
}
} // namespace shared
} // namespace rdk
} // namespace starboard
} // namespace third_party