Implements #5215: Smart sizing using libcairo

This commit is contained in:
Armin Novak
2019-01-25 11:47:15 +01:00
parent 306427977d
commit 8d6a6df48a
8 changed files with 206 additions and 27 deletions

View File

@@ -37,9 +37,19 @@ set(${MODULE_PREFIX}_SRCS
wlf_channels.h
)
find_package(Cairo)
list (APPEND ${MODULE_PREFIX}_LIBS freerdp-client freerdp uwac)
if (CAIRO_FOUND)
add_definitions(-DCAIRO_FOUND=1)
include_directories(${CAIRO_INCLUDE_DIR})
list(APPEND ${MODULE_PREFIX}_LIBS ${CAIRO_LIBRARY})
else(CAIRO_FOUND)
message(WARNING "libcairo not detected, compiling without wayland smart scaling support!")
endif(CAIRO_FOUND)
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp-client freerdp uwac)
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT client)

View File

@@ -23,33 +23,57 @@
#include <freerdp/locale/keyboard.h>
#include "wlfreerdp.h"
#include "wlf_input.h"
BOOL wlf_handle_pointer_enter(freerdp* instance, UwacPointerEnterLeaveEvent* ev)
BOOL wlf_handle_pointer_enter(freerdp* instance, const UwacPointerEnterLeaveEvent* ev)
{
uint32_t x, y;
if (!instance || !ev || !instance->input)
return FALSE;
return freerdp_input_send_mouse_event(instance->input, PTR_FLAGS_MOVE, ev->x, ev->y);
x = ev->x;
y = ev->y;
if (!wlf_scale_coordinates(instance->context, &x, &y))
return FALSE;
return freerdp_input_send_mouse_event(instance->input, PTR_FLAGS_MOVE, x, y);
}
BOOL wlf_handle_pointer_motion(freerdp* instance, UwacPointerMotionEvent* ev)
BOOL wlf_handle_pointer_motion(freerdp* instance, const UwacPointerMotionEvent* ev)
{
uint32_t x, y;
if (!instance || !ev || !instance->input)
return FALSE;
return freerdp_input_send_mouse_event(instance->input, PTR_FLAGS_MOVE, ev->x, ev->y);
x = ev->x;
y = ev->y;
if (!wlf_scale_coordinates(instance->context, &x, &y))
return FALSE;
return freerdp_input_send_mouse_event(instance->input, PTR_FLAGS_MOVE, x, y);
}
BOOL wlf_handle_pointer_buttons(freerdp* instance, UwacPointerButtonEvent* ev)
BOOL wlf_handle_pointer_buttons(freerdp* instance, const UwacPointerButtonEvent* ev)
{
rdpInput* input;
UINT16 flags = 0;
UINT16 xflags = 0;
uint32_t x, y;
if (!instance || !ev || !instance->input)
return FALSE;
x = ev->x;
y = ev->y;
if (!wlf_scale_coordinates(instance->context, &x, &y))
return FALSE;
input = instance->input;
if (ev->state == WL_POINTER_BUTTON_STATE_PRESSED)
@@ -85,24 +109,31 @@ BOOL wlf_handle_pointer_buttons(freerdp* instance, UwacPointerButtonEvent* ev)
}
if ((flags & ~PTR_FLAGS_DOWN) != 0)
return freerdp_input_send_mouse_event(input, flags, ev->x, ev->y);
return freerdp_input_send_mouse_event(input, flags, x, y);
if ((xflags & ~PTR_XFLAGS_DOWN) != 0)
return freerdp_input_send_extended_mouse_event(input, xflags, ev->x, ev->y);
return freerdp_input_send_extended_mouse_event(input, xflags, x, y);
return FALSE;
}
BOOL wlf_handle_pointer_axis(freerdp* instance, UwacPointerAxisEvent* ev)
BOOL wlf_handle_pointer_axis(freerdp* instance, const UwacPointerAxisEvent* ev)
{
rdpInput* input;
UINT16 flags = 0;
int direction;
uint32_t x, y;
if (!instance || !ev || !instance->input)
return FALSE;
x = ev->x;
y = ev->y;
if (!wlf_scale_coordinates(instance->context, &x, &y))
return FALSE;
input = instance->input;
switch (ev->axis)
@@ -125,10 +156,10 @@ BOOL wlf_handle_pointer_axis(freerdp* instance, UwacPointerAxisEvent* ev)
if (direction < 0)
flags |= PTR_FLAGS_WHEEL_NEGATIVE;
return freerdp_input_send_mouse_event(input, flags, ev->x, ev->y);
return freerdp_input_send_mouse_event(input, flags, x, y);
}
BOOL wlf_handle_key(freerdp* instance, UwacKeyEvent* ev)
BOOL wlf_handle_key(freerdp* instance, const UwacKeyEvent* ev)
{
rdpInput* input;
DWORD rdp_scancode;
@@ -145,7 +176,7 @@ BOOL wlf_handle_key(freerdp* instance, UwacKeyEvent* ev)
return freerdp_input_send_keyboard_event_ex(input, ev->pressed, rdp_scancode);
}
BOOL wlf_keyboard_enter(freerdp* instance, UwacKeyboardEnterLeaveEvent* ev)
BOOL wlf_keyboard_enter(freerdp* instance, const UwacKeyboardEnterLeaveEvent* ev)
{
rdpInput* input;

View File

@@ -27,12 +27,12 @@
#include <uwac/uwac.h>
BOOL wlf_handle_pointer_enter(freerdp* instance,
UwacPointerEnterLeaveEvent* ev);
BOOL wlf_handle_pointer_motion(freerdp* instance, UwacPointerMotionEvent* ev);
BOOL wlf_handle_pointer_buttons(freerdp* instance, UwacPointerButtonEvent* ev);
BOOL wlf_handle_pointer_axis(freerdp* instance, UwacPointerAxisEvent* ev);
const UwacPointerEnterLeaveEvent* ev);
BOOL wlf_handle_pointer_motion(freerdp* instance, const UwacPointerMotionEvent* ev);
BOOL wlf_handle_pointer_buttons(freerdp* instance, const UwacPointerButtonEvent* ev);
BOOL wlf_handle_pointer_axis(freerdp* instance, const UwacPointerAxisEvent* ev);
BOOL wlf_handle_key(freerdp* instance, UwacKeyEvent* ev);
BOOL wlf_keyboard_enter(freerdp* instance, UwacKeyboardEnterLeaveEvent* ev);
BOOL wlf_handle_key(freerdp* instance, const UwacKeyEvent* ev);
BOOL wlf_keyboard_enter(freerdp* instance, const UwacKeyboardEnterLeaveEvent* ev);
#endif /* FREERDP_CLIENT_WAYLAND_INPUT_H */

