diff --git a/assets/sunshine.conf b/assets/sunshine.conf index bb846029..7ada209f 100644 --- a/assets/sunshine.conf +++ b/assets/sunshine.conf @@ -77,6 +77,20 @@ # 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 # ping_timeout = 10000 diff --git a/assets/web/config.html b/assets/web/config.html index d7b67b98..c14a24b3 100644 --- a/assets/web/config.html +++ b/assets/web/config.html @@ -111,6 +111,18 @@ are supported. + +
+ + +
+
+ It may be possible that you cannot send the Windows Key from Moonlight directly.
+ In those cases it may be usefull to make Sunshine think the Right Alt key is the Windows key +
@@ -533,6 +545,7 @@ delete this.config.status; delete this.config.platform; //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.upnp = this.config.upnp || 'disabled'; this.config.min_log_level = this.config.min_log_level || 2; @@ -561,6 +574,7 @@ let nl = this.config === 'windows' ? "\r\n" : "\n"; this.config.resolutions = "[" + nl + " " + this.resolutions.join("," + nl + " ") + nl + "]"; this.config.fps = JSON.stringify(this.fps); + fetch("/api/config", { method: "POST", body: JSON.stringify(this.config) @@ -588,4 +602,4 @@ font-size: 12px; font-weight: bold; } - + \ No newline at end of file diff --git a/sunshine/config.cpp b/sunshine/config.cpp index 02a5438a..3008047f 100644 --- a/sunshine/config.cpp +++ b/sunshine/config.cpp @@ -210,6 +210,11 @@ nvhttp_t nvhttp { }; input_t input { + { + { 0x10, 0xA0 }, + { 0x11, 0xA2 }, + { 0x12, 0xA4 }, + }, 2s, // back_button_timeout 500ms, // key_repeat_delay std::chrono::duration { 1 / 24.9 }, // key_repeat_period @@ -399,8 +404,20 @@ void int_f(std::unordered_map &vars, const std::string return; } - auto &val = it->second; - input = util::from_chars(&val[0], &val[0] + val.size()); + std::string_view val = it->second; + + // 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(val.substr(2)); + } + else { + input = util::from_view(val); + } vars.erase(it); } @@ -412,8 +429,20 @@ void int_f(std::unordered_map &vars, const std::string return; } - auto &val = it->second; - input = util::from_chars(&val[0], &val[0] + val.size()); + std::string_view val = it->second; + + // 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(val.substr(2)); + } + else { + input = util::from_view(val); + } vars.erase(it); } @@ -545,7 +574,42 @@ void list_int_f(std::unordered_map &vars, const std::s list_string_f(vars, name, 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(val.substr(2)); + } + else { + tmp = util::from_view(val); + } + input.emplace_back(tmp); + } +} + +void map_int_int_f(std::unordered_map &vars, const std::string &name, std::unordered_map &input) { + std::vector 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 &&vars) { path_f(vars, "file_apps", stream.file_apps); 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::min(); int_f(vars, "back_button_timeout", to); diff --git a/sunshine/config.h b/sunshine/config.h index 3c0537dd..80d4052d 100644 --- a/sunshine/config.h +++ b/sunshine/config.h @@ -11,7 +11,7 @@ namespace config { struct video_t { // ffmpeg params - int qp; // higher == more compression and less quality + int qp; // higher == more compression and less quality int hevc_mode; @@ -73,6 +73,8 @@ struct nvhttp_t { }; struct input_t { + std::unordered_map keybindings; + std::chrono::milliseconds back_button_timeout; std::chrono::milliseconds key_repeat_delay; std::chrono::duration key_repeat_period; diff --git a/sunshine/input.cpp b/sunshine/input.cpp index fa3241f0..6568ad7c 100644 --- a/sunshine/input.cpp +++ b/sunshine/input.cpp @@ -409,13 +409,9 @@ void repeat_key(short key_code) { } short map_keycode(short keycode) { - switch(keycode) { - case 0x10: - return 0xA0; - case 0x11: - return 0xA2; - case 0x12: - return 0xA4; + auto it = config::input.keybindings.find(keycode); + if(it != std::end(config::input.keybindings)) { + return it->second; } return keycode; diff --git a/sunshine/nvhttp.cpp b/sunshine/nvhttp.cpp index cffc65dd..68806106 100644 --- a/sunshine/nvhttp.cpp +++ b/sunshine/nvhttp.cpp @@ -190,7 +190,7 @@ stream::launch_session_t make_launch_session(bool host_audio, const args_t &args stream::launch_session_t launch_session; launch_session.host_audio = host_audio; - launch_session.gcm_key = *util::from_hex(args.at("rikey"s), true); + launch_session.gcm_key = util::from_hex(args.at("rikey"s), true); uint32_t prepend_iv = util::endian::big(util::from_view(args.at("rikeyid"s))); 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>(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(key); tree.put("root.paired", 1); diff --git a/sunshine/platform/linux/display.cpp b/sunshine/platform/linux/display.cpp index f6a89511..133e5905 100644 --- a/sunshine/platform/linux/display.cpp +++ b/sunshine/platform/linux/display.cpp @@ -495,7 +495,6 @@ std::vector display_names() { names.reserve(monitor); for(auto x = 0; x < monitor; ++x) { - BOOST_LOG(fatal) << x; names.emplace_back(std::to_string(x)); } diff --git a/sunshine/utility.h b/sunshine/utility.h index e827d441..4740ae6a 100644 --- a/sunshine/utility.h +++ b/sunshine/utility.h @@ -268,11 +268,12 @@ std::string hex_vec(C &&c, bool rev = false) { } template -std::optional 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)]; static char constexpr shift_bit = 'a' - 'A'; - auto is_convertable = [](char ch) -> bool { + + auto is_convertable = [](char ch) -> bool { if(isdigit(ch)) { return true; } @@ -287,9 +288,7 @@ std::optional 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; - if(buf_size != sizeof(T)) { - return std::nullopt; - } + auto padding = sizeof(T) - buf_size; const char *data = hex.data() + hex.size() - 1; @@ -301,7 +300,9 @@ std::optional from_hex(const std::string_view &hex, bool rev = false) { 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; } std::uint8_t ch_r = convert(*data--); @@ -309,7 +310,7 @@ std::optional from_hex(const std::string_view &hex, bool rev = false) { std::uint8_t ch_l = convert(*data--); el = (ch_l << 4) | ch_r; - } + }); if(rev) { std::reverse(std::begin(buf), std::end(buf));