From e4135a8302a35049793cbe396faab29ce0d16beb Mon Sep 17 00:00:00 2001 From: loki Date: Fri, 10 Jan 2020 22:19:50 +0100 Subject: [PATCH] display cursor on Windows --- CMakeLists.txt | 7 +- sunshine/platform/windows.cpp | 123 ++++++++++++++++++++++++++++------ 2 files changed, 106 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e6252df..68fb7c09 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,13 +5,14 @@ project(Sunshine) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) +add_subdirectory(Simple-Web-Server) +add_subdirectory(moonlight-common-c/enet) + find_package(Threads REQUIRED) find_package(OpenSSL REQUIRED) find_package(FFmpeg REQUIRED) -find_package(Boost COMPONENTS filesystem log REQUIRED) +find_package(Boost COMPONENTS log filesystem REQUIRED) -add_subdirectory(Simple-Web-Server) -add_subdirectory(moonlight-common-c/enet) if(WIN32) set(PLATFORM_TARGET_FILES diff --git a/sunshine/platform/windows.cpp b/sunshine/platform/windows.cpp index ba9cd283..383d6e66 100644 --- a/sunshine/platform/windows.cpp +++ b/sunshine/platform/windows.cpp @@ -119,15 +119,98 @@ struct img_t : public ::platf::img_t { }; struct cursor_t { - int x, y; - int width, height; - int pitch; - std::vector img_data; - bool visible; + DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info; + int x, y; + bool visible; }; +void blend_cursor_monochrome(const cursor_t &cursor, img_t &img) { + int height = cursor.shape_info.Height / 2; + int width = cursor.shape_info.Width; + int pitch = cursor.shape_info.Pitch; + + int delta_height = std::min(height, std::max(0, img.height - cursor.y)); + int delta_width = std::min(width, std::max(0, img.width - cursor.x)); + + auto pixels_per_byte = width / pitch; + auto bytes_per_row = delta_width / pixels_per_byte; + + auto img_data = (int*)img.data; + for(int i = 0; i < delta_height; ++i) { + auto and_mask = &cursor.img_data[i * pitch]; + auto xor_mask = &cursor.img_data[(i + height) * pitch]; + + auto img_pixel_p = &img_data[(i + cursor.y) * img.width + cursor.x]; + + for(int x = 0; x < bytes_per_row; ++x) { + for(auto bit = 0u; bit < 8; ++bit) { + int and_ = *and_mask & (1 << (7 - bit)) ? -1 : 0; + int xor_ = *xor_mask & (1 << (7 - bit)) ? -1 : 0; + + *img_pixel_p &= and_; + *img_pixel_p ^= xor_; + + ++img_pixel_p; + } + + ++and_mask; + ++xor_mask; + } + } +} + +void blend_cursor_color(const cursor_t &cursor, img_t &img) { + int height = cursor.shape_info.Height; + int width = cursor.shape_info.Width; + int pitch = cursor.shape_info.Pitch; + + int delta_height = std::min(height, std::max(0, img.height - cursor.y)); + int delta_width = std::min(width, std::max(0, img.width - cursor.x)); + + auto img_data = (int*)img.data; + auto size_of_pixel = pitch / width; + BOOST_LOG(info) << "size_of_pixel ["sv << size_of_pixel << ']'; + for(int i = 0; i < delta_height; ++i) { + auto cursor_begin = &cursor.img_data[i * pitch]; + auto cursor_end = cursor_begin + delta_width * size_of_pixel; + + auto img_pixel_p = &img_data[(i + cursor.y) * img.width + cursor.x]; + for(auto cursor_p = cursor_begin; cursor_p != cursor_end; cursor_p += size_of_pixel) { + auto colors_in = (uint8_t*)img_pixel_p; + + //TODO: When use of IDXGIOutput5 is implemented, support different color formats + auto alpha = cursor_p[0]; + if(alpha == 255) { + colors_in[0] = cursor_p[3]; + colors_in[1] = cursor_p[2]; + colors_in[2] = cursor_p[1]; + } + else { + colors_in[0] = cursor_p[3] + (colors_in[0] * (255 - alpha) + 255/2) / 255; + colors_in[1] = cursor_p[2] + (colors_in[1] * (255 - alpha) + 255/2) / 255; + colors_in[2] = cursor_p[1] + (colors_in[2] * (255 - alpha) + 255/2) / 255; + } + ++img_pixel_p; + } + } +} + +void blend_cursor(const cursor_t &cursor, img_t &img) { + switch(cursor.shape_info.Type) { + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: + blend_cursor_color(cursor, img); + break; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME: + blend_cursor_monochrome(cursor, img); + break; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: + default: + BOOST_LOG(warning) << "Unsupported cursor format ["sv << cursor.shape_info.Type << ']'; + } +} + class display_t : public ::platf::display_t, public std::enable_shared_from_this { public: capture_e snapshot(std::unique_ptr<::platf::img_t> &img_base, bool cursor_visible) override { @@ -145,28 +228,26 @@ public: } if (frame_info.PointerShapeBufferSize > 0) { - cursor.img_data.resize(frame_info.PointerShapeBufferSize); - DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info; - auto &img_data = cursor.img_data; + img_data.resize(frame_info.PointerShapeBufferSize); + UINT dummy; - status = dup.dup->GetFramePointerShape(img_data.size(), img_data.data(), &dummy, &shape_info); + status = dup.dup->GetFramePointerShape(img_data.size(), img_data.data(), &dummy, &cursor.shape_info); if (FAILED(status)) { BOOST_LOG(error) << "Failed to get new pointer shape [0x"sv << util::hex(status).to_string_view() << ']'; return capture_e::error; } - - if (shape_info.Type != DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR) { - BOOST_LOG(warning) << "Unsupported cursor format ["sv << shape_info.Type << ']'; - } - - cursor.width = shape_info.Width; - cursor.height = shape_info.Height; - cursor.pitch = shape_info.Pitch; } + if(frame_info.LastMouseUpdateTime.QuadPart) { + cursor.x = frame_info.PointerPosition.Position.x; + cursor.y = frame_info.PointerPosition.Position.y; + cursor.visible = frame_info.PointerPosition.Visible; + } + + // If frame has not been updated, skip if (frame_info.LastPresentTime.QuadPart == 0) { return capture_e::timeout; } @@ -185,10 +266,6 @@ public: device_ctx->CopyResource((ID3D11Resource *) img->texture.get(), (ID3D11Resource *) src.get()); } - cursor.x = frame_info.PointerPosition.Position.x; - cursor.y = frame_info.PointerPosition.Position.y; - cursor.visible = frame_info.PointerPosition.Visible; - status = device_ctx->Map((ID3D11Resource *) img->texture.get(), 0, D3D11_MAP_READ, 0, &img->map); if (FAILED(status)) { if (status == DXGI_ERROR_WAS_STILL_DRAWING) { @@ -204,6 +281,10 @@ public: img->width = width; img->height = height; + if(cursor_visible && cursor.visible) { + blend_cursor(cursor, *img); + } + return capture_e::ok; }