View File

@@ -22,6 +22,7 @@
#include <stdio.h>
#include <errno.h>
#include <locale.h>
#include <float.h>
#include <freerdp/client/cmdline.h>
#include <freerdp/channels/channels.h>
@@ -33,6 +34,9 @@
#include <linux/input.h>
#include <uwac/uwac.h>
#if defined(CAIRO_FOUND)
#include <cairo.h>
#endif
#include "wlfreerdp.h"
#include "wlf_input.h"
@@ -67,6 +71,7 @@ static BOOL wl_update_buffer(wlfContext* context_w, INT32 ix, INT32 iy, INT32 iw
UwacSize geometry;
size_t stride;
UwacReturnCode rc;
RECTANGLE_16 area;
if (!context_w)
return FALSE;
@@ -93,16 +98,21 @@ static BOOL wl_update_buffer(wlfContext* context_w, INT32 ix, INT32 iy, INT32 iw
if ((x > geometry.width) || (y > geometry.height))
return TRUE;
baseSrcOffset = y * gdi->stride + x * GetBytesPerPixel(gdi->dstFormat);
baseDstOffset = y * stride + x * 4;
for (i = 0; i < MIN(h, geometry.height - y); i++)
{
const size_t srcOffset = i * gdi->stride + baseSrcOffset;
const size_t dstOffset = i * stride + baseDstOffset;
area.left = x;
area.top = y;
area.right = x + w;
area.bottom = y + h;
memcpy(data + dstOffset, gdi->primary_buffer + srcOffset,
MIN(w, geometry.width - x) * GetBytesPerPixel(gdi->dstFormat));
}
if (!wlf_copy_image(gdi->primary_buffer, gdi->stride, gdi->width, gdi->height,
data, stride, geometry.width, geometry.height, &area,
context_w->context.settings->SmartSizing))
return FALSE;
if (!wlf_scale_coordinates(&context_w->context, &x, &y))
return FALSE;
if (!wlf_scale_coordinates(&context_w->context, &w, &h))
return FALSE;
if (UwacWindowAddDamage(context_w->window, x, y, w, h) != UWAC_SUCCESS)
return FALSE;
@@ -299,6 +309,9 @@ static BOOL handle_uwac_events(freerdp* instance, UwacDisplay* display)
break;
case UWAC_EVENT_FRAME_DONE:
if (UwacWindowSubmitBuffer(context->window, true) != UWAC_SUCCESS)
return FALSE;
break;
case UWAC_EVENT_POINTER_ENTER:
@@ -334,6 +347,7 @@ static BOOL handle_uwac_events(freerdp* instance, UwacDisplay* display)
case UWAC_EVENT_KEYBOARD_ENTER:
if (instance->context->settings->GrabKeyboard)
UwacSeatInhibitShortcuts(event.keyboard_enter_leave.seat, true);
if (!wlf_keyboard_enter(instance, &event.keyboard_enter_leave))
return FALSE;
@@ -556,3 +570,96 @@ fail:
freerdp_client_context_free(context);
return rc;
}
BOOL wlf_copy_image(const void* src, size_t srcStride, size_t srcWidth, size_t srcHeight,
void* dst, size_t dstStride, size_t dstWidth, size_t dstHeight,
const RECTANGLE_16* area, BOOL scale)
{
BOOL rc = FALSE;
if (!src || !dst || !area)
return FALSE;
if (scale)
{
#if defined(CAIRO_FOUND)
const double sx = (double)dstWidth / (double)srcWidth;
const double sy = (double)dstHeight / (double)srcHeight;
cairo_t* cairo_context;
cairo_surface_t* csrc = cairo_image_surface_create_for_data(src, CAIRO_FORMAT_ARGB32, srcWidth,
srcHeight, srcStride);
cairo_surface_t* cdst = cairo_image_surface_create_for_data(dst, CAIRO_FORMAT_ARGB32, dstWidth,
dstHeight, dstStride);
if (!csrc || !cdst)
goto fail;
cairo_context = cairo_create(cdst);
if (!cairo_context)
goto fail2;
cairo_scale(cairo_context, sx, sy);
cairo_set_operator(cairo_context, CAIRO_OPERATOR_SOURCE);
cairo_set_source_surface(cairo_context, csrc, 0, 0);
cairo_paint(cairo_context);
rc = TRUE;
fail2:
cairo_destroy(cairo_context);
fail:
cairo_surface_destroy(csrc);
cairo_surface_destroy(cdst);
#else
WLog_WARN(TAG, "SmartScaling requested but compiled without libcairo support!");
#endif
}
else
{
size_t i;
const size_t baseSrcOffset = area->top * srcStride + area->left * 4;
const size_t baseDstOffset = area->top * dstStride + area->left * 4;
const size_t width = MIN(area->right - area->left, dstWidth - area->left);
const size_t height = MIN(area->bottom - area->top, dstHeight - area->top);
const BYTE* psrc = (const BYTE*)src;
BYTE* pdst = (BYTE*)dst;
for (i = 0; i < height; i++)
{
const size_t srcOffset = i * srcStride + baseSrcOffset;
const size_t dstOffset = i * dstStride + baseDstOffset;
memcpy(&pdst[dstOffset], &psrc[srcOffset], width * 4);
}
rc = TRUE;
}
return rc;
}
BOOL wlf_scale_coordinates(rdpContext* context, UINT32* px, UINT32* py)
{
wlfContext* wlf = (wlfContext*)context;
rdpGdi* gdi;
UwacSize geometry;
double sx, sy;
if (!context || !px || !py || !context->gdi)
return FALSE;
if (!context->settings->SmartSizing)
return TRUE;
/* If libcairo is not compiled, smart scaling is ignored. */
#if defined(CAIRO_FOUND)
gdi = context->gdi;
if (UwacWindowGetDrawingBufferGeometry(wlf->window, &geometry, NULL) != UWAC_SUCCESS)
return FALSE;
sx = geometry.width / (double)gdi->width;
sy = geometry.height / (double)gdi->height;
*px /= sx;
*py /= sy;
#endif
return TRUE;
}

View File

@@ -54,5 +54,10 @@ struct wlf_context
wLog* log;
};
BOOL wlf_scale_coordinates(rdpContext* context, UINT32* px, UINT32* py);
BOOL wlf_copy_image(const void* src, size_t srcStride, size_t srcWidth, size_t srcHeight,
void* dst, size_t dstStride, size_t dstWidth, size_t dstHeight,
const RECTANGLE_16* area, BOOL scale);
#endif /* FREERDP_CLIENT_WAYLAND_FREERDP_H */

