mirror of
https://github.com/LizardByte/Sunshine.git
synced 2025-02-28 12:40:33 +00:00
Switch monitors based on keyboard shortcuts
This commit is contained in:
parent
c243e82047
commit
2af179630a
@ -97,6 +97,7 @@ sunshine needs access to uinput to create mouse and gamepad events:
|
|||||||
|
|
||||||
All shortcuts start with CTRL + ALT + SHIFT, just like Moonlight
|
All shortcuts start with CTRL + ALT + SHIFT, just like Moonlight
|
||||||
- CTRL + ALT + SHIFT + N --> Hide/Unhide the cursor (This may be usefull for Remote Desktop Mode for Moonlight)
|
- CTRL + ALT + SHIFT + N --> Hide/Unhide the cursor (This may be usefull for Remote Desktop Mode for Moonlight)
|
||||||
|
- CTRL + ALT + SHIFT + F1/F13 --> Switch to different monitor for Streaming
|
||||||
|
|
||||||
## Note:
|
## Note:
|
||||||
- The Windows key is not passed through by Moonlight, therefore Sunshine maps Right-Alt key to the Windows key
|
- The Windows key is not passed through by Moonlight, therefore Sunshine maps Right-Alt key to the Windows key
|
||||||
|
@ -136,7 +136,16 @@ struct input_t {
|
|||||||
* return 0
|
* return 0
|
||||||
*/
|
*/
|
||||||
inline int apply_shortcut(short keyCode) {
|
inline int apply_shortcut(short keyCode) {
|
||||||
|
constexpr auto VK_F1 = 0x70;
|
||||||
|
constexpr auto VK_F13 = 0x7C;
|
||||||
|
|
||||||
BOOST_LOG(debug) << "Apply Shortcut: 0x"sv << util::hex((std::uint8_t)keyCode).to_string_view();
|
BOOST_LOG(debug) << "Apply Shortcut: 0x"sv << util::hex((std::uint8_t)keyCode).to_string_view();
|
||||||
|
|
||||||
|
if(keyCode >= VK_F1 && keyCode <= VK_F13) {
|
||||||
|
mail::man->event<int>(mail::switch_display)->raise(keyCode - VK_F1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
switch(keyCode) {
|
switch(keyCode) {
|
||||||
case 0x4E /* VKEY_N */:
|
case 0x4E /* VKEY_N */:
|
||||||
display_cursor = !display_cursor;
|
display_cursor = !display_cursor;
|
||||||
|
@ -44,6 +44,8 @@ MAIL(broadcast_shutdown);
|
|||||||
MAIL(video_packets);
|
MAIL(video_packets);
|
||||||
MAIL(audio_packets);
|
MAIL(audio_packets);
|
||||||
|
|
||||||
|
MAIL(switch_display);
|
||||||
|
|
||||||
// Local mail
|
// Local mail
|
||||||
MAIL(touch_port);
|
MAIL(touch_port);
|
||||||
MAIL(idr);
|
MAIL(idr);
|
||||||
|
@ -271,7 +271,18 @@ std::string from_sockaddr(const sockaddr *const);
|
|||||||
std::pair<std::uint16_t, std::string> from_sockaddr_ex(const sockaddr *const);
|
std::pair<std::uint16_t, std::string> from_sockaddr_ex(const sockaddr *const);
|
||||||
|
|
||||||
std::unique_ptr<audio_control_t> audio_control();
|
std::unique_ptr<audio_control_t> audio_control();
|
||||||
std::shared_ptr<display_t> display(mem_type_e hwdevice_type, int framerate);
|
|
||||||
|
/**
|
||||||
|
* display_name --> The name of the monitor that SHOULD be displayed
|
||||||
|
* If display_name is empty --> Use the first monitor that's compatible you can find
|
||||||
|
* If you require to use this parameter in a seperate thread --> make a copy of it.
|
||||||
|
*
|
||||||
|
* framerate --> The peak number of images per second
|
||||||
|
*
|
||||||
|
* Returns display_t based on hwdevice_type
|
||||||
|
*/
|
||||||
|
std::shared_ptr<display_t> display(mem_type_e hwdevice_type, const std::string &display_name, int framerate);
|
||||||
|
std::vector<std::string> display_names();
|
||||||
|
|
||||||
input_t input();
|
input_t input();
|
||||||
void move_mouse(input_t &input, int deltaX, int deltaY);
|
void move_mouse(input_t &input, int deltaX, int deltaY);
|
||||||
|
@ -155,7 +155,7 @@ struct x11_attr_t : public display_t {
|
|||||||
XInitThreads();
|
XInitThreads();
|
||||||
}
|
}
|
||||||
|
|
||||||
int init(int framerate) {
|
int init(int framerate, const std::string &output_name) {
|
||||||
if(!xdisplay) {
|
if(!xdisplay) {
|
||||||
BOOST_LOG(error) << "Could not open X11 display"sv;
|
BOOST_LOG(error) << "Could not open X11 display"sv;
|
||||||
return -1;
|
return -1;
|
||||||
@ -168,8 +168,8 @@ struct x11_attr_t : public display_t {
|
|||||||
refresh();
|
refresh();
|
||||||
|
|
||||||
int streamedMonitor = -1;
|
int streamedMonitor = -1;
|
||||||
if(!config::video.output_name.empty()) {
|
if(!output_name.empty()) {
|
||||||
streamedMonitor = (int)util::from_view(config::video.output_name);
|
streamedMonitor = (int)util::from_view(output_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(streamedMonitor != -1) {
|
if(streamedMonitor != -1) {
|
||||||
@ -399,8 +399,8 @@ struct shm_attr_t : public x11_attr_t {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int init(int framerate) {
|
int init(int framerate, const std::string &output_name) {
|
||||||
if(x11_attr_t::init(framerate)) {
|
if(x11_attr_t::init(framerate, output_name)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,7 +443,7 @@ struct shm_attr_t : public x11_attr_t {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<display_t> display(platf::mem_type_e hwdevice_type, int framerate) {
|
std::shared_ptr<display_t> display(platf::mem_type_e hwdevice_type, const std::string &output_name, int framerate) {
|
||||||
if(hwdevice_type != platf::mem_type_e::system && hwdevice_type != platf::mem_type_e::vaapi && hwdevice_type != platf::mem_type_e::cuda) {
|
if(hwdevice_type != platf::mem_type_e::system && hwdevice_type != platf::mem_type_e::vaapi && hwdevice_type != platf::mem_type_e::cuda) {
|
||||||
BOOST_LOG(error) << "Could not initialize display with the given hw device type."sv;
|
BOOST_LOG(error) << "Could not initialize display with the given hw device type."sv;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -452,7 +452,7 @@ std::shared_ptr<display_t> display(platf::mem_type_e hwdevice_type, int framerat
|
|||||||
// Attempt to use shared memory X11 to avoid copying the frame
|
// Attempt to use shared memory X11 to avoid copying the frame
|
||||||
auto shm_disp = std::make_shared<shm_attr_t>(hwdevice_type);
|
auto shm_disp = std::make_shared<shm_attr_t>(hwdevice_type);
|
||||||
|
|
||||||
auto status = shm_disp->init(framerate);
|
auto status = shm_disp->init(framerate, output_name);
|
||||||
if(status > 0) {
|
if(status > 0) {
|
||||||
// x11_attr_t::init() failed, don't bother trying again.
|
// x11_attr_t::init() failed, don't bother trying again.
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -464,13 +464,44 @@ std::shared_ptr<display_t> display(platf::mem_type_e hwdevice_type, int framerat
|
|||||||
|
|
||||||
// Fallback
|
// Fallback
|
||||||
auto x11_disp = std::make_shared<x11_attr_t>(hwdevice_type);
|
auto x11_disp = std::make_shared<x11_attr_t>(hwdevice_type);
|
||||||
if(x11_disp->init(framerate)) {
|
if(x11_disp->init(framerate, output_name)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return x11_disp;
|
return x11_disp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> display_names() {
|
||||||
|
BOOST_LOG(info) << "Detecting connected monitors"sv;
|
||||||
|
|
||||||
|
xdisplay_t xdisplay { XOpenDisplay(nullptr) };
|
||||||
|
if(!xdisplay) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xwindow = DefaultRootWindow(xdisplay.get());
|
||||||
|
screen_res_t screenr { XRRGetScreenResources(xdisplay.get(), xwindow) };
|
||||||
|
int output = screenr->noutput;
|
||||||
|
|
||||||
|
int monitor = 0;
|
||||||
|
for(int x = 0; x < output; ++x) {
|
||||||
|
output_info_t out_info { XRRGetOutputInfo(xdisplay.get(), screenr.get(), screenr->outputs[x]) };
|
||||||
|
if(out_info && out_info->connection == RR_Connected) {
|
||||||
|
++monitor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> names;
|
||||||
|
names.reserve(monitor);
|
||||||
|
|
||||||
|
for(auto x = 0; x < monitor; ++x) {
|
||||||
|
BOOST_LOG(fatal) << x;
|
||||||
|
names.emplace_back(std::to_string(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
void freeImage(XImage *p) {
|
void freeImage(XImage *p) {
|
||||||
XDestroyImage(p);
|
XDestroyImage(p);
|
||||||
}
|
}
|
||||||
|
@ -551,11 +551,11 @@ static std::vector<encoder_t> encoders {
|
|||||||
software
|
software
|
||||||
};
|
};
|
||||||
|
|
||||||
void reset_display(std::shared_ptr<platf::display_t> &disp, AVHWDeviceType type, int framerate) {
|
void reset_display(std::shared_ptr<platf::display_t> &disp, AVHWDeviceType type, const std::string &display_name, int framerate) {
|
||||||
// We try this twice, in case we still get an error on reinitialization
|
// We try this twice, in case we still get an error on reinitialization
|
||||||
for(int x = 0; x < 2; ++x) {
|
for(int x = 0; x < 2; ++x) {
|
||||||
disp.reset();
|
disp.reset();
|
||||||
disp = platf::display(map_dev_type(type), framerate);
|
disp = platf::display(map_dev_type(type), display_name, framerate);
|
||||||
if(disp) {
|
if(disp) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -583,11 +583,30 @@ void captureThread(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
auto switch_display_event = mail::man->event<int>(mail::switch_display);
|
||||||
|
|
||||||
|
// Get all the monitor names now, rather than at boot, to
|
||||||
|
// get the most up-to-date list available monitors
|
||||||
|
auto display_names = platf::display_names();
|
||||||
|
int display_p = 0;
|
||||||
|
|
||||||
|
if(display_names.empty()) {
|
||||||
|
display_names.emplace_back(config::video.output_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int x = 0; x < display_names.size(); ++x) {
|
||||||
|
if(display_names[x] == config::video.output_name) {
|
||||||
|
display_p = x;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(auto capture_ctx = capture_ctx_queue->pop()) {
|
if(auto capture_ctx = capture_ctx_queue->pop()) {
|
||||||
capture_ctxs.emplace_back(std::move(*capture_ctx));
|
capture_ctxs.emplace_back(std::move(*capture_ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto disp = platf::display(map_dev_type(encoder.dev_type), capture_ctxs.front().framerate);
|
auto disp = platf::display(map_dev_type(encoder.dev_type), display_names[display_p], capture_ctxs.front().framerate);
|
||||||
if(!disp) {
|
if(!disp) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -605,11 +624,9 @@ void captureThread(
|
|||||||
}
|
}
|
||||||
|
|
||||||
while(capture_ctx_queue->running()) {
|
while(capture_ctx_queue->running()) {
|
||||||
auto status = disp->capture([&](std::shared_ptr<platf::img_t> &img) -> std::shared_ptr<platf::img_t> {
|
bool artificial_reinit = false;
|
||||||
while(capture_ctx_queue->peek()) {
|
|
||||||
capture_ctxs.emplace_back(std::move(*capture_ctx_queue->pop()));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
auto status = disp->capture([&](std::shared_ptr<platf::img_t> &img) -> std::shared_ptr<platf::img_t> {
|
||||||
KITTY_WHILE_LOOP(auto capture_ctx = std::begin(capture_ctxs), capture_ctx != std::end(capture_ctxs), {
|
KITTY_WHILE_LOOP(auto capture_ctx = std::begin(capture_ctxs), capture_ctx != std::end(capture_ctxs), {
|
||||||
if(!capture_ctx->images->running()) {
|
if(!capture_ctx->images->running()) {
|
||||||
capture_ctx = capture_ctxs.erase(capture_ctx);
|
capture_ctx = capture_ctxs.erase(capture_ctx);
|
||||||
@ -624,6 +641,16 @@ void captureThread(
|
|||||||
if(!capture_ctx_queue->running()) {
|
if(!capture_ctx_queue->running()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
while(capture_ctx_queue->peek()) {
|
||||||
|
capture_ctxs.emplace_back(std::move(*capture_ctx_queue->pop()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(switch_display_event->peek()) {
|
||||||
|
artificial_reinit = true;
|
||||||
|
|
||||||
|
display_p = std::clamp(*switch_display_event->pop(), 0, (int)display_names.size() - 1);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
auto &next_img = *round_robin++;
|
auto &next_img = *round_robin++;
|
||||||
while(next_img.use_count() > 1) {}
|
while(next_img.use_count() > 1) {}
|
||||||
@ -633,6 +660,12 @@ void captureThread(
|
|||||||
*round_robin++, &display_cursor);
|
*round_robin++, &display_cursor);
|
||||||
|
|
||||||
|
|
||||||
|
if(artificial_reinit && status != platf::capture_e::error) {
|
||||||
|
status = platf::capture_e::reinit;
|
||||||
|
|
||||||
|
artificial_reinit = false;
|
||||||
|
}
|
||||||
|
|
||||||
switch(status) {
|
switch(status) {
|
||||||
case platf::capture_e::reinit: {
|
case platf::capture_e::reinit: {
|
||||||
reinit_event.raise(true);
|
reinit_event.raise(true);
|
||||||
@ -651,7 +684,7 @@ void captureThread(
|
|||||||
}
|
}
|
||||||
|
|
||||||
while(capture_ctx_queue->running()) {
|
while(capture_ctx_queue->running()) {
|
||||||
reset_display(disp, encoder.dev_type, capture_ctxs.front().framerate);
|
reset_display(disp, encoder.dev_type, display_names[display_p], capture_ctxs.front().framerate);
|
||||||
|
|
||||||
if(disp) {
|
if(disp) {
|
||||||
break;
|
break;
|
||||||
@ -1080,11 +1113,17 @@ std::optional<sync_session_t> make_synced_session(platf::display_t *disp, const
|
|||||||
return std::move(encode_session);
|
return std::move(encode_session);
|
||||||
}
|
}
|
||||||
|
|
||||||
encode_e encode_run_sync(std::vector<std::unique_ptr<sync_session_ctx_t>> &synced_session_ctxs, encode_session_ctx_queue_t &encode_session_ctx_queue) {
|
encode_e encode_run_sync(
|
||||||
|
std::vector<std::unique_ptr<sync_session_ctx_t>> &synced_session_ctxs,
|
||||||
|
encode_session_ctx_queue_t &encode_session_ctx_queue,
|
||||||
|
int &display_p, const std::vector<std::string> &display_names) {
|
||||||
|
|
||||||
const auto &encoder = encoders.front();
|
const auto &encoder = encoders.front();
|
||||||
|
|
||||||
std::shared_ptr<platf::display_t> disp;
|
std::shared_ptr<platf::display_t> disp;
|
||||||
|
|
||||||
|
auto switch_display_event = mail::man->event<int>(mail::switch_display);
|
||||||
|
|
||||||
if(synced_session_ctxs.empty()) {
|
if(synced_session_ctxs.empty()) {
|
||||||
auto ctx = encode_session_ctx_queue.pop();
|
auto ctx = encode_session_ctx_queue.pop();
|
||||||
if(!ctx) {
|
if(!ctx) {
|
||||||
@ -1097,7 +1136,7 @@ encode_e encode_run_sync(std::vector<std::unique_ptr<sync_session_ctx_t>> &synce
|
|||||||
int framerate = synced_session_ctxs.front()->config.framerate;
|
int framerate = synced_session_ctxs.front()->config.framerate;
|
||||||
|
|
||||||
while(encode_session_ctx_queue.running()) {
|
while(encode_session_ctx_queue.running()) {
|
||||||
reset_display(disp, encoder.dev_type, framerate);
|
reset_display(disp, encoder.dev_type, display_names[display_p], framerate);
|
||||||
if(disp) {
|
if(disp) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1190,6 +1229,13 @@ encode_e encode_run_sync(std::vector<std::unique_ptr<sync_session_ctx_t>> &synce
|
|||||||
++pos;
|
++pos;
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if(switch_display_event->peek()) {
|
||||||
|
ec = platf::capture_e::reinit;
|
||||||
|
|
||||||
|
display_p = std::clamp(*switch_display_event->pop(), 0, (int)display_names.size() - 1);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
return img;
|
return img;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1226,7 +1272,22 @@ void captureThreadSync() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
while(encode_run_sync(synced_session_ctxs, ctx) == encode_e::reinit) {}
|
auto display_names = platf::display_names();
|
||||||
|
int display_p = 0;
|
||||||
|
|
||||||
|
if(display_names.empty()) {
|
||||||
|
display_names.emplace_back(config::video.output_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int x = 0; x < display_names.size(); ++x) {
|
||||||
|
if(display_names[x] == config::video.output_name) {
|
||||||
|
display_p = x;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while(encode_run_sync(synced_session_ctxs, ctx, display_p, display_names) == encode_e::reinit) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
void capture_async(
|
void capture_async(
|
||||||
@ -1338,7 +1399,7 @@ enum validate_flag_e {
|
|||||||
};
|
};
|
||||||
|
|
||||||
int validate_config(std::shared_ptr<platf::display_t> &disp, const encoder_t &encoder, const config_t &config) {
|
int validate_config(std::shared_ptr<platf::display_t> &disp, const encoder_t &encoder, const config_t &config) {
|
||||||
reset_display(disp, encoder.dev_type, config.framerate);
|
reset_display(disp, encoder.dev_type, config::video.output_name, config.framerate);
|
||||||
if(!disp) {
|
if(!disp) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user