display cursor on Windows

This commit is contained in:
loki 2020-01-10 22:19:50 +01:00
commit e4135a8302
2 changed files with 106 additions and 24 deletions

View file

@ -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

View file

@ -119,15 +119,98 @@ struct img_t : public ::platf::img_t {
};
struct cursor_t {
int x, y;
int width, height;
int pitch;
std::vector<std::uint8_t> 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<display_t> {
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;
}