24
cmake/FindCairo.cmake Normal file
View File

@@ -0,0 +1,24 @@
find_package(PkgConfig QUIET)
pkg_check_modules(CAIRO QUIET libcairo)
find_path(CAIRO_INCLUDE_DIR
NAMES cairo.h
PATHS ${CAIRO_INCLUDE_DIRS}
PATH_SUFFIXES cairo
)
# Finally the library itself
find_library(CAIRO_LIBRARY
NAMES cairo
PATHS ${CAIRO_LIBRARY_DIRS}
)
mark_as_advanced(CAIRO_INCLUDE_DIR)
mark_as_advanced(CAIRO_LIBRARY)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(CAIRO
REQUIRED_VARS
CAIRO_LIBRARY CAIRO_INCLUDE_DIR
VERSION_VAR
CAIRO_VERSION_STRING
HANDLE_COMPONENTS)

View File

@@ -27,6 +27,7 @@ Build-Depends:
libxdamage-dev,
libxtst-dev,
libcups2-dev,
libcairo2-dev,
libpcsclite-dev,
libasound2-dev,
libpulse-dev,

View File

@@ -40,6 +40,7 @@ BuildRequires: libXv-devel
BuildRequires: libXdamage-devel
BuildRequires: libXtst-devel
BuildRequires: cups-devel
BuildRequires: cairo-devel
BuildRequires: pcsc-lite-devel
BuildRequires: uuid-devel
BuildRequires: libxml2-devel