Make keybindings configurable
This commit is contained in:
@@ -77,6 +77,20 @@
|
|||||||
# 3840x1600,
|
# 3840x1600,
|
||||||
# ]
|
# ]
|
||||||
|
|
||||||
|
# Sometimes it may be usefull to map keybindings.
|
||||||
|
# Wayland won't allow clients to capture the Win Key for example
|
||||||
|
#
|
||||||
|
# See https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
|
||||||
|
#
|
||||||
|
# Note:
|
||||||
|
# keybindings needs to have a multiple of two elements
|
||||||
|
# keybindings = [
|
||||||
|
# 0x10, 0xA0,
|
||||||
|
# 0x11, 0xA2,
|
||||||
|
# 0x12, 0xA4,
|
||||||
|
# 0x4A, 0x4B
|
||||||
|
# ]
|
||||||
|
|
||||||
# How long to wait in milliseconds for data from moonlight before shutting down the stream
|
# How long to wait in milliseconds for data from moonlight before shutting down the stream
|
||||||
# ping_timeout = 10000
|
# ping_timeout = 10000
|
||||||
|
|
||||||
|
|||||||
@@ -111,6 +111,18 @@
|
|||||||
are supported.
|
are supported.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Mapping Key AltRight to Key Windows -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="mapkey" class="form-label">Map Right Alt key to Windows key</label>
|
||||||
|
<select id="mapkey" class="form-select" v-model="config.key_rightalt_to_key_win">
|
||||||
|
<option value="disabled">Disabled</option>
|
||||||
|
<option value="enabled">Enabled</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-text">
|
||||||
|
It may be possible that you cannot send the Windows Key from Moonlight directly.<br />
|
||||||
|
In those cases it may be usefull to make Sunshine think the Right Alt key is the Windows key
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!--Files Tab-->
|
<!--Files Tab-->
|
||||||
<div v-if="currentTab === 'files'" class="config-page">
|
<div v-if="currentTab === 'files'" class="config-page">
|
||||||
@@ -533,6 +545,7 @@
|
|||||||
delete this.config.status;
|
delete this.config.status;
|
||||||
delete this.config.platform;
|
delete this.config.platform;
|
||||||
//Populate default values if not present in config
|
//Populate default values if not present in config
|
||||||
|
this.config.key_rightalt_to_key_win = this.config.key_rightalt_to_key_win || "disabled";
|
||||||
this.config.gamepad = this.config.gamepad || 'x360';
|
this.config.gamepad = this.config.gamepad || 'x360';
|
||||||
this.config.upnp = this.config.upnp || 'disabled';
|
this.config.upnp = this.config.upnp || 'disabled';
|
||||||
this.config.min_log_level = this.config.min_log_level || 2;
|
this.config.min_log_level = this.config.min_log_level || 2;
|
||||||
@@ -561,6 +574,7 @@
|
|||||||
let nl = this.config === 'windows' ? "\r\n" : "\n";
|
let nl = this.config === 'windows' ? "\r\n" : "\n";
|
||||||
this.config.resolutions = "[" + nl + " " + this.resolutions.join("," + nl + " ") + nl + "]";
|
this.config.resolutions = "[" + nl + " " + this.resolutions.join("," + nl + " ") + nl + "]";
|
||||||
this.config.fps = JSON.stringify(this.fps);
|
this.config.fps = JSON.stringify(this.fps);
|
||||||
|
|
||||||
fetch("/api/config", {
|
fetch("/api/config", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(this.config)
|
body: JSON.stringify(this.config)
|
||||||
@@ -588,4 +602,4 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -210,6 +210,11 @@ nvhttp_t nvhttp {
|
|||||||
};
|
};
|
||||||
|
|
||||||
input_t input {
|
input_t input {
|
||||||
|
{
|
||||||
|
{ 0x10, 0xA0 },
|
||||||
|
{ 0x11, 0xA2 },
|
||||||
|
{ 0x12, 0xA4 },
|
||||||
|
},
|
||||||
2s, // back_button_timeout
|
2s, // back_button_timeout
|
||||||
500ms, // key_repeat_delay
|
500ms, // key_repeat_delay
|
||||||
std::chrono::duration<double> { 1 / 24.9 }, // key_repeat_period
|
std::chrono::duration<double> { 1 / 24.9 }, // key_repeat_period
|
||||||
@@ -399,8 +404,20 @@ void int_f(std::unordered_map<std::string, std::string> &vars, const std::string
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &val = it->second;
|
std::string_view val = it->second;
|
||||||
input = util::from_chars(&val[0], &val[0] + val.size());
|
|
||||||
|
// If value is something like: "756" instead of 756
|
||||||
|
if(val.size() >= 2 && val[0] == '"') {
|
||||||
|
val = val.substr(1, val.size() - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If that integer is in hexadecimal
|
||||||
|
if(val.size() >= 2 && val.substr(0, 2) == "0x"sv) {
|
||||||
|
input = util::from_hex<int>(val.substr(2));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
input = util::from_view(val);
|
||||||
|
}
|
||||||
|
|
||||||
vars.erase(it);
|
vars.erase(it);
|
||||||
}
|
}
|
||||||
@@ -412,8 +429,20 @@ void int_f(std::unordered_map<std::string, std::string> &vars, const std::string
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &val = it->second;
|
std::string_view val = it->second;
|
||||||
input = util::from_chars(&val[0], &val[0] + val.size());
|
|
||||||
|
// If value is something like: "756" instead of 756
|
||||||
|
if(val.size() >= 2 && val[0] == '"') {
|
||||||
|
val = val.substr(1, val.size() - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If that integer is in hexadecimal
|
||||||
|
if(val.size() >= 2 && val.substr(0, 2) == "0x"sv) {
|
||||||
|
input = util::from_hex<int>(val.substr(2));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
input = util::from_view(val);
|
||||||
|
}
|
||||||
|
|
||||||
vars.erase(it);
|
vars.erase(it);
|
||||||
}
|
}
|
||||||
@@ -545,7 +574,42 @@ void list_int_f(std::unordered_map<std::string, std::string> &vars, const std::s
|
|||||||
list_string_f(vars, name, list);
|
list_string_f(vars, name, list);
|
||||||
|
|
||||||
for(auto &el : list) {
|
for(auto &el : list) {
|
||||||
input.emplace_back(util::from_view(el));
|
std::string_view val = el;
|
||||||
|
|
||||||
|
// If value is something like: "756" instead of 756
|
||||||
|
if(val.size() >= 2 && val[0] == '"') {
|
||||||
|
val = val.substr(1, val.size() - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int tmp;
|
||||||
|
|
||||||
|
// If the integer is a hexadecimal
|
||||||
|
if(val.size() >= 2 && val.substr(0, 2) == "0x"sv) {
|
||||||
|
tmp = util::from_hex<int>(val.substr(2));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tmp = util::from_view(val);
|
||||||
|
}
|
||||||
|
input.emplace_back(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void map_int_int_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::unordered_map<int, int> &input) {
|
||||||
|
std::vector<int> list;
|
||||||
|
list_int_f(vars, name, list);
|
||||||
|
|
||||||
|
// The list needs to be a multiple of 2
|
||||||
|
if(list.size() % 2) {
|
||||||
|
std::cout << "Warning: expected "sv << name << " to have a multiple of two elements --> not "sv << list.size() << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int x = 0;
|
||||||
|
while(x < list.size()) {
|
||||||
|
auto key = list[x++];
|
||||||
|
auto val = list[x++];
|
||||||
|
|
||||||
|
input.emplace(key, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -633,6 +697,17 @@ void apply_config(std::unordered_map<std::string, std::string> &&vars) {
|
|||||||
path_f(vars, "file_apps", stream.file_apps);
|
path_f(vars, "file_apps", stream.file_apps);
|
||||||
int_between_f(vars, "fec_percentage", stream.fec_percentage, { 1, 255 });
|
int_between_f(vars, "fec_percentage", stream.fec_percentage, { 1, 255 });
|
||||||
|
|
||||||
|
map_int_int_f(vars, "keybindings"s, input.keybindings);
|
||||||
|
|
||||||
|
// This config option will only be used by the UI
|
||||||
|
// When editing in the config file itself, use "keybindings"
|
||||||
|
bool map_rightalt_to_win = false;
|
||||||
|
bool_f(vars, "key_rightalt_to_key_win", map_rightalt_to_win);
|
||||||
|
|
||||||
|
if(map_rightalt_to_win) {
|
||||||
|
input.keybindings.emplace(0xA5, 0x5B);
|
||||||
|
}
|
||||||
|
|
||||||
to = std::numeric_limits<int>::min();
|
to = std::numeric_limits<int>::min();
|
||||||
int_f(vars, "back_button_timeout", to);
|
int_f(vars, "back_button_timeout", to);
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
namespace config {
|
namespace config {
|
||||||
struct video_t {
|
struct video_t {
|
||||||
// ffmpeg params
|
// ffmpeg params
|
||||||
int qp; // higher == more compression and less quality
|
int qp; // higher == more compression and less quality
|
||||||
|
|
||||||
int hevc_mode;
|
int hevc_mode;
|
||||||
|
|
||||||
@@ -73,6 +73,8 @@ struct nvhttp_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct input_t {
|
struct input_t {
|
||||||
|
std::unordered_map<int, int> keybindings;
|
||||||
|
|
||||||
std::chrono::milliseconds back_button_timeout;
|
std::chrono::milliseconds back_button_timeout;
|
||||||
std::chrono::milliseconds key_repeat_delay;
|
std::chrono::milliseconds key_repeat_delay;
|
||||||
std::chrono::duration<double> key_repeat_period;
|
std::chrono::duration<double> key_repeat_period;
|
||||||
|
|||||||
@@ -409,13 +409,9 @@ void repeat_key(short key_code) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
short map_keycode(short keycode) {
|
short map_keycode(short keycode) {
|
||||||
switch(keycode) {
|
auto it = config::input.keybindings.find(keycode);
|
||||||
case 0x10:
|
if(it != std::end(config::input.keybindings)) {
|
||||||
return 0xA0;
|
return it->second;
|
||||||
case 0x11:
|
|
||||||
return 0xA2;
|
|
||||||
case 0x12:
|
|
||||||
return 0xA4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return keycode;
|
return keycode;
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ stream::launch_session_t make_launch_session(bool host_audio, const args_t &args
|
|||||||
stream::launch_session_t launch_session;
|
stream::launch_session_t launch_session;
|
||||||
|
|
||||||
launch_session.host_audio = host_audio;
|
launch_session.host_audio = host_audio;
|
||||||
launch_session.gcm_key = *util::from_hex<crypto::aes_t>(args.at("rikey"s), true);
|
launch_session.gcm_key = util::from_hex<crypto::aes_t>(args.at("rikey"s), true);
|
||||||
uint32_t prepend_iv = util::endian::big<uint32_t>(util::from_view(args.at("rikeyid"s)));
|
uint32_t prepend_iv = util::endian::big<uint32_t>(util::from_view(args.at("rikeyid"s)));
|
||||||
auto prepend_iv_p = (uint8_t *)&prepend_iv;
|
auto prepend_iv_p = (uint8_t *)&prepend_iv;
|
||||||
|
|
||||||
@@ -211,7 +211,7 @@ void getservercert(pair_session_t &sess, pt::ptree &tree, const std::string &pin
|
|||||||
|
|
||||||
auto salt = util::from_hex<std::array<uint8_t, 16>>(salt_view, true);
|
auto salt = util::from_hex<std::array<uint8_t, 16>>(salt_view, true);
|
||||||
|
|
||||||
auto key = crypto::gen_aes_key(*salt, pin);
|
auto key = crypto::gen_aes_key(salt, pin);
|
||||||
sess.cipher_key = std::make_unique<crypto::aes_t>(key);
|
sess.cipher_key = std::make_unique<crypto::aes_t>(key);
|
||||||
|
|
||||||
tree.put("root.paired", 1);
|
tree.put("root.paired", 1);
|
||||||
|
|||||||
@@ -495,7 +495,6 @@ std::vector<std::string> display_names() {
|
|||||||
names.reserve(monitor);
|
names.reserve(monitor);
|
||||||
|
|
||||||
for(auto x = 0; x < monitor; ++x) {
|
for(auto x = 0; x < monitor; ++x) {
|
||||||
BOOST_LOG(fatal) << x;
|
|
||||||
names.emplace_back(std::to_string(x));
|
names.emplace_back(std::to_string(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -268,11 +268,12 @@ std::string hex_vec(C &&c, bool rev = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
std::optional<T> from_hex(const std::string_view &hex, bool rev = false) {
|
T from_hex(const std::string_view &hex, bool rev = false) {
|
||||||
std::uint8_t buf[sizeof(T)];
|
std::uint8_t buf[sizeof(T)];
|
||||||
|
|
||||||
static char constexpr shift_bit = 'a' - 'A';
|
static char constexpr shift_bit = 'a' - 'A';
|
||||||
auto is_convertable = [](char ch) -> bool {
|
|
||||||
|
auto is_convertable = [](char ch) -> bool {
|
||||||
if(isdigit(ch)) {
|
if(isdigit(ch)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -287,9 +288,7 @@ std::optional<T> from_hex(const std::string_view &hex, bool rev = false) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
auto buf_size = std::count_if(std::begin(hex), std::end(hex), is_convertable) / 2;
|
auto buf_size = std::count_if(std::begin(hex), std::end(hex), is_convertable) / 2;
|
||||||
if(buf_size != sizeof(T)) {
|
auto padding = sizeof(T) - buf_size;
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *data = hex.data() + hex.size() - 1;
|
const char *data = hex.data() + hex.size() - 1;
|
||||||
|
|
||||||
@@ -301,7 +300,9 @@ std::optional<T> from_hex(const std::string_view &hex, bool rev = false) {
|
|||||||
return (std::uint8_t)(ch | (char)32) - 'a' + (char)10;
|
return (std::uint8_t)(ch | (char)32) - 'a' + (char)10;
|
||||||
};
|
};
|
||||||
|
|
||||||
for(auto &el : buf) {
|
std::fill_n(buf + buf_size, padding, 0);
|
||||||
|
|
||||||
|
std::for_each_n(buf, buf_size, [&](auto &el) {
|
||||||
while(!is_convertable(*data)) { --data; }
|
while(!is_convertable(*data)) { --data; }
|
||||||
std::uint8_t ch_r = convert(*data--);
|
std::uint8_t ch_r = convert(*data--);
|
||||||
|
|
||||||
@@ -309,7 +310,7 @@ std::optional<T> from_hex(const std::string_view &hex, bool rev = false) {
|
|||||||
std::uint8_t ch_l = convert(*data--);
|
std::uint8_t ch_l = convert(*data--);
|
||||||
|
|
||||||
el = (ch_l << 4) | ch_r;
|
el = (ch_l << 4) | ch_r;
|
||||||
}
|
});
|
||||||
|
|
||||||
if(rev) {
|
if(rev) {
|
||||||
std::reverse(std::begin(buf), std::end(buf));
|
std::reverse(std::begin(buf), std::end(buf));
|
||||||
|
|||||||
Reference in New Issue
Block a user