advertise display modes
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,4 +6,5 @@ cmake-build*
|
|||||||
*.swp
|
*.swp
|
||||||
*.kdev4
|
*.kdev4
|
||||||
|
|
||||||
|
.cache
|
||||||
.idea
|
.idea
|
||||||
@@ -37,6 +37,26 @@
|
|||||||
# The file where current state of Sunshine is stored
|
# The file where current state of Sunshine is stored
|
||||||
# file_state = sunshine_state.json
|
# file_state = sunshine_state.json
|
||||||
|
|
||||||
|
# The display modes advertised by Sunshine
|
||||||
|
#
|
||||||
|
# Some versions of Moonlight, such as Moonlight-nx (Switch),
|
||||||
|
# rely on this list to ensure that the requested resolutions and fps
|
||||||
|
# are supported.
|
||||||
|
#
|
||||||
|
# fps = [10, 30, 60, 90, 120]
|
||||||
|
# resolutions = [
|
||||||
|
# 352x240,
|
||||||
|
# 480x360,
|
||||||
|
# 858x480,
|
||||||
|
# 1280x720,
|
||||||
|
# 1920x1080,
|
||||||
|
# 2560x1080,
|
||||||
|
# 3440x1440,
|
||||||
|
# 1920x1200,
|
||||||
|
# 3860x2160,
|
||||||
|
# 3840x1600,
|
||||||
|
# ]
|
||||||
|
|
||||||
# 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 = 2000
|
# ping_timeout = 2000
|
||||||
|
|
||||||
@@ -210,4 +230,4 @@
|
|||||||
# To set the initial state of flags -0 and -1 to on, set the following flags:
|
# To set the initial state of flags -0 and -1 to on, set the following flags:
|
||||||
# flags = 01
|
# flags = 01
|
||||||
#
|
#
|
||||||
# See: sunshine --help for all options under the header: flags
|
# See: sunshine --help for all options under the header: flags
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#include <algorithm>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -183,7 +184,22 @@ nvhttp_t nvhttp {
|
|||||||
CERTIFICATE_FILE,
|
CERTIFICATE_FILE,
|
||||||
|
|
||||||
boost::asio::ip::host_name(), // sunshine_name,
|
boost::asio::ip::host_name(), // sunshine_name,
|
||||||
"sunshine_state.json"s // file_state
|
"sunshine_state.json"s, // file_state
|
||||||
|
{}, // external_ip
|
||||||
|
{
|
||||||
|
"352x240"s,
|
||||||
|
"480x360"s,
|
||||||
|
"858x480"s,
|
||||||
|
"1280x720"s,
|
||||||
|
"1920x1080"s,
|
||||||
|
"2560x1080"s,
|
||||||
|
"3440x1440"s
|
||||||
|
"1920x1200"s,
|
||||||
|
"3860x2160"s,
|
||||||
|
"3840x1600"s,
|
||||||
|
}, // supported resolutions
|
||||||
|
|
||||||
|
{ 10, 30, 60, 90, 120 }, // supported fps
|
||||||
};
|
};
|
||||||
|
|
||||||
input_t input {
|
input_t input {
|
||||||
@@ -197,28 +213,84 @@ sunshine_t sunshine {
|
|||||||
0 // flags
|
0 // flags
|
||||||
};
|
};
|
||||||
|
|
||||||
bool whitespace(char ch) {
|
bool endline(char ch) {
|
||||||
|
return ch == '\r' || ch == '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
bool space_tab(char ch) {
|
||||||
return ch == ' ' || ch == '\t';
|
return ch == ' ' || ch == '\t';
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string to_string(const char *begin, const char *end) {
|
bool whitespace(char ch) {
|
||||||
return { begin, (std::size_t)(end - begin) };
|
return space_tab(ch) || endline(ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::pair<std::string, std::string>> parse_line(std::string_view::const_iterator begin, std::string_view::const_iterator end) {
|
std::string to_string(const char *begin, const char *end) {
|
||||||
begin = std::find_if(begin, end, std::not_fn(whitespace));
|
std::string result;
|
||||||
end = std::find(begin, end, '#');
|
|
||||||
end = std::find_if(std::make_reverse_iterator(end), std::make_reverse_iterator(begin), std::not_fn(whitespace)).base();
|
|
||||||
|
|
||||||
auto eq = std::find(begin, end, '=');
|
KITTY_WHILE_LOOP(auto pos = begin, pos != end, {
|
||||||
if(eq == end || eq == begin) {
|
auto comment = std::find(pos, end, '#');
|
||||||
return std::nullopt;
|
auto endl = std::find_if(comment, end, endline);
|
||||||
|
|
||||||
|
result.append(pos, comment);
|
||||||
|
|
||||||
|
pos = endl;
|
||||||
|
})
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class It>
|
||||||
|
It skip_list(It skipper, It end) {
|
||||||
|
int stack = 1;
|
||||||
|
while(skipper != end && stack) {
|
||||||
|
if(*skipper == '[') {
|
||||||
|
++stack;
|
||||||
|
}
|
||||||
|
if(*skipper == ']') {
|
||||||
|
--stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
++skipper;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto end_name = std::find_if(std::make_reverse_iterator(eq), std::make_reverse_iterator(begin), std::not_fn(whitespace)).base();
|
return skipper;
|
||||||
auto begin_val = std::find_if(eq + 1, end, std::not_fn(whitespace));
|
}
|
||||||
|
|
||||||
return std::pair { to_string(begin, end_name), to_string(begin_val, end) };
|
std::pair<
|
||||||
|
std::string_view::const_iterator,
|
||||||
|
std::optional<std::pair<std::string, std::string>>>
|
||||||
|
parse_option(std::string_view::const_iterator begin, std::string_view::const_iterator end) {
|
||||||
|
begin = std::find_if_not(begin, end, whitespace);
|
||||||
|
auto endl = std::find_if(begin, end, endline);
|
||||||
|
auto endc = std::find(begin, endl, '#');
|
||||||
|
endc = std::find_if(std::make_reverse_iterator(endc), std::make_reverse_iterator(begin), std::not_fn(whitespace)).base();
|
||||||
|
|
||||||
|
auto eq = std::find(begin, endc, '=');
|
||||||
|
if(eq == endc || eq == begin) {
|
||||||
|
return std::make_pair(endl, std::nullopt);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto end_name = std::find_if_not(std::make_reverse_iterator(eq), std::make_reverse_iterator(begin), space_tab).base();
|
||||||
|
auto begin_val = std::find_if_not(eq + 1, endc, space_tab);
|
||||||
|
|
||||||
|
if(begin_val == endl) {
|
||||||
|
return std::make_pair(endl, std::nullopt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lists might contain newlines
|
||||||
|
if(*begin_val == '[') {
|
||||||
|
endl = skip_list(begin_val + 1, end);
|
||||||
|
if(endl == end) {
|
||||||
|
std::cout << "Warning: Config option ["sv << to_string(begin, end_name) << "] Missing ']'"sv;
|
||||||
|
|
||||||
|
return std::make_pair(endl, std::nullopt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair(
|
||||||
|
endl,
|
||||||
|
std::make_pair(to_string(begin, end_name), to_string(begin_val, endl)));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unordered_map<std::string, std::string> parse_config(std::string_view file_content) {
|
std::unordered_map<std::string, std::string> parse_config(std::string_view file_content) {
|
||||||
@@ -228,10 +300,14 @@ std::unordered_map<std::string, std::string> parse_config(std::string_view file_
|
|||||||
auto end = std::end(file_content);
|
auto end = std::end(file_content);
|
||||||
|
|
||||||
while(pos < end) {
|
while(pos < end) {
|
||||||
auto newline = std::find_if(pos, end, [](auto ch) { return ch == '\n' || ch == '\r'; });
|
// auto newline = std::find_if(pos, end, [](auto ch) { return ch == '\n' || ch == '\r'; });
|
||||||
auto var = parse_line(pos, newline);
|
TUPLE_2D(endl, var, parse_option(pos, end));
|
||||||
|
|
||||||
|
pos = endl;
|
||||||
|
if(pos != end) {
|
||||||
|
pos += (*pos == '\r') ? 2 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
pos = (*newline == '\r') ? newline + 2 : newline + 1;
|
|
||||||
if(!var) {
|
if(!var) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -368,16 +444,68 @@ void double_between_f(std::unordered_map<std::string, std::string> &vars, const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void list_string_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::vector<std::string> &input) {
|
||||||
|
std::string string;
|
||||||
|
string_f(vars, name, string);
|
||||||
|
|
||||||
|
if(string.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.clear();
|
||||||
|
|
||||||
|
auto begin = std::cbegin(string);
|
||||||
|
if(*begin == '[') {
|
||||||
|
++begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
begin = std::find_if_not(begin, std::cend(string), whitespace);
|
||||||
|
if(begin == std::cend(string)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pos = begin;
|
||||||
|
while(pos < std::cend(string)) {
|
||||||
|
if(*pos == '[') {
|
||||||
|
pos = skip_list(pos + 1, std::cend(string)) + 1;
|
||||||
|
}
|
||||||
|
else if(*pos == ']') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if(*pos == ',') {
|
||||||
|
input.emplace_back(begin, pos);
|
||||||
|
pos = begin = std::find_if_not(pos + 1, std::cend(string), whitespace);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pos != begin) {
|
||||||
|
input.emplace_back(begin, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void list_int_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::vector<int> &input) {
|
||||||
|
std::vector<std::string> list;
|
||||||
|
list_string_f(vars, name, list);
|
||||||
|
|
||||||
|
for(auto &el : list) {
|
||||||
|
input.emplace_back(util::from_view(el));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void print_help(const char *name) {
|
void print_help(const char *name) {
|
||||||
std::cout << "Usage: "sv << name << " [options] [/path/to/configuration_file]"sv << std::endl
|
std::cout
|
||||||
<< " Any configurable option can be overwritten with: \"name=value\""sv << std::endl
|
<< "Usage: "sv << name << " [options] [/path/to/configuration_file]"sv << std::endl
|
||||||
<< std::endl
|
<< " Any configurable option can be overwritten with: \"name=value\""sv << std::endl
|
||||||
<< " --help | print help"sv << std::endl
|
<< std::endl
|
||||||
<< std::endl
|
<< " --help | print help"sv << std::endl
|
||||||
<< " flags"sv << std::endl
|
<< std::endl
|
||||||
<< " -0 | Read PIN from stdin"sv << std::endl
|
<< " flags"sv << std::endl
|
||||||
<< " -1 | Do not load previously saved state and do retain any state after shutdown"sv << std::endl
|
<< " -0 | Read PIN from stdin"sv << std::endl
|
||||||
<< " | Effectively starting as if for the first time without overwriting any pairings with your devices"sv;
|
<< " -1 | Do not load previously saved state and do retain any state after shutdown"sv << std::endl
|
||||||
|
<< " | Effectively starting as if for the first time without overwriting any pairings with your devices"sv;
|
||||||
}
|
}
|
||||||
|
|
||||||
int apply_flags(const char *line) {
|
int apply_flags(const char *line) {
|
||||||
@@ -432,6 +560,8 @@ void apply_config(std::unordered_map<std::string, std::string> &&vars) {
|
|||||||
string_f(vars, "sunshine_name", nvhttp.sunshine_name);
|
string_f(vars, "sunshine_name", nvhttp.sunshine_name);
|
||||||
string_f(vars, "file_state", nvhttp.file_state);
|
string_f(vars, "file_state", nvhttp.file_state);
|
||||||
string_f(vars, "external_ip", nvhttp.external_ip);
|
string_f(vars, "external_ip", nvhttp.external_ip);
|
||||||
|
list_string_f(vars, "resolutions"s, nvhttp.resolutions);
|
||||||
|
list_int_f(vars, "fps"s, nvhttp.fps);
|
||||||
|
|
||||||
string_f(vars, "audio_sink", audio.sink);
|
string_f(vars, "audio_sink", audio.sink);
|
||||||
string_f(vars, "virtual_sink", audio.virtual_sink);
|
string_f(vars, "virtual_sink", audio.virtual_sink);
|
||||||
@@ -536,7 +666,7 @@ int parse(int argc, char *argv[]) {
|
|||||||
config_file = line;
|
config_file = line;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
auto var = parse_line(line, line_end);
|
TUPLE_EL(var, 1, parse_option(line, line_end));
|
||||||
if(!var) {
|
if(!var) {
|
||||||
print_help(*argv);
|
print_help(*argv);
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace config {
|
namespace config {
|
||||||
struct video_t {
|
struct video_t {
|
||||||
@@ -66,6 +67,8 @@ struct nvhttp_t {
|
|||||||
std::string file_state;
|
std::string file_state;
|
||||||
|
|
||||||
std::string external_ip;
|
std::string external_ip;
|
||||||
|
std::vector<std::string> resolutions;
|
||||||
|
std::vector<int> fps;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct input_t {
|
struct input_t {
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
// Created by loki on 6/3/19.
|
// Created by loki on 6/3/19.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
|
||||||
|
|
||||||
#include "process.h"
|
#include "process.h"
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
@@ -511,10 +513,35 @@ void serverinfo(std::shared_ptr<typename SimpleWeb::ServerBase<T>::Response> res
|
|||||||
tree.put("root.ExternalIP", config::nvhttp.external_ip);
|
tree.put("root.ExternalIP", config::nvhttp.external_ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pt::ptree display_nodes;
|
||||||
|
for(auto &resolution : config::nvhttp.resolutions) {
|
||||||
|
auto pred = [](auto ch) { return ch == ' ' || ch == '\t' || ch == 'x'; };
|
||||||
|
|
||||||
|
auto middle = std::find_if(std::begin(resolution), std::end(resolution), pred);
|
||||||
|
if(middle == std::end(resolution)) {
|
||||||
|
BOOST_LOG(warning) << resolution << " is not in the proper format for a resolution: WIDTHxHEIGHT"sv;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto width = util::from_chars(&*std::begin(resolution), &*middle);
|
||||||
|
auto height = util::from_chars(&*(middle + 1), &*std::end(resolution));
|
||||||
|
for(auto fps : config::nvhttp.fps) {
|
||||||
|
pt::ptree display_node;
|
||||||
|
display_node.put("Width", width);
|
||||||
|
display_node.put("Height", height);
|
||||||
|
display_node.put("RefreshRate", fps);
|
||||||
|
|
||||||
|
display_nodes.add_child("DisplayMode", display_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!config::nvhttp.resolutions.empty()) {
|
||||||
|
tree.add_child("root.SupportedDisplayMode", display_nodes);
|
||||||
|
}
|
||||||
auto current_appid = proc::proc.running();
|
auto current_appid = proc::proc.running();
|
||||||
tree.put("root.PairStatus", pair_status);
|
tree.put("root.PairStatus", pair_status);
|
||||||
tree.put("root.currentgame", current_appid >= 0 ? current_appid + 1 : 0);
|
tree.put("root.currentgame", current_appid >= 0 ? current_appid + 1 : 0);
|
||||||
tree.put("root.state", current_appid >= 0 ? "_SERVER_BUSY" : "_SERVER_FREE");
|
tree.put("root.state", current_appid >= 0 ? "SUNSHINE_SERVER_BUSY" : "SUNSHINE_SERVER_FREE");
|
||||||
|
|
||||||
std::ostringstream data;
|
std::ostringstream data;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
// Created by loki on 12/14/19.
|
// Created by loki on 12/14/19.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
|
||||||
|
|
||||||
#include "process.h"
|
#include "process.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|||||||
Reference in New Issue
Block a user