diff --git a/.cirrus.yml b/.cirrus.yml index cfeb53ade2..5ac17b2eac 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -6,11 +6,11 @@ FreeBSD_task: freebsd_instance: image_family: freebsd-12-2 prepare_script: - - pkg install -y $SSL git autoconf automake libtool pkgconf opus jpeg-turbo fdk-aac pixman libX11 libXfixes libXrandr nasm fusefs-libs check + - pkg install -y $SSL git autoconf automake libtool pkgconf opus jpeg-turbo fdk-aac pixman libX11 libXfixes libXrandr nasm fusefs-libs check imlib2 - git submodule update --init --recursive configure_script: - ./bootstrap - - env CPPFLAGS=-I/usr/local/include LDFLAGS=-L/usr/local/lib ./configure --localstatedir=/var --enable-strict-locations --with-pkgconfigdir=/usr/local/libdata/pkgconfig --enable-strict-locations --enable-ipv6 --enable-opus --enable-jpeg --enable-fdkaac --enable-painter --enable-pixman --enable-fuse + - env CPPFLAGS=-I/usr/local/include LDFLAGS=-L/usr/local/lib ./configure --localstatedir=/var --enable-strict-locations --with-pkgconfigdir=/usr/local/libdata/pkgconfig --enable-strict-locations --enable-ipv6 --enable-opus --enable-jpeg --enable-fdkaac --enable-painter --enable-pixman --enable-fuse --with-imlib2 build_script: - make -j $(sysctl -n hw.ncpu || echo 4) install_script: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 11ef2a8aad..6f3986360e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -94,10 +94,10 @@ jobs: --disable-pixman" CONF_FLAGS_amd64_max: "--enable-ipv6 --enable-jpeg --enable-fuse --enable-mp3lame --enable-fdkaac --enable-opus --enable-rfxcodec --enable-painter - --enable-pixman" + --enable-pixman --with-imlib2" CONF_FLAGS_i386_max: "--enable-ipv6 --enable-jpeg --enable-fuse --enable-mp3lame --enable-fdkaac --enable-opus --enable-rfxcodec --enable-painter - --disable-pixman --host=i686-linux" + --disable-pixman --with-imlib2 --host=i686-linux" PKG_CONFIG_PATH_i386: "/usr/lib/i386-linux-gnu/pkgconfig" CFLAGS_i386: "-m32" diff --git a/configure.ac b/configure.ac index 0081ff4953..796193ab24 100644 --- a/configure.ac +++ b/configure.ac @@ -166,6 +166,8 @@ AC_ARG_ENABLE(rdpsndaudin, AS_HELP_STRING([--enable-rdpsndaudin], [], [enable_rdpsndaudin=no]) AM_CONDITIONAL(XRDP_RDPSNDAUDIN, [test x$enable_rdpsndaudin = xyes]) +AC_ARG_WITH(imlib2, AC_HELP_STRING([--with-imlib2=ARG], [imlib2 library to use for non-BMP backgrounds (ARG=yes/no/)]),,) + # Obsolete options AC_ARG_ENABLE(xrdpdebug, AS_HELP_STRING([--enable-xrdpdebug], [This option is no longer supported - use --enable-devel-all])) @@ -216,6 +218,44 @@ AC_CHECK_HEADER([security/_pam_types.h], AC_CHECK_HEADER([security/pam_constants.h], [AC_DEFINE([HAVE_PAM_CONSTANTS_H], 1, [Using OpenPAM], [])]) +# Find imlib2 +case "$with_imlib2" in + '' | no) AC_MSG_NOTICE([imlib2 will not be supported]) + use_imlib2=no + ;; + yes) + PKG_CHECK_MODULES([IMLIB2], [imlib2 >= 1.4.10], + [use_imlib2=yes], + [AC_MSG_ERROR([please install libimlib2-dev or imlib2-devel])]) + ;; + /*) AC_MSG_CHECKING([for imlib2 in $with_imlib2]) + if test -d $with_imlib2/lib; then + IMLIB2_LIBS="-L$with_imlib2/lib -lImlib2" + elif test -d $with_imlib2/lib64; then + IMLIB2_LIBS="-L$with_imlib2/lib64 -lImlib2" + else + AC_MSG_RESULT([no]) + AC_MSG_ERROR([Can't find libImlib2 in $with_imlib2]) + fi + + if test -f $with_imlib2/include/Imlib2.h; then + IMLIB2_CFLAGS="-I $with_imlib2/include" + else + AC_MSG_RESULT([no]) + AC_MSG_ERROR([Can't find $with_imlib2/include/Imlib2.h]) + fi + AC_MSG_RESULT([yes]) + AC_SUBST([IMLIB2_LIBS]) + AC_SUBST([IMLIB2_CFLAGS]) + use_imlib2=yes + ;; + *) AC_MSG_ERROR([--with-imlib2 needs yes/no or absolute path]) +esac + +if test x$use_imlib2 = xyes; then + AC_DEFINE([USE_IMLIB2],1, [Compile with imlib2 support]) +fi + # Check only one auth mechanism is specified, and give it a name auth_cnt=0 auth_mech="Builtin" @@ -454,6 +494,7 @@ AC_CONFIG_FILES([ tests/Makefile tests/common/Makefile tests/memtest/Makefile + tests/xrdp/Makefile tools/Makefile tools/devel/Makefile tools/devel/tcp_proxy/Makefile @@ -485,6 +526,12 @@ echo " vsock $enable_vsock" echo " auth mechanism $auth_mech" echo " rdpsndaudin $enable_rdpsndaudin" echo +if test x$use_imlib2 = xyes; then + echo " with imlib2 yes" +else + echo " with imlib2 no" +fi +echo echo " development logging $devel_logging" echo " development streamcheck $devel_streamcheck" echo "" diff --git a/scripts/install_xrdp_build_dependencies_with_apt.sh b/scripts/install_xrdp_build_dependencies_with_apt.sh index 3d48172453..89b59a211d 100755 --- a/scripts/install_xrdp_build_dependencies_with_apt.sh +++ b/scripts/install_xrdp_build_dependencies_with_apt.sh @@ -50,6 +50,7 @@ in libjpeg-dev \ libmp3lame-dev \ libfdk-aac-dev \ + libimlib2-dev \ libopus-dev \ libpixman-1-dev" ;; @@ -66,6 +67,7 @@ in libgl1-mesa-dev:i386 \ libglu1-mesa-dev:i386 \ libjpeg-dev:i386 \ + libimlib2-dev:i386 \ libmp3lame-dev:i386 \ libfdk-aac-dev:i386 \ libopus-dev:i386 \ diff --git a/tests/Makefile.am b/tests/Makefile.am index c5342cd855..42b0df1c86 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -4,4 +4,5 @@ EXTRA_DIST = \ SUBDIRS = \ common \ - memtest + memtest \ + xrdp diff --git a/tests/xrdp/Makefile.am b/tests/xrdp/Makefile.am new file mode 100644 index 0000000000..2a2742d066 --- /dev/null +++ b/tests/xrdp/Makefile.am @@ -0,0 +1,41 @@ +AM_CPPFLAGS = \ + -I$(top_builddir) \ + -I$(top_srcdir)/xrdp \ + -I$(top_srcdir)/libxrdp \ + -I$(top_srcdir)/common \ + $(IMLIB2_CFLAGS) + +LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) \ + $(top_srcdir)/tap-driver.sh + +PACKAGE_STRING="XRDP daemon" + +EXTRA_DIST = \ + test_4bit.bmp \ + test_8bit.bmp \ + test_24bit.bmp \ + test_not4_4bit.bmp \ + test_not4_8bit.bmp \ + test_not4_24bit.bmp \ + test1.jpg \ + test_alpha_blend.png + +TESTS = test_xrdp +check_PROGRAMS = test_xrdp + +test_xrdp_SOURCES = \ + test_xrdp.h \ + test_xrdp_main.c \ + test_bitmap_load.c + +test_xrdp_CFLAGS = \ + -D IMAGEDIR=\"$(srcdir)\" \ + @CHECK_CFLAGS@ + +test_xrdp_LDADD = \ + $(top_builddir)/xrdp/xrdp_bitmap_load.o \ + $(top_builddir)/xrdp/xrdp_bitmap_common.o \ + $(top_builddir)/xrdp/funcs.o \ + $(top_builddir)/common/libcommon.la \ + $(IMLIB2_LIBS) \ + @CHECK_LIBS@ diff --git a/tests/xrdp/test1.jpg b/tests/xrdp/test1.jpg new file mode 100644 index 0000000000..93434c0025 Binary files /dev/null and b/tests/xrdp/test1.jpg differ diff --git a/tests/xrdp/test_24bit.bmp b/tests/xrdp/test_24bit.bmp new file mode 100644 index 0000000000..2125d82485 Binary files /dev/null and b/tests/xrdp/test_24bit.bmp differ diff --git a/tests/xrdp/test_4bit.bmp b/tests/xrdp/test_4bit.bmp new file mode 100644 index 0000000000..9506afaf77 Binary files /dev/null and b/tests/xrdp/test_4bit.bmp differ diff --git a/tests/xrdp/test_8bit.bmp b/tests/xrdp/test_8bit.bmp new file mode 100644 index 0000000000..50d4a746f5 Binary files /dev/null and b/tests/xrdp/test_8bit.bmp differ diff --git a/tests/xrdp/test_alpha_blend.png b/tests/xrdp/test_alpha_blend.png new file mode 100644 index 0000000000..66a7176e4b Binary files /dev/null and b/tests/xrdp/test_alpha_blend.png differ diff --git a/tests/xrdp/test_bitmap_load.c b/tests/xrdp/test_bitmap_load.c new file mode 100644 index 0000000000..42b5a32593 --- /dev/null +++ b/tests/xrdp/test_bitmap_load.c @@ -0,0 +1,337 @@ +#if defined(HAVE_CONFIG_H) +#include "config_ac.h" +#endif + +#include "test_xrdp.h" +#include "xrdp.h" + +/* Where the image files are located */ +#ifndef IMAGEDIR +#define IMAGEDIR "." +#endif + +/* Handy color definitions. These are variables so they are displayed + * in assert messages if tests fail */ +static int RED = COLOR24RGB(255, 0, 0); +static int GREEN = COLOR24RGB(0, 255, 0); +static int BLUE = COLOR24RGB(0, 0, 255); +static int WHITE = COLOR24RGB(255, 255, 255); + +/* Virtual desktop maxima and minima [MS-RDPBCGR] 2.2.1.3.6 */ +#define MAX_VDESKTOP_WIDTH 32766 +#define MAX_VDESKTOP_HEIGHT 32766 +#define MIN_VDESKTOP_WIDTH 200 +#define MIN_VDESKTOP_HEIGHT 200 + +/* Characteristics of the test bitmap(s) */ +#define TEST_BM_WIDTH 256 +#define TEST_BM_HEIGHT 256 + +#define TEST_NOT4_BM_WIDTH 62 +#define TEST_NOT4_BM_HEIGHT 62 + +#define TEST_BM_TOP_LEFT_PIXEL RED +#define TEST_BM_TOP_RIGHT_PIXEL GREEN +#define TEST_BM_BOTTOM_LEFT_PIXEL BLUE +#define TEST_BM_BOTTOM_RIGHT_PIXEL WHITE + +/* + * Scaling bitmaps properly will introduce color changes with dithering. + * Also some filetypes use compression, and these do not represent colors + * perfectly. + * + * This is the Pythagorean distance we allow between two colors for them to + * be considered close enough to each other */ +#define MAX_SIMILAR_COLOR_DISTANCE 3 + +void setup(void) +{ +} + +void teardown(void) +{ +} + +/* Tests an error is returned for a non-existent file */ +START_TEST(test_bitmap_load__with_invalid_image__fail) +{ + struct xrdp_wm *wm = NULL; + int result; + + struct xrdp_bitmap *bm = xrdp_bitmap_create(4, 4, 32, WND_TYPE_IMAGE, wm); + + ck_assert_ptr_ne(bm, NULL); + + result = xrdp_bitmap_load(bm, "/dev/null", NULL, 0, XBLT_NONE, 0, 0); + + ck_assert_int_ne(result, 0); + + xrdp_bitmap_delete(bm); +} +END_TEST + +/* Checks a particular pixmap value is expected */ +static void +check_pixel(struct xrdp_bitmap *bm, int i, int j, int expected) +{ + int pixel = xrdp_bitmap_get_pixel(bm, i, j); + if (pixel != expected) + { + ck_abort_msg("Pixmap (%d,%d) expected 0x%06x, got 0x%06x", + i, j, expected, pixel); + } +} + +/* Calculates whether two colors are close enough to be considered the same */ +static void +check_is_close_color(struct xrdp_bitmap *bm, int i, int j, int expected) +{ + int pixel = xrdp_bitmap_get_pixel(bm, i, j); + int r1; + int g1; + int b1; + int r2; + int g2; + int b2; + int variance; + + SPLITCOLOR32(r1, g1, b1, pixel); + SPLITCOLOR32(r2, g2, b2, expected); + + variance = ((r1 - r2) * (r1 - r2) + + (g1 - g2) * (g1 - g2) + + (b1 - b2) * (b1 - b2)); + + if (variance > MAX_SIMILAR_COLOR_DISTANCE * MAX_SIMILAR_COLOR_DISTANCE) + { + ck_abort_msg("Pixmap (%d,%d) expected 0x%06x, got 0x%06x" + " which exceeds distance of %d", + i, j, expected, pixel, MAX_SIMILAR_COLOR_DISTANCE); + } +} + +/* Check we can load images of various depths with various transforms */ +static void +load_and_transform_img(const char *name, + enum xrdp_bitmap_load_transform transform, + int twidth, int theight) +{ + struct xrdp_wm *wm = NULL; + int result; + + int width; + int height; + + char full_name[256]; + + struct xrdp_bitmap *bm = xrdp_bitmap_create(4, 4, 32, WND_TYPE_IMAGE, wm); + + ck_assert_ptr_ne(bm, NULL); + + g_snprintf(full_name, sizeof(full_name), IMAGEDIR "/%s", name); + result = xrdp_bitmap_load(bm, full_name, NULL, HCOLOR(bm->bpp, WHITE), + transform, twidth, theight); + + ck_assert_int_eq(result, 0); + + /* Bitmap right size? */ + if (transform == XBLT_NONE) + { + width = TEST_BM_WIDTH; + height = TEST_BM_HEIGHT; + ck_assert_int_eq(bm->width, TEST_BM_WIDTH); + ck_assert_int_eq(bm->height, TEST_BM_HEIGHT); + } + else + { + width = twidth; + height = theight; + ck_assert_int_eq(bm->width, twidth); + ck_assert_int_eq(bm->height, theight); + } + + /* Corners OK? Allow for dithering */ + check_is_close_color(bm, 0, 0, TEST_BM_TOP_LEFT_PIXEL); + check_is_close_color(bm, width - 1, 0, TEST_BM_TOP_RIGHT_PIXEL); + check_is_close_color(bm, 0, height - 1, TEST_BM_BOTTOM_LEFT_PIXEL); + check_is_close_color(bm, width - 1, height - 1, TEST_BM_BOTTOM_RIGHT_PIXEL); + + xrdp_bitmap_delete(bm); +} + +/* Check we can load bitmaps that aren't a multiple of 4 pixels wide */ +static void +load_not4_img(const char *name) +{ + struct xrdp_wm *wm = NULL; + int result; + + const int width = TEST_NOT4_BM_WIDTH; + const int height = TEST_NOT4_BM_HEIGHT; + + char full_name[256]; + int i; + int j; + + struct xrdp_bitmap *bm = xrdp_bitmap_create(4, 4, 32, WND_TYPE_IMAGE, wm); + + ck_assert_ptr_ne(bm, NULL); + + g_snprintf(full_name, sizeof(full_name), IMAGEDIR "/%s", name); + result = xrdp_bitmap_load(bm, full_name, NULL, HCOLOR(bm->bpp, WHITE), + XBLT_NONE, 0, 0); + + ck_assert_int_eq(result, 0); + + /* Bitmap right size? */ + ck_assert_int_eq(bm->width, TEST_NOT4_BM_WIDTH); + ck_assert_int_eq(bm->height, TEST_NOT4_BM_HEIGHT); + + /* Check all data */ + for (i = 0 ; i < width / 2 ; ++i) + { + for (j = 0 ; j < height / 2 ; ++j) + { + check_pixel(bm, i, j, TEST_BM_TOP_LEFT_PIXEL); + check_pixel(bm, i + width / 2, j, TEST_BM_TOP_RIGHT_PIXEL); + check_pixel(bm, i, j + height / 2, TEST_BM_BOTTOM_LEFT_PIXEL); + check_pixel(bm, i + width / 2, j + height / 2, + TEST_BM_BOTTOM_RIGHT_PIXEL); + } + } + + xrdp_bitmap_delete(bm); +} + +START_TEST(test_bitmap_load__4_bit__ok) +{ + load_and_transform_img("test_4bit.bmp", XBLT_NONE, 0, 0); +} +END_TEST + +START_TEST(test_bitmap_load__8_bit__ok) +{ + load_and_transform_img("test_8bit.bmp", XBLT_NONE, 0, 0); +} +END_TEST + +START_TEST(test_bitmap_load__24_bit__ok) +{ + load_and_transform_img("test_24bit.bmp", XBLT_NONE, 0, 0); +} +END_TEST + +START_TEST(test_bitmap_load__max_width_zoom__ok) +{ + load_and_transform_img("test_24bit.bmp", + XBLT_ZOOM, MAX_VDESKTOP_WIDTH, MIN_VDESKTOP_HEIGHT); +} +END_TEST + +START_TEST(test_bitmap_load__max_height_zoom__ok) +{ + load_and_transform_img("test_24bit.bmp", + XBLT_ZOOM, MIN_VDESKTOP_WIDTH, MAX_VDESKTOP_HEIGHT); +} +END_TEST + +START_TEST(test_bitmap_load__min_zoom__ok) +{ + load_and_transform_img("test_24bit.bmp", + XBLT_ZOOM, MIN_VDESKTOP_WIDTH, MIN_VDESKTOP_HEIGHT); +} +END_TEST + +START_TEST(test_bitmap_load__max_width_scale__ok) +{ + load_and_transform_img("test_24bit.bmp", + XBLT_SCALE, MAX_VDESKTOP_WIDTH, MIN_VDESKTOP_HEIGHT); +} +END_TEST + +START_TEST(test_bitmap_load__max_height_scale__ok) +{ + load_and_transform_img("test_24bit.bmp", + XBLT_SCALE, MIN_VDESKTOP_WIDTH, MAX_VDESKTOP_HEIGHT); +} +END_TEST + +START_TEST(test_bitmap_load__min_scale__ok) +{ + load_and_transform_img("test_24bit.bmp", + XBLT_SCALE, MIN_VDESKTOP_WIDTH, MIN_VDESKTOP_HEIGHT); +} +END_TEST + +START_TEST(test_bitmap_load__not_4_pixels_wide_4_bit__ok) +{ + load_not4_img("test_not4_4bit.bmp"); +} +END_TEST + +START_TEST(test_bitmap_load__not_4_pixels_wide_8_bit__ok) +{ + load_not4_img("test_not4_8bit.bmp"); +} +END_TEST + +START_TEST(test_bitmap_load__not_4_pixels_wide_24_bit__ok) +{ + load_not4_img("test_not4_24bit.bmp"); +} +END_TEST + +#ifdef USE_IMLIB2 +START_TEST(test_png_load__blend_ok) +{ + load_and_transform_img("test_alpha_blend.png", XBLT_NONE, 0, 0); +} +END_TEST + +START_TEST(test_jpg_load__ok) +{ + load_and_transform_img("test1.jpg", XBLT_NONE, 0, 0); +} +END_TEST +#endif + +/******************************************************************************/ +Suite * +make_suite_test_bitmap_load(void) +{ + Suite *s; + TCase *tc_bitmap_load; +#ifdef USE_IMLIB2 + TCase *tc_other_load; +#endif + + s = suite_create("BitmapLoad"); + + tc_bitmap_load = tcase_create("xrdp_bitmap_load"); + tcase_add_checked_fixture(tc_bitmap_load, setup, teardown); + tcase_add_test(tc_bitmap_load, test_bitmap_load__with_invalid_image__fail); + tcase_add_test(tc_bitmap_load, test_bitmap_load__4_bit__ok); + tcase_add_test(tc_bitmap_load, test_bitmap_load__8_bit__ok); + tcase_add_test(tc_bitmap_load, test_bitmap_load__24_bit__ok); + tcase_add_test(tc_bitmap_load, test_bitmap_load__max_width_zoom__ok); + tcase_add_test(tc_bitmap_load, test_bitmap_load__max_height_zoom__ok); + tcase_add_test(tc_bitmap_load, test_bitmap_load__min_zoom__ok); + tcase_add_test(tc_bitmap_load, test_bitmap_load__max_width_scale__ok); + tcase_add_test(tc_bitmap_load, test_bitmap_load__max_height_scale__ok); + tcase_add_test(tc_bitmap_load, test_bitmap_load__min_scale__ok); + tcase_add_test(tc_bitmap_load, test_bitmap_load__not_4_pixels_wide_4_bit__ok); + tcase_add_test(tc_bitmap_load, test_bitmap_load__not_4_pixels_wide_8_bit__ok); + tcase_add_test(tc_bitmap_load, test_bitmap_load__not_4_pixels_wide_24_bit__ok); + + suite_add_tcase(s, tc_bitmap_load); + +#ifdef USE_IMLIB2 + tc_other_load = tcase_create("xrdp_other_load"); + tcase_add_checked_fixture(tc_other_load, setup, teardown); + tcase_add_test(tc_other_load, test_png_load__blend_ok); + tcase_add_test(tc_other_load, test_jpg_load__ok); + suite_add_tcase(s, tc_other_load); +#endif + + return s; +} diff --git a/tests/xrdp/test_not4_24bit.bmp b/tests/xrdp/test_not4_24bit.bmp new file mode 100644 index 0000000000..fa21f24fd8 Binary files /dev/null and b/tests/xrdp/test_not4_24bit.bmp differ diff --git a/tests/xrdp/test_not4_4bit.bmp b/tests/xrdp/test_not4_4bit.bmp new file mode 100644 index 0000000000..8fa3a8ca7b Binary files /dev/null and b/tests/xrdp/test_not4_4bit.bmp differ diff --git a/tests/xrdp/test_not4_8bit.bmp b/tests/xrdp/test_not4_8bit.bmp new file mode 100644 index 0000000000..87405a37e4 Binary files /dev/null and b/tests/xrdp/test_not4_8bit.bmp differ diff --git a/tests/xrdp/test_xrdp.h b/tests/xrdp/test_xrdp.h new file mode 100644 index 0000000000..d41e831987 --- /dev/null +++ b/tests/xrdp/test_xrdp.h @@ -0,0 +1,8 @@ +#ifndef TEST_XRDP_H +#define TEST_XRDP_H + +#include + +Suite *make_suite_test_bitmap_load(void); + +#endif /* TEST_XRDP_H */ diff --git a/tests/xrdp/test_xrdp_main.c b/tests/xrdp/test_xrdp_main.c new file mode 100644 index 0000000000..eb118a1e7f --- /dev/null +++ b/tests/xrdp/test_xrdp_main.c @@ -0,0 +1,62 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2004-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Test driver for XRDP routines + * + * If you want to run this driver under valgrind to check for memory leaks, + * use the following command line:- + * + * CK_FORK=no valgrind --leak-check=full --show-leak-kinds=all \ + * .libs/test_xrdp + * + * without the 'CK_FORK=no', memory still allocated by the test driver will + * be logged + */ + +#if defined(HAVE_CONFIG_H) +#include "config_ac.h" +#endif + +#include "log.h" +#include "os_calls.h" +#include "test_xrdp.h" +#include + +int main (void) +{ + int number_failed; + SRunner *sr; + struct log_config *logging; + + /* Configure the logging sub-system so that functions can use + * the log functions as appropriate */ + logging = log_config_init_for_console(LOG_LEVEL_INFO, + g_getenv("TEST_LOG_LEVEL")); + log_start_from_param(logging); + log_config_free(logging); + + sr = srunner_create (make_suite_test_bitmap_load()); + + srunner_set_tap(sr, "-"); + srunner_run_all (sr, CK_ENV); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + + log_end(); + + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/xrdp/Makefile.am b/xrdp/Makefile.am index 1a065d7ef2..63968e4544 100644 --- a/xrdp/Makefile.am +++ b/xrdp/Makefile.am @@ -12,7 +12,8 @@ AM_CPPFLAGS = \ -DXRDP_SOCKET_PATH=\"${socketdir}\" \ -I$(top_builddir) \ -I$(top_srcdir)/common \ - -I$(top_srcdir)/libxrdp + -I$(top_srcdir)/libxrdp \ + $(IMLIB2_CFLAGS) XRDP_EXTRA_LIBS = @@ -43,6 +44,8 @@ xrdp_SOURCES = \ xrdp.c \ xrdp.h \ xrdp_bitmap.c \ + xrdp_bitmap_load.c \ + xrdp_bitmap_common.c \ xrdp_cache.c \ xrdp_encoder.c \ xrdp_encoder.h \ @@ -59,6 +62,7 @@ xrdp_SOURCES = \ xrdp_LDADD = \ $(top_builddir)/common/libcommon.la \ $(top_builddir)/libxrdp/libxrdp.la \ + $(IMLIB2_LIBS) \ $(XRDP_EXTRA_LIBS) xrdpsysconfdir=$(sysconfdir)/xrdp diff --git a/xrdp/xrdp.h b/xrdp/xrdp.h index f096538894..32da64dbd9 100644 --- a/xrdp/xrdp.h +++ b/xrdp/xrdp.h @@ -185,7 +185,7 @@ int xrdp_region_get_rect(struct xrdp_region *self, int index, struct xrdp_rect *rect); -/* xrdp_bitmap.c */ +/* xrdp_bitmap_common.c */ struct xrdp_bitmap * xrdp_bitmap_create(int width, int height, int bpp, int type, struct xrdp_wm *wm); @@ -195,15 +195,9 @@ xrdp_bitmap_create_with_data(int width, int height, struct xrdp_wm *wm); void xrdp_bitmap_delete(struct xrdp_bitmap *self); -struct xrdp_bitmap * -xrdp_bitmap_get_child_by_id(struct xrdp_bitmap *self, int id); -int -xrdp_bitmap_set_focus(struct xrdp_bitmap *self, int focused); int xrdp_bitmap_resize(struct xrdp_bitmap *self, int width, int height); int -xrdp_bitmap_load(struct xrdp_bitmap *self, const char *filename, int *palette); -int xrdp_bitmap_get_pixel(struct xrdp_bitmap *self, int x, int y); int xrdp_bitmap_set_pixel(struct xrdp_bitmap *self, int x, int y, int pixel); @@ -211,6 +205,12 @@ int xrdp_bitmap_copy_box(struct xrdp_bitmap *self, struct xrdp_bitmap *dest, int x, int y, int cx, int cy); + +/* xrdp_bitmap.c */ +struct xrdp_bitmap * +xrdp_bitmap_get_child_by_id(struct xrdp_bitmap *self, int id); +int +xrdp_bitmap_set_focus(struct xrdp_bitmap *self, int focused); int xrdp_bitmap_hash_crc(struct xrdp_bitmap *self); int @@ -239,6 +239,35 @@ xrdp_bitmap_get_screen_clip(struct xrdp_bitmap *self, struct xrdp_rect *rect, int *dx, int *dy); +/* xrdp_bitmap_load.c */ +/** + * Loads a bitmap from a file and (optionally) transforms it + * + * @param self from rdp_bitmap_create() + * @param filename Filename to load + * @param[in] palette For 8-bit conversions. Currently unused + * @param background Background color for alpha-blending + * @param transform Transform to apply to the image after loading + * @param twidth target width if transform != XBLT_NONE + * @param theight target height if transform != XBLT_NONE + * @return 0 for success. + * + * The background color is only used if the specified image contains + * an alpha layer. It is in HCOLOR format, and the bpp must correspond to + * the bpp used to create 'self'. + * + * After a successful call, the bitmap is resized to the image file size. + * + * If the call is not successful, the bitmap will be in an indeterminate + * state and should not be used. + */ +int +xrdp_bitmap_load(struct xrdp_bitmap *self, const char *filename, + const int *palette, + int background, + enum xrdp_bitmap_load_transform transform, + int twidth, + int theight); /* xrdp_painter.c */ struct xrdp_painter * xrdp_painter_create(struct xrdp_wm *wm, struct xrdp_session *session); diff --git a/xrdp/xrdp.ini.in b/xrdp/xrdp.ini.in index c84aad13c2..c3ae197d84 100644 --- a/xrdp/xrdp.ini.in +++ b/xrdp/xrdp.ini.in @@ -123,12 +123,27 @@ ls_height=430 ; login screen background color in RGB format ls_bg_color=dedede -; optional background image filename (bmp format). +; optional background image filename. BMP format is always supported, +; but other formats will be supported if xrdp is build with imlib2 +; The transform can be one of the following:- +; none : No transformation. Image is placed in bottom-right corner +; of the screen. +; scale : Image is scaled to the screen size. The image aspect +; ratio is not preserved. +; zoom : Image is scaled to the screen size. The image aspect +; ratio is preserved by clipping the image. #ls_background_image= +#ls_background_transform=none ; logo -; full path to bmp-file or file in shared folder +; full path to file or file in shared folder. BMP format is always supported, +; but other formats will be supported if xrdp is build with imlib2 +; For transform values, see 'ls_background_transform'. The logo width and +; logo height are ignored for a transform of 'none'. ls_logo_filename= +#ls_logo_transform=none +#ls_logo_width=240 +#ls_logo_height=140 ls_logo_x_pos=55 ls_logo_y_pos=50 diff --git a/xrdp/xrdp_bitmap.c b/xrdp/xrdp_bitmap.c index 2721fd5fa8..2d68edf9ed 100644 --- a/xrdp/xrdp_bitmap.c +++ b/xrdp/xrdp_bitmap.c @@ -25,8 +25,6 @@ #include #endif -#include - #include "xrdp.h" #include "log.h" #include "string_calls.h" @@ -86,258 +84,6 @@ static const unsigned int g_crc_table[256] = (in_crc) = g_crc_table[((in_crc) ^ (in_pixel)) & 0xff] ^ ((in_crc) >> 8) #define CRC_END(in_crc) (in_crc) = ((in_crc) ^ 0xFFFFFFFF) -/*****************************************************************************/ -/* Allocate bitmap for specified dimensions, checking for int overflow */ -static char * -alloc_bitmap_data(int width, int height, int Bpp) -{ - char *result = NULL; - if (width > 0 && height > 0 && Bpp > 0) - { - int len = width; - /* g_malloc() currently takes an 'int' size */ - if (len < INT_MAX / height) - { - len *= height; - if (len < INT_MAX / Bpp) - { - len *= Bpp; - result = (char *)malloc(len); - } - } - } - - return result; -} - -/*****************************************************************************/ -struct xrdp_bitmap * -xrdp_bitmap_create(int width, int height, int bpp, - int type, struct xrdp_wm *wm) -{ - struct xrdp_bitmap *self = (struct xrdp_bitmap *)NULL; - int Bpp = 0; - - self = (struct xrdp_bitmap *)g_malloc(sizeof(struct xrdp_bitmap), 1); - self->type = type; - self->width = width; - self->height = height; - self->bpp = bpp; - Bpp = 4; - - switch (bpp) - { - case 8: - Bpp = 1; - break; - case 15: - Bpp = 2; - break; - case 16: - Bpp = 2; - break; - } - - if (self->type == WND_TYPE_BITMAP || self->type == WND_TYPE_IMAGE) - { - self->data = alloc_bitmap_data(width, height, Bpp); - if (self->data == NULL) - { - LOG(LOG_LEVEL_ERROR, "xrdp_bitmap_create: size overflow %dx%dx%d", - width, height, Bpp); - g_free(self); - return NULL; - } - } - -#if defined(XRDP_PAINTER) - if (self->type == WND_TYPE_SCREEN) /* noorders */ - { - LOG_DEVEL(LOG_LEVEL_INFO, "xrdp_bitmap_create: noorders"); - self->data = alloc_bitmap_data(width, height, Bpp); - if (self->data == NULL) - { - LOG(LOG_LEVEL_ERROR, "xrdp_bitmap_create: size overflow %dx%dx%d", - width, height, Bpp); - g_free(self); - return NULL; - } - } -#endif - - if (self->type != WND_TYPE_BITMAP) - { - self->child_list = list_create(); - } - - self->line_size = width * Bpp; - - if (self->type == WND_TYPE_COMBO) - { - self->string_list = list_create(); - self->string_list->auto_free = 1; - self->data_list = list_create(); - self->data_list->auto_free = 1; - } - - self->wm = wm; - return self; -} - -/*****************************************************************************/ -struct xrdp_bitmap * -xrdp_bitmap_create_with_data(int width, int height, - int bpp, char *data, - struct xrdp_wm *wm) -{ - struct xrdp_bitmap *self = (struct xrdp_bitmap *)NULL; - int Bpp; -#if defined(NEED_ALIGN) - tintptr data_as_int; -#endif - - self = (struct xrdp_bitmap *)g_malloc(sizeof(struct xrdp_bitmap), 1); - self->type = WND_TYPE_BITMAP; - self->width = width; - self->height = height; - self->bpp = bpp; - self->wm = wm; - - Bpp = 4; - switch (bpp) - { - case 8: - Bpp = 1; - break; - case 15: - Bpp = 2; - break; - case 16: - Bpp = 2; - break; - } - self->line_size = width * Bpp; - -#if defined(NEED_ALIGN) - data_as_int = (tintptr) data; - if (((bpp >= 24) && (data_as_int & 3)) || - (((bpp == 15) || (bpp == 16)) && (data_as_int & 1))) - { - /* got to copy data here, it's not aligned - other calls in this file assume alignment */ - self->data = (char *)g_malloc(width * height * Bpp, 0); - g_memcpy(self->data, data, width * height * Bpp); - return self; - } -#endif - self->data = data; - self->do_not_free_data = 1; - return self; -} - -/*****************************************************************************/ -void -xrdp_bitmap_delete(struct xrdp_bitmap *self) -{ - int i = 0; - struct xrdp_mod_data *mod_data = (struct xrdp_mod_data *)NULL; - - if (self == 0) - { - return; - } - - if (self->wm != 0) - { - if (self->wm->focused_window != 0) - { - if (self->wm->focused_window->focused_control == self) - { - self->wm->focused_window->focused_control = 0; - } - } - - if (self->wm->focused_window == self) - { - self->wm->focused_window = 0; - } - - if (self->wm->dragging_window == self) - { - self->wm->dragging_window = 0; - } - - if (self->wm->button_down == self) - { - self->wm->button_down = 0; - } - - if (self->wm->popup_wnd == self) - { - self->wm->popup_wnd = 0; - } - - if (self->wm->login_window == self) - { - self->wm->login_window = 0; - } - - if (self->wm->log_wnd == self) - { - self->wm->log_wnd = 0; - } - } - - if (self->child_list != 0) - { - for (i = self->child_list->count - 1; i >= 0; i--) - { - xrdp_bitmap_delete((struct xrdp_bitmap *)self->child_list->items[i]); - } - - list_delete(self->child_list); - } - - if (self->parent != 0) - { - i = list_index_of(self->parent->child_list, (long)self); - - if (i >= 0) - { - list_remove_item(self->parent->child_list, i); - } - } - - if (self->string_list != 0) /* for combo */ - { - list_delete(self->string_list); - } - - if (self->data_list != 0) /* for combo */ - { - for (i = 0; i < self->data_list->count; i++) - { - mod_data = (struct xrdp_mod_data *)list_get_item(self->data_list, i); - - if (mod_data != 0) - { - list_delete(mod_data->names); - list_delete(mod_data->values); - } - } - - list_delete(self->data_list); - } - - if (!self->do_not_free_data) - { - g_free(self->data); - } - - g_free(self->caption1); - g_free(self); -} - /*****************************************************************************/ struct xrdp_bitmap * xrdp_bitmap_get_child_by_id(struct xrdp_bitmap *self, int id) @@ -401,593 +147,6 @@ xrdp_bitmap_set_focus(struct xrdp_bitmap *self, int focused) return 0; } -/*****************************************************************************/ -static int -xrdp_bitmap_get_index(struct xrdp_bitmap *self, int *palette, int color) -{ - int r = 0; - int g = 0; - int b = 0; - - r = (color & 0xff0000) >> 16; - g = (color & 0x00ff00) >> 8; - b = (color & 0x0000ff) >> 0; - r = (r >> 5) << 0; - g = (g >> 5) << 3; - b = (b >> 6) << 6; - return (b | g | r); -} - -/*****************************************************************************/ -/* returns error */ -int -xrdp_bitmap_resize(struct xrdp_bitmap *self, int width, int height) -{ - int Bpp = 0; - - if ((width == self->width) && (height == self->height)) - { - return 0; - } - - if (self->do_not_free_data) - { - return 1; - } - - self->width = width; - self->height = height; - Bpp = 4; - - switch (self->bpp) - { - case 8: - Bpp = 1; - break; - case 15: - Bpp = 2; - break; - case 16: - Bpp = 2; - break; - } - - g_free(self->data); - self->data = (char *)g_malloc(width * height * Bpp, 0); - self->line_size = width * Bpp; - return 0; -} - -/*****************************************************************************/ -/* load a bmp file */ -/* return 0 ok */ -/* return 1 error */ -int -xrdp_bitmap_load(struct xrdp_bitmap *self, const char *filename, int *palette) -{ - int fd = 0; - int len = 0; - int i = 0; - int j = 0; - int k = 0; - int color = 0; - int size = 0; - int palette1[256]; - char type1[4]; - struct xrdp_bmp_header header; - struct stream *s = (struct stream *)NULL; - - g_memset(palette1, 0, sizeof(int) * 256); - g_memset(type1, 0, sizeof(char) * 4); - g_memset(&header, 0, sizeof(struct xrdp_bmp_header)); - - if (!g_file_exist(filename)) - { - LOG(LOG_LEVEL_ERROR, "xrdp_bitmap_load: error bitmap file [%s] " - "does not exist", filename); - return 1; - } - - fd = g_file_open(filename); - - if (fd != -1) - { - /* read file type */ - if (g_file_read(fd, type1, 2) != 2) - { - LOG(LOG_LEVEL_ERROR, "xrdp_bitmap_load: error bitmap file [%s] " - "read error", filename); - g_file_close(fd); - return 1; - } - - if ((type1[0] != 'B') || (type1[1] != 'M')) - { - LOG(LOG_LEVEL_ERROR, "xrdp_bitmap_load: error bitmap file [%s] " - "not BMP file", filename); - g_file_close(fd); - return 1; - } - - /* read file size */ - make_stream(s); - init_stream(s, 8192); - if (g_file_read(fd, s->data, 4) != 4) - { - LOG(LOG_LEVEL_ERROR, "xrdp_bitmap_load: missing length in file %s", - filename); - free_stream(s); - g_file_close(fd); - return 1; - } - s->end = s->data + 4; - in_uint32_le(s, size); - /* read bmp header */ - if (g_file_seek(fd, 14) < 0) - { - LOG(LOG_LEVEL_ERROR, "xrdp_bitmap_load: seek error in file %s", - filename); - free_stream(s); - g_file_close(fd); - return 1; - } - init_stream(s, 8192); - len = g_file_read(fd, s->data, 40); /* size better be 40 */ - if (len != 40) - { - LOG(LOG_LEVEL_ERROR, "xrdp_bitmap_load: " - "unexpected read length %d in file %s", - len, filename); - free_stream(s); - g_file_close(fd); - return 1; - } - s->end = s->data + len; - in_uint32_le(s, header.size); - in_uint32_le(s, header.image_width); - in_uint32_le(s, header.image_height); - in_uint16_le(s, header.planes); - in_uint16_le(s, header.bit_count); - in_uint32_le(s, header.compression); - in_uint32_le(s, header.image_size); - in_uint32_le(s, header.x_pels_per_meter); - in_uint32_le(s, header.y_pels_per_meter); - in_uint32_le(s, header.clr_used); - in_uint32_le(s, header.clr_important); - - if ((header.bit_count != 4) && (header.bit_count != 8) && - (header.bit_count != 24)) - { - LOG(LOG_LEVEL_ERROR, "xrdp_bitmap_load: error bitmap file [%s] " - "bad bpp %d", filename, header.bit_count); - free_stream(s); - g_file_close(fd); - return 1; - } - - if (header.bit_count == 24) /* 24 bit bitmap */ - { - if (g_file_seek(fd, 14 + header.size) < 0) - { - LOG(LOG_LEVEL_WARNING, "xrdp_bitmap_load: seek error in file %s", - filename); - } - xrdp_bitmap_resize(self, header.image_width, header.image_height); - size = header.image_width * header.image_height * 3; - init_stream(s, size); - /* Pre-fill the buffer, so if we get short reads we're - * not working with uninitialised data */ - g_memset(s->data, 0, size); - s->end = s->data + size; - - /* read data */ - for (i = header.image_height - 1; i >= 0; i--) - { - size = header.image_width * 3; - k = g_file_read(fd, s->data + i * size, size); - - if (k != size) - { - LOG(LOG_LEVEL_WARNING, "xrdp_bitmap_load: error bitmap " - "file [%s] read", filename); - } - } - - for (i = 0; i < self->height; i++) - { - for (j = 0; j < self->width; j++) - { - in_uint8(s, k); - color = k; - in_uint8(s, k); - color |= k << 8; - in_uint8(s, k); - color |= k << 16; - - if (self->bpp == 8) - { - color = xrdp_bitmap_get_index(self, palette, color); - } - else if (self->bpp == 15) - { - color = COLOR15((color & 0xff0000) >> 16, - (color & 0x00ff00) >> 8, - (color & 0x0000ff) >> 0); - } - else if (self->bpp == 16) - { - color = COLOR16((color & 0xff0000) >> 16, - (color & 0x00ff00) >> 8, - (color & 0x0000ff) >> 0); - } - - xrdp_bitmap_set_pixel(self, j, i, color); - } - } - } - else if (header.bit_count == 8) /* 8 bit bitmap */ - { - /* read palette */ - if (g_file_seek(fd, 14 + header.size) < 0) - { - LOG(LOG_LEVEL_WARNING, "xrdp_bitmap_load: seek error in file %s", - filename); - } - size = header.clr_used * sizeof(int); - - init_stream(s, size); - /* Pre-fill the buffer, so if we get short reads we're - * not working with uninitialised data */ - g_memset(s->data, 0, size); - s->end = s->data + size; - - len = g_file_read(fd, s->data, size); - if (len != size) - { - LOG(LOG_LEVEL_ERROR, "xrdp_bitmap_load: " - "unexpected read length in file %s", - filename); - } - - for (i = 0; i < header.clr_used; i++) - { - in_uint32_le(s, palette1[i]); - } - - xrdp_bitmap_resize(self, header.image_width, header.image_height); - size = header.image_width * header.image_height; - init_stream(s, size); - /* Pre-fill the buffer, so if we get short reads we're - * not working with uninitialised data */ - g_memset(s->data, 0, size); - s->end = s->data + size; - - /* read data */ - for (i = header.image_height - 1; i >= 0; i--) - { - size = header.image_width; - k = g_file_read(fd, s->data + i * size, size); - - if (k != size) - { - LOG(LOG_LEVEL_WARNING, "xrdp_bitmap_load: error bitmap " - "file [%s] read", filename); - } - } - - for (i = 0; i < self->height; i++) - { - for (j = 0; j < self->width; j++) - { - in_uint8(s, k); - color = palette1[k]; - - if (self->bpp == 8) - { - color = xrdp_bitmap_get_index(self, palette, color); - } - else if (self->bpp == 15) - { - color = COLOR15((color & 0xff0000) >> 16, - (color & 0x00ff00) >> 8, - (color & 0x0000ff) >> 0); - } - else if (self->bpp == 16) - { - color = COLOR16((color & 0xff0000) >> 16, - (color & 0x00ff00) >> 8, - (color & 0x0000ff) >> 0); - } - - xrdp_bitmap_set_pixel(self, j, i, color); - } - } - } - else if (header.bit_count == 4) /* 4 bit bitmap */ - { - /* read palette */ - if (g_file_seek(fd, 14 + header.size) < 0) - { - LOG(LOG_LEVEL_WARNING, "xrdp_bitmap_load: seek error in file %s", - filename); - } - size = header.clr_used * sizeof(int); - - init_stream(s, size); - /* Pre-fill the buffer, so if we get short reads we're - * not working with uninitialised data */ - g_memset(s->data, 0, size); - s->end = s->data + size; - - len = g_file_read(fd, s->data, size); - if (len != size) - { - LOG(LOG_LEVEL_ERROR, "xrdp_bitmap_load: " - "unexpected read length in file %s", - filename); - } - - for (i = 0; i < header.clr_used; i++) - { - in_uint32_le(s, palette1[i]); - } - - xrdp_bitmap_resize(self, header.image_width, header.image_height); - size = (header.image_width * header.image_height) / 2; - init_stream(s, size); - /* Pre-fill the buffer, so if we get short reads we're - * not working with uninitialised data */ - g_memset(s->data, 0, size); - s->end = s->data + size; - - /* read data */ - for (i = header.image_height - 1; i >= 0; i--) - { - size = header.image_width / 2; - k = g_file_read(fd, s->data + i * size, size); - - if (k != size) - { - LOG(LOG_LEVEL_WARNING, "xrdp_bitmap_load: error bitmap " - "file [%s] read", filename); - } - } - - for (i = 0; i < self->height; i++) - { - for (j = 0; j < self->width; j++) - { - if ((j & 1) == 0) - { - in_uint8(s, k); - color = (k >> 4) & 0xf; - } - else - { - color = k & 0xf; - } - - color = palette1[color]; - - if (self->bpp == 8) - { - color = xrdp_bitmap_get_index(self, palette, color); - } - else if (self->bpp == 15) - { - color = COLOR15((color & 0xff0000) >> 16, - (color & 0x00ff00) >> 8, - (color & 0x0000ff) >> 0); - } - else if (self->bpp == 16) - { - color = COLOR16((color & 0xff0000) >> 16, - (color & 0x00ff00) >> 8, - (color & 0x0000ff) >> 0); - } - - xrdp_bitmap_set_pixel(self, j, i, color); - } - } - } - - g_file_close(fd); - free_stream(s); - } - else - { - LOG(LOG_LEVEL_ERROR, "xrdp_bitmap_load: error loading bitmap " - "from file [%s]", filename); - return 1; - } - - return 0; -} - -/*****************************************************************************/ -int -xrdp_bitmap_get_pixel(struct xrdp_bitmap *self, int x, int y) -{ - if (self == 0) - { - return 0; - } - - if (self->data == 0) - { - return 0; - } - - if (x >= 0 && x < self->width && y >= 0 && y < self->height) - { - if (self->bpp == 8) - { - return GETPIXEL8(self->data, x, y, self->width); - } - else if (self->bpp == 15 || self->bpp == 16) - { - return GETPIXEL16(self->data, x, y, self->width); - } - else if (self->bpp >= 24) - { - return GETPIXEL32(self->data, x, y, self->width); - } - } - - return 0; -} - -/*****************************************************************************/ -int -xrdp_bitmap_set_pixel(struct xrdp_bitmap *self, int x, int y, int pixel) -{ - if (self == 0) - { - return 0; - } - - if (self->data == 0) - { - return 0; - } - - if (x >= 0 && x < self->width && y >= 0 && y < self->height) - { - if (self->bpp == 8) - { - SETPIXEL8(self->data, x, y, self->width, pixel); - } - else if (self->bpp == 15 || self->bpp == 16) - { - SETPIXEL16(self->data, x, y, self->width, pixel); - } - else if (self->bpp >= 24) - { - SETPIXEL32(self->data, x, y, self->width, pixel); - } - } - - return 0; -} - -/*****************************************************************************/ -/* copy part of self at x, y to 0, 0 in dest */ -/* returns error */ -int -xrdp_bitmap_copy_box(struct xrdp_bitmap *self, - struct xrdp_bitmap *dest, - int x, int y, int cx, int cy) -{ - int i; - int destx; - int desty; - int incs; - int incd; - tui8 *s8; - tui8 *d8; - tui16 *s16; - tui16 *d16; - tui32 *s32; - tui32 *d32; - - if (self == 0) - { - return 1; - } - - if (dest == 0) - { - return 1; - } - - if (self->type != WND_TYPE_BITMAP && self->type != WND_TYPE_IMAGE) - { - return 1; - } - - if (dest->type != WND_TYPE_BITMAP && dest->type != WND_TYPE_IMAGE) - { - return 1; - } - - if (self->bpp != dest->bpp) - { - return 1; - } - - destx = 0; - desty = 0; - - if (!check_bounds(self, &x, &y, &cx, &cy)) - { - return 1; - } - - if (!check_bounds(dest, &destx, &desty, &cx, &cy)) - { - return 1; - } - - if (self->bpp >= 24) - { - s32 = ((tui32 *)(self->data)) + (self->width * y + x); - d32 = ((tui32 *)(dest->data)) + (dest->width * desty + destx); - incs = self->width - cx; - incd = dest->width - cx; - - for (i = 0; i < cy; i++) - { - g_memcpy(d32, s32, cx * 4); - s32 += cx; - d32 += cx; - - s32 += incs; - d32 += incd; - } - - } - else if (self->bpp == 15 || self->bpp == 16) - { - s16 = ((tui16 *)(self->data)) + (self->width * y + x); - d16 = ((tui16 *)(dest->data)) + (dest->width * desty + destx); - incs = self->width - cx; - incd = dest->width - cx; - - for (i = 0; i < cy; i++) - { - g_memcpy(d16, s16, cx * 2); - s16 += cx; - d16 += cx; - - s16 += incs; - d16 += incd; - } - } - else if (self->bpp == 8) - { - s8 = ((tui8 *)(self->data)) + (self->width * y + x); - d8 = ((tui8 *)(dest->data)) + (dest->width * desty + destx); - incs = self->width - cx; - incd = dest->width - cx; - - for (i = 0; i < cy; i++) - { - g_memcpy(d8, s8, cx); - s8 += cx; - d8 += cx; - - s8 += incs; - d8 += incd; - } - } - else - { - return 1; - } - - return 0; -} - /*****************************************************************************/ int xrdp_bitmap_hash_crc(struct xrdp_bitmap *self) diff --git a/xrdp/xrdp_bitmap_common.c b/xrdp/xrdp_bitmap_common.c new file mode 100644 index 0000000000..836ba819e1 --- /dev/null +++ b/xrdp/xrdp_bitmap_common.c @@ -0,0 +1,509 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2004-2014 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Common bitmap functions for all xrdp_bitmap*.c files + */ + +#if defined(HAVE_CONFIG_H) +#include +#endif + +#include + +#include "xrdp.h" + +/*****************************************************************************/ +/* Allocate bitmap for specified dimensions, checking for int overflow */ +static char * +alloc_bitmap_data(int width, int height, int Bpp) +{ + char *result = NULL; + if (width > 0 && height > 0 && Bpp > 0) + { + int len = width; + /* g_malloc() currently takes an 'int' size */ + if (len < INT_MAX / height) + { + len *= height; + if (len < INT_MAX / Bpp) + { + len *= Bpp; + result = (char *)malloc(len); + } + } + } + + return result; +} + +/*****************************************************************************/ +struct xrdp_bitmap * +xrdp_bitmap_create(int width, int height, int bpp, + int type, struct xrdp_wm *wm) +{ + struct xrdp_bitmap *self = (struct xrdp_bitmap *)NULL; + int Bpp = 0; + + self = (struct xrdp_bitmap *)g_malloc(sizeof(struct xrdp_bitmap), 1); + if (self == NULL) + { + LOG(LOG_LEVEL_ERROR, "xrdp_bitmap_create: no memory"); + return self; + } + + self->type = type; + self->width = width; + self->height = height; + self->bpp = bpp; + Bpp = 4; + + switch (bpp) + { + case 8: + Bpp = 1; + break; + case 15: + Bpp = 2; + break; + case 16: + Bpp = 2; + break; + } + + if (self->type == WND_TYPE_BITMAP || self->type == WND_TYPE_IMAGE) + { + self->data = alloc_bitmap_data(width, height, Bpp); + if (self->data == NULL) + { + LOG(LOG_LEVEL_ERROR, "xrdp_bitmap_create: size overflow %dx%dx%d", + width, height, Bpp); + g_free(self); + return NULL; + } + } + +#if defined(XRDP_PAINTER) + if (self->type == WND_TYPE_SCREEN) /* noorders */ + { + LOG_DEVEL(LOG_LEVEL_INFO, "xrdp_bitmap_create: noorders"); + self->data = alloc_bitmap_data(width, height, Bpp); + if (self->data == NULL) + { + LOG(LOG_LEVEL_ERROR, "xrdp_bitmap_create: size overflow %dx%dx%d", + width, height, Bpp); + g_free(self); + return NULL; + } + } +#endif + + if (self->type != WND_TYPE_BITMAP) + { + self->child_list = list_create(); + } + + self->line_size = width * Bpp; + + if (self->type == WND_TYPE_COMBO) + { + self->string_list = list_create(); + self->string_list->auto_free = 1; + self->data_list = list_create(); + self->data_list->auto_free = 1; + } + + self->wm = wm; + return self; +} + +/*****************************************************************************/ +struct xrdp_bitmap * +xrdp_bitmap_create_with_data(int width, int height, + int bpp, char *data, + struct xrdp_wm *wm) +{ + struct xrdp_bitmap *self = (struct xrdp_bitmap *)NULL; + int Bpp; +#if defined(NEED_ALIGN) + tintptr data_as_int; +#endif + + self = (struct xrdp_bitmap *)g_malloc(sizeof(struct xrdp_bitmap), 1); + self->type = WND_TYPE_BITMAP; + self->width = width; + self->height = height; + self->bpp = bpp; + self->wm = wm; + + Bpp = 4; + switch (bpp) + { + case 8: + Bpp = 1; + break; + case 15: + Bpp = 2; + break; + case 16: + Bpp = 2; + break; + } + self->line_size = width * Bpp; + +#if defined(NEED_ALIGN) + data_as_int = (tintptr) data; + if (((bpp >= 24) && (data_as_int & 3)) || + (((bpp == 15) || (bpp == 16)) && (data_as_int & 1))) + { + /* got to copy data here, it's not aligned + other calls in this file assume alignment */ + self->data = (char *)g_malloc(width * height * Bpp, 0); + g_memcpy(self->data, data, width * height * Bpp); + return self; + } +#endif + self->data = data; + self->do_not_free_data = 1; + return self; +} + +/*****************************************************************************/ +void +xrdp_bitmap_delete(struct xrdp_bitmap *self) +{ + int i = 0; + struct xrdp_mod_data *mod_data = (struct xrdp_mod_data *)NULL; + + if (self == 0) + { + return; + } + + if (self->wm != 0) + { + if (self->wm->focused_window != 0) + { + if (self->wm->focused_window->focused_control == self) + { + self->wm->focused_window->focused_control = 0; + } + } + + if (self->wm->focused_window == self) + { + self->wm->focused_window = 0; + } + + if (self->wm->dragging_window == self) + { + self->wm->dragging_window = 0; + } + + if (self->wm->button_down == self) + { + self->wm->button_down = 0; + } + + if (self->wm->popup_wnd == self) + { + self->wm->popup_wnd = 0; + } + + if (self->wm->login_window == self) + { + self->wm->login_window = 0; + } + + if (self->wm->log_wnd == self) + { + self->wm->log_wnd = 0; + } + } + + if (self->child_list != 0) + { + for (i = self->child_list->count - 1; i >= 0; i--) + { + xrdp_bitmap_delete((struct xrdp_bitmap *)self->child_list->items[i]); + } + + list_delete(self->child_list); + } + + if (self->parent != 0) + { + i = list_index_of(self->parent->child_list, (long)self); + + if (i >= 0) + { + list_remove_item(self->parent->child_list, i); + } + } + + if (self->string_list != 0) /* for combo */ + { + list_delete(self->string_list); + } + + if (self->data_list != 0) /* for combo */ + { + for (i = 0; i < self->data_list->count; i++) + { + mod_data = (struct xrdp_mod_data *)list_get_item(self->data_list, i); + + if (mod_data != 0) + { + list_delete(mod_data->names); + list_delete(mod_data->values); + } + } + + list_delete(self->data_list); + } + + if (!self->do_not_free_data) + { + g_free(self->data); + } + + g_free(self->caption1); + g_free(self); +} + +/*****************************************************************************/ +/* returns error */ +int +xrdp_bitmap_resize(struct xrdp_bitmap *self, int width, int height) +{ + int Bpp = 0; + + if ((width == self->width) && (height == self->height)) + { + return 0; + } + + if (self->do_not_free_data) + { + return 1; + } + + self->width = width; + self->height = height; + Bpp = 4; + + switch (self->bpp) + { + case 8: + Bpp = 1; + break; + case 15: + Bpp = 2; + break; + case 16: + Bpp = 2; + break; + } + + g_free(self->data); + self->data = (char *)g_malloc(width * height * Bpp, 0); + self->line_size = width * Bpp; + return 0; +} + +/*****************************************************************************/ +int +xrdp_bitmap_get_pixel(struct xrdp_bitmap *self, int x, int y) +{ + if (self == 0) + { + return 0; + } + + if (self->data == 0) + { + return 0; + } + + if (x >= 0 && x < self->width && y >= 0 && y < self->height) + { + if (self->bpp == 8) + { + return GETPIXEL8(self->data, x, y, self->width); + } + else if (self->bpp == 15 || self->bpp == 16) + { + return GETPIXEL16(self->data, x, y, self->width); + } + else if (self->bpp >= 24) + { + return GETPIXEL32(self->data, x, y, self->width); + } + } + + return 0; +} + +/*****************************************************************************/ +int +xrdp_bitmap_set_pixel(struct xrdp_bitmap *self, int x, int y, int pixel) +{ + if (self == 0) + { + return 0; + } + + if (self->data == 0) + { + return 0; + } + + if (x >= 0 && x < self->width && y >= 0 && y < self->height) + { + if (self->bpp == 8) + { + SETPIXEL8(self->data, x, y, self->width, pixel); + } + else if (self->bpp == 15 || self->bpp == 16) + { + SETPIXEL16(self->data, x, y, self->width, pixel); + } + else if (self->bpp >= 24) + { + SETPIXEL32(self->data, x, y, self->width, pixel); + } + } + + return 0; +} + +/*****************************************************************************/ +/* copy part of self at x, y to 0, 0 in dest */ +/* returns error */ +int +xrdp_bitmap_copy_box(struct xrdp_bitmap *self, + struct xrdp_bitmap *dest, + int x, int y, int cx, int cy) +{ + int i; + int destx; + int desty; + int incs; + int incd; + tui8 *s8; + tui8 *d8; + tui16 *s16; + tui16 *d16; + tui32 *s32; + tui32 *d32; + + if (self == 0) + { + return 1; + } + + if (dest == 0) + { + return 1; + } + + if (self->type != WND_TYPE_BITMAP && self->type != WND_TYPE_IMAGE) + { + return 1; + } + + if (dest->type != WND_TYPE_BITMAP && dest->type != WND_TYPE_IMAGE) + { + return 1; + } + + if (self->bpp != dest->bpp) + { + return 1; + } + + destx = 0; + desty = 0; + + if (!check_bounds(self, &x, &y, &cx, &cy)) + { + return 1; + } + + if (!check_bounds(dest, &destx, &desty, &cx, &cy)) + { + return 1; + } + + if (self->bpp >= 24) + { + s32 = ((tui32 *)(self->data)) + (self->width * y + x); + d32 = ((tui32 *)(dest->data)) + (dest->width * desty + destx); + incs = self->width - cx; + incd = dest->width - cx; + + for (i = 0; i < cy; i++) + { + g_memcpy(d32, s32, cx * 4); + s32 += cx; + d32 += cx; + + s32 += incs; + d32 += incd; + } + + } + else if (self->bpp == 15 || self->bpp == 16) + { + s16 = ((tui16 *)(self->data)) + (self->width * y + x); + d16 = ((tui16 *)(dest->data)) + (dest->width * desty + destx); + incs = self->width - cx; + incd = dest->width - cx; + + for (i = 0; i < cy; i++) + { + g_memcpy(d16, s16, cx * 2); + s16 += cx; + d16 += cx; + + s16 += incs; + d16 += incd; + } + } + else if (self->bpp == 8) + { + s8 = ((tui8 *)(self->data)) + (self->width * y + x); + d8 = ((tui8 *)(dest->data)) + (dest->width * desty + destx); + incs = self->width - cx; + incd = dest->width - cx; + + for (i = 0; i < cy; i++) + { + g_memcpy(d8, s8, cx); + s8 += cx; + d8 += cx; + + s8 += incs; + d8 += incd; + } + } + else + { + return 1; + } + + return 0; +} diff --git a/xrdp/xrdp_bitmap_load.c b/xrdp/xrdp_bitmap_load.c new file mode 100644 index 0000000000..c454a1c92c --- /dev/null +++ b/xrdp/xrdp_bitmap_load.c @@ -0,0 +1,1099 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2004-2014 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Load xrdp_bitmap from file + */ + +#if defined(HAVE_CONFIG_H) +#include +#endif + +#ifdef USE_IMLIB2 +# include +#endif + +#include "xrdp.h" +#include "log.h" + +/* Rounds up to the nearest multiple of 4 */ +#define ROUND4(x) (((x) + 3) / 4 * 4) + +/* Are we using the builtin BMP format-only loader */ + +#ifdef USE_BUILTIN_LOADER +# undef USE_BUILTIN_LOADER +#endif + +#ifndef USE_IMLIB2 +# define USE_BUILTIN_LOADER +#endif + +/** + * Describes a box within an image + */ +struct box +{ + int left_margin; + int width; + int top_margin; + int height; +}; + +/**************************************************************************//** + * Calculates a zoom box, from source and destination image dimensions + * + * The zoom box is the largest centred part of the source image which + * preserves the aspect ratio of the destination image. We find it + * by cutting off the left and right sides of the source, or the top + * and bottom. + * + * @param src_width Width of source image + * @param src_height Height of source image + * @param dst_width Width of destination image + * @param dst_height Height of destination image + * @param[out] zb_return Zoom box + * @return 0 for success + */ +static int +calculate_zoom_box(int src_width, int src_height, + int dst_width, int dst_height, + struct box *zb_return) +{ + int result = 1; + struct box zb; + + if (dst_height == 0 || src_height == 0) + { + LOG(LOG_LEVEL_ERROR, "Can't zoom to or from zero-width images"); + } + else + { + double dst_ratio = (double)dst_width / dst_height; + double src_ratio = (double)src_width / src_height; + + if (src_ratio > dst_ratio) + { + /* Source is relatively wider than source. Select a box + * narrower than the source, but the same height */ + zb.width = (int)(dst_ratio * src_height + .5); + zb.left_margin = (src_width - zb.width) / 2; + zb.height = src_height; + zb.top_margin = 0; + } + else + { + /* Source is relatively taller than source (or same shape) */ + zb.width = src_width; + zb.left_margin = 0; + zb.height = (int)(src_width / dst_ratio + .5); + zb.top_margin = (src_height - zb.height) / 2; + } + + /* Only allow meaningful zoom boxes */ + if (zb.width < 1 || zb.height < 1) + { + LOG(LOG_LEVEL_WARNING, "Ignoring pathological zoom" + " request (%dx%d) -> (%dx%d)", src_width, src_height, + dst_width, dst_height); + } + else + { + *zb_return = zb; + result = 0; + } + } + + return result; +} + +/*****************************************************************************/ +static int +xrdp_bitmap_get_index(struct xrdp_bitmap *self, const int *palette, int color) +{ + int r = 0; + int g = 0; + int b = 0; + + r = (color & 0xff0000) >> 16; + g = (color & 0x00ff00) >> 8; + b = (color & 0x0000ff) >> 0; + r = (r >> 5) << 0; + g = (g >> 5) << 3; + b = (b >> 6) << 6; + return (b | g | r); +} + +#ifdef USE_BUILTIN_LOADER +/**************************************************************************//** + * Private routine to swap pixel data between two pixmaps + * @param a First bitmap + * @param b Second bitmap + * + * The main use-case for this routine is to modify an existing bitmap using + * the following logic:- + * - Create a temporary WND_TYPE_BITMAP + * - Process the data in a bitmap in some way, moving it to the temporary + * - Call this routine + * - Delete the temporary + * + */ +static void +swap_pixel_data(struct xrdp_bitmap *a, struct xrdp_bitmap *b) +{ + int tmp_width = a->width; + int tmp_height = a->height; + int tmp_bpp = a->bpp; + int tmp_line_size = a->line_size; + char *tmp_data = a->data; + int tmp_do_not_free_data = a->do_not_free_data; + + a->width = b->width; + a->height = b->height; + a->bpp = b->bpp; + a->line_size = b->line_size; + a->data = b->data; + a->do_not_free_data = b->do_not_free_data; + + b->width = tmp_width; + b->height = tmp_height; + b->bpp = tmp_bpp; + b->line_size = tmp_line_size; + b->data = tmp_data; + b->do_not_free_data = tmp_do_not_free_data; +} +#endif /* USE_BUILTIN_LOADER */ + +#ifdef USE_BUILTIN_LOADER +/**************************************************************************//** + * Scales a bitmap image + * + * @param self Bitmap to scale + * @param target_width target width + * @param target_height target height + * @return 0 for success + */ +static int +xrdp_bitmap_scale(struct xrdp_bitmap *self, int targ_width, int targ_height) +{ + int src_width = self->width; + int src_height = self->height; + + if (src_width != targ_width || src_height != targ_height) + { + struct xrdp_bitmap *target = + xrdp_bitmap_create(targ_width, targ_height, + self->bpp, WND_TYPE_BITMAP, 0); + int targ_x, targ_y; + + if (target == NULL) + { + /* Error is logged */ + return 1; + } + + /* For each pixel in the target pixmap, scale to one in the source */ + for (targ_x = 0 ; targ_x < targ_width; ++targ_x) + { + int src_x = targ_x * src_width / targ_width; + for (targ_y = 0 ; targ_y < targ_height; ++targ_y) + { + int src_y = targ_y * src_height / targ_height; + int pixel = xrdp_bitmap_get_pixel(self, src_x, src_y); + xrdp_bitmap_set_pixel(target, targ_x, targ_y, pixel); + } + } + swap_pixel_data(self, target); + xrdp_bitmap_delete(target); + } + + return 0; +} +#endif /* USE_BUILTIN_LOADER */ + + +#ifdef USE_BUILTIN_LOADER +/**************************************************************************//** + * Zooms a bitmap image + * + * @param self Bitmap to zoom + * @param target_width target width + * @param target_height target height + * @return 0 for success + * + * This works the same way as a scaled image, but the aspect ratio is + * maintained by removing pixels from the top-and-bottom, + * or the left-and-right before scaling. + */ +static int +xrdp_bitmap_zoom(struct xrdp_bitmap *self, int targ_width, int targ_height) +{ + struct box zb; + + int result = 0; + + if (calculate_zoom_box(self->width, self->height, + targ_width, targ_height, &zb) == 0) + { + /* Need to chop anything? */ + if (zb.top_margin != 0 || zb.left_margin != 0) + { + struct xrdp_bitmap *zbitmap; + zbitmap = xrdp_bitmap_create(zb.width, zb.height, self->bpp, + WND_TYPE_BITMAP, 0); + if (zbitmap == NULL) + { + LOG(LOG_LEVEL_ERROR, "xrdp_bitmap_zoom: no memory"); + result = 1; + } + else + { + result = xrdp_bitmap_copy_box(self, zbitmap, + zb.left_margin, zb.top_margin, + zb.width, zb.height); + if (result != 0) + { + LOG(LOG_LEVEL_ERROR, "xrdp_bitmap_zoom: can't copy box"); + } + else + { + swap_pixel_data(self, zbitmap); + } + xrdp_bitmap_delete(zbitmap); + } + } + } + + if (result == 0) + { + result = xrdp_bitmap_scale(self, targ_width, targ_height); + } + + return result; +} +#endif /* USE_BUILTIN_LOADER */ + +#ifdef USE_BUILTIN_LOADER +/**************************************************************************//** + * reads the palette from a bmp file with a palette embedded in it + * + * @pre The read position in the file is at the end of the bmp DIB header. + * + * @param filename Name of file + * @param fd File descriptor for file + * @param header Pointer to BMP header info struct + * @param palette output. Must be at least 256 elements + */ +static void +read_palette(const char *filename, int fd, + const struct xrdp_bmp_header *header, int *palette) +{ + struct stream *s; + int clr_used; + int palette_size; + int len; + int i; + + /* Find the number of colors used in the bitmap */ + if (header->clr_used != 0) + { + clr_used = header->clr_used; + /* Is the header value sane? */ + if (clr_used < 0 || clr_used > 256) + { + clr_used = 256; + LOG(LOG_LEVEL_WARNING, "%s : Invalid palette size %d in file %s", + __func__, header->clr_used, filename); + } + } + else if (header->bit_count == 4) + { + clr_used = 16; + } + else + { + clr_used = 256; + } + + palette_size = clr_used * 4; + + make_stream(s); + init_stream(s, palette_size); + + /* Pre-fill the buffer, so if we get short reads we're + * not working with uninitialised data */ + g_memset(s->data, 0, palette_size); + s->end = s->data + palette_size; + + len = g_file_read(fd, s->data, palette_size); + if (len != palette_size) + { + LOG(LOG_LEVEL_WARNING, "%s: unexpected read length in file %s", + __func__, filename); + } + + for (i = 0; i < clr_used; ++i) + { + in_uint32_le(s, palette[i]); + } + + free_stream(s); +} +#endif /* USE_BUILTIN_LOADER */ + +#ifdef USE_BUILTIN_LOADER +/**************************************************************************//** + * Process a row of data from a 24-bit bmp file + * + * @param self Bitmap we're filling in + * @param s Stream containing bitmap data + * @param in_palette Palette from bmp file (unused) + * @param out_palette Palette for output bitmap + * @param row Row number + */ +static void +process_row_data_24bit(struct xrdp_bitmap *self, + struct stream *s, + const int *in_palette, + const int *out_palette, + int row) +{ + int j; + int k; + int color; + + for (j = 0; j < self->width; ++j) + { + in_uint8(s, k); + color = k; + in_uint8(s, k); + color |= k << 8; + in_uint8(s, k); + color |= k << 16; + + if (self->bpp == 8) + { + color = xrdp_bitmap_get_index(self, out_palette, color); + } + else if (self->bpp == 15) + { + color = COLOR15((color & 0xff0000) >> 16, + (color & 0x00ff00) >> 8, + (color & 0x0000ff) >> 0); + } + else if (self->bpp == 16) + { + color = COLOR16((color & 0xff0000) >> 16, + (color & 0x00ff00) >> 8, + (color & 0x0000ff) >> 0); + } + + xrdp_bitmap_set_pixel(self, j, row, color); + } +} +#endif /* USE_BUILTIN_LOADER */ + +#ifdef USE_BUILTIN_LOADER +/**************************************************************************//** + * Process a row of data from an 8-bit bmp file + * + * @param self Bitmap we're filling in + * @param s Stream containing bitmap data + * @param in_palette Palette from bmp file + * @param out_palette Palette for output bitmap + * @param row Row number + */ +static void +process_row_data_8bit(struct xrdp_bitmap *self, + struct stream *s, + const int *in_palette, + const int *out_palette, + int row) +{ + int j; + int k; + int color; + + for (j = 0; j < self->width; ++j) + { + in_uint8(s, k); + color = in_palette[k]; + + if (self->bpp == 8) + { + color = xrdp_bitmap_get_index(self, out_palette, color); + } + else if (self->bpp == 15) + { + color = COLOR15((color & 0xff0000) >> 16, + (color & 0x00ff00) >> 8, + (color & 0x0000ff) >> 0); + } + else if (self->bpp == 16) + { + color = COLOR16((color & 0xff0000) >> 16, + (color & 0x00ff00) >> 8, + (color & 0x0000ff) >> 0); + } + + xrdp_bitmap_set_pixel(self, j, row, color); + } +} +#endif /* USE_BUILTIN_LOADER */ + +#ifdef USE_BUILTIN_LOADER +/**************************************************************************//** + * Process a row of data from an 4-bit bmp file + * + * @param self Bitmap we're filling in + * @param s Stream containing bitmap data + * @param in_palette Palette from bmp file + * @param out_palette Palette for output bitmap + * @param row Row number + */ +static void +process_row_data_4bit(struct xrdp_bitmap *self, + struct stream *s, + const int *in_palette, + const int *out_palette, + int row) +{ + int j; + int k = 0; + int color; + + for (j = 0; j < self->width; ++j) + { + if ((j & 1) == 0) + { + in_uint8(s, k); + color = (k >> 4) & 0xf; + } + else + { + color = k & 0xf; + } + + color = in_palette[color]; + + if (self->bpp == 8) + { + color = xrdp_bitmap_get_index(self, out_palette, color); + } + else if (self->bpp == 15) + { + color = COLOR15((color & 0xff0000) >> 16, + (color & 0x00ff00) >> 8, + (color & 0x0000ff) >> 0); + } + else if (self->bpp == 16) + { + color = COLOR16((color & 0xff0000) >> 16, + (color & 0x00ff00) >> 8, + (color & 0x0000ff) >> 0); + } + + xrdp_bitmap_set_pixel(self, j, row, color); + } +} +#endif /* USE_BUILTIN_LOADER */ + +#ifdef USE_BUILTIN_LOADER +/*****************************************************************************/ +/* load a bmp file */ +/* return 0 ok */ +/* return 1 error */ +static int +xrdp_bitmap_load_bmp(struct xrdp_bitmap *self, const char *filename, + const int *out_palette) +{ + int fd = 0; + int len = 0; + int row = 0; + int row_size = 0; + int bmp_palette[256] = {0}; + char fixed_header[14] = {0}; + struct xrdp_bmp_header header = {0}; + struct stream *s = (struct stream *)NULL; + /* Pointer to row data processing function */ + void (*process_row_data)(struct xrdp_bitmap * self, + struct stream * s, + const int *in_palette, + const int *out_palette, + int row); + + if (!g_file_exist(filename)) + { + LOG(LOG_LEVEL_ERROR, "%s: error bitmap file [%s] does not exist", + __func__, filename); + return 1; + } + + fd = g_file_open(filename); + + if (fd == -1) + { + LOG(LOG_LEVEL_ERROR, "%s: error loading bitmap from file [%s]", + __func__, filename); + return 1; + } + + /* read the fixed file header */ + if (g_file_read(fd, fixed_header, 14) != 14) + { + LOG(LOG_LEVEL_ERROR, "%s: error bitmap file [%s] read error", + __func__, filename); + g_file_close(fd); + return 1; + } + + if ((fixed_header[0] != 'B') || (fixed_header[1] != 'M')) + { + LOG(LOG_LEVEL_ERROR, "%s: error bitmap file [%s] not BMP file", + __func__, filename); + g_file_close(fd); + return 1; + } + + /* read information header */ + make_stream(s); + init_stream(s, 8192); + len = g_file_read(fd, s->data, 40); /* size better be 40 */ + if (len != 40) + { + LOG(LOG_LEVEL_ERROR, "%s: unexpected read length %d in file %s", + __func__, len, filename); + free_stream(s); + g_file_close(fd); + return 1; + } + s->end = s->data + len; + in_uint32_le(s, header.size); + in_uint32_le(s, header.image_width); + in_uint32_le(s, header.image_height); + in_uint16_le(s, header.planes); + in_uint16_le(s, header.bit_count); + in_uint32_le(s, header.compression); + in_uint32_le(s, header.image_size); + in_uint32_le(s, header.x_pels_per_meter); + in_uint32_le(s, header.y_pels_per_meter); + in_uint32_le(s, header.clr_used); + in_uint32_le(s, header.clr_important); + + if (header.compression != 0) + { + LOG(LOG_LEVEL_WARNING, "%s: error bitmap file [%s]" + " unsupported compression value %d", + __func__, filename, header.compression); + } + + if (g_file_seek(fd, 14 + header.size) < 0) + { + LOG(LOG_LEVEL_WARNING, "%s: seek error in file %s", + __func__, filename); + } + + /* Validate the bit count for the file, read any palette, and + * dtermine the row size and row processing function */ + switch (header.bit_count) + { + case 24: + row_size = ROUND4(header.image_width * 3); + process_row_data = process_row_data_24bit; + break; + + case 8: + read_palette(filename, fd, &header, bmp_palette); + row_size = ROUND4(header.image_width); + process_row_data = process_row_data_8bit; + break; + + case 4: + read_palette(filename, fd, &header, bmp_palette); + /* The storage for a row is a complex calculation for 4 bit pixels. + * a width of 1-8 pixels takes 4 bytes + * a width of 9-16 pixels takes 8 bytes, etc + * So bytes = (width + 7) / 8 * 4 + */ + row_size = ((header.image_width + 7) / 8 * 4); + process_row_data = process_row_data_4bit; + break; + + default: + LOG(LOG_LEVEL_ERROR, "%s: error bitmap file [%s] bad bpp %d", + __func__, filename, header.bit_count); + free_stream(s); + g_file_close(fd); + return 1; + } + + xrdp_bitmap_resize(self, header.image_width, header.image_height); + + /* Set up the row data buffer. Pre fill it, so if we get short reads + * we're not working with uninitialised data */ + init_stream(s, row_size); + g_memset(s->data, 0, row_size); + s->end = s->data + row_size; + + /* read and process the pixel data a row at a time */ + for (row = header.image_height - 1; row >= 0; row--) + { + len = g_file_read(fd, s->data, row_size); + + if (len != row_size) + { + LOG(LOG_LEVEL_WARNING, "%s: error bitmap file [%s] read", + __func__, filename); + } + + s->p = s->data; + (*process_row_data)(self, s, bmp_palette, out_palette, row); + } + + g_file_close(fd); + free_stream(s); + + return 0; +} +#endif /* USE_BUILTIN_LOADER */ + +#ifdef USE_BUILTIN_LOADER +/*****************************************************************************/ +int +xrdp_bitmap_load(struct xrdp_bitmap *self, const char *filename, + const int *palette, + int background, + enum xrdp_bitmap_load_transform transform, + int twidth, + int theight) +{ + /* this is the default bmp-only implementation if a graphics library + * isn't built in */ + + int result = xrdp_bitmap_load_bmp(self, filename, palette); + if (result == 0) + { + switch (transform) + { + case XBLT_NONE: + break; + + case XBLT_SCALE: + result = xrdp_bitmap_scale(self, twidth, theight); + break; + + case XBLT_ZOOM: + result = xrdp_bitmap_zoom(self, twidth, theight); + break; + + default: + LOG(LOG_LEVEL_WARNING, "Invalid bitmap transform %d specified", + transform); + } + } + + return result; +} +#endif /* USE_BUILTIN_LOADER */ + +#ifdef USE_IMLIB2 +/**************************************************************************//** + * Log an error from the Imlib2 library + * + * @param level Log level to use + * @param filename file we're trying to load + * @param lerr Error return from imlib2 + */ +static void +log_imlib2_error(enum logLevels level, const char *filename, + Imlib_Load_Error lerr) +{ + const char *msg; + char buff[256]; + + switch (lerr) + { + case IMLIB_LOAD_ERROR_NONE: + msg = "No error"; + break; + case IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST: + msg = "No such file"; + break; + case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_READ: + msg = "Permission denied"; + break; + case IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT: + msg = "Unrecognised file format"; + break; + case IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY: + case IMLIB_LOAD_ERROR_PATH_TOO_LONG: + case IMLIB_LOAD_ERROR_PATH_COMPONENT_NON_EXISTANT: + case IMLIB_LOAD_ERROR_PATH_POINTS_OUTSIDE_ADDRESS_SPACE: + case IMLIB_LOAD_ERROR_TOO_MANY_SYMBOLIC_LINKS: + msg = "Bad filename"; + break; + case IMLIB_LOAD_ERROR_OUT_OF_MEMORY: + msg = " No memory"; + break; + case IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS: + msg = "No file decriptors"; + break; + case IMLIB_LOAD_ERROR_OUT_OF_DISK_SPACE: + msg = "No disk space"; + break; + case IMLIB_LOAD_ERROR_UNKNOWN: + msg = "Unknown error"; + break; + default: + g_snprintf(buff, sizeof(buff), "Unrecognised code %d", lerr); + msg = buff; + } + + LOG(LOG_LEVEL_ERROR, "Error loading %s [%s]", filename, msg); +} +#endif /* USE_IMLIB2 */ + +#ifdef USE_IMLIB2 +/**************************************************************************//** + * Blend an imlib2 image onto a background of the specified color + * + * The current context image is merged. On return the new image is the + * current context image, and the old image is deleted. + * + * @param filename Filename we're working on (for error reporting) + * @param r Background red + * @param g Background green + * @param g Background blue + * + * @return 0 for success. On failure the current context image is unchanged. + */ +static int +blend_imlib_image_onto_background(const char *filename, int r, int g, int b) +{ + int result = 0; + Imlib_Image img = imlib_context_get_image(); + + if (img == NULL) + { + LOG(LOG_LEVEL_ERROR, "No context for blending image"); + result = 1; + } + else + { + int width = imlib_image_get_width(); + int height = imlib_image_get_height(); + + /* Create a suitable image to merge this one onto */ + Imlib_Image bg = imlib_create_image(width, height); + if (bg == NULL) + { + log_imlib2_error(LOG_LEVEL_ERROR, filename, + IMLIB_LOAD_ERROR_OUT_OF_MEMORY); + result = 1; + } + else + { + imlib_context_set_image(bg); + imlib_context_set_color(r, g, b, 0xff); + imlib_image_fill_rectangle(0, 0, width, height); + imlib_blend_image_onto_image(img, 0, + 0, 0, width, height, + 0, 0, width, height); + imlib_context_set_image(img); + imlib_free_image(); + imlib_context_set_image(bg); + } + } + return result; +} +#endif /* USE_IMLIB2 */ + +#ifdef USE_IMLIB2 +/**************************************************************************//** + * Scales an imlib2 image + * + * The current context image is scaled. On return the new image is the + * current context image, and the old image is deleted. + * + * @param filename Filename we're working on (for error reporting) + * @param twidth target width + * @param theight target height + * @return 0 for success + */ +static int +scale_imlib_image(const char *filename, int twidth, int theight) +{ + int result = 0; + Imlib_Image img = imlib_context_get_image(); + + if (img == NULL) + { + LOG(LOG_LEVEL_ERROR, "No context for scaling image"); + result = 1; + } + else + { + int width = imlib_image_get_width(); + int height = imlib_image_get_height(); + + Imlib_Image newimg = imlib_create_cropped_scaled_image( + 0, 0, width, height, twidth, theight); + if (newimg == NULL) + { + log_imlib2_error(LOG_LEVEL_ERROR, filename, + IMLIB_LOAD_ERROR_OUT_OF_MEMORY); + result = 1; + } + else + { + imlib_free_image(); + imlib_context_set_image(newimg); + } + } + return result; +} +#endif /* USE_IMLIB2 */ + +#ifdef USE_IMLIB2 +/**************************************************************************//** + * Zooms an imlib2 image + * + * @param filename Filename we're working on (for error reporting) + * @param twidth target width + * @param theight target height + * @return 0 for success + */ +static int +zoom_imlib_image(const char *filename, int twidth, int theight) +{ + int result = 0; + Imlib_Image img = imlib_context_get_image(); + + if (img == NULL) + { + LOG(LOG_LEVEL_ERROR, "No context for zooming image"); + result = 1; + } + else + { + struct box zb; + Imlib_Image newimg = NULL; + int width = imlib_image_get_width(); + int height = imlib_image_get_height(); + + if (calculate_zoom_box(width, height, + twidth, theight, &zb) == 0) + { + newimg = imlib_create_cropped_scaled_image( + zb.left_margin, zb.top_margin, + zb.width, zb.height, twidth, theight); + } + else + { + /* Can't zoom - scale the image instead */ + newimg = imlib_create_cropped_scaled_image( + 0, 0, width, height, twidth, theight); + } + if (newimg == NULL) + + { + log_imlib2_error(LOG_LEVEL_ERROR, filename, + IMLIB_LOAD_ERROR_OUT_OF_MEMORY); + result = 1; + } + else + { + imlib_free_image(); + imlib_context_set_image(newimg); + } + } + return result; +} +#endif /* USE_IMLIB2 */ + +#ifdef USE_IMLIB2 +/**************************************************************************//** + * Copies imlib2 image data to a bitmap + * + * @param self bitmap to copy data to + * @param out_palette Palette for output bitmap + * @return 0 for success + */ +static int +copy_imlib_data_to_bitmap(struct xrdp_bitmap *self, + const int *out_palette) +{ + int result = 0; + Imlib_Image img = imlib_context_get_image(); + + if (img == NULL) + { + LOG(LOG_LEVEL_ERROR, "No context for zooming image"); + result = 1; + } + else + { + int width = imlib_image_get_width(); + int height = imlib_image_get_height(); + int i; + int j; + DATA32 *bdata; + int color; + xrdp_bitmap_resize(self, width, height); + + bdata = imlib_image_get_data_for_reading_only(); + for (j = 0 ; j < height; ++j) + { + for (i = 0 ; i < width ; ++i) + { + color = (*bdata++ & 0xffffff); + + if (self->bpp == 8) + { + color = xrdp_bitmap_get_index(self, out_palette, color); + } + else if (self->bpp == 15) + { + color = COLOR15((color & 0xff0000) >> 16, + (color & 0x00ff00) >> 8, + (color & 0x0000ff) >> 0); + } + else if (self->bpp == 16) + { + color = COLOR16((color & 0xff0000) >> 16, + (color & 0x00ff00) >> 8, + (color & 0x0000ff) >> 0); + } + + xrdp_bitmap_set_pixel(self, i, j, color); + } + } + } + + return result; +} +#endif /* USE_IMLIB2 */ + +#ifdef USE_IMLIB2 +/** + * Converts an xrdp HCOLOR into RGB values used by imlib2 + * + * @param hcolor Color to convert + * @param bpp Bits-per-pixel for the hcolor + * @param[out] r Red value + * @param[out] g Green value + * @param[out] b Blue value + */ +static void hcolor_to_rgb(int hcolor, int bpp, int *r, int *g, int *b) +{ + switch (bpp) + { + case 8: + *r = (hcolor & 0x7) << 5; + *g = (hcolor & 0x38) << 2; + *b = (hcolor & 0xc0); + break; + + case 15: + SPLITCOLOR15(*r, *g, *b, hcolor); + break; + + case 16: + SPLITCOLOR16(*r, *g, *b, hcolor); + break; + + default: + /* Beware : HCOLOR is BGR, not RGB */ + *r = hcolor & 0xff; + *g = (hcolor >> 8) & 0xff; + *b = (hcolor >> 16) & 0xff; + break; + } +} +#endif + +#ifdef USE_IMLIB2 +/*****************************************************************************/ +int +xrdp_bitmap_load(struct xrdp_bitmap *self, const char *filename, + const int *palette, + int background, + enum xrdp_bitmap_load_transform transform, + int twidth, + int theight) +{ + int result = 0; + Imlib_Load_Error lerr; + int free_context_image = 0; /* Set if we've got an image loaded */ + Imlib_Image img = imlib_load_image_with_error_return(filename, &lerr); + + /* Load the image */ + if (img == NULL) + { + log_imlib2_error(LOG_LEVEL_ERROR, filename, lerr); + result = 1; + } + else + { + imlib_context_set_image(img); + free_context_image = 1; + } + + /* Sort out the background */ + if (result == 0 && imlib_image_has_alpha()) + { + int r; + int g; + int b; + hcolor_to_rgb(background, self->bpp, &r, &g, &b); + + result = blend_imlib_image_onto_background(filename, r, g, b); + } + + if (result == 0) + { + switch (transform) + { + case XBLT_NONE: + break; + + case XBLT_SCALE: + result = scale_imlib_image(filename, twidth, theight); + break; + + case XBLT_ZOOM: + result = zoom_imlib_image(filename, twidth, theight); + break; + + default: + LOG(LOG_LEVEL_WARNING, "Invalid bitmap transform %d specified", + transform); + } + } + + if (result == 0) + { + result = copy_imlib_data_to_bitmap(self, palette); + } + + if (free_context_image) + { + imlib_free_image(); + } + + return result; +} +#endif /* USE_IMLIB2 */ diff --git a/xrdp/xrdp_login_wnd.c b/xrdp/xrdp_login_wnd.c index 0d640f85b3..bf25391241 100644 --- a/xrdp/xrdp_login_wnd.c +++ b/xrdp/xrdp_login_wnd.c @@ -643,7 +643,9 @@ xrdp_login_wnd_create(struct xrdp_wm *self) int log_width; int log_height; int regular; - int primary_x_offset; + int primary_width; /* Dimensions of primary screen */ + int primary_height; + int primary_x_offset; /* Offset of centre of primary screen */ int primary_y_offset; int index; int x; @@ -653,8 +655,10 @@ xrdp_login_wnd_create(struct xrdp_wm *self) globals = &self->xrdp_config->cfg_globals; - primary_x_offset = self->screen->width / 2; - primary_y_offset = self->screen->height / 2; + primary_width = self->screen->width; + primary_height = self->screen->height; + primary_x_offset = primary_width / 2; + primary_y_offset = primary_height / 2; log_width = globals->ls_width; log_height = globals->ls_height; @@ -686,8 +690,10 @@ xrdp_login_wnd_create(struct xrdp_wm *self) cx = self->client_info->minfo_wm[index].right; cy = self->client_info->minfo_wm[index].bottom; - primary_x_offset = x + ((cx - x) / 2); - primary_y_offset = y + ((cy - y) / 2); + primary_width = cx - x; + primary_height = cy - y; + primary_x_offset = x + (primary_width / 2); + primary_y_offset = y + (primary_height / 2); break; } } @@ -725,27 +731,62 @@ xrdp_login_wnd_create(struct xrdp_wm *self) { /* Load the background image. */ /* If no file is specified no default image will be loaded. */ - /* We only load the image if bpp > 8 */ - if (globals->ls_background_image[0] != 0 && self->screen->bpp > 8) + /* We only load the image if bpp > 8, and if the user hasn't + * disabled wallpaer in the performance settings */ + if (globals->ls_background_image[0] != 0) { - char fileName[256] ; - but = xrdp_bitmap_create(4, 4, self->screen->bpp, WND_TYPE_IMAGE, self); - if (globals->ls_background_image[0] == '/') + if (self->screen->bpp <= 8) { - g_snprintf(fileName, 255, "%s", globals->ls_background_image); + LOG(LOG_LEVEL_INFO, "Login background not loaded for bpp=%d", + self->screen->bpp); + } + else if ((self->client_info->rdp5_performanceflags & + RDP5_NO_WALLPAPER) != 0) + { + LOG(LOG_LEVEL_INFO, "Login background not loaded as client " + "has requested PERF_DISABLE_WALLPAPER"); } else { - g_snprintf(fileName, 255, "%s/%s", - XRDP_SHARE_PATH, globals->ls_background_image); + char fileName[256] ; + but = xrdp_bitmap_create(4, 4, self->screen->bpp, + WND_TYPE_IMAGE, self); + if (globals->ls_background_image[0] == '/') + { + g_snprintf(fileName, 255, "%s", + globals->ls_background_image); + } + else + { + g_snprintf(fileName, 255, "%s/%s", + XRDP_SHARE_PATH, globals->ls_background_image); + } + LOG(LOG_LEVEL_DEBUG, "We try to load the following background file: %s", fileName); + if (globals->ls_background_transform == XBLT_NONE) + { + xrdp_bitmap_load(but, fileName, self->palette, + globals->ls_top_window_bg_color, + globals->ls_background_transform, + 0, 0); + /* Place the background in the bottom right corner */ + but->left = primary_x_offset + (primary_width / 2) - + but->width; + but->top = primary_y_offset + (primary_height / 2) - + but->height; + } + else + { + xrdp_bitmap_load(but, fileName, self->palette, + globals->ls_top_window_bg_color, + globals->ls_background_transform, + primary_width, primary_height); + but->left = primary_x_offset - (primary_width / 2); + but->top = primary_y_offset - (primary_height / 2); + } + but->parent = self->screen; + but->owner = self->screen; + list_add_item(self->screen->child_list, (long)but); } - LOG(LOG_LEVEL_DEBUG, "We try to load the following background file: %s", fileName); - xrdp_bitmap_load(but, fileName, self->palette); - but->parent = self->screen; - but->owner = self->screen; - but->left = self->screen->width - but->width; - but->top = self->screen->height - but->height; - list_add_item(self->screen->child_list, (long)but); } /* if logo image not specified, use default */ @@ -762,7 +803,11 @@ xrdp_login_wnd_create(struct xrdp_wm *self) g_snprintf(globals->ls_logo_filename, 255, "%s/ad256.bmp", XRDP_SHARE_PATH); } - xrdp_bitmap_load(but, globals->ls_logo_filename, self->palette); + xrdp_bitmap_load(but, globals->ls_logo_filename, self->palette, + globals->ls_bg_color, + globals->ls_logo_transform, + globals->ls_logo_width, + globals->ls_logo_height); but->parent = self->login_window; but->owner = self->login_window; but->left = globals->ls_logo_x_pos; @@ -831,6 +876,42 @@ xrdp_login_wnd_create(struct xrdp_wm *self) return 0; } +/** + * Map a bitmap transform string to a value + * + * @param param Param we're trying to read + * @param str String we're trying to map + * + * @return enum xrdp_bitmap_load_transform value + * + * A warning is logged if the string is not recognised + *****************************************************************************/ +static enum xrdp_bitmap_load_transform +bitmap_transform_str_to_val(const char *param, const char *str) +{ + enum xrdp_bitmap_load_transform rv; + if (g_strcmp(str, "none") == 0) + { + rv = XBLT_NONE; + } + else if (g_strcmp(str, "scale") == 0) + { + rv = XBLT_SCALE; + } + else if (g_strcmp(str, "zoom") == 0) + { + rv = XBLT_ZOOM; + } + else + { + LOG(LOG_LEVEL_WARNING, "Param '%s' has unrecognised value '%s'" + " - assuming 'none'", param, str); + rv = XBLT_NONE; + } + + return rv; +} + /** * Load configuration from xrdp.ini file * @@ -866,6 +947,8 @@ load_xrdp_config(struct xrdp_config *config, const char *xrdp_ini, int bpp) globals->ls_bg_color = HCOLOR(bpp, xrdp_wm_htoi("dedede")); globals->ls_width = 350; globals->ls_height = 350; + globals->ls_background_transform = XBLT_NONE; + globals->ls_logo_transform = XBLT_NONE; globals->ls_logo_x_pos = 63; globals->ls_logo_y_pos = 50; globals->ls_label_x_pos = 30; @@ -1103,16 +1186,39 @@ load_xrdp_config(struct xrdp_config *config, const char *xrdp_ini, int bpp) globals->ls_title[255] = 0; } + else if (g_strncmp(n, "ls_background_image", 255) == 0) + { + g_strncpy(globals->ls_background_image, v, 255); + globals->ls_background_image[255] = 0; + } + + else if (g_strncmp(n, "ls_background_transform", 255) == 0) + { + globals->ls_background_transform = + bitmap_transform_str_to_val(n, v); + } + else if (g_strncmp(n, "ls_logo_filename", 255) == 0) { g_strncpy(globals->ls_logo_filename, v, 255); globals->ls_logo_filename[255] = 0; } - else if (g_strncmp(n, "ls_background_image", 255) == 0) + + else if (g_strncmp(n, "ls_logo_transform", 255) == 0) { - g_strncpy(globals->ls_background_image, v, 255); - globals->ls_background_image[255] = 0; + globals->ls_logo_transform = bitmap_transform_str_to_val(n, v); + } + + else if (g_strncmp(n, "ls_logo_width", 64) == 0) + { + globals->ls_logo_width = g_atoi(v); + } + + else if (g_strncmp(n, "ls_logo_height", 64) == 0) + { + globals->ls_logo_height = g_atoi(v); } + else if (g_strncmp(n, "ls_logo_x_pos", 64) == 0) { globals->ls_logo_x_pos = g_atoi(v); diff --git a/xrdp/xrdp_types.h b/xrdp/xrdp_types.h index 39d9996473..692a7190d6 100644 --- a/xrdp/xrdp_types.h +++ b/xrdp/xrdp_types.h @@ -168,6 +168,16 @@ struct xrdp_mod struct source_info *si; }; +/** + * Transform to apply to loaded images + */ +enum xrdp_bitmap_load_transform +{ + XBLT_NONE = 0, + XBLT_SCALE, + XBLT_ZOOM +}; + /* header for bmp file */ struct xrdp_bmp_header { @@ -634,8 +644,14 @@ struct xrdp_cfg_globals int ls_width; /* window width */ int ls_height; /* window height */ int ls_bg_color; /* background color */ - char ls_logo_filename[256]; /* logo filename */ char ls_background_image[256]; /* background image file name */ + enum xrdp_bitmap_load_transform ls_background_transform; + /* transform to apply to background image */ + char ls_logo_filename[256]; /* logo filename */ + enum xrdp_bitmap_load_transform ls_logo_transform; + /* transform to apply to logo */ + int ls_logo_width; /* logo width (optional) */ + int ls_logo_height; /* logo height (optional) */ int ls_logo_x_pos; /* logo x co-ordinate */ int ls_logo_y_pos; /* logo y co-ordinate */ int ls_label_x_pos; /* x pos of labels */