diff --git a/sunshine/platform/linux.cpp b/sunshine/platform/linux.cpp index 5efeb0b6..831dbb9a 100644 --- a/sunshine/platform/linux.cpp +++ b/sunshine/platform/linux.cpp @@ -3,6 +3,7 @@ // #include "common.h" +#include "../main.h" #include #include @@ -22,6 +23,7 @@ #include #include +#include namespace platf { using namespace std::literals; @@ -57,6 +59,7 @@ class shm_data_t { public: shm_data_t() : data {(void*)-1 } {} shm_data_t(void *data) : data {data } {} + shm_data_t(shm_data_t &&other) noexcept : data(other.data) { other.data = (void*)-1; } @@ -130,21 +133,21 @@ void blend_cursor(Display *display, std::uint8_t *img_data, int width, int heigh } } struct x11_attr_t : public display_t { - x11_attr_t() : display {XOpenDisplay(nullptr) }, window {DefaultRootWindow(display.get()) }, attr {} { + x11_attr_t() : xdisplay {XOpenDisplay(nullptr) }, xwindow {DefaultRootWindow(xdisplay.get()) }, xattr {} { refresh(); } void refresh() { - XGetWindowAttributes(display.get(), window, &attr); + XGetWindowAttributes(xdisplay.get(), xwindow, &xattr); } - std::unique_ptr snapshot(bool cursor) { + std::unique_ptr snapshot(bool cursor) override { refresh(); XImage *img { XGetImage( - display.get(), - window, + xdisplay.get(), + xwindow, 0, 0, - attr.width, attr.height, + xattr.width, xattr.height, AllPlanes, ZPixmap) }; @@ -152,18 +155,17 @@ struct x11_attr_t : public display_t { return std::make_unique((std::uint8_t*)img->data, img->width, img->height, img); } - blend_cursor(display.get(), (std::uint8_t*)img->data, img->width, img->height); + blend_cursor(xdisplay.get(), (std::uint8_t*)img->data, img->width, img->height); return std::make_unique((std::uint8_t*)img->data, img->width, img->height, img); } - xdisplay_t display; - Window window; - XWindowAttributes attr; + xdisplay_t xdisplay; + Window xwindow; + XWindowAttributes xattr; }; -struct shm_attr_t : display_t { - xdisplay_t xdisplay; - +struct shm_attr_t : public x11_attr_t { + xdisplay_t shm_xdisplay; // Prevent race condition with x11_attr_t::xdisplay xcb_connect_t xcb; xcb_screen_t *display; std::uint32_t seg; @@ -172,7 +174,31 @@ struct shm_attr_t : display_t { shm_data_t data; + util::TaskPool::task_id_t refresh_task_id; + void delayed_refresh() { + refresh(); + + refresh_task_id = task_pool.pushDelayed(&shm_attr_t::delayed_refresh, 1s, this).task_id; + } + + shm_attr_t() : x11_attr_t(), shm_xdisplay {XOpenDisplay(nullptr) } { + refresh_task_id = task_pool.pushDelayed(&shm_attr_t::delayed_refresh, 1s, this).task_id; + } + + ~shm_attr_t() { + while(!task_pool.cancel(refresh_task_id)); + } + std::unique_ptr snapshot(bool cursor) override { + if(display->width_in_pixels != xattr.width || display->height_in_pixels != xattr.height) { + deinit(); + if(init()) { + std::cout << "FATAL ERROR: Couldn't reinitialize shm_attr_t"sv << std::endl; + + std::abort(); + } + } + auto img_cookie = xcb_shm_get_image_unchecked( xcb.get(), display->root, @@ -186,8 +212,8 @@ struct shm_attr_t : display_t { xcb_img_t img_reply { xcb_shm_get_image_reply(xcb.get(), img_cookie, nullptr) }; if(!img_reply) { - std::cout << "FATAL ERROR: Could not get image reply"sv << std::endl; - std::abort(); + std::cout << "Info: Could not get image reply"sv << std::endl; + return nullptr; } auto img = std::make_unique(); @@ -201,11 +227,52 @@ struct shm_attr_t : display_t { return img; } - blend_cursor(xdisplay.get(), img->data, img->width, img->height); + blend_cursor(shm_xdisplay.get(), img->data, img->width, img->height); return img; } + void deinit() { + data.~shm_data_t(); + shm_id.~shm_id_t(); + xcb.reset(nullptr); + } + + int init() { + shm_xdisplay.reset(XOpenDisplay(nullptr)); + xcb.reset(xcb_connect(nullptr, nullptr)); + if(xcb_connection_has_error(xcb.get())) { + return -1; + } + + if(!xcb_get_extension_data(xcb.get(), &xcb_shm_id)->present) { + std::cout << "Missing SHM extension"sv << std::endl; + + return -1; + } + + auto iter = xcb_setup_roots_iterator(xcb_get_setup(xcb.get())); + display = iter.data; + seg = xcb_generate_id(xcb.get()); + + shm_id.id = shmget(IPC_PRIVATE, frame_size(), IPC_CREAT | 0777); + if(shm_id.id == -1) { + std::cout << "shmget failed"sv << std::endl; + return -1; + } + + xcb_shm_attach(xcb.get(), seg, shm_id.id, false); + data.data = shmat(shm_id.id, nullptr, 0); + + if ((uintptr_t)data.data == -1) { + std::cout << "shmat failed"sv << std::endl; + + return -1; + } + + return 0; + } + std::uint32_t frame_size() { return display->height_in_pixels * display->width_in_pixels * 4; } @@ -233,34 +300,7 @@ struct mic_attr_t : public mic_t { std::unique_ptr shm_display() { auto shm = std::make_unique(); - shm->xdisplay.reset(XOpenDisplay(nullptr)); - shm->xcb.reset(xcb_connect(nullptr, nullptr)); - if(xcb_connection_has_error(shm->xcb.get())) { - return nullptr; - } - - if(!xcb_get_extension_data(shm->xcb.get(), &xcb_shm_id)->present) { - std::cout << "Missing SHM extension"sv << std::endl; - - return nullptr; - } - - auto iter = xcb_setup_roots_iterator(xcb_get_setup(shm->xcb.get())); - shm->display = iter.data; - shm->seg = xcb_generate_id(shm->xcb.get()); - - shm->shm_id.id = shmget(IPC_PRIVATE, shm->frame_size(), IPC_CREAT | 0777); - if(shm->shm_id.id == -1) { - std::cout << "shmget failed"sv << std::endl; - return nullptr; - } - - xcb_shm_attach(shm->xcb.get(), shm->seg, shm->shm_id.id, false); - shm->data.data = shmat(shm->shm_id.id, nullptr, 0); - - if ((uintptr_t)shm->data.data == -1) { - std::cout << "shmat failed"sv << std::endl; - + if(shm->init()) { return nullptr; }