Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View File

@@ -0,0 +1,198 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP X11 Client
#
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
# Copyright 2013 Corey Clayton <can.of.tuna@gmail.com>
#
# 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.
cmake_minimum_required(VERSION 3.13)
if(NOT FREERDP_DEFAULT_PROJECT_VERSION)
set(FREERDP_DEFAULT_PROJECT_VERSION "1.0.0.0")
endif()
project(xfreerdp-client LANGUAGES C VERSION ${FREERDP_DEFAULT_PROJECT_VERSION})
message("project ${PROJECT_NAME} is using version ${PROJECT_VERSION}")
set(MODULE_NAME "xfreerdp")
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/)
include(ProjectCStandard)
include(CommonConfigOptions)
include(ConfigureFreeRDP)
find_package(X11 REQUIRED)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../resources)
include_directories(SYSTEM ${X11_INCLUDE_DIRS})
include_directories(SYSTEM ${OPENSSL_INCLUDE_DIR})
set(SRCS
xf_types.h
xf_utils.h
xf_utils.c
xf_x11_utils.c
xf_gfx.c
xf_gfx.h
xf_rail.c
xf_rail.h
xf_input.c
xf_input.h
xf_debug.h
xf_event.c
xf_event.h
xf_floatbar.c
xf_floatbar.h
xf_input.c
xf_input.h
xf_channels.c
xf_channels.h
xf_cliprdr.c
xf_cliprdr.h
xf_monitor.c
xf_monitor.h
xf_disp.c
xf_disp.h
xf_graphics.c
xf_graphics.h
xf_keyboard.c
xf_keyboard.h
keyboard_x11.h
keyboard_x11.c
xkb_layout_ids.h
xkb_layout_ids.c
xf_video.c
xf_video.h
xf_window.c
xf_window.h
xf_client.c
xf_client.h
)
if(CHANNEL_TSMF_CLIENT)
list(APPEND SRCS xf_tsmf.c xf_tsmf.h)
endif()
if(CLIENT_INTERFACE_SHARED)
addtargetwithresourcefile(${PROJECT_NAME} "SHARED" "${PROJECT_VERSION}" SRCS)
else()
addtargetwithresourcefile(${PROJECT_NAME} "STATIC" "${PROJECT_VERSION}" SRCS)
endif()
target_include_directories(${PROJECT_NAME} INTERFACE $<INSTALL_INTERFACE:include>)
set(PRIV_LIBS ${X11_LIBRARIES})
find_package(X11 REQUIRED)
if(X11_XShm_FOUND)
add_compile_definitions(WITH_XSHM)
include_directories(SYSTEM ${X11_XShm_INCLUDE_PATH})
list(APPEND PRIV_LIBS ${X11_Xext_LIB})
endif()
option(WITH_XINERAMA "[X11] enable xinerama" ON)
if(WITH_XINERAMA)
find_package(X11 REQUIRED)
if(X11_Xinerama_FOUND)
add_compile_definitions(WITH_XINERAMA)
include_directories(SYSTEM ${X11_Xinerama_INCLUDE_PATH})
list(APPEND PRIV_LIBS ${X11_Xinerama_LIB})
endif()
endif()
option(WITH_XEXT "[X11] enable Xext" ON)
if(WITH_XEXT)
find_package(X11 REQUIRED)
if(X11_Xext_FOUND)
add_compile_definitions(WITH_XEXT)
list(APPEND PRIV_LIBS ${X11_Xext_LIB})
endif()
endif()
option(WITH_XCURSOR "[X11] enable Xcursor" ON)
if(WITH_XCURSOR)
find_package(X11 REQUIRED)
if(X11_Xcursor_FOUND)
add_compile_definitions(WITH_XCURSOR)
include_directories(SYSTEM ${X11_Xcursor_INCLUDE_PATH})
list(APPEND PRIV_LIBS ${X11_Xcursor_LIB})
endif()
endif()
option(WITH_XV "[X11] enable Xv" ON)
if(WITH_XV)
find_package(X11 REQUIRED)
if(X11_Xv_FOUND)
add_compile_definitions(WITH_XV)
include_directories(SYSTEM ${X11_Xv_INCLUDE_PATH})
list(APPEND PRIV_LIBS ${X11_Xv_LIB})
endif()
endif()
option(WITH_XI "[X11] enable Xi" ON)
if(WITH_XI)
find_package(X11 REQUIRED)
if(X11_Xi_FOUND)
add_compile_definitions(WITH_XI)
include_directories(SYSTEM ${X11_Xi_INCLUDE_PATH})
list(APPEND PRIV_LIBS ${X11_Xi_LIB})
endif()
endif()
option(WITH_XRENDER "[X11] enable XRender" ON)
if(WITH_XRENDER)
find_package(X11 REQUIRED)
if(X11_Xrender_FOUND)
add_compile_definitions(WITH_XRENDER)
include_directories(SYSTEM ${X11_Xrender_INCLUDE_PATH})
list(APPEND PRIV_LIBS ${X11_Xrender_LIB})
endif()
endif()
option(WITH_XRANDR "[X11] enable XRandR" ON)
if(WITH_XRANDR)
find_package(X11 REQUIRED)
if(X11_Xrandr_FOUND)
add_compile_definitions(WITH_XRANDR)
include_directories(SYSTEM ${X11_Xrandr_INCLUDE_PATH})
list(APPEND PRIV_LIBS ${X11_Xrandr_LIB})
endif()
endif()
option(WITH_XFIXES "[X11] enable Xfixes" ON)
if(WITH_XFIXES)
find_package(X11 REQUIRED)
if(X11_Xfixes_FOUND)
add_compile_definitions(WITH_XFIXES)
include_directories(SYSTEM ${X11_Xfixes_INCLUDE_PATH})
list(APPEND PRIV_LIBS ${X11_Xfixes_LIB})
endif()
endif()
list(APPEND PUB_LIBS freerdp-client)
list(APPEND PRIV_LIBS m)
if(NOT APPLE)
list(APPEND PRIV_LIBS rt)
endif()
target_link_libraries(${PROJECT_NAME} PUBLIC ${PUB_LIBS})
target_link_libraries(${PROJECT_NAME} PRIVATE ${PRIV_LIBS})
if(WITH_CLIENT_INTERFACE)
installwithrpath(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries)
endif()
add_subdirectory(cli)
add_subdirectory(man)
set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER "Client/X11")

View File

@@ -0,0 +1,3 @@
set(FREERDP_CLIENT_NAME "xfreerdp")
set(FREERDP_CLIENT_PLATFORM "X11")
set(FREERDP_CLIENT_VENDOR "FreeRDP")

View File

@@ -0,0 +1,39 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP X11 cmake build script
#
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
#
# 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.
set(SRCS xfreerdp.c)
addtargetwithresourcefile(${MODULE_NAME} TRUE "${PROJECT_VERSION}" SRCS)
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "..")
set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER "Client/X11")
list(APPEND LIBS xfreerdp-client)
target_link_libraries(${MODULE_NAME} PRIVATE ${LIBS})
installwithrpath(
TARGETS
${MODULE_NAME}
RUNTIME
DESTINATION
${CMAKE_INSTALL_BINDIR}
COMPONENT
client
)
install_freerdp_desktop("${MODULE_NAME}")
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Client/X11")

View File

@@ -0,0 +1,120 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Client
*
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2012 HP Development Company, LLC
*
* 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.
*/
#include <freerdp/config.h>
#include <winpr/crt.h>
#include <winpr/synch.h>
#include <winpr/thread.h>
#include <freerdp/streamdump.h>
#include <freerdp/freerdp.h>
#include <freerdp/client/cmdline.h>
#include "../xf_client.h"
#include "../xfreerdp.h"
static void xfreerdp_print_help(void)
{
printf("Keyboard Shortcuts:\n");
printf("\t<Right CTRL>\n");
printf("\t\treleases keyboard and mouse grab\n");
printf("\t<CTRL>+<ALT>+<Return>\n");
printf("\t\ttoggles fullscreen state of the application\n");
printf("\t<CTRL>+<ALT>+c\n");
printf("\t\ttoggles remote control in a remote assistance session\n");
printf("\t<CTRL>+<ALT>+m\n");
printf("\t\tminimizes the application\n");
printf("\tAction Script\n");
printf("\t\tExecutes a predefined script on key press.\n");
printf("\t\tShould the script not exist it is ignored.\n");
printf("\t\tScripts can be provided at the default location ~/.config/freerdp/action.sh or as "
"command line argument /action:script:<path>\n");
printf("\t\tThe script will receive the current key combination as argument.\n");
printf("\t\tThe output of the script is parsed for 'key-local' which tells that the script "
"used the key combination, otherwise the combination is forwarded to the remote.\n");
}
int main(int argc, char* argv[])
{
int rc = 1;
int status = 0;
HANDLE thread = nullptr;
xfContext* xfc = nullptr;
DWORD dwExitCode = 0;
rdpContext* context = nullptr;
rdpSettings* settings = nullptr;
RDP_CLIENT_ENTRY_POINTS clientEntryPoints = WINPR_C_ARRAY_INIT;
clientEntryPoints.Size = sizeof(RDP_CLIENT_ENTRY_POINTS);
clientEntryPoints.Version = RDP_CLIENT_INTERFACE_VERSION;
RdpClientEntry(&clientEntryPoints);
context = freerdp_client_context_new(&clientEntryPoints);
if (!context)
return 1;
settings = context->settings;
xfc = (xfContext*)context;
status = freerdp_client_settings_parse_command_line(context->settings, argc, argv, FALSE);
if (status)
{
rc = freerdp_client_settings_command_line_status_print(settings, status, argc, argv);
if (freerdp_settings_get_bool(settings, FreeRDP_ListMonitors))
xf_list_monitors(xfc);
else
{
switch (status)
{
case COMMAND_LINE_STATUS_PRINT:
case COMMAND_LINE_STATUS_PRINT_VERSION:
case COMMAND_LINE_STATUS_PRINT_BUILDCONFIG:
break;
case COMMAND_LINE_STATUS_PRINT_HELP:
default:
xfreerdp_print_help();
break;
}
}
goto out;
}
if (!stream_dump_register_handlers(context, CONNECTION_STATE_MCS_CREATE_REQUEST, FALSE))
goto out;
if (freerdp_client_start(context) != 0)
goto out;
thread = freerdp_client_get_thread(context);
(void)WaitForSingleObject(thread, INFINITE);
GetExitCodeThread(thread, &dwExitCode);
rc = xf_exit_code_from_disconnect_reason(dwExitCode);
freerdp_client_stop(context);
out:
freerdp_client_context_free(context);
return rc;
}

View File

@@ -0,0 +1,147 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Keyboard Mapping
*
* Copyright 2009-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2023 Bernhard Miklautz <bernhard.miklautz@thincast.com>
*
* 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.
*/
#include <string.h>
#include <X11/X.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include "xf_debug.h"
#include "keyboard_x11.h"
#include "xkb_layout_ids.h"
#include "xf_utils.h"
static BOOL parse_xkb_rule_names(char* xkb_rule, unsigned long num_bytes, char** layout,
char** variant)
{
/* Sample output for "Canadian Multilingual Standard"
*
* _XKB_RULES_NAMES_BACKUP(STRING) = "xorg", "pc105", "ca", "multi", "magic"
*
* Format: "rules", "model", "layout", "variant", "options"
*
* Where "xorg" is the set of rules
* "pc105" the keyboard model
* "ca" the keyboard layout(s) (can also be something like 'us,uk')
* "multi" the keyboard layout variant(s) (in the examples, “,winkeys” - which means first
* layout uses some “default” variant and second uses “winkeys” variant)
* "magic" - configuration option (in the examples,
* “eurosign:e,lv3:ralt_switch,grp:rctrl_toggle”
* - three options)
*/
for (size_t i = 0, index = 0; i < num_bytes; i++, index++)
{
char* ptr = xkb_rule + i;
i += strnlen(ptr, num_bytes - i);
switch (index)
{
case 0: // rules
break;
case 1: // model
break;
case 2: // layout
{
/* If multiple languages are present we just take the first one */
char* delimiter = strchr(ptr, ',');
if (delimiter)
*delimiter = '\0';
*layout = ptr;
break;
}
case 3: // variant
{
/* If multiple variants are present we just take the first one */
char* delimiter = strchr(ptr, ',');
if (delimiter)
*delimiter = '\0';
*variant = ptr;
}
break;
case 4: // option
break;
default:
break;
}
}
return TRUE;
}
static DWORD kbd_layout_id_from_x_property(wLog* log, Display* display, Window root,
char* property_name)
{
char* layout = nullptr;
char* variant = nullptr;
char* rule = nullptr;
Atom type = None;
int item_size = 0;
unsigned long items = 0;
unsigned long unread_items = 0;
DWORD layout_id = 0;
Atom property = XInternAtom(display, property_name, False);
if (property == None)
return 0;
if (LogDynAndXGetWindowProperty(log, display, root, property, 0, 1024, False, XA_STRING, &type,
&item_size, &items, &unread_items,
(unsigned char**)&rule) != Success)
return 0;
if (type != XA_STRING || item_size != 8 || unread_items != 0)
{
XFree(rule);
return 0;
}
parse_xkb_rule_names(rule, items, &layout, &variant);
DEBUG_X11("%s layout: %s, variant: %s", property_name, layout, variant);
layout_id = xf_find_keyboard_layout_in_xorg_rules(layout, variant);
XFree(rule);
return layout_id;
}
int xf_detect_keyboard_layout_from_xkb(wLog* log, DWORD* keyboardLayoutId)
{
Display* display = XOpenDisplay(nullptr);
if (!display)
return 0;
Window root = DefaultRootWindow(display);
if (!root)
return 0;
/* We start by looking for _XKB_RULES_NAMES_BACKUP which appears to be used by libxklavier */
DWORD id = kbd_layout_id_from_x_property(log, display, root, "_XKB_RULES_NAMES_BACKUP");
if (0 == id)
id = kbd_layout_id_from_x_property(log, display, root, "_XKB_RULES_NAMES");
if (0 != id)
*keyboardLayoutId = id;
LogDynAndXCloseDisplay(log, display);
return (int)id;
}

View File

@@ -0,0 +1,25 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Keyboard Mapping
*
* Copyright 2009-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#pragma once
#include <winpr/wlog.h>
#include <freerdp/api.h>
int xf_detect_keyboard_layout_from_xkb(wLog* log, DWORD* keyboardLayoutId);

View File

@@ -0,0 +1,14 @@
set(DEPS
../../common/man/freerdp-global-options.1
xfreerdp-shortcuts.1
../../common/man/freerdp-global-envvar.1
../../common/man/freerdp-global-config.1
xfreerdp-global-config.1
xfreerdp-examples.1
../../common/man/freerdp-global-links.1
)
include(GetSysconfDir)
get_sysconf_dir("" SYSCONF_DIR)
set(VAR_NAMES "VENDOR" "PRODUCT" "VENDOR_PRODUCT" "SYSCONF_DIR")
generate_and_install_freerdp_man_from_xml(${MODULE_NAME} "1" "${DEPS}" "${VAR_NAMES}")

View File

@@ -0,0 +1,181 @@
.SH "EXAMPLES"
.PP
.RS 4
.sp
.if n \{\
.RS 4
.\}
.nf
#!/bin/bash
# we got a key combination
if [ "$1" = "key" ];
then
# we only got one argument \*(Aqkey\*(Aq
# list all supported combinations with echo
if [ $# \-eq 1 ];
then
echo "ctrl+alt+f1"
echo "ctrl+alt+f2"
else
# We want the action for a single combination
# use \*(Aqkey\-local\*(Aq to not forward to RDP session
if [ "$2" = "ctrl+alt+f1" ];
then
echo "key\-local"
fi
if [ "$2" = "ctrl+alt+f2" ];
then
echo "/usr/local/bin/somescript\&.sh"
fi
fi
fi
if [ "$1" = "xevent" ];
then
if [ $# \-eq 1 ];
then
echo "FocusIn"
echo "SelectionClear"
else
if [ "$2" = "SelectionNotify" ];
then
echo "/usr/local/bin/someprogram"
fi
fi
fi
.fi
.if n \{\
.RE
.\}
Example action script for key events, listing
\fIctrl+alt+f1\fR
to be handled by local window manager and
\fIctrl+alt+f2\fR
executing a script
.sp
The return value of the program determines if the key is handled locally or remotely (0 for local, > 0 for remote, < 0 for errors)
.RE
.PP
\fB@MANPAGE_NAME@ connection\&.rdp /p:Pwd123! /f\fR
.RS 4
Connect in fullscreen mode using a stored configuration
\fIconnection\&.rdp\fR
and the password
\fIPwd123!\fR
.RE
.PP
\fB@MANPAGE_NAME@ /u:USER /size:50%h /v:rdp\&.contoso\&.com\fR
.RS 4
Connect to host
\fIrdp\&.contoso\&.com\fR
with user
\fIUSER\fR
and a size of
\fI50 percent of the height\fR\&. If width (w) is set instead of height (h) like /size:50%w\&. 50 percent of the width is used\&.
.RE
.PP
\fB@MANPAGE_NAME@ /u:CONTOSO\e\eJohnDoe /p:Pwd123! /v:rdp\&.contoso\&.com\fR
.RS 4
Connect to host
\fIrdp\&.contoso\&.com\fR
with user
\fICONTOSO\e\eJohnDoe\fR
and password
\fIPwd123!\fR
.RE
.PP
\fB@MANPAGE_NAME@ /u:JohnDoe /p:Pwd123! /w:1366 /h:768 /v:192\&.168\&.1\&.100:4489\fR
.RS 4
Connect to host
\fI192\&.168\&.1\&.100\fR
on port
\fI4489\fR
with user
\fIJohnDoe\fR, password
\fIPwd123!\fR\&. The screen width is set to
\fI1366\fR
and the height to
\fI768\fR
.RE
.PP
\fB@MANPAGE_NAME@ /u:JohnDoe /p:Pwd123! /vmconnect:C824F53E\-95D2\-46C6\-9A18\-23A5BB403532 /v:192\&.168\&.1\&.100\fR
.RS 4
Establish a connection to host
\fI192\&.168\&.1\&.100\fR
with user
\fIJohnDoe\fR, password
\fIPwd123!\fR
and connect to Hyper\-V console (use port 2179, disable negotiation) with VMID
\fIC824F53E\-95D2\-46C6\-9A18\-23A5BB403532\fR
.RE
.PP
\fB+clipboard\fR
.RS 4
Activate clipboard redirection
.RE
.PP
\fB/drive:home,/home/user\fR
.RS 4
Activate drive redirection of
\fI/home/user\fR
as home drive
.RE
.PP
\fB/smartcard:<device>\fR
.RS 4
Activate smartcard redirection for device
\fIdevice\fR
.RE
.PP
\fB/printer:<device>,<driver>\fR
.RS 4
Activate printer redirection for printer
\fIdevice\fR
using driver
\fIdriver\fR
.RE
.PP
\fB/serial:<device>\fR
.RS 4
Activate serial port redirection for port
\fIdevice\fR
.RE
.PP
\fB/parallel:<device>\fR
.RS 4
Activate parallel port redirection for port
\fIdevice\fR
.RE
.PP
\fB/sound:sys:alsa\fR
.RS 4
Activate audio output redirection using device
\fIsys:alsa\fR
.RE
.PP
\fB/microphone:sys:alsa\fR
.RS 4
Activate audio input redirection using device
\fIsys:alsa\fR
.RE
.PP
\fB/multimedia:sys:alsa\fR
.RS 4
Activate multimedia redirection using device
\fIsys:alsa\fR
.RE
.PP
\fB/usb:id,dev:054c:0268\fR
.RS 4
Activate USB device redirection for the device identified by
\fI054c:0268\fR
.RE
.PP
\fB/kbd:remap:29=58,remap:58=29\fR
.RS 4
Swap left Control (\
\fI29\fR\
) and Caps Lock (\
\fI58\fR\
).
.RE

View File

@@ -0,0 +1,22 @@
.SH "GLOBAL CONFIGURATION (X11 client)"
.PP
The X11 client configuration location is
\fI@SYSCONF_DIR@/xfreerdp\&.json\fR
.br
File format is JSON
.RE
.PP
Supported options:
.RS 4
.PP
\fIisActionScriptAllowed\fR
.RS 4
.PP
.RS 4
\fIJSON boolean\fR
.br
Allow or block action scripts. Default (if option is not set) is to allow action scripts.
.RE
.RE

View File

@@ -0,0 +1,44 @@
.SH "KEYBOARD SHORTCUTS"
.PP
<Right CTRL>
.RS 4
releases keyboard and mouse grab.
.br
If keyboard is grabbed the local system shortcuts do no longer work and are sent to the remote system.
.br
If the Mouse is grabbed (optional) local gesture detection does not work and the mouse might not be able to leave the RDP window. Mouse events are not altered.
.RE
.PP
<CTRL>+<ALT>+<Return>
.RS 4
toggles fullscreen state of the application
.RE
.PP
<CTRL>+<ALT>+<m>
.RS 4
Minimizes the application
.RE
.PP
<CTRL>+<ALT>+c
.RS 4
toggles remote control in a remote assistance session
.RE
.PP
<CTRL>+<ALT>+<d>
.RS 4
Disconnect the session and terminate application
.RE
.PP
Action Script
.RS 4
executes a predefined script on key press\&.
Should the script not exist it is ignored\&.
Scripts can be provided at the default location
\fI$XDG_CONFIG_HOME/freerdp/action\&.sh\fR
or as command line argument
\fI/action:script:<path>\fR\&.
The script will receive the current key combination as argument\&.
The output of the script is parsed for
\fIkey\-local\fR
which tells that the script used the key combination, otherwise the combination is forwarded to the remote\&.
.RE

View File

@@ -0,0 +1,15 @@
.TH "@MANPAGE_NAME@" "1" "@MAN_TODAY@" "freerdp" "@MANPAGE_NAME@"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.nh
.ad l
.SH "NAME"
@MANPAGE_NAME@ \- FreeRDP X11 client
.SH "SYNOPSIS"
.PP
\fB@MANPAGE_NAME@\fR
[file] [options] [/v:server[:port]]
.SH "DESCRIPTION"
.PP
\fB@MANPAGE_NAME@\fR
is an X11 Remote Desktop Protocol (RDP) client which is part of the FreeRDP project\&. An RDP server is built\-in to many editions of Windows\&. Alternative servers included ogon, gnome\-remote\-desktop, xrdp and VRDP (VirtualBox)\&.

View File

@@ -0,0 +1,11 @@
#define close_width 24
#define close_height 24
static unsigned char close_bits[] =
{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x7c, 0xfe, 0xff, 0x38, 0xfe, 0xff, 0x11, 0xff, 0xff, 0x83, 0xff,
0xff, 0xc7, 0xff, 0xff, 0x83, 0xff, 0xff, 0x11, 0xff, 0xff, 0x38, 0xfe,
0xff, 0x7c, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};

View File

@@ -0,0 +1,11 @@
#define lock_width 24
#define lock_height 24
static unsigned char lock_bits[] =
{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0xff,
0xff, 0x83, 0xff, 0xff, 0xc7, 0xff, 0xff, 0xc7, 0xff, 0xff, 0xc7, 0xff,
0xff, 0x00, 0xfe, 0xff, 0x00, 0xfe, 0xff, 0xef, 0xff, 0xff, 0xef, 0xff,
0xff, 0xef, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};

View File

@@ -0,0 +1,11 @@
#define minimize_width 24
#define minimize_height 24
static unsigned char minimize_bits[] =
{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0xfc,
0x3f, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};

View File

@@ -0,0 +1,11 @@
#define restore_width 24
#define restore_height 24
static unsigned char restore_bits[] =
{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x03, 0xff, 0xff, 0x03, 0xff, 0xff, 0x3b, 0xff, 0x7f, 0x20, 0xff,
0x7f, 0x20, 0xff, 0x7f, 0x07, 0xff, 0x7f, 0xe7, 0xff, 0x7f, 0xe7, 0xff,
0x7f, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};

View File

@@ -0,0 +1,11 @@
#define unlock_width 24
#define unlock_height 24
static unsigned char unlock_bits[] =
{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xf3, 0xff, 0xff, 0xf3, 0xff, 0xff, 0x73, 0xfe, 0xff, 0x03, 0xfe,
0x3f, 0x00, 0xfe, 0xff, 0x03, 0xfe, 0xff, 0x73, 0xfe, 0xff, 0xf3, 0xff,
0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};

View File

@@ -0,0 +1,132 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Client Channels
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <freerdp/config.h>
#include <winpr/assert.h>
#include <freerdp/gdi/video.h>
#include "xf_channels.h"
#include "xf_client.h"
#include "xfreerdp.h"
#include "xf_gfx.h"
#if defined(CHANNEL_TSMF_CLIENT)
#include "xf_tsmf.h"
#endif
#include "xf_rail.h"
#include "xf_cliprdr.h"
#include "xf_disp.h"
#include "xf_video.h"
void xf_OnChannelConnectedEventHandler(void* context, const ChannelConnectedEventArgs* e)
{
xfContext* xfc = (xfContext*)context;
rdpSettings* settings = nullptr;
WINPR_ASSERT(xfc);
WINPR_ASSERT(e);
WINPR_ASSERT(e->name);
settings = xfc->common.context.settings;
WINPR_ASSERT(settings);
if (FALSE)
{
}
#if defined(CHANNEL_TSMF_CLIENT)
else if (strcmp(e->name, TSMF_DVC_CHANNEL_NAME) == 0)
{
xf_tsmf_init(xfc, (TsmfClientContext*)e->pInterface);
}
#endif
else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
{
xf_graphics_pipeline_init(xfc, (RdpgfxClientContext*)e->pInterface);
}
else if (strcmp(e->name, RAIL_SVC_CHANNEL_NAME) == 0)
{
xf_rail_init(xfc, (RailClientContext*)e->pInterface);
}
else if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0)
{
xf_cliprdr_init(xfc, (CliprdrClientContext*)e->pInterface);
}
else if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) == 0)
{
xf_disp_init(xfc->xfDisp, (DispClientContext*)e->pInterface);
}
else if (strcmp(e->name, VIDEO_CONTROL_DVC_CHANNEL_NAME) == 0)
{
if (freerdp_settings_get_bool(settings, FreeRDP_SoftwareGdi))
gdi_video_control_init(xfc->common.context.gdi, (VideoClientContext*)e->pInterface);
else
xf_video_control_init(xfc, (VideoClientContext*)e->pInterface);
}
else
freerdp_client_OnChannelConnectedEventHandler(context, e);
}
void xf_OnChannelDisconnectedEventHandler(void* context, const ChannelDisconnectedEventArgs* e)
{
xfContext* xfc = (xfContext*)context;
rdpSettings* settings = nullptr;
WINPR_ASSERT(xfc);
WINPR_ASSERT(e);
WINPR_ASSERT(e->name);
settings = xfc->common.context.settings;
WINPR_ASSERT(settings);
if (FALSE)
{
}
else if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) == 0)
{
xf_disp_uninit(xfc->xfDisp, (DispClientContext*)e->pInterface);
}
#if defined(CHANNEL_TSMF_CLIENT)
else if (strcmp(e->name, TSMF_DVC_CHANNEL_NAME) == 0)
{
xf_tsmf_uninit(xfc, (TsmfClientContext*)e->pInterface);
}
#endif
else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
{
xf_graphics_pipeline_uninit(xfc, (RdpgfxClientContext*)e->pInterface);
}
else if (strcmp(e->name, RAIL_SVC_CHANNEL_NAME) == 0)
{
xf_rail_uninit(xfc, (RailClientContext*)e->pInterface);
}
else if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0)
{
xf_cliprdr_uninit(xfc, (CliprdrClientContext*)e->pInterface);
}
else if (strcmp(e->name, VIDEO_CONTROL_DVC_CHANNEL_NAME) == 0)
{
if (freerdp_settings_get_bool(settings, FreeRDP_SoftwareGdi))
gdi_video_control_uninit(xfc->common.context.gdi, (VideoClientContext*)e->pInterface);
else
xf_video_control_uninit(xfc, (VideoClientContext*)e->pInterface);
}
else
freerdp_client_OnChannelDisconnectedEventHandler(context, e);
}

View File

@@ -0,0 +1,37 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Client Channels
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#ifndef FREERDP_CLIENT_X11_CHANNELS_H
#define FREERDP_CLIENT_X11_CHANNELS_H
#include <freerdp/freerdp.h>
#include <freerdp/client/channels.h>
#include <freerdp/client/rdpei.h>
#include <freerdp/client/rail.h>
#include <freerdp/client/cliprdr.h>
#include <freerdp/client/rdpgfx.h>
#include <freerdp/client/encomsp.h>
#include <freerdp/client/disp.h>
#include <freerdp/client/geometry.h>
#include <freerdp/client/video.h>
void xf_OnChannelConnectedEventHandler(void* context, const ChannelConnectedEventArgs* e);
void xf_OnChannelDisconnectedEventHandler(void* context, const ChannelDisconnectedEventArgs* e);
#endif /* FREERDP_CLIENT_X11_CHANNELS_H */

2104
third_party/FreeRDP/client/X11/xf_client.c vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,53 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Client Interface
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#ifndef FREERDP_CLIENT_X11_CLIENT_H
#define FREERDP_CLIENT_X11_CLIENT_H
#include <winpr/crt.h>
#include <winpr/synch.h>
#include <winpr/thread.h>
#include <winpr/collections.h>
#include <freerdp/api.h>
#include <freerdp/freerdp.h>
#include <freerdp/client.h>
#include <freerdp/gdi/gdi.h>
#include <freerdp/gdi/dc.h>
#include <freerdp/gdi/region.h>
#include <freerdp/channels/channels.h>
#ifdef __cplusplus
extern "C"
{
#endif
/**
* Client Interface
*/
FREERDP_API int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_CLIENT_X11_CLIENT_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,43 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Clipboard Redirection
*
* Copyright 2010-2011 Vic Lee
*
* 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.
*/
#ifndef FREERDP_CLIENT_X11_CLIPRDR_H
#define FREERDP_CLIENT_X11_CLIPRDR_H
#include <freerdp/types.h>
#include <freerdp/client/cliprdr.h>
#include <X11/Xlib.h>
#include "xf_types.h"
typedef struct xf_clipboard xfClipboard;
void xf_clipboard_free(xfClipboard* clipboard);
WINPR_ATTR_MALLOC(xf_clipboard_free, 1)
WINPR_ATTR_NODISCARD
xfClipboard* xf_clipboard_new(xfContext* xfc, BOOL relieveFilenameRestriction);
void xf_cliprdr_init(xfContext* xfc, CliprdrClientContext* cliprdr);
void xf_cliprdr_uninit(xfContext* xfc, CliprdrClientContext* cliprdr);
void xf_cliprdr_handle_xevent(xfContext* xfc, const XEvent* event);
#endif /* FREERDP_CLIENT_X11_CLIPRDR_H */

View File

@@ -0,0 +1,35 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 debug helper header
*
* Copyright 2025 Armin Novak <anovak@thincast.com>
* Copyright 2025 Thincast Technologies GmbH
*
* 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.
*/
#pragma once
#include <freerdp/config.h>
#include <freerdp/log.h>
#define DBG_TAG CLIENT_TAG("x11")
#ifdef WITH_DEBUG_X11
#define DEBUG_X11(...) WLog_DBG(DBG_TAG, __VA_ARGS__)
#else
#define DEBUG_X11(...) \
do \
{ \
} while (0)
#endif

589
third_party/FreeRDP/client/X11/xf_disp.c vendored Normal file
View File

@@ -0,0 +1,589 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Display Control channel
*
* Copyright 2017 David Fort <contact@hardening-consulting.com>
*
* 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.
*/
#include <math.h>
#include <winpr/assert.h>
#include <winpr/sysinfo.h>
#include <freerdp/timer.h>
#include <X11/Xutil.h>
#ifdef WITH_XRANDR
#include <X11/extensions/Xrandr.h>
#include <X11/extensions/randr.h>
#if (RANDR_MAJOR * 100 + RANDR_MINOR) >= 105
#define USABLE_XRANDR
#endif
#endif
#include "xfreerdp.h"
#include "xf_disp.h"
#include "xf_monitor.h"
#include <freerdp/log.h>
#define TAG CLIENT_TAG("x11disp")
#define RESIZE_MIN_DELAY_NS 200000000UL /* minimum delay in ms between two resizes */
struct s_xfDispContext
{
xfContext* xfc;
DispClientContext* disp;
BOOL haveXRandr;
int eventBase;
int errorBase;
UINT32 lastSentWidth;
UINT32 lastSentHeight;
BYTE reserved[4];
UINT64 lastSentDate;
UINT32 targetWidth;
UINT32 targetHeight;
BOOL activated;
BOOL fullscreen;
UINT16 lastSentDesktopOrientation;
BYTE reserved2[2];
UINT32 lastSentDesktopScaleFactor;
UINT32 lastSentDeviceScaleFactor;
BYTE reserved3[4];
FreeRDP_TimerID timerID;
};
static BOOL xf_disp_check_context(void* context, xfContext** ppXfc, xfDispContext** ppXfDisp,
rdpSettings** ppSettings);
static BOOL xf_disp_sendResize(xfDispContext* xfDisp, BOOL fromTimer);
static UINT xf_disp_sendLayout(DispClientContext* disp, const rdpMonitor* monitors,
UINT32 nmonitors);
static BOOL xf_disp_settings_changed(xfDispContext* xfDisp)
{
rdpSettings* settings = nullptr;
WINPR_ASSERT(xfDisp);
WINPR_ASSERT(xfDisp->xfc);
settings = xfDisp->xfc->common.context.settings;
WINPR_ASSERT(settings);
if (xfDisp->lastSentWidth != xfDisp->targetWidth)
return TRUE;
if (xfDisp->lastSentHeight != xfDisp->targetHeight)
return TRUE;
if (xfDisp->lastSentDesktopOrientation !=
freerdp_settings_get_uint16(settings, FreeRDP_DesktopOrientation))
return TRUE;
if (xfDisp->lastSentDesktopScaleFactor !=
freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor))
return TRUE;
if (xfDisp->lastSentDeviceScaleFactor !=
freerdp_settings_get_uint32(settings, FreeRDP_DeviceScaleFactor))
return TRUE;
if (xfDisp->fullscreen != xfDisp->xfc->fullscreen)
return TRUE;
return FALSE;
}
static BOOL xf_update_last_sent(xfDispContext* xfDisp)
{
rdpSettings* settings = nullptr;
WINPR_ASSERT(xfDisp);
WINPR_ASSERT(xfDisp->xfc);
settings = xfDisp->xfc->common.context.settings;
WINPR_ASSERT(settings);
xfDisp->lastSentWidth = xfDisp->targetWidth;
xfDisp->lastSentHeight = xfDisp->targetHeight;
xfDisp->lastSentDesktopOrientation =
freerdp_settings_get_uint16(settings, FreeRDP_DesktopOrientation);
xfDisp->lastSentDesktopScaleFactor =
freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor);
xfDisp->lastSentDeviceScaleFactor =
freerdp_settings_get_uint32(settings, FreeRDP_DeviceScaleFactor);
xfDisp->fullscreen = xfDisp->xfc->fullscreen;
return TRUE;
}
static uint64_t xf_disp_OnTimer(rdpContext* context, WINPR_ATTR_UNUSED void* userdata,
WINPR_ATTR_UNUSED FreeRDP_TimerID timerID,
WINPR_ATTR_UNUSED uint64_t timestamp,
WINPR_ATTR_UNUSED uint64_t interval)
{
xfContext* xfc = nullptr;
xfDispContext* xfDisp = nullptr;
rdpSettings* settings = nullptr;
if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
return interval;
if (!xfDisp->activated)
return interval;
xf_disp_sendResize(xfDisp, TRUE);
xfDisp->timerID = 0;
return 0;
}
static BOOL update_timer(xfDispContext* xfDisp, uint64_t intervalNS)
{
WINPR_ASSERT(xfDisp);
if (xfDisp->timerID == 0)
{
rdpContext* context = &xfDisp->xfc->common.context;
xfDisp->timerID = freerdp_timer_add(context, intervalNS, xf_disp_OnTimer, nullptr, true);
}
return TRUE;
}
BOOL xf_disp_sendResize(xfDispContext* xfDisp, BOOL fromTimer)
{
DISPLAY_CONTROL_MONITOR_LAYOUT layout = WINPR_C_ARRAY_INIT;
if (!xfDisp || !xfDisp->xfc)
return FALSE;
/* If there is already a timer running skip the update and wait for the timer to expire. */
if ((xfDisp->timerID != 0) && !fromTimer)
return TRUE;
xfContext* xfc = xfDisp->xfc;
rdpSettings* settings = xfc->common.context.settings;
if (!settings)
return FALSE;
if (!xfDisp->activated || !xfDisp->disp)
return update_timer(xfDisp, RESIZE_MIN_DELAY_NS);
const uint64_t diff = winpr_GetTickCount64NS() - xfDisp->lastSentDate;
if (diff < RESIZE_MIN_DELAY_NS)
{
const uint64_t interval = RESIZE_MIN_DELAY_NS - diff;
return update_timer(xfDisp, interval);
}
if (!xf_disp_settings_changed(xfDisp))
return TRUE;
xfDisp->lastSentDate = winpr_GetTickCount64NS();
const UINT32 mcount = freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount);
if (mcount > 1)
{
const rdpMonitor* monitors =
freerdp_settings_get_pointer(settings, FreeRDP_MonitorDefArray);
if (xf_disp_sendLayout(xfDisp->disp, monitors, mcount) != CHANNEL_RC_OK)
return FALSE;
}
else
{
layout.Flags = DISPLAY_CONTROL_MONITOR_PRIMARY;
layout.Top = layout.Left = 0;
layout.Width = xfDisp->targetWidth;
layout.Height = xfDisp->targetHeight;
layout.Orientation = freerdp_settings_get_uint16(settings, FreeRDP_DesktopOrientation);
layout.DesktopScaleFactor =
freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor);
layout.DeviceScaleFactor = freerdp_settings_get_uint32(settings, FreeRDP_DeviceScaleFactor);
const double dw = xfDisp->targetWidth / 75.0 * 25.4;
const double dh = xfDisp->targetHeight / 75.0 * 25.4;
layout.PhysicalWidth = (UINT32)lround(dw);
layout.PhysicalHeight = (UINT32)lround(dh);
if (IFCALLRESULT(CHANNEL_RC_OK, xfDisp->disp->SendMonitorLayout, xfDisp->disp, 1,
&layout) != CHANNEL_RC_OK)
return FALSE;
}
return xf_update_last_sent(xfDisp);
}
static BOOL xf_disp_queueResize(xfDispContext* xfDisp, UINT32 width, UINT32 height)
{
if ((xfDisp->targetWidth == (INT64)width) && (xfDisp->targetHeight == (INT64)height))
return TRUE;
xfDisp->targetWidth = width;
xfDisp->targetHeight = height;
return xf_disp_sendResize(xfDisp, FALSE);
}
static BOOL xf_disp_set_window_resizable(xfDispContext* xfDisp)
{
XSizeHints* size_hints = nullptr;
if (!(size_hints = XAllocSizeHints()))
return FALSE;
size_hints->flags = PMinSize | PMaxSize | PWinGravity;
size_hints->win_gravity = NorthWestGravity;
size_hints->min_width = size_hints->min_height = 320;
size_hints->max_width = size_hints->max_height = 8192;
if (xfDisp->xfc->window)
XSetWMNormalHints(xfDisp->xfc->display, xfDisp->xfc->window->handle, size_hints);
XFree(size_hints);
return TRUE;
}
BOOL xf_disp_check_context(void* context, xfContext** ppXfc, xfDispContext** ppXfDisp,
rdpSettings** ppSettings)
{
xfContext* xfc = nullptr;
if (!context)
return FALSE;
xfc = (xfContext*)context;
if (!(xfc->xfDisp))
return FALSE;
if (!xfc->common.context.settings)
return FALSE;
*ppXfc = xfc;
*ppXfDisp = xfc->xfDisp;
*ppSettings = xfc->common.context.settings;
return TRUE;
}
static void xf_disp_OnActivated(void* context, const ActivatedEventArgs* e)
{
xfContext* xfc = nullptr;
xfDispContext* xfDisp = nullptr;
rdpSettings* settings = nullptr;
if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
return;
if (xfDisp->activated && !xfc->fullscreen)
{
xf_disp_set_window_resizable(xfDisp);
if (e->firstActivation)
return;
xf_disp_sendResize(xfDisp, FALSE);
}
}
static void xf_disp_OnGraphicsReset(void* context, const GraphicsResetEventArgs* e)
{
xfContext* xfc = nullptr;
xfDispContext* xfDisp = nullptr;
rdpSettings* settings = nullptr;
WINPR_UNUSED(e);
if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
return;
if (xfDisp->activated && !freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
{
xf_disp_set_window_resizable(xfDisp);
xf_disp_sendResize(xfDisp, FALSE);
}
}
static void xf_disp_OnWindowStateChange(void* context, const WindowStateChangeEventArgs* e)
{
xfContext* xfc = nullptr;
xfDispContext* xfDisp = nullptr;
rdpSettings* settings = nullptr;
WINPR_UNUSED(e);
if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
return;
if (!xfDisp->activated || !xfc->fullscreen)
return;
xf_disp_sendResize(xfDisp, FALSE);
}
xfDispContext* xf_disp_new(xfContext* xfc)
{
WINPR_ASSERT(xfc);
wPubSub* pubSub = xfc->common.context.pubSub;
WINPR_ASSERT(pubSub);
const rdpSettings* settings = xfc->common.context.settings;
WINPR_ASSERT(settings);
if (PubSub_SubscribeActivated(pubSub, xf_disp_OnActivated) < 0)
return nullptr;
if (PubSub_SubscribeGraphicsReset(pubSub, xf_disp_OnGraphicsReset) < 0)
return nullptr;
if (PubSub_SubscribeWindowStateChange(pubSub, xf_disp_OnWindowStateChange) < 0)
return nullptr;
xfDispContext* ret = calloc(1, sizeof(xfDispContext));
if (!ret)
return nullptr;
ret->xfc = xfc;
#ifdef USABLE_XRANDR
if (XRRQueryExtension(xfc->display, &ret->eventBase, &ret->errorBase))
{
ret->haveXRandr = TRUE;
}
#endif
ret->lastSentWidth = ret->targetWidth =
freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
ret->lastSentHeight = ret->targetHeight =
freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
return ret;
}
void xf_disp_free(xfDispContext* disp)
{
if (!disp)
return;
if (disp->xfc)
{
wPubSub* pubSub = disp->xfc->common.context.pubSub;
PubSub_UnsubscribeActivated(pubSub, xf_disp_OnActivated);
PubSub_UnsubscribeGraphicsReset(pubSub, xf_disp_OnGraphicsReset);
PubSub_UnsubscribeWindowStateChange(pubSub, xf_disp_OnWindowStateChange);
}
free(disp);
}
UINT xf_disp_sendLayout(DispClientContext* disp, const rdpMonitor* monitors, UINT32 nmonitors)
{
UINT ret = CHANNEL_RC_OK;
xfDispContext* xfDisp = nullptr;
rdpSettings* settings = nullptr;
DISPLAY_CONTROL_MONITOR_LAYOUT* layouts = nullptr;
WINPR_ASSERT(disp);
WINPR_ASSERT(monitors);
WINPR_ASSERT(nmonitors > 0);
xfDisp = (xfDispContext*)disp->custom;
WINPR_ASSERT(xfDisp);
WINPR_ASSERT(xfDisp->xfc);
settings = xfDisp->xfc->common.context.settings;
WINPR_ASSERT(settings);
layouts = calloc(nmonitors, sizeof(DISPLAY_CONTROL_MONITOR_LAYOUT));
if (!layouts)
return CHANNEL_RC_NO_MEMORY;
for (UINT32 i = 0; i < nmonitors; i++)
{
const rdpMonitor* monitor = &monitors[i];
DISPLAY_CONTROL_MONITOR_LAYOUT* layout = &layouts[i];
layout->Flags = (monitor->is_primary ? DISPLAY_CONTROL_MONITOR_PRIMARY : 0);
layout->Left = monitor->x;
layout->Top = monitor->y;
layout->Width = WINPR_ASSERTING_INT_CAST(uint32_t, monitor->width);
layout->Height = WINPR_ASSERTING_INT_CAST(uint32_t, monitor->height);
layout->Orientation = ORIENTATION_LANDSCAPE;
layout->PhysicalWidth = monitor->attributes.physicalWidth;
layout->PhysicalHeight = monitor->attributes.physicalHeight;
switch (monitor->attributes.orientation)
{
case 90:
layout->Orientation = ORIENTATION_PORTRAIT;
break;
case 180:
layout->Orientation = ORIENTATION_LANDSCAPE_FLIPPED;
break;
case 270:
layout->Orientation = ORIENTATION_PORTRAIT_FLIPPED;
break;
case 0:
default:
/* MS-RDPEDISP - 2.2.2.2.1:
* Orientation (4 bytes): A 32-bit unsigned integer that specifies the
* orientation of the monitor in degrees. Valid values are 0, 90, 180
* or 270
*
* So we default to ORIENTATION_LANDSCAPE
*/
layout->Orientation = ORIENTATION_LANDSCAPE;
break;
}
layout->DesktopScaleFactor =
freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor);
layout->DeviceScaleFactor =
freerdp_settings_get_uint32(settings, FreeRDP_DeviceScaleFactor);
}
ret = IFCALLRESULT(CHANNEL_RC_OK, disp->SendMonitorLayout, disp, nmonitors, layouts);
free(layouts);
return ret;
}
BOOL xf_disp_handle_xevent(xfContext* xfc, const XEvent* event)
{
xfDispContext* xfDisp = nullptr;
rdpSettings* settings = nullptr;
UINT32 maxWidth = 0;
UINT32 maxHeight = 0;
if (!xfc || !event)
return FALSE;
xfDisp = xfc->xfDisp;
if (!xfDisp)
return FALSE;
settings = xfc->common.context.settings;
if (!settings)
return FALSE;
if (!xfDisp->haveXRandr || !xfDisp->disp)
return TRUE;
#ifdef USABLE_XRANDR
if (event->type != xfDisp->eventBase + RRScreenChangeNotify)
return TRUE;
#endif
WLog_DBG(TAG, "RRScreenChangeNotify event");
xf_detect_monitors(xfc, &maxWidth, &maxHeight);
const rdpMonitor* monitors = freerdp_settings_get_pointer(settings, FreeRDP_MonitorDefArray);
const UINT32 mcount = freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount);
return xf_disp_sendLayout(xfDisp->disp, monitors, mcount) == CHANNEL_RC_OK;
}
BOOL xf_disp_handle_configureNotify(xfContext* xfc, int width, int height)
{
xfDispContext* xfDisp = nullptr;
if (!xfc)
return FALSE;
xfDisp = xfc->xfDisp;
if (!xfDisp)
return FALSE;
WLog_DBG(TAG, "ConfigureNotify (%dx%d)", width, height);
return xf_disp_queueResize(xfDisp, WINPR_ASSERTING_INT_CAST(uint32_t, width),
WINPR_ASSERTING_INT_CAST(uint32_t, height));
}
static UINT xf_DisplayControlCaps(DispClientContext* disp, UINT32 maxNumMonitors,
UINT32 maxMonitorAreaFactorA, UINT32 maxMonitorAreaFactorB)
{
/* we're called only if dynamic resolution update is activated */
xfDispContext* xfDisp = nullptr;
rdpSettings* settings = nullptr;
WINPR_ASSERT(disp);
xfDisp = (xfDispContext*)disp->custom;
WINPR_ASSERT(xfDisp);
WINPR_ASSERT(xfDisp->xfc);
settings = xfDisp->xfc->common.context.settings;
WINPR_ASSERT(settings);
WLog_DBG(TAG,
"DisplayControlCapsPdu: MaxNumMonitors: %" PRIu32 " MaxMonitorAreaFactorA: %" PRIu32
" MaxMonitorAreaFactorB: %" PRIu32 "",
maxNumMonitors, maxMonitorAreaFactorA, maxMonitorAreaFactorB);
xfDisp->activated = TRUE;
if (freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
return CHANNEL_RC_OK;
WLog_DBG(TAG, "DisplayControlCapsPdu: setting the window as resizable");
return xf_disp_set_window_resizable(xfDisp) ? CHANNEL_RC_OK : CHANNEL_RC_NO_MEMORY;
}
BOOL xf_disp_init(xfDispContext* xfDisp, DispClientContext* disp)
{
rdpSettings* settings = nullptr;
if (!xfDisp || !xfDisp->xfc || !disp)
return FALSE;
settings = xfDisp->xfc->common.context.settings;
if (!settings)
return FALSE;
xfDisp->disp = disp;
disp->custom = (void*)xfDisp;
if (freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate))
{
disp->DisplayControlCaps = xf_DisplayControlCaps;
#ifdef USABLE_XRANDR
if (freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
{
/* ask X11 to notify us of screen changes */
XRRSelectInput(xfDisp->xfc->display, DefaultRootWindow(xfDisp->xfc->display),
RRScreenChangeNotifyMask);
}
#endif
}
WLog_DBG(TAG, "Channel %s opened", DISP_CHANNEL_NAME);
return TRUE;
}
BOOL xf_disp_uninit(xfDispContext* xfDisp, DispClientContext* disp)
{
if (!xfDisp || !disp)
return FALSE;
WLog_DBG(TAG, "Channel %s closed", DISP_CHANNEL_NAME);
xfDisp->disp = nullptr;
return TRUE;
}

View File

@@ -0,0 +1,44 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Display Control channel
*
* Copyright 2017 David Fort <contact@hardening-consulting.com>
*
* 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.
*/
#ifndef FREERDP_CLIENT_X11_DISP_H
#define FREERDP_CLIENT_X11_DISP_H
#include <freerdp/types.h>
#include <freerdp/client/disp.h>
#include <X11/Xlib.h>
#include "xf_types.h"
typedef struct s_xfDispContext xfDispContext;
FREERDP_API BOOL xf_disp_init(xfDispContext* xfDisp, DispClientContext* disp);
FREERDP_API BOOL xf_disp_uninit(xfDispContext* xfDisp, DispClientContext* disp);
void xf_disp_free(xfDispContext* disp);
WINPR_ATTR_MALLOC(xf_disp_free, 1)
WINPR_ATTR_NODISCARD
xfDispContext* xf_disp_new(xfContext* xfc);
BOOL xf_disp_handle_xevent(xfContext* xfc, const XEvent* event);
BOOL xf_disp_handle_configureNotify(xfContext* xfc, int width, int height);
void xf_disp_resized(xfDispContext* disp);
#endif /* FREERDP_CLIENT_X11_DISP_H */

1433
third_party/FreeRDP/client/X11/xf_event.c vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,61 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Event Handling
*
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#ifndef FREERDP_CLIENT_X11_EVENT_H
#define FREERDP_CLIENT_X11_EVENT_H
#include "xf_keyboard.h"
#include "xf_client.h"
#include "xfreerdp.h"
const char* x11_event_string(int event);
BOOL xf_event_action_script_init(xfContext* xfc);
void xf_event_action_script_free(xfContext* xfc);
BOOL xf_event_process(freerdp* instance, const XEvent* event);
void xf_event_SendClientEvent(xfContext* xfc, xfWindow* window, Atom atom, unsigned int numArgs,
...);
void xf_event_adjust_coordinates(xfContext* xfc, int* x, int* y);
void xf_adjust_coordinates_to_screen(xfContext* xfc, UINT32* x, UINT32* y);
#define xf_generic_MotionNotify(xfc, x, y, window, app) \
xf_generic_MotionNotify_((xfc), (x), (y), (window), (app), __FILE__, __func__, __LINE__)
BOOL xf_generic_MotionNotify_(xfContext* xfc, int x, int y, Window window, BOOL app,
const char* file, const char* fkt, size_t line);
#define xf_generic_RawMotionNotify(xfc, x, y, window, app) \
xf_generic_RawMotionNotify_((xfc), (x), (y), (window), (app), __FILE__, __func__, __LINE__)
BOOL xf_generic_RawMotionNotify_(xfContext* xfc, int x, int y, Window window, BOOL app,
const char* file, const char* fkt, size_t line);
#define xf_generic_ButtonEvent(xfc, x, y, button, window, app, down) \
xf_generic_ButtonEvent_((xfc), (x), (y), (button), (window), (app), (down), __FILE__, \
__func__, __LINE__)
BOOL xf_generic_ButtonEvent_(xfContext* xfc, int x, int y, int button, Window window, BOOL app,
BOOL down, const char* file, const char* fkt, size_t line);
#define xf_generic_RawButtonEvent(xfc, button, app, down) \
xf_generic_RawButtonEvent_((xfc), (button), (app), (down), __FILE__, __func__, __LINE__)
BOOL xf_generic_RawButtonEvent_(xfContext* xfc, int button, BOOL app, BOOL down, const char* file,
const char* fkt, size_t line);
#endif /* FREERDP_CLIENT_X11_EVENT_H */

View File

@@ -0,0 +1,959 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Windows
*
* Licensed under the Apache License, Version 2.0 (the "License");n
* 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.
*/
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/extensions/shape.h>
#include <X11/cursorfont.h>
#include <winpr/assert.h>
#include <winpr/cast.h>
#include "xf_floatbar.h"
#include "xf_utils.h"
#include "resource/close.xbm"
#include "resource/lock.xbm"
#include "resource/unlock.xbm"
#include "resource/minimize.xbm"
#include "resource/restore.xbm"
#include <freerdp/log.h>
#define TAG CLIENT_TAG("x11")
#define FLOATBAR_HEIGHT 26
#define FLOATBAR_DEFAULT_WIDTH 576
#define FLOATBAR_MIN_WIDTH 200
#define FLOATBAR_BORDER 24
#define FLOATBAR_BUTTON_WIDTH 24
#define FLOATBAR_COLOR_BACKGROUND "RGB:31/6c/a9"
#define FLOATBAR_COLOR_BORDER "RGB:75/9a/c8"
#define FLOATBAR_COLOR_FOREGROUND "RGB:FF/FF/FF"
#define XF_FLOATBAR_MODE_NONE 0
#define XF_FLOATBAR_MODE_DRAGGING 1
#define XF_FLOATBAR_MODE_RESIZE_LEFT 2
#define XF_FLOATBAR_MODE_RESIZE_RIGHT 3
#define XF_FLOATBAR_BUTTON_CLOSE 1
#define XF_FLOATBAR_BUTTON_RESTORE 2
#define XF_FLOATBAR_BUTTON_MINIMIZE 3
#define XF_FLOATBAR_BUTTON_LOCKED 4
typedef BOOL (*OnClick)(xfFloatbar*);
typedef struct
{
int x;
int y;
int type;
bool focus;
bool clicked;
OnClick onclick;
Window handle;
} xfFloatbarButton;
struct xf_floatbar
{
int x;
int y;
int width;
int height;
int mode;
int last_motion_x_root;
int last_motion_y_root;
BOOL locked;
xfFloatbarButton* buttons[4];
Window handle;
BOOL hasCursor;
xfContext* xfc;
DWORD flags;
BOOL created;
Window root_window;
char* title;
XFontSet fontSet;
};
static xfFloatbarButton* xf_floatbar_new_button(xfFloatbar* floatbar, int type);
static BOOL xf_floatbar_button_onclick_close(xfFloatbar* floatbar)
{
if (!floatbar)
return FALSE;
return freerdp_abort_connect_context(&floatbar->xfc->common.context);
}
static BOOL xf_floatbar_button_onclick_minimize(xfFloatbar* floatbar)
{
xfContext* xfc = nullptr;
if (!floatbar || !floatbar->xfc)
return FALSE;
xfc = floatbar->xfc;
xf_SetWindowMinimized(xfc, xfc->window);
return TRUE;
}
static BOOL xf_floatbar_button_onclick_restore(xfFloatbar* floatbar)
{
if (!floatbar)
return FALSE;
xf_toggle_fullscreen(floatbar->xfc);
return TRUE;
}
static BOOL xf_floatbar_button_onclick_locked(xfFloatbar* floatbar)
{
if (!floatbar)
return FALSE;
floatbar->locked = !((floatbar->locked));
return xf_floatbar_hide_and_show(floatbar);
}
BOOL xf_floatbar_set_root_y(xfFloatbar* floatbar, int y)
{
if (!floatbar)
return FALSE;
floatbar->last_motion_y_root = y;
return TRUE;
}
BOOL xf_floatbar_hide_and_show(xfFloatbar* floatbar)
{
xfContext* xfc = nullptr;
if (!floatbar || !floatbar->xfc)
return FALSE;
if (!floatbar->created)
return TRUE;
xfc = floatbar->xfc;
WINPR_ASSERT(xfc);
WINPR_ASSERT(xfc->display);
if (!floatbar->locked)
{
if ((floatbar->mode == XF_FLOATBAR_MODE_NONE) && (floatbar->last_motion_y_root > 10) &&
(floatbar->y > (FLOATBAR_HEIGHT * -1)))
{
floatbar->y = floatbar->y - 1;
LogDynAndXMoveWindow(xfc->log, xfc->display, floatbar->handle, floatbar->x,
floatbar->y);
}
else if (floatbar->y < 0 && (floatbar->last_motion_y_root < 10))
{
floatbar->y = floatbar->y + 1;
LogDynAndXMoveWindow(xfc->log, xfc->display, floatbar->handle, floatbar->x,
floatbar->y);
}
}
return TRUE;
}
static BOOL create_floatbar(xfFloatbar* floatbar)
{
xfContext* xfc = nullptr;
Status status = 0;
XWindowAttributes attr = WINPR_C_ARRAY_INIT;
WINPR_ASSERT(floatbar);
if (floatbar->created)
return TRUE;
xfc = floatbar->xfc;
WINPR_ASSERT(xfc);
WINPR_ASSERT(xfc->display);
status = XGetWindowAttributes(xfc->display, floatbar->root_window, &attr);
if (status == 0)
{
WLog_WARN(TAG, "XGetWindowAttributes failed");
return FALSE;
}
floatbar->x = attr.x + attr.width / 2 - FLOATBAR_DEFAULT_WIDTH / 2;
floatbar->y = 0;
if (((floatbar->flags & 0x0004) == 0) && !floatbar->locked)
floatbar->y = -FLOATBAR_HEIGHT + 1;
floatbar->handle = LogDynAndXCreateWindow(
xfc->log, xfc->display, floatbar->root_window, floatbar->x, 0, FLOATBAR_DEFAULT_WIDTH,
FLOATBAR_HEIGHT, 0, CopyFromParent, InputOutput, CopyFromParent, 0, nullptr);
floatbar->width = FLOATBAR_DEFAULT_WIDTH;
floatbar->height = FLOATBAR_HEIGHT;
floatbar->mode = XF_FLOATBAR_MODE_NONE;
floatbar->buttons[0] = xf_floatbar_new_button(floatbar, XF_FLOATBAR_BUTTON_CLOSE);
floatbar->buttons[1] = xf_floatbar_new_button(floatbar, XF_FLOATBAR_BUTTON_RESTORE);
floatbar->buttons[2] = xf_floatbar_new_button(floatbar, XF_FLOATBAR_BUTTON_MINIMIZE);
floatbar->buttons[3] = xf_floatbar_new_button(floatbar, XF_FLOATBAR_BUTTON_LOCKED);
XSelectInput(xfc->display, floatbar->handle,
ExposureMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
FocusChangeMask | LeaveWindowMask | EnterWindowMask | StructureNotifyMask |
PropertyChangeMask);
floatbar->created = TRUE;
return TRUE;
}
BOOL xf_floatbar_toggle_fullscreen(xfFloatbar* floatbar, bool fullscreen)
{
int size = 0;
bool visible = False;
xfContext* xfc = nullptr;
if (!floatbar || !floatbar->xfc)
return FALSE;
xfc = floatbar->xfc;
WINPR_ASSERT(xfc->display);
/* Only visible if enabled */
if (floatbar->flags & 0x0001)
{
/* Visible if fullscreen and flag visible in fullscreen mode */
visible |= ((floatbar->flags & 0x0010) != 0) && fullscreen;
/* Visible if window and flag visible in window mode */
visible |= ((floatbar->flags & 0x0020) != 0) && !fullscreen;
}
if (visible)
{
if (!create_floatbar(floatbar))
return FALSE;
LogDynAndXMapWindow(xfc->log, xfc->display, floatbar->handle);
size = ARRAYSIZE(floatbar->buttons);
for (int i = 0; i < size; i++)
{
xfFloatbarButton* button = floatbar->buttons[i];
LogDynAndXMapWindow(xfc->log, xfc->display, button->handle);
}
/* If default is hidden (and not sticky) don't show on fullscreen state changes */
if (((floatbar->flags & 0x0004) == 0) && !floatbar->locked)
floatbar->y = -FLOATBAR_HEIGHT + 1;
xf_floatbar_hide_and_show(floatbar);
}
else if (floatbar->created)
{
XUnmapSubwindows(xfc->display, floatbar->handle);
LogDynAndXUnmapWindow(xfc->log, xfc->display, floatbar->handle);
}
return TRUE;
}
xfFloatbarButton* xf_floatbar_new_button(xfFloatbar* floatbar, int type)
{
xfFloatbarButton* button = nullptr;
WINPR_ASSERT(floatbar);
WINPR_ASSERT(floatbar->xfc);
WINPR_ASSERT(floatbar->xfc->display);
WINPR_ASSERT(floatbar->handle);
button = (xfFloatbarButton*)calloc(1, sizeof(xfFloatbarButton));
button->type = type;
switch (type)
{
case XF_FLOATBAR_BUTTON_CLOSE:
button->x = floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * type;
button->onclick = xf_floatbar_button_onclick_close;
break;
case XF_FLOATBAR_BUTTON_RESTORE:
button->x = floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * type;
button->onclick = xf_floatbar_button_onclick_restore;
break;
case XF_FLOATBAR_BUTTON_MINIMIZE:
button->x = floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * type;
button->onclick = xf_floatbar_button_onclick_minimize;
break;
case XF_FLOATBAR_BUTTON_LOCKED:
button->x = FLOATBAR_BORDER;
button->onclick = xf_floatbar_button_onclick_locked;
break;
default:
break;
}
button->y = 0;
button->focus = FALSE;
button->handle =
LogDynAndXCreateWindow(floatbar->xfc->log, floatbar->xfc->display, floatbar->handle,
button->x, 0, FLOATBAR_BUTTON_WIDTH, FLOATBAR_BUTTON_WIDTH, 0,
CopyFromParent, InputOutput, CopyFromParent, 0, nullptr);
XSelectInput(floatbar->xfc->display, button->handle,
ExposureMask | ButtonPressMask | ButtonReleaseMask | FocusChangeMask |
LeaveWindowMask | EnterWindowMask | StructureNotifyMask);
return button;
}
xfFloatbar* xf_floatbar_new(xfContext* xfc, Window window, const char* title, DWORD flags)
{
WINPR_ASSERT(xfc);
WINPR_ASSERT(xfc->display);
WINPR_ASSERT(title);
/* Floatbar not enabled */
if ((flags & 0x0001) == 0)
return nullptr;
if (!xfc)
return nullptr;
/* Force disable with remote app */
if (xfc->remote_app)
return nullptr;
xfFloatbar* floatbar = (xfFloatbar*)calloc(1, sizeof(xfFloatbar));
if (!floatbar)
return nullptr;
floatbar->title = _strdup(title);
if (!floatbar->title)
goto fail;
floatbar->root_window = window;
floatbar->flags = flags;
floatbar->xfc = xfc;
floatbar->locked = (flags & 0x0002) != 0;
xf_floatbar_toggle_fullscreen(floatbar, FALSE);
{
char** missingList = nullptr;
int missingCount = 0;
char* defString = nullptr;
floatbar->fontSet = XCreateFontSet(floatbar->xfc->display, "-*-*-*-*-*-*-*-*-*-*-*-*-*-*",
&missingList, &missingCount, &defString);
if (floatbar->fontSet == nullptr)
{
WLog_ERR(TAG, "Failed to create fontset");
}
XFreeStringList(missingList);
}
return floatbar;
fail:
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
xf_floatbar_free(floatbar);
WINPR_PRAGMA_DIAG_POP
return nullptr;
}
static unsigned long xf_floatbar_get_color(xfFloatbar* floatbar, char* rgb_value)
{
XColor color;
WINPR_ASSERT(floatbar);
WINPR_ASSERT(floatbar->xfc);
Display* display = floatbar->xfc->display;
WINPR_ASSERT(display);
Colormap cmap = DefaultColormap(display, XDefaultScreen(display));
XParseColor(display, cmap, rgb_value, &color);
XAllocColor(display, cmap, &color);
return color.pixel;
}
static void xf_floatbar_event_expose(xfFloatbar* floatbar)
{
GC gc = nullptr;
GC shape_gc = nullptr;
Pixmap pmap = 0;
XPoint shape[5] = WINPR_C_ARRAY_INIT;
XPoint border[5] = WINPR_C_ARRAY_INIT;
WINPR_ASSERT(floatbar);
WINPR_ASSERT(floatbar->xfc);
Display* display = floatbar->xfc->display;
WINPR_ASSERT(display);
/* create the pixmap that we'll use for shaping the window */
pmap = LogDynAndXCreatePixmap(floatbar->xfc->log, display, floatbar->handle,
WINPR_ASSERTING_INT_CAST(uint32_t, floatbar->width),
WINPR_ASSERTING_INT_CAST(uint32_t, floatbar->height), 1);
gc = LogDynAndXCreateGC(floatbar->xfc->log, display, floatbar->handle, 0, 0);
shape_gc = LogDynAndXCreateGC(floatbar->xfc->log, display, pmap, 0, 0);
/* points for drawing the floatbar */
shape[0].x = 0;
shape[0].y = 0;
shape[1].x = WINPR_ASSERTING_INT_CAST(short, floatbar->width);
shape[1].y = 0;
shape[2].x = WINPR_ASSERTING_INT_CAST(short, shape[1].x - FLOATBAR_BORDER);
shape[2].y = FLOATBAR_HEIGHT;
shape[3].x = WINPR_ASSERTING_INT_CAST(short, shape[0].x + FLOATBAR_BORDER);
shape[3].y = FLOATBAR_HEIGHT;
shape[4].x = shape[0].x;
shape[4].y = shape[0].y;
/* points for drawing the border of the floatbar */
border[0].x = shape[0].x;
border[0].y = WINPR_ASSERTING_INT_CAST(short, shape[0].y - 1);
border[1].x = WINPR_ASSERTING_INT_CAST(short, shape[1].x - 1);
border[1].y = WINPR_ASSERTING_INT_CAST(short, shape[1].y - 1);
border[2].x = shape[2].x;
border[2].y = WINPR_ASSERTING_INT_CAST(short, shape[2].y - 1);
border[3].x = WINPR_ASSERTING_INT_CAST(short, shape[3].x - 1);
border[3].y = WINPR_ASSERTING_INT_CAST(short, shape[3].y - 1);
border[4].x = border[0].x;
border[4].y = border[0].y;
/* Fill all pixels with 0 */
LogDynAndXSetForeground(floatbar->xfc->log, display, shape_gc, 0);
LogDynAndXFillRectangle(floatbar->xfc->log, display, pmap, shape_gc, 0, 0,
WINPR_ASSERTING_INT_CAST(uint32_t, floatbar->width),
WINPR_ASSERTING_INT_CAST(uint32_t, floatbar->height));
/* Fill all pixels which should be shown with 1 */
LogDynAndXSetForeground(floatbar->xfc->log, display, shape_gc, 1);
XFillPolygon(display, pmap, shape_gc, shape, 5, 0, CoordModeOrigin);
XShapeCombineMask(display, floatbar->handle, ShapeBounding, 0, 0, pmap, ShapeSet);
/* draw the float bar */
LogDynAndXSetForeground(floatbar->xfc->log, display, gc,
xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_BACKGROUND));
XFillPolygon(display, floatbar->handle, gc, shape, 4, 0, CoordModeOrigin);
/* draw an border for the floatbar */
LogDynAndXSetForeground(floatbar->xfc->log, display, gc,
xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_BORDER));
XDrawLines(display, floatbar->handle, gc, border, 5, CoordModeOrigin);
/* draw the host name connected to (limit to maximum file name) */
const size_t len = strnlen(floatbar->title, MAX_PATH);
LogDynAndXSetForeground(floatbar->xfc->log, display, gc,
xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_FOREGROUND));
WINPR_ASSERT(len <= INT32_MAX / 2);
const int fx = floatbar->width / 2 - (int)len * 2;
if (floatbar->fontSet != nullptr)
{
XmbDrawString(display, floatbar->handle, floatbar->fontSet, gc, fx, 15, floatbar->title,
(int)len);
}
else
{
XDrawString(display, floatbar->handle, gc, fx, 15, floatbar->title, (int)len);
}
LogDynAndXFreeGC(floatbar->xfc->log, display, gc);
LogDynAndXFreeGC(floatbar->xfc->log, display, shape_gc);
}
static xfFloatbarButton* xf_floatbar_get_button(xfFloatbar* floatbar, Window window)
{
WINPR_ASSERT(floatbar);
const size_t size = ARRAYSIZE(floatbar->buttons);
for (size_t i = 0; i < size; i++)
{
xfFloatbarButton* button = floatbar->buttons[i];
if (button->handle == window)
{
return button;
}
}
return nullptr;
}
static void xf_floatbar_button_update_positon(xfFloatbar* floatbar)
{
xfFloatbarButton* button = nullptr;
WINPR_ASSERT(floatbar);
xfContext* xfc = floatbar->xfc;
const size_t size = ARRAYSIZE(floatbar->buttons);
for (size_t i = 0; i < size; i++)
{
button = floatbar->buttons[i];
switch (button->type)
{
case XF_FLOATBAR_BUTTON_CLOSE:
button->x =
floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * button->type;
break;
case XF_FLOATBAR_BUTTON_RESTORE:
button->x =
floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * button->type;
break;
case XF_FLOATBAR_BUTTON_MINIMIZE:
button->x =
floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * button->type;
break;
default:
break;
}
WINPR_ASSERT(xfc);
WINPR_ASSERT(xfc->display);
LogDynAndXMoveWindow(xfc->log, xfc->display, button->handle, button->x, button->y);
xf_floatbar_event_expose(floatbar);
}
}
static void xf_floatbar_button_event_expose(xfFloatbar* floatbar, Window window)
{
xfFloatbarButton* button = xf_floatbar_get_button(floatbar, window);
static unsigned char* bits;
GC gc = nullptr;
Pixmap pattern = 0;
xfContext* xfc = floatbar->xfc;
if (!button)
return;
WINPR_ASSERT(xfc);
WINPR_ASSERT(xfc->display);
WINPR_ASSERT(xfc->window);
gc = LogDynAndXCreateGC(xfc->log, xfc->display, button->handle, 0, 0);
floatbar = xfc->window->floatbar;
WINPR_ASSERT(floatbar);
switch (button->type)
{
case XF_FLOATBAR_BUTTON_CLOSE:
bits = close_bits;
break;
case XF_FLOATBAR_BUTTON_RESTORE:
bits = restore_bits;
break;
case XF_FLOATBAR_BUTTON_MINIMIZE:
bits = minimize_bits;
break;
case XF_FLOATBAR_BUTTON_LOCKED:
if (floatbar->locked)
bits = lock_bits;
else
bits = unlock_bits;
break;
default:
break;
}
pattern = XCreateBitmapFromData(xfc->display, button->handle, (const char*)bits,
FLOATBAR_BUTTON_WIDTH, FLOATBAR_BUTTON_WIDTH);
if (!(button->focus))
LogDynAndXSetForeground(floatbar->xfc->log, xfc->display, gc,
xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_BACKGROUND));
else
LogDynAndXSetForeground(floatbar->xfc->log, xfc->display, gc,
xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_BORDER));
LogDynAndXSetBackground(xfc->log, xfc->display, gc,
xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_FOREGROUND));
XCopyPlane(xfc->display, pattern, button->handle, gc, 0, 0, FLOATBAR_BUTTON_WIDTH,
FLOATBAR_BUTTON_WIDTH, 0, 0, 1);
LogDynAndXFreePixmap(xfc->log, xfc->display, pattern);
LogDynAndXFreeGC(xfc->log, xfc->display, gc);
}
static void xf_floatbar_button_event_buttonpress(xfFloatbar* floatbar, const XButtonEvent* event)
{
WINPR_ASSERT(event);
xfFloatbarButton* button = xf_floatbar_get_button(floatbar, event->window);
if (button)
button->clicked = TRUE;
}
static void xf_floatbar_button_event_buttonrelease(xfFloatbar* floatbar, const XButtonEvent* event)
{
xfFloatbarButton* button = nullptr;
WINPR_ASSERT(floatbar);
WINPR_ASSERT(event);
button = xf_floatbar_get_button(floatbar, event->window);
if (button)
{
if (button->clicked)
button->onclick(floatbar);
button->clicked = FALSE;
}
}
static void xf_floatbar_event_buttonpress(xfFloatbar* floatbar, const XButtonEvent* event)
{
WINPR_ASSERT(floatbar);
WINPR_ASSERT(event);
switch (event->button)
{
case Button1:
if (event->x <= FLOATBAR_BORDER)
floatbar->mode = XF_FLOATBAR_MODE_RESIZE_LEFT;
else if (event->x >= (floatbar->width - FLOATBAR_BORDER))
floatbar->mode = XF_FLOATBAR_MODE_RESIZE_RIGHT;
else
floatbar->mode = XF_FLOATBAR_MODE_DRAGGING;
break;
default:
break;
}
}
static void xf_floatbar_event_buttonrelease(xfFloatbar* floatbar, const XButtonEvent* event)
{
WINPR_ASSERT(floatbar);
WINPR_ASSERT(event);
switch (event->button)
{
case Button1:
floatbar->mode = XF_FLOATBAR_MODE_NONE;
break;
default:
break;
}
}
static void xf_floatbar_resize(xfFloatbar* floatbar, const XMotionEvent* event)
{
int x = 0;
int width = 0;
int movement = 0;
WINPR_ASSERT(floatbar);
WINPR_ASSERT(event);
xfContext* xfc = floatbar->xfc;
WINPR_ASSERT(xfc);
WINPR_ASSERT(xfc->display);
/* calculate movement which happened on the root window */
movement = event->x_root - floatbar->last_motion_x_root;
/* set x and width depending if movement happens on the left or right */
if (floatbar->mode == XF_FLOATBAR_MODE_RESIZE_LEFT)
{
x = floatbar->x + movement;
width = floatbar->width + movement * -1;
}
else
{
x = floatbar->x;
width = floatbar->width + movement;
}
/* only resize and move window if still above minimum width */
if (FLOATBAR_MIN_WIDTH < width)
{
LogDynAndXMoveResizeWindow(xfc->log, xfc->display, floatbar->handle, x, 0,
WINPR_ASSERTING_INT_CAST(uint32_t, width),
WINPR_ASSERTING_INT_CAST(uint32_t, floatbar->height));
floatbar->x = x;
floatbar->width = width;
}
}
static void xf_floatbar_dragging(xfFloatbar* floatbar, const XMotionEvent* event)
{
int x = 0;
int movement = 0;
WINPR_ASSERT(floatbar);
WINPR_ASSERT(event);
xfContext* xfc = floatbar->xfc;
WINPR_ASSERT(xfc);
WINPR_ASSERT(xfc->window);
WINPR_ASSERT(xfc->display);
/* calculate movement and new x position */
movement = event->x_root - floatbar->last_motion_x_root;
x = floatbar->x + movement;
/* do nothing if floatbar would be moved out of the window */
if (x < 0 || (x + floatbar->width) > xfc->window->width)
return;
/* move window to new x position */
LogDynAndXMoveWindow(xfc->log, xfc->display, floatbar->handle, x, 0);
/* update struct values for the next event */
floatbar->last_motion_x_root = floatbar->last_motion_x_root + movement;
floatbar->x = x;
}
static void xf_floatbar_event_motionnotify(xfFloatbar* floatbar, const XMotionEvent* event)
{
int mode = 0;
Cursor cursor = 0;
WINPR_ASSERT(floatbar);
WINPR_ASSERT(event);
xfContext* xfc = floatbar->xfc;
WINPR_ASSERT(xfc);
WINPR_ASSERT(xfc->display);
mode = floatbar->mode;
cursor = XCreateFontCursor(xfc->display, XC_arrow);
if ((event->state & Button1Mask) && (mode > XF_FLOATBAR_MODE_DRAGGING))
{
xf_floatbar_resize(floatbar, event);
}
else if ((event->state & Button1Mask) && (mode == XF_FLOATBAR_MODE_DRAGGING))
{
xf_floatbar_dragging(floatbar, event);
}
else
{
if (event->x <= FLOATBAR_BORDER || event->x >= floatbar->width - FLOATBAR_BORDER)
cursor = XCreateFontCursor(xfc->display, XC_sb_h_double_arrow);
}
XDefineCursor(xfc->display, xfc->window->handle, cursor);
XFreeCursor(xfc->display, cursor);
floatbar->last_motion_x_root = event->x_root;
}
static void xf_floatbar_button_event_focusin(xfFloatbar* floatbar, const XAnyEvent* event)
{
xfFloatbarButton* button = nullptr;
WINPR_ASSERT(floatbar);
WINPR_ASSERT(event);
button = xf_floatbar_get_button(floatbar, event->window);
if (button)
{
button->focus = TRUE;
xf_floatbar_button_event_expose(floatbar, event->window);
}
}
static void xf_floatbar_button_event_focusout(xfFloatbar* floatbar, const XAnyEvent* event)
{
xfFloatbarButton* button = nullptr;
WINPR_ASSERT(floatbar);
WINPR_ASSERT(event);
button = xf_floatbar_get_button(floatbar, event->window);
if (button)
{
button->focus = FALSE;
xf_floatbar_button_event_expose(floatbar, event->window);
}
}
static void xf_floatbar_event_focusout(xfFloatbar* floatbar)
{
WINPR_ASSERT(floatbar);
xfContext* xfc = floatbar->xfc;
WINPR_ASSERT(xfc);
if (xfc->pointer)
{
WINPR_ASSERT(xfc->window);
WINPR_ASSERT(xfc->pointer);
XDefineCursor(xfc->display, xfc->window->handle, xfc->pointer->cursor);
}
}
BOOL xf_floatbar_check_event(xfFloatbar* floatbar, const XEvent* event)
{
if (!floatbar || !floatbar->xfc || !event)
return FALSE;
if (!floatbar->created)
return FALSE;
if (event->xany.window == floatbar->handle)
return TRUE;
size_t size = ARRAYSIZE(floatbar->buttons);
for (size_t i = 0; i < size; i++)
{
const xfFloatbarButton* button = floatbar->buttons[i];
if (event->xany.window == button->handle)
return TRUE;
}
return FALSE;
}
BOOL xf_floatbar_event_process(xfFloatbar* floatbar, const XEvent* event)
{
if (!floatbar || !floatbar->xfc || !event)
return FALSE;
if (!floatbar->created)
return FALSE;
switch (event->type)
{
case Expose:
if (event->xexpose.window == floatbar->handle)
xf_floatbar_event_expose(floatbar);
else
xf_floatbar_button_event_expose(floatbar, event->xexpose.window);
break;
case MotionNotify:
xf_floatbar_event_motionnotify(floatbar, &event->xmotion);
break;
case ButtonPress:
if (event->xany.window == floatbar->handle)
xf_floatbar_event_buttonpress(floatbar, &event->xbutton);
else
xf_floatbar_button_event_buttonpress(floatbar, &event->xbutton);
break;
case ButtonRelease:
if (event->xany.window == floatbar->handle)
xf_floatbar_event_buttonrelease(floatbar, &event->xbutton);
else
xf_floatbar_button_event_buttonrelease(floatbar, &event->xbutton);
break;
case EnterNotify:
case FocusIn:
if (event->xany.window != floatbar->handle)
xf_floatbar_button_event_focusin(floatbar, &event->xany);
break;
case LeaveNotify:
case FocusOut:
if (event->xany.window == floatbar->handle)
xf_floatbar_event_focusout(floatbar);
else
xf_floatbar_button_event_focusout(floatbar, &event->xany);
break;
case ConfigureNotify:
if (event->xany.window == floatbar->handle)
xf_floatbar_button_update_positon(floatbar);
break;
case PropertyNotify:
if (event->xany.window == floatbar->handle)
xf_floatbar_button_update_positon(floatbar);
break;
default:
break;
}
return floatbar->handle == event->xany.window;
}
static void xf_floatbar_button_free(xfContext* xfc, xfFloatbarButton* button)
{
if (!button)
return;
if (button->handle)
{
WINPR_ASSERT(xfc);
WINPR_ASSERT(xfc->display);
LogDynAndXUnmapWindow(xfc->log, xfc->display, button->handle);
LogDynAndXDestroyWindow(xfc->log, xfc->display, button->handle);
}
free(button);
}
void xf_floatbar_free(xfFloatbar* floatbar)
{
size_t size = 0;
xfContext* xfc = nullptr;
if (!floatbar)
return;
free(floatbar->title);
xfc = floatbar->xfc;
WINPR_ASSERT(xfc);
size = ARRAYSIZE(floatbar->buttons);
for (size_t i = 0; i < size; i++)
{
xf_floatbar_button_free(xfc, floatbar->buttons[i]);
floatbar->buttons[i] = nullptr;
}
if (floatbar->handle)
{
WINPR_ASSERT(xfc->display);
LogDynAndXUnmapWindow(xfc->log, xfc->display, floatbar->handle);
LogDynAndXDestroyWindow(xfc->log, xfc->display, floatbar->handle);
}
free(floatbar);
}
BOOL xf_floatbar_is_locked(xfFloatbar* floatbar)
{
if (!floatbar)
return FALSE;
return floatbar->mode != XF_FLOATBAR_MODE_NONE;
}
BOOL xf_floatbar_is_window(xfFloatbar* floatbar, Window window)
{
if (!floatbar)
return FALSE;
return floatbar->handle == window;
}
BOOL xfc_is_floatbar_window(xfContext* xfc, Window window)
{
if (!xfc || !xfc->window)
return FALSE;
return xf_floatbar_is_window(xfc->window->floatbar, window);
}

View File

@@ -0,0 +1,45 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Windows
*
* 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.
*/
#ifndef FREERDP_CLIENT_X11_FLOATBAR_H
#define FREERDP_CLIENT_X11_FLOATBAR_H
#include <winpr/wtypes.h>
#include <X11/Xlib.h>
#include "xf_types.h"
typedef struct xf_floatbar xfFloatbar;
void xf_floatbar_free(xfFloatbar* floatbar);
WINPR_ATTR_MALLOC(xf_floatbar_free, 1)
WINPR_ATTR_NODISCARD
xfFloatbar* xf_floatbar_new(xfContext* xfc, Window window, const char* title, DWORD flags);
BOOL xf_floatbar_is_window(xfFloatbar* floatbar, Window window);
BOOL xf_floatbar_is_locked(xfFloatbar* floatbar);
BOOL xf_floatbar_event_process(xfFloatbar* floatbar, const XEvent* event);
BOOL xf_floatbar_check_event(xfFloatbar* floatbar, const XEvent* event);
BOOL xf_floatbar_toggle_fullscreen(xfFloatbar* floatbar, bool fullscreen);
BOOL xf_floatbar_hide_and_show(xfFloatbar* floatbar);
BOOL xf_floatbar_set_root_y(xfFloatbar* floatbar, int y);
BOOL xfc_is_floatbar_window(xfContext* xfc, Window window);
#endif /* FREERDP_CLIENT_X11_FLOATBAR_H */

537
third_party/FreeRDP/client/X11/xf_gfx.c vendored Normal file
View File

@@ -0,0 +1,537 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Graphics Pipeline
*
* Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2016 Armin Novak <armin.novak@thincast.com>
* Copyright 2016 Thincast Technologies GmbH
*
* 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.
*/
#include <freerdp/config.h>
#include <math.h>
#include <winpr/assert.h>
#include <winpr/cast.h>
#include <freerdp/log.h>
#include "xf_gfx.h"
#include "xf_rail.h"
#include "xf_utils.h"
#include "xf_window.h"
#include <X11/Xutil.h>
#define TAG CLIENT_TAG("x11")
static UINT xf_OutputUpdate(xfContext* xfc, xfGfxSurface* surface)
{
UINT rc = ERROR_INTERNAL_ERROR;
UINT32 surfaceX = 0;
UINT32 surfaceY = 0;
RECTANGLE_16 surfaceRect = WINPR_C_ARRAY_INIT;
UINT32 nbRects = 0;
const RECTANGLE_16* rects = nullptr;
WINPR_ASSERT(xfc);
WINPR_ASSERT(surface);
rdpGdi* gdi = xfc->common.context.gdi;
WINPR_ASSERT(gdi);
rdpSettings* settings = xfc->common.context.settings;
WINPR_ASSERT(settings);
surfaceX = surface->gdi.outputOriginX;
surfaceY = surface->gdi.outputOriginY;
surfaceRect.left = 0;
surfaceRect.top = 0;
surfaceRect.right = WINPR_ASSERTING_INT_CAST(UINT16, surface->gdi.mappedWidth);
surfaceRect.bottom = WINPR_ASSERTING_INT_CAST(UINT16, surface->gdi.mappedHeight);
LogDynAndXSetClipMask(xfc->log, xfc->display, xfc->gc, None);
LogDynAndXSetFunction(xfc->log, xfc->display, xfc->gc, GXcopy);
LogDynAndXSetFillStyle(xfc->log, xfc->display, xfc->gc, FillSolid);
if (!region16_intersect_rect(&(surface->gdi.invalidRegion), &(surface->gdi.invalidRegion),
&surfaceRect))
return ERROR_INTERNAL_ERROR;
WINPR_ASSERT(surface->gdi.mappedWidth);
WINPR_ASSERT(surface->gdi.mappedHeight);
const double sx = 1.0 * surface->gdi.outputTargetWidth / (double)surface->gdi.mappedWidth;
const double sy = 1.0 * surface->gdi.outputTargetHeight / (double)surface->gdi.mappedHeight;
if (!(rects = region16_rects(&surface->gdi.invalidRegion, &nbRects)))
return CHANNEL_RC_OK;
xf_lock_x11(xfc);
for (UINT32 x = 0; x < nbRects; x++)
{
const RECTANGLE_16* rect = &rects[x];
const UINT32 nXSrc = rect->left;
const UINT32 nYSrc = rect->top;
const UINT32 swidth = rect->right - nXSrc;
const UINT32 sheight = rect->bottom - nYSrc;
const UINT32 nXDst = (UINT32)lround(1.0 * surfaceX + nXSrc * sx);
const UINT32 nYDst = (UINT32)lround(1.0 * surfaceY + nYSrc * sy);
const UINT32 dwidth = (UINT32)lround(1.0 * swidth * sx);
const UINT32 dheight = (UINT32)lround(1.0 * sheight * sy);
if (surface->stage)
{
if (!freerdp_image_scale(surface->stage, gdi->dstFormat, surface->stageScanline, nXSrc,
nYSrc, dwidth, dheight, surface->gdi.data, surface->gdi.format,
surface->gdi.scanline, nXSrc, nYSrc, swidth, sheight))
goto fail;
}
if (xfc->remote_app)
{
LogDynAndXPutImage(xfc->log, xfc->display, xfc->primary, xfc->gc, surface->image,
WINPR_ASSERTING_INT_CAST(int, nXSrc),
WINPR_ASSERTING_INT_CAST(int, nYSrc),
WINPR_ASSERTING_INT_CAST(int, nXDst),
WINPR_ASSERTING_INT_CAST(int, nYDst), dwidth, dheight);
xf_rail_paint_surface(xfc, surface->gdi.windowId, rect);
}
else
#ifdef WITH_XRENDER
if (freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) ||
freerdp_settings_get_bool(settings, FreeRDP_MultiTouchGestures))
{
LogDynAndXPutImage(xfc->log, xfc->display, xfc->primary, xfc->gc, surface->image,
WINPR_ASSERTING_INT_CAST(int, nXSrc),
WINPR_ASSERTING_INT_CAST(int, nYSrc),
WINPR_ASSERTING_INT_CAST(int, nXDst),
WINPR_ASSERTING_INT_CAST(int, nYDst), dwidth, dheight);
xf_draw_screen(xfc, WINPR_ASSERTING_INT_CAST(int32_t, nXDst),
WINPR_ASSERTING_INT_CAST(int32_t, nYDst),
WINPR_ASSERTING_INT_CAST(int32_t, dwidth),
WINPR_ASSERTING_INT_CAST(int32_t, dheight));
}
else
#endif
{
LogDynAndXPutImage(xfc->log, xfc->display, xfc->drawable, xfc->gc, surface->image,
WINPR_ASSERTING_INT_CAST(int, nXSrc),
WINPR_ASSERTING_INT_CAST(int, nYSrc),
WINPR_ASSERTING_INT_CAST(int, nXDst),
WINPR_ASSERTING_INT_CAST(int, nYDst), dwidth, dheight);
}
}
rc = CHANNEL_RC_OK;
fail:
region16_clear(&surface->gdi.invalidRegion);
LogDynAndXSetClipMask(xfc->log, xfc->display, xfc->gc, None);
LogDynAndXSync(xfc->log, xfc->display, False);
xf_unlock_x11(xfc);
return rc;
}
static UINT xf_WindowUpdate(RdpgfxClientContext* context, xfGfxSurface* surface)
{
WINPR_ASSERT(context);
WINPR_ASSERT(surface);
return IFCALLRESULT(CHANNEL_RC_OK, context->UpdateWindowFromSurface, context, &surface->gdi);
}
static UINT xf_UpdateSurfaces(RdpgfxClientContext* context)
{
UINT16 count = 0;
UINT status = CHANNEL_RC_OK;
UINT16* pSurfaceIds = nullptr;
rdpGdi* gdi = (rdpGdi*)context->custom;
xfContext* xfc = nullptr;
if (!gdi)
return status;
if (gdi->suppressOutput)
return CHANNEL_RC_OK;
xfc = (xfContext*)gdi->context;
EnterCriticalSection(&context->mux);
status = context->GetSurfaceIds(context, &pSurfaceIds, &count);
if (status != CHANNEL_RC_OK)
goto fail;
for (UINT32 index = 0; index < count; index++)
{
xfGfxSurface* surface = (xfGfxSurface*)context->GetSurfaceData(context, pSurfaceIds[index]);
if (!surface)
continue;
/* If UpdateSurfaceArea callback is available, the output has already been updated. */
if (context->UpdateSurfaceArea)
{
if (surface->gdi.handleInUpdateSurfaceArea)
continue;
}
if (surface->gdi.outputMapped)
status = xf_OutputUpdate(xfc, surface);
else if (surface->gdi.windowMapped)
status = xf_WindowUpdate(context, surface);
if (status != CHANNEL_RC_OK)
break;
}
fail:
free(pSurfaceIds);
LeaveCriticalSection(&context->mux);
return status;
}
UINT xf_OutputExpose(xfContext* xfc, UINT32 x, UINT32 y, UINT32 width, UINT32 height)
{
UINT16 count = 0;
UINT status = ERROR_INTERNAL_ERROR;
RECTANGLE_16 invalidRect = WINPR_C_ARRAY_INIT;
RECTANGLE_16 intersection = WINPR_C_ARRAY_INIT;
UINT16* pSurfaceIds = nullptr;
RdpgfxClientContext* context = nullptr;
WINPR_ASSERT(xfc);
WINPR_ASSERT(xfc->common.context.gdi);
context = xfc->common.context.gdi->gfx;
WINPR_ASSERT(context);
invalidRect.left = WINPR_ASSERTING_INT_CAST(UINT16, x);
invalidRect.top = WINPR_ASSERTING_INT_CAST(UINT16, y);
invalidRect.right = WINPR_ASSERTING_INT_CAST(UINT16, x + width);
invalidRect.bottom = WINPR_ASSERTING_INT_CAST(UINT16, y + height);
status = context->GetSurfaceIds(context, &pSurfaceIds, &count);
if (status != CHANNEL_RC_OK)
goto fail;
if (!TryEnterCriticalSection(&context->mux))
{
free(pSurfaceIds);
return CHANNEL_RC_OK;
}
for (UINT32 index = 0; index < count; index++)
{
RECTANGLE_16 surfaceRect = WINPR_C_ARRAY_INIT;
xfGfxSurface* surface = (xfGfxSurface*)context->GetSurfaceData(context, pSurfaceIds[index]);
if (!surface || (!surface->gdi.outputMapped && !surface->gdi.windowMapped))
continue;
surfaceRect.left = WINPR_ASSERTING_INT_CAST(UINT16, surface->gdi.outputOriginX);
surfaceRect.top = WINPR_ASSERTING_INT_CAST(UINT16, surface->gdi.outputOriginY);
surfaceRect.right = WINPR_ASSERTING_INT_CAST(UINT16, surface->gdi.outputOriginX +
surface->gdi.outputTargetWidth);
surfaceRect.bottom = WINPR_ASSERTING_INT_CAST(UINT16, surface->gdi.outputOriginY +
surface->gdi.outputTargetHeight);
if (rectangles_intersection(&invalidRect, &surfaceRect, &intersection))
{
/* Invalid rects are specified relative to surface origin */
intersection.left -= surfaceRect.left;
intersection.top -= surfaceRect.top;
intersection.right -= surfaceRect.left;
intersection.bottom -= surfaceRect.top;
if (!region16_union_rect(&surface->gdi.invalidRegion, &surface->gdi.invalidRegion,
&intersection))
{
free(pSurfaceIds);
LeaveCriticalSection(&context->mux);
goto fail;
}
}
}
free(pSurfaceIds);
LeaveCriticalSection(&context->mux);
IFCALLRET(context->UpdateSurfaces, status, context);
if (status != CHANNEL_RC_OK)
goto fail;
fail:
return status;
}
static UINT32 x11_pad_scanline(UINT32 scanline, UINT32 inPad)
{
/* Ensure X11 alignment is met */
if (inPad > 0)
{
const UINT32 align = inPad / 8;
const UINT32 pad = align - scanline % align;
if (align != pad)
scanline += pad;
}
/* 16 byte alignment is required for ASM optimized code */
if (scanline % 16)
scanline += 16 - scanline % 16;
return scanline;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT xf_CreateSurface(RdpgfxClientContext* context,
const RDPGFX_CREATE_SURFACE_PDU* createSurface)
{
UINT ret = CHANNEL_RC_NO_MEMORY;
size_t size = 0;
xfGfxSurface* surface = nullptr;
rdpGdi* gdi = (rdpGdi*)context->custom;
xfContext* xfc = (xfContext*)gdi->context;
surface = (xfGfxSurface*)calloc(1, sizeof(xfGfxSurface));
if (!surface)
return CHANNEL_RC_NO_MEMORY;
surface->gdi.codecs = context->codecs;
if (!surface->gdi.codecs)
{
WLog_ERR(TAG, "global GDI codecs aren't set");
goto out_free;
}
surface->gdi.surfaceId = createSurface->surfaceId;
surface->gdi.width = x11_pad_scanline(createSurface->width, 0);
surface->gdi.height = x11_pad_scanline(createSurface->height, 0);
surface->gdi.mappedWidth = createSurface->width;
surface->gdi.mappedHeight = createSurface->height;
surface->gdi.outputTargetWidth = createSurface->width;
surface->gdi.outputTargetHeight = createSurface->height;
switch (createSurface->pixelFormat)
{
case GFX_PIXEL_FORMAT_ARGB_8888:
surface->gdi.format = PIXEL_FORMAT_BGRA32;
break;
case GFX_PIXEL_FORMAT_XRGB_8888:
surface->gdi.format = PIXEL_FORMAT_BGRX32;
break;
default:
WLog_ERR(TAG, "unknown pixelFormat 0x%" PRIx32 "", createSurface->pixelFormat);
ret = ERROR_INTERNAL_ERROR;
goto out_free;
}
surface->gdi.scanline = surface->gdi.width * FreeRDPGetBytesPerPixel(surface->gdi.format);
surface->gdi.scanline = x11_pad_scanline(surface->gdi.scanline,
WINPR_ASSERTING_INT_CAST(uint32_t, xfc->scanline_pad));
size = 1ull * surface->gdi.scanline * surface->gdi.height;
surface->gdi.data = (BYTE*)winpr_aligned_malloc(size, 16);
if (!surface->gdi.data)
{
WLog_ERR(TAG, "unable to allocate GDI data");
goto out_free;
}
ZeroMemory(surface->gdi.data, size);
if (FreeRDPAreColorFormatsEqualNoAlpha(gdi->dstFormat, surface->gdi.format))
{
WINPR_ASSERT(xfc->depth != 0);
surface->image = LogDynAndXCreateImage(
xfc->log, xfc->display, xfc->visual, WINPR_ASSERTING_INT_CAST(uint32_t, xfc->depth),
ZPixmap, 0, (char*)surface->gdi.data, surface->gdi.mappedWidth,
surface->gdi.mappedHeight, xfc->scanline_pad,
WINPR_ASSERTING_INT_CAST(int, surface->gdi.scanline));
}
else
{
UINT32 width = surface->gdi.width;
UINT32 bytes = FreeRDPGetBytesPerPixel(gdi->dstFormat);
surface->stageScanline = width * bytes;
surface->stageScanline = x11_pad_scanline(
surface->stageScanline, WINPR_ASSERTING_INT_CAST(uint32_t, xfc->scanline_pad));
size = 1ull * surface->stageScanline * surface->gdi.height;
surface->stage = (BYTE*)winpr_aligned_malloc(size, 16);
if (!surface->stage)
{
WLog_ERR(TAG, "unable to allocate stage buffer");
goto out_free_gdidata;
}
ZeroMemory(surface->stage, size);
WINPR_ASSERT(xfc->depth != 0);
surface->image = LogDynAndXCreateImage(
xfc->log, xfc->display, xfc->visual, WINPR_ASSERTING_INT_CAST(uint32_t, xfc->depth),
ZPixmap, 0, (char*)surface->stage, surface->gdi.mappedWidth, surface->gdi.mappedHeight,
xfc->scanline_pad, WINPR_ASSERTING_INT_CAST(int, surface->stageScanline));
}
if (!surface->image)
{
WLog_ERR(TAG, "an error occurred when creating the XImage");
goto error_surface_image;
}
surface->image->byte_order = LSBFirst;
surface->image->bitmap_bit_order = LSBFirst;
region16_init(&surface->gdi.invalidRegion);
if (context->SetSurfaceData(context, surface->gdi.surfaceId, (void*)surface) != CHANNEL_RC_OK)
{
WLog_ERR(TAG, "an error occurred during SetSurfaceData");
goto error_set_surface_data;
}
return CHANNEL_RC_OK;
error_set_surface_data:
surface->image->data = nullptr;
XDestroyImage(surface->image);
error_surface_image:
winpr_aligned_free(surface->stage);
out_free_gdidata:
winpr_aligned_free(surface->gdi.data);
out_free:
free(surface);
return ret;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT xf_DeleteSurface(RdpgfxClientContext* context,
const RDPGFX_DELETE_SURFACE_PDU* deleteSurface)
{
rdpCodecs* codecs = nullptr;
UINT status = 0;
EnterCriticalSection(&context->mux);
xfGfxSurface* surface =
(xfGfxSurface*)context->GetSurfaceData(context, deleteSurface->surfaceId);
if (surface)
{
if (surface->gdi.windowMapped)
{
status = IFCALLRESULT(CHANNEL_RC_OK, context->UnmapWindowForSurface, context,
surface->gdi.windowId);
if (status != CHANNEL_RC_OK)
goto fail;
}
#ifdef WITH_GFX_H264
h264_context_free(surface->gdi.h264);
#endif
surface->image->data = nullptr;
XDestroyImage(surface->image);
winpr_aligned_free(surface->gdi.data);
winpr_aligned_free(surface->stage);
region16_uninit(&surface->gdi.invalidRegion);
codecs = surface->gdi.codecs;
free(surface);
}
status = context->SetSurfaceData(context, deleteSurface->surfaceId, nullptr);
if (codecs && codecs->progressive)
progressive_delete_surface_context(codecs->progressive, deleteSurface->surfaceId);
fail:
LeaveCriticalSection(&context->mux);
return status;
}
static UINT xf_UnmapWindowForSurface(RdpgfxClientContext* context, UINT64 windowID)
{
WINPR_ASSERT(context);
rdpGdi* gdi = (rdpGdi*)context->custom;
WINPR_ASSERT(gdi);
xfContext* xfc = (xfContext*)gdi->context;
WINPR_ASSERT(gdi->context);
if (freerdp_settings_get_bool(gdi->context->settings, FreeRDP_RemoteApplicationMode))
{
xfAppWindow* appWindow = xf_rail_get_window(xfc, windowID, FALSE);
if (appWindow)
xf_AppWindowDestroyImage(appWindow);
xf_rail_return_window(appWindow, FALSE);
}
WLog_WARN(TAG, "function not implemented");
return CHANNEL_RC_OK;
}
static UINT xf_UpdateWindowFromSurface(RdpgfxClientContext* context, gdiGfxSurface* surface)
{
WINPR_ASSERT(context);
WINPR_ASSERT(surface);
rdpGdi* gdi = (rdpGdi*)context->custom;
WINPR_ASSERT(gdi);
xfContext* xfc = (xfContext*)gdi->context;
WINPR_ASSERT(gdi->context);
if (freerdp_settings_get_bool(gdi->context->settings, FreeRDP_RemoteApplicationMode))
return xf_AppUpdateWindowFromSurface(xfc, surface);
WLog_WARN(TAG, "function not implemented");
return CHANNEL_RC_OK;
}
void xf_graphics_pipeline_init(xfContext* xfc, RdpgfxClientContext* gfx)
{
rdpGdi* gdi = nullptr;
const rdpSettings* settings = nullptr;
WINPR_ASSERT(xfc);
WINPR_ASSERT(gfx);
settings = xfc->common.context.settings;
WINPR_ASSERT(settings);
gdi = xfc->common.context.gdi;
gdi_graphics_pipeline_init(gdi, gfx);
if (!freerdp_settings_get_bool(settings, FreeRDP_SoftwareGdi))
{
gfx->UpdateSurfaces = xf_UpdateSurfaces;
gfx->CreateSurface = xf_CreateSurface;
gfx->DeleteSurface = xf_DeleteSurface;
}
gfx->UpdateWindowFromSurface = xf_UpdateWindowFromSurface;
gfx->UnmapWindowForSurface = xf_UnmapWindowForSurface;
}
void xf_graphics_pipeline_uninit(xfContext* xfc, RdpgfxClientContext* gfx)
{
rdpGdi* gdi = nullptr;
WINPR_ASSERT(xfc);
gdi = xfc->common.context.gdi;
gdi_graphics_pipeline_uninit(gdi, gfx);
}

45
third_party/FreeRDP/client/X11/xf_gfx.h vendored Normal file
View File

@@ -0,0 +1,45 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Graphics Pipeline
*
* Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2016 Thincast Technologies GmbH
* Copyright 2016 Armin Novak <armin.novak@thincast.com>
*
* 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.
*/
#ifndef FREERDP_CLIENT_X11_GFX_H
#define FREERDP_CLIENT_X11_GFX_H
#include "xf_client.h"
#include "xfreerdp.h"
#include <freerdp/gdi/gfx.h>
struct xf_gfx_surface
{
gdiGfxSurface gdi;
BYTE* stage;
UINT32 stageScanline;
XImage* image;
};
typedef struct xf_gfx_surface xfGfxSurface;
UINT xf_OutputExpose(xfContext* xfc, UINT32 x, UINT32 y, UINT32 width, UINT32 height);
void xf_graphics_pipeline_init(xfContext* xfc, RdpgfxClientContext* gfx);
void xf_graphics_pipeline_uninit(xfContext* xfc, RdpgfxClientContext* gfx);
#endif /* FREERDP_CLIENT_X11_GFX_H */

View File

@@ -0,0 +1,532 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Graphical Objects
*
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2016 Thincast Technologies GmbH
* Copyright 2016 Armin Novak <armin.novak@thincast.com>
*
* 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.
*/
#include <freerdp/config.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#ifdef WITH_XCURSOR
#include <X11/Xcursor/Xcursor.h>
#endif
#include <float.h>
#include <math.h>
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <freerdp/codec/bitmap.h>
#include <freerdp/codec/rfx.h>
#include "xf_graphics.h"
#include "xf_event.h"
#include "xf_utils.h"
#include <freerdp/log.h>
#define TAG CLIENT_TAG("x11")
static BOOL xf_Pointer_Set(rdpContext* context, rdpPointer* pointer);
BOOL xf_decode_color(xfContext* xfc, const UINT32 srcColor, XColor* color)
{
UINT32 SrcFormat = 0;
BYTE r = 0;
BYTE g = 0;
BYTE b = 0;
BYTE a = 0;
if (!xfc || !color)
return FALSE;
rdpGdi* gdi = xfc->common.context.gdi;
if (!gdi)
return FALSE;
rdpSettings* settings = xfc->common.context.settings;
if (!settings)
return FALSE;
switch (freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth))
{
case 32:
case 24:
SrcFormat = PIXEL_FORMAT_BGR24;
break;
case 16:
SrcFormat = PIXEL_FORMAT_RGB16;
break;
case 15:
SrcFormat = PIXEL_FORMAT_RGB15;
break;
case 8:
SrcFormat = PIXEL_FORMAT_RGB8;
break;
default:
return FALSE;
}
FreeRDPSplitColor(srcColor, SrcFormat, &r, &g, &b, &a, &gdi->palette);
color->blue = (unsigned short)(b << 8);
color->green = (unsigned short)(g << 8);
color->red = (unsigned short)(r << 8);
color->flags = DoRed | DoGreen | DoBlue;
return (XAllocColor(xfc->display, xfc->colormap, color) != 0);
}
static BOOL xf_Pointer_GetCursorForCurrentScale(rdpContext* context, rdpPointer* pointer,
Cursor* cursor)
{
#if defined(WITH_XCURSOR) && defined(WITH_XRENDER)
xfContext* xfc = (xfContext*)context;
xfPointer* xpointer = (xfPointer*)pointer;
XcursorImage ci = WINPR_C_ARRAY_INIT;
int cursorIndex = -1;
if (!context || !pointer || !context->gdi)
return FALSE;
rdpSettings* settings = xfc->common.context.settings;
if (!settings)
return FALSE;
const double dw = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
const double xscale =
(freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) ? 1.0 * xfc->scaledWidth / dw
: 1);
const double dh = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
const double yscale =
(freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) ? 1.0 * xfc->scaledHeight / dh
: 1);
const UINT32 xTargetSize = MAX(1, (UINT32)lround(1.0 * pointer->width * xscale));
const UINT32 yTargetSize = MAX(1, (UINT32)lround(1.0 * pointer->height * yscale));
WLog_DBG(TAG, "scaled: %" PRId32 "x%" PRId32 ", desktop: %" PRIu32 "x%" PRIu32,
xfc->scaledWidth, xfc->scaledHeight,
freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight));
for (UINT32 i = 0; i < xpointer->nCursors; i++)
{
if ((xpointer->cursorWidths[i] == xTargetSize) &&
(xpointer->cursorHeights[i] == yTargetSize))
{
cursorIndex = WINPR_ASSERTING_INT_CAST(int, i);
}
}
if (cursorIndex == -1)
{
UINT32 CursorFormat = 0;
xf_lock_x11(xfc);
if (!xfc->invert)
CursorFormat = (!xfc->big_endian) ? PIXEL_FORMAT_RGBA32 : PIXEL_FORMAT_ABGR32;
else
CursorFormat = (!xfc->big_endian) ? PIXEL_FORMAT_BGRA32 : PIXEL_FORMAT_ARGB32;
if (xpointer->nCursors == xpointer->mCursors)
{
void* tmp2 = nullptr;
xpointer->mCursors = (xpointer->mCursors == 0 ? 1 : xpointer->mCursors * 2);
tmp2 = realloc(xpointer->cursorWidths, sizeof(UINT32) * xpointer->mCursors);
if (!tmp2)
{
xf_unlock_x11(xfc);
return FALSE;
}
xpointer->cursorWidths = tmp2;
tmp2 = realloc(xpointer->cursorHeights, sizeof(UINT32) * xpointer->mCursors);
if (!tmp2)
{
xf_unlock_x11(xfc);
return FALSE;
}
xpointer->cursorHeights = (UINT32*)tmp2;
tmp2 = realloc(xpointer->cursors, sizeof(Cursor) * xpointer->mCursors);
if (!tmp2)
{
xf_unlock_x11(xfc);
return FALSE;
}
xpointer->cursors = (Cursor*)tmp2;
}
ci.version = XCURSOR_IMAGE_VERSION;
ci.size = sizeof(ci);
ci.width = xTargetSize;
ci.height = yTargetSize;
ci.xhot = (XcursorDim)lround(1.0 * pointer->xPos * xscale);
ci.yhot = (XcursorDim)lround(1.0 * pointer->yPos * yscale);
const size_t size = 1ull * ci.height * ci.width * FreeRDPGetBytesPerPixel(CursorFormat);
void* tmp = winpr_aligned_malloc(size, 16);
if (!tmp)
{
xf_unlock_x11(xfc);
return FALSE;
}
ci.pixels = (XcursorPixel*)tmp;
const double xs = fabs(fabs(xscale) - 1.0);
const double ys = fabs(fabs(yscale) - 1.0);
WLog_DBG(TAG,
"cursorIndex %" PRId32 " scaling pointer %" PRIu32 "x%" PRIu32 " --> %" PRIu32
"x%" PRIu32 " [%lfx%lf]",
cursorIndex, pointer->width, pointer->height, ci.width, ci.height, xscale, yscale);
if ((xs > DBL_EPSILON) || (ys > DBL_EPSILON))
{
if (!freerdp_image_scale((BYTE*)ci.pixels, CursorFormat, 0, 0, 0, ci.width, ci.height,
(BYTE*)xpointer->cursorPixels, CursorFormat, 0, 0, 0,
pointer->width, pointer->height))
{
winpr_aligned_free(tmp);
xf_unlock_x11(xfc);
return FALSE;
}
}
else
{
ci.pixels = xpointer->cursorPixels;
}
const size_t idx = xpointer->nCursors;
xpointer->cursorWidths[idx] = ci.width;
xpointer->cursorHeights[idx] = ci.height;
xpointer->cursors[idx] = XcursorImageLoadCursor(xfc->display, &ci);
cursorIndex = WINPR_ASSERTING_INT_CAST(int, idx);
xpointer->nCursors += 1;
winpr_aligned_free(tmp);
xf_unlock_x11(xfc);
}
else
{
WLog_DBG(TAG, "using cached cursor %" PRId32, cursorIndex);
}
cursor[0] = xpointer->cursors[cursorIndex];
#endif
return TRUE;
}
/* Pointer Class */
static Window xf_Pointer_get_window(xfContext* xfc)
{
if (!xfc)
{
WLog_WARN(TAG, "xf_Pointer: Invalid context");
return 0;
}
if (xfc->remote_app)
{
Window w = 0;
xf_AppWindowsLock(xfc);
if (!xfc->appWindow)
WLog_WARN(TAG, "xf_Pointer: Invalid appWindow");
else
w = xfc->appWindow->handle;
xf_AppWindowsUnlock(xfc);
return w;
}
else
{
if (!xfc->window)
{
WLog_WARN(TAG, "xf_Pointer: Invalid window");
return 0;
}
return xfc->window->handle;
}
}
BOOL xf_pointer_update_scale(xfContext* xfc)
{
xfPointer* pointer = nullptr;
WINPR_ASSERT(xfc);
pointer = xfc->pointer;
if (!pointer)
return TRUE;
return xf_Pointer_Set(&xfc->common.context, &xfc->pointer->pointer);
}
static BOOL xf_Pointer_New(rdpContext* context, rdpPointer* pointer)
{
BOOL rc = FALSE;
#ifdef WITH_XCURSOR
UINT32 CursorFormat = 0;
xfContext* xfc = (xfContext*)context;
xfPointer* xpointer = (xfPointer*)pointer;
if (!context || !pointer || !context->gdi)
goto fail;
if (!xfc->invert)
CursorFormat = (!xfc->big_endian) ? PIXEL_FORMAT_RGBA32 : PIXEL_FORMAT_ABGR32;
else
CursorFormat = (!xfc->big_endian) ? PIXEL_FORMAT_BGRA32 : PIXEL_FORMAT_ARGB32;
xpointer->nCursors = 0;
xpointer->mCursors = 0;
{
const size_t size =
1ull * pointer->height * pointer->width * FreeRDPGetBytesPerPixel(CursorFormat);
xpointer->cursorPixels = (XcursorPixel*)winpr_aligned_malloc(size, 16);
if (!xpointer->cursorPixels)
goto fail;
}
if (!freerdp_image_copy_from_pointer_data(
(BYTE*)xpointer->cursorPixels, CursorFormat, 0, 0, 0, pointer->width, pointer->height,
pointer->xorMaskData, pointer->lengthXorMask, pointer->andMaskData,
pointer->lengthAndMask, pointer->xorBpp, &context->gdi->palette))
goto fail;
#endif
rc = TRUE;
fail:
WLog_DBG(TAG, "%p", WINPR_CXX_COMPAT_CAST(const void*, rc ? pointer : nullptr));
return rc;
}
static void xf_Pointer_Free(rdpContext* context, rdpPointer* pointer)
{
WLog_DBG(TAG, "%p", WINPR_CXX_COMPAT_CAST(const void*, pointer));
#ifdef WITH_XCURSOR
xfContext* xfc = (xfContext*)context;
xfPointer* xpointer = (xfPointer*)pointer;
xf_lock_x11(xfc);
winpr_aligned_free(xpointer->cursorPixels);
free(xpointer->cursorWidths);
free(xpointer->cursorHeights);
for (UINT32 i = 0; i < xpointer->nCursors; i++)
{
XFreeCursor(xfc->display, xpointer->cursors[i]);
}
free(xpointer->cursors);
xpointer->nCursors = 0;
xpointer->mCursors = 0;
xf_unlock_x11(xfc);
#endif
}
static BOOL xf_Pointer_Set(rdpContext* context, rdpPointer* pointer)
{
WLog_DBG(TAG, "%p", WINPR_CXX_COMPAT_CAST(const void*, pointer));
#ifdef WITH_XCURSOR
xfContext* xfc = (xfContext*)context;
Window handle = xf_Pointer_get_window(xfc);
WINPR_ASSERT(xfc);
WINPR_ASSERT(pointer);
xfc->pointer = (xfPointer*)pointer;
/* in RemoteApp mode, window can be null if none has had focus */
if (handle)
{
if (!xf_Pointer_GetCursorForCurrentScale(context, pointer, &(xfc->pointer->cursor)))
return FALSE;
xf_lock_x11(xfc);
XDefineCursor(xfc->display, handle, xfc->pointer->cursor);
xf_unlock_x11(xfc);
}
else
{
WLog_WARN(TAG, "handle=%lu", handle);
}
xfc->isCursorHidden = false;
#endif
return TRUE;
}
static BOOL xf_Pointer_SetNull(rdpContext* context)
{
WLog_DBG(TAG, "called");
#ifdef WITH_XCURSOR
xfContext* xfc = (xfContext*)context;
static Cursor nullcursor = None;
Window handle = xf_Pointer_get_window(xfc);
xf_lock_x11(xfc);
if (nullcursor == None)
{
XcursorImage ci = WINPR_C_ARRAY_INIT;
XcursorPixel xp = 0;
ci.version = XCURSOR_IMAGE_VERSION;
ci.size = sizeof(ci);
ci.width = ci.height = 1;
ci.xhot = ci.yhot = 0;
ci.pixels = &xp;
nullcursor = XcursorImageLoadCursor(xfc->display, &ci);
}
xfc->pointer = nullptr;
if ((handle) && (nullcursor != None))
XDefineCursor(xfc->display, handle, nullcursor);
xf_unlock_x11(xfc);
xfc->isCursorHidden = true;
#endif
return TRUE;
}
static BOOL xf_Pointer_SetDefault(rdpContext* context)
{
WLog_DBG(TAG, "called");
#ifdef WITH_XCURSOR
xfContext* xfc = (xfContext*)context;
Window handle = xf_Pointer_get_window(xfc);
xf_lock_x11(xfc);
xfc->pointer = nullptr;
if (handle)
XUndefineCursor(xfc->display, handle);
xf_unlock_x11(xfc);
xfc->isCursorHidden = false;
#endif
return TRUE;
}
static BOOL xf_Pointer_SetPosition(rdpContext* context, UINT32 x, UINT32 y)
{
xfContext* xfc = (xfContext*)context;
XWindowAttributes current = WINPR_C_ARRAY_INIT;
XSetWindowAttributes tmp = WINPR_C_ARRAY_INIT;
BOOL ret = FALSE;
Status rc = 0;
Window handle = xf_Pointer_get_window(xfc);
if (!handle)
{
WLog_WARN(TAG, "focus %d, handle%lu", xfc->focused, handle);
return TRUE;
}
WLog_DBG(TAG, "%" PRIu32 "x%" PRIu32, x, y);
if (!xfc->focused)
return TRUE;
xf_adjust_coordinates_to_screen(xfc, &x, &y);
xf_lock_x11(xfc);
rc = XGetWindowAttributes(xfc->display, handle, &current);
if (rc == 0)
{
WLog_WARN(TAG, "XGetWindowAttributes==%d", rc);
goto out;
}
tmp.event_mask = (current.your_event_mask & ~(PointerMotionMask));
rc = LogDynAndXChangeWindowAttributes(xfc->log, xfc->display, handle, CWEventMask, &tmp);
if (rc == 0)
goto out;
rc = XWarpPointer(xfc->display, handle, handle, 0, 0, 0, 0, WINPR_ASSERTING_INT_CAST(int, x),
WINPR_ASSERTING_INT_CAST(int, y));
if (rc == 0)
WLog_WARN(TAG, "XWarpPointer==%d", rc);
tmp.event_mask = current.your_event_mask;
LogDynAndXChangeWindowAttributes(xfc->log, xfc->display, handle, CWEventMask, &tmp);
ret = TRUE;
out:
xf_unlock_x11(xfc);
return ret;
}
/* Graphics Module */
BOOL xf_register_pointer(rdpGraphics* graphics)
{
rdpPointer pointer = WINPR_C_ARRAY_INIT;
pointer.size = sizeof(xfPointer);
pointer.New = xf_Pointer_New;
pointer.Free = xf_Pointer_Free;
pointer.Set = xf_Pointer_Set;
pointer.SetNull = xf_Pointer_SetNull;
pointer.SetDefault = xf_Pointer_SetDefault;
pointer.SetPosition = xf_Pointer_SetPosition;
graphics_register_pointer(graphics, &pointer);
return TRUE;
}
UINT32 xf_get_local_color_format(xfContext* xfc, BOOL aligned)
{
UINT32 DstFormat = 0;
BOOL invert = FALSE;
if (!xfc)
return 0;
invert = xfc->invert;
WINPR_ASSERT(xfc->depth != 0);
if (xfc->depth == 32)
DstFormat = (!invert) ? PIXEL_FORMAT_RGBA32 : PIXEL_FORMAT_BGRA32;
else if (xfc->depth == 30)
DstFormat = (!invert) ? PIXEL_FORMAT_RGBX32_DEPTH30 : PIXEL_FORMAT_BGRX32_DEPTH30;
else if (xfc->depth == 24)
{
if (aligned)
DstFormat = (!invert) ? PIXEL_FORMAT_RGBX32 : PIXEL_FORMAT_BGRX32;
else
DstFormat = (!invert) ? PIXEL_FORMAT_RGB24 : PIXEL_FORMAT_BGR24;
}
else if (xfc->depth == 16)
DstFormat = PIXEL_FORMAT_RGB16;
else if (xfc->depth == 15)
DstFormat = PIXEL_FORMAT_RGB15;
else
DstFormat = (!invert) ? PIXEL_FORMAT_RGBX32 : PIXEL_FORMAT_BGRX32;
return DstFormat;
}

View File

@@ -0,0 +1,33 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Graphical Objects
*
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#ifndef FREERDP_CLIENT_X11_GRAPHICS_H
#define FREERDP_CLIENT_X11_GRAPHICS_H
#include "xf_client.h"
#include "xfreerdp.h"
BOOL xf_register_pointer(rdpGraphics* graphics);
BOOL xf_decode_color(xfContext* xfc, UINT32 srcColor, XColor* color);
UINT32 xf_get_local_color_format(xfContext* xfc, BOOL aligned);
BOOL xf_pointer_update_scale(xfContext* xfc);
#endif /* FREERDP_CLIENT_X11_GRAPHICS_H */

View File

@@ -0,0 +1,920 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Input
*
* Copyright 2013 Corey Clayton <can.of.tuna@gmail.com>
*
* 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.
*/
#include <freerdp/config.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#ifdef WITH_XCURSOR
#include <X11/Xcursor/Xcursor.h>
#endif
#ifdef WITH_XI
#include <X11/extensions/XInput2.h>
#endif
#include <math.h>
#include <float.h>
#include <limits.h>
#include "xf_event.h"
#include "xf_input.h"
#include "xf_utils.h"
#include <winpr/assert.h>
#include <winpr/wtypes.h>
#include <freerdp/log.h>
#define TAG CLIENT_TAG("x11")
#ifdef WITH_XI
#define PAN_THRESHOLD 50
#define ZOOM_THRESHOLD 10
#define MIN_FINGER_DIST 5
static int xf_input_event(xfContext* xfc, WINPR_ATTR_UNUSED const XEvent* xevent,
XIDeviceEvent* event, int evtype);
#ifdef DEBUG_XINPUT
static const char* xf_input_get_class_string(int class)
{
if (class == XIKeyClass)
return "XIKeyClass";
else if (class == XIButtonClass)
return "XIButtonClass";
else if (class == XIValuatorClass)
return "XIValuatorClass";
else if (class == XIScrollClass)
return "XIScrollClass";
else if (class == XITouchClass)
return "XITouchClass";
return "XIUnknownClass";
}
#endif
static BOOL register_input_events(xfContext* xfc, Window window)
{
#define MAX_NR_MASKS 64
int ndevices = 0;
int nmasks = 0;
XIEventMask evmasks[MAX_NR_MASKS] = WINPR_C_ARRAY_INIT;
BYTE masks[MAX_NR_MASKS][XIMaskLen(XI_LASTEVENT)] = WINPR_C_ARRAY_INIT;
WINPR_ASSERT(xfc);
rdpSettings* settings = xfc->common.context.settings;
WINPR_ASSERT(settings);
XIDeviceInfo* info = XIQueryDevice(xfc->display, XIAllDevices, &ndevices);
for (int i = 0; i < MIN(ndevices, MAX_NR_MASKS); i++)
{
BOOL used = FALSE;
XIDeviceInfo* dev = &info[i];
evmasks[nmasks].mask = masks[nmasks];
evmasks[nmasks].mask_len = sizeof(masks[0]);
evmasks[nmasks].deviceid = dev->deviceid;
/* Ignore virtual core pointer */
if (strcmp(dev->name, "Virtual core pointer") == 0)
continue;
for (int j = 0; j < dev->num_classes; j++)
{
const XIAnyClassInfo* c_class = dev->classes[j];
switch (c_class->type)
{
case XITouchClass:
if (freerdp_settings_get_bool(settings, FreeRDP_MultiTouchInput))
{
const XITouchClassInfo* t = (const XITouchClassInfo*)c_class;
if (t->mode == XIDirectTouch)
{
WLog_DBG(
TAG,
"%s %s touch device (id: %d, mode: %d), supporting %d touches.",
dev->name, (t->mode == XIDirectTouch) ? "direct" : "dependent",
dev->deviceid, t->mode, t->num_touches);
XISetMask(masks[nmasks], XI_TouchBegin);
XISetMask(masks[nmasks], XI_TouchUpdate);
XISetMask(masks[nmasks], XI_TouchEnd);
}
}
break;
case XIButtonClass:
{
const XIButtonClassInfo* t = (const XIButtonClassInfo*)c_class;
WLog_DBG(TAG, "%s button device (id: %d, mode: %d)", dev->name, dev->deviceid,
t->num_buttons);
XISetMask(masks[nmasks], XI_ButtonPress);
XISetMask(masks[nmasks], XI_ButtonRelease);
XISetMask(masks[nmasks], XI_Motion);
used = TRUE;
break;
}
case XIValuatorClass:
{
static wLog* log = nullptr;
if (!log)
log = WLog_Get(TAG);
const XIValuatorClassInfo* t = (const XIValuatorClassInfo*)c_class;
char* name =
t->label ? Safe_XGetAtomName(log, xfc->display, t->label) : nullptr;
WLog_Print(log, WLOG_DEBUG,
"%s device (id: %d) valuator %d label %s range %f - %f", dev->name,
dev->deviceid, t->number, name ? name : "None", t->min, t->max);
free(name);
if (t->number == 2)
{
double max_pressure = t->max;
char devName[200] = WINPR_C_ARRAY_INIT;
strncpy(devName, dev->name, ARRAYSIZE(devName) - 1);
CharLowerBuffA(devName, ARRAYSIZE(devName));
if (strstr(devName, "eraser") != nullptr)
{
if (freerdp_client_handle_pen(&xfc->common,
FREERDP_PEN_REGISTER |
FREERDP_PEN_IS_INVERTED |
FREERDP_PEN_HAS_PRESSURE,
dev->deviceid, max_pressure))
WLog_DBG(TAG, "registered eraser");
}
else if (strstr(devName, "stylus") != nullptr ||
strstr(devName, "pen") != nullptr)
{
if (freerdp_client_handle_pen(
&xfc->common, FREERDP_PEN_REGISTER | FREERDP_PEN_HAS_PRESSURE,
dev->deviceid, max_pressure))
WLog_DBG(TAG, "registered pen");
}
}
break;
}
default:
break;
}
}
if (used)
nmasks++;
}
XIFreeDeviceInfo(info);
if (nmasks > 0)
{
Status xstatus = XISelectEvents(xfc->display, window, evmasks, nmasks);
if (xstatus != 0)
WLog_WARN(TAG, "XISelectEvents returned %d", xstatus);
}
return TRUE;
}
static BOOL register_raw_events(xfContext* xfc, Window window)
{
XIEventMask mask;
unsigned char mask_bytes[XIMaskLen(XI_LASTEVENT)] = WINPR_C_ARRAY_INIT;
rdpSettings* settings = nullptr;
WINPR_ASSERT(xfc);
settings = xfc->common.context.settings;
WINPR_ASSERT(settings);
if (freerdp_settings_get_bool(settings, FreeRDP_MouseUseRelativeMove))
{
XISetMask(mask_bytes, XI_RawMotion);
XISetMask(mask_bytes, XI_RawButtonPress);
XISetMask(mask_bytes, XI_RawButtonRelease);
mask.deviceid = XIAllMasterDevices;
mask.mask_len = sizeof(mask_bytes);
mask.mask = mask_bytes;
XISelectEvents(xfc->display, window, &mask, 1);
}
return TRUE;
}
static BOOL register_device_events(xfContext* xfc, Window window)
{
XIEventMask mask = WINPR_C_ARRAY_INIT;
unsigned char mask_bytes[XIMaskLen(XI_LASTEVENT)] = WINPR_C_ARRAY_INIT;
WINPR_ASSERT(xfc);
XISetMask(mask_bytes, XI_DeviceChanged);
XISetMask(mask_bytes, XI_HierarchyChanged);
mask.deviceid = XIAllDevices;
mask.mask_len = sizeof(mask_bytes);
mask.mask = mask_bytes;
XISelectEvents(xfc->display, window, &mask, 1);
return TRUE;
}
int xf_input_init(xfContext* xfc, Window window)
{
int major = XI_2_Major;
int minor = XI_2_Minor;
int opcode = 0;
int event = 0;
int error = 0;
WINPR_ASSERT(xfc);
xfc->firstDist = -1.0;
xfc->z_vector = 0;
xfc->px_vector = 0;
xfc->py_vector = 0;
xfc->active_contacts = 0;
if (!XQueryExtension(xfc->display, "XInputExtension", &opcode, &event, &error))
{
WLog_WARN(TAG, "XInput extension not available.");
return -1;
}
xfc->XInputOpcode = opcode;
XIQueryVersion(xfc->display, &major, &minor);
if ((major < XI_2_Major) || ((major == XI_2_Major) && (minor < 2)))
{
WLog_WARN(TAG, "Server does not support XI 2.2");
return -1;
}
else
{
int scr = DefaultScreen(xfc->display);
Window root = RootWindow(xfc->display, scr);
if (!register_raw_events(xfc, root))
return -1;
if (!register_input_events(xfc, window))
return -1;
if (!register_device_events(xfc, window))
return -1;
}
return 0;
}
static BOOL xf_input_is_duplicate(xfContext* xfc, const XGenericEventCookie* cookie)
{
const XIDeviceEvent* event = nullptr;
WINPR_ASSERT(xfc);
WINPR_ASSERT(cookie);
event = cookie->data;
WINPR_ASSERT(event);
return ((xfc->lastEvent.time == event->time) && (xfc->lastEvType == cookie->evtype) &&
(xfc->lastEvent.detail == event->detail) &&
(fabs(xfc->lastEvent.event_x - event->event_x) < DBL_EPSILON) &&
(fabs(xfc->lastEvent.event_y - event->event_y) < DBL_EPSILON));
}
static void xf_input_save_last_event(xfContext* xfc, const XGenericEventCookie* cookie)
{
const XIDeviceEvent* event = nullptr;
WINPR_ASSERT(xfc);
WINPR_ASSERT(cookie);
event = cookie->data;
WINPR_ASSERT(event);
xfc->lastEvType = cookie->evtype;
xfc->lastEvent.time = event->time;
xfc->lastEvent.detail = event->detail;
xfc->lastEvent.event_x = event->event_x;
xfc->lastEvent.event_y = event->event_y;
}
static void xf_input_detect_pan(xfContext* xfc)
{
WINPR_ASSERT(xfc);
rdpContext* ctx = &xfc->common.context;
WINPR_ASSERT(ctx);
if (xfc->active_contacts != 2)
{
return;
}
const double dx[] = { xfc->contacts[0].pos_x - xfc->contacts[0].last_x,
xfc->contacts[1].pos_x - xfc->contacts[1].last_x };
const double dy[] = { xfc->contacts[0].pos_y - xfc->contacts[0].last_y,
xfc->contacts[1].pos_y - xfc->contacts[1].last_y };
const double px = fabs(dx[0]) < fabs(dx[1]) ? dx[0] : dx[1];
const double py = fabs(dy[0]) < fabs(dy[1]) ? dy[0] : dy[1];
xfc->px_vector += px;
xfc->py_vector += py;
const double dist_x = fabs(xfc->contacts[0].pos_x - xfc->contacts[1].pos_x);
const double dist_y = fabs(xfc->contacts[0].pos_y - xfc->contacts[1].pos_y);
if (dist_y > MIN_FINGER_DIST)
{
if (xfc->px_vector > PAN_THRESHOLD)
{
{
PanningChangeEventArgs e;
EventArgsInit(&e, "xfreerdp");
e.dx = 5;
e.dy = 0;
PubSub_OnPanningChange(ctx->pubSub, xfc, &e);
}
xfc->px_vector = 0;
xfc->py_vector = 0;
xfc->z_vector = 0;
}
else if (xfc->px_vector < -PAN_THRESHOLD)
{
{
PanningChangeEventArgs e;
EventArgsInit(&e, "xfreerdp");
e.dx = -5;
e.dy = 0;
PubSub_OnPanningChange(ctx->pubSub, xfc, &e);
}
xfc->px_vector = 0;
xfc->py_vector = 0;
xfc->z_vector = 0;
}
}
if (dist_x > MIN_FINGER_DIST)
{
if (xfc->py_vector > PAN_THRESHOLD)
{
{
PanningChangeEventArgs e;
EventArgsInit(&e, "xfreerdp");
e.dx = 0;
e.dy = 5;
PubSub_OnPanningChange(ctx->pubSub, xfc, &e);
}
xfc->py_vector = 0;
xfc->px_vector = 0;
xfc->z_vector = 0;
}
else if (xfc->py_vector < -PAN_THRESHOLD)
{
{
PanningChangeEventArgs e;
EventArgsInit(&e, "xfreerdp");
e.dx = 0;
e.dy = -5;
PubSub_OnPanningChange(ctx->pubSub, xfc, &e);
}
xfc->py_vector = 0;
xfc->px_vector = 0;
xfc->z_vector = 0;
}
}
}
static void xf_input_detect_pinch(xfContext* xfc)
{
ZoomingChangeEventArgs e = WINPR_C_ARRAY_INIT;
WINPR_ASSERT(xfc);
rdpContext* ctx = &xfc->common.context;
WINPR_ASSERT(ctx);
if (xfc->active_contacts != 2)
{
xfc->firstDist = -1.0;
return;
}
/* first calculate the distance */
const double dist = sqrt(pow(xfc->contacts[1].pos_x - xfc->contacts[0].last_x, 2.0) +
pow(xfc->contacts[1].pos_y - xfc->contacts[0].last_y, 2.0));
/* if this is the first 2pt touch */
if (xfc->firstDist <= 0)
{
xfc->firstDist = dist;
xfc->lastDist = xfc->firstDist;
xfc->z_vector = 0;
xfc->px_vector = 0;
xfc->py_vector = 0;
}
else
{
double delta = xfc->lastDist - dist;
if (delta > 1.0)
delta = 1.0;
if (delta < -1.0)
delta = -1.0;
/* compare the current distance to the first one */
xfc->z_vector += delta;
xfc->lastDist = dist;
if (xfc->z_vector > ZOOM_THRESHOLD)
{
EventArgsInit(&e, "xfreerdp");
e.dx = e.dy = -10;
PubSub_OnZoomingChange(ctx->pubSub, xfc, &e);
xfc->z_vector = 0;
xfc->px_vector = 0;
xfc->py_vector = 0;
}
if (xfc->z_vector < -ZOOM_THRESHOLD)
{
EventArgsInit(&e, "xfreerdp");
e.dx = e.dy = 10;
PubSub_OnZoomingChange(ctx->pubSub, xfc, &e);
xfc->z_vector = 0;
xfc->px_vector = 0;
xfc->py_vector = 0;
}
}
}
static void xf_input_touch_begin(xfContext* xfc, const XIDeviceEvent* event)
{
WINPR_UNUSED(xfc);
for (int i = 0; i < MAX_CONTACTS; i++)
{
if (xfc->contacts[i].id == 0)
{
xfc->contacts[i].id = event->detail;
xfc->contacts[i].count = 1;
xfc->contacts[i].pos_x = event->event_x;
xfc->contacts[i].pos_y = event->event_y;
xfc->active_contacts++;
break;
}
}
}
static void xf_input_touch_update(xfContext* xfc, const XIDeviceEvent* event)
{
WINPR_ASSERT(xfc);
WINPR_ASSERT(event);
for (int i = 0; i < MAX_CONTACTS; i++)
{
if (xfc->contacts[i].id == event->detail)
{
xfc->contacts[i].count++;
xfc->contacts[i].last_x = xfc->contacts[i].pos_x;
xfc->contacts[i].last_y = xfc->contacts[i].pos_y;
xfc->contacts[i].pos_x = event->event_x;
xfc->contacts[i].pos_y = event->event_y;
xf_input_detect_pinch(xfc);
xf_input_detect_pan(xfc);
break;
}
}
}
static void xf_input_touch_end(xfContext* xfc, const XIDeviceEvent* event)
{
WINPR_UNUSED(xfc);
for (int i = 0; i < MAX_CONTACTS; i++)
{
if (xfc->contacts[i].id == event->detail)
{
xfc->contacts[i].id = 0;
xfc->contacts[i].count = 0;
xfc->active_contacts--;
break;
}
}
}
static int xf_input_handle_event_local(xfContext* xfc, const XEvent* event)
{
union
{
const XGenericEventCookie* cc;
XGenericEventCookie* vc;
} cookie;
cookie.cc = &event->xcookie;
XGetEventData(xfc->display, cookie.vc);
if ((cookie.cc->type == GenericEvent) && (cookie.cc->extension == xfc->XInputOpcode))
{
switch (cookie.cc->evtype)
{
case XI_TouchBegin:
if (xf_input_is_duplicate(xfc, cookie.cc) == FALSE)
xf_input_touch_begin(xfc, cookie.cc->data);
xf_input_save_last_event(xfc, cookie.cc);
break;
case XI_TouchUpdate:
if (xf_input_is_duplicate(xfc, cookie.cc) == FALSE)
xf_input_touch_update(xfc, cookie.cc->data);
xf_input_save_last_event(xfc, cookie.cc);
break;
case XI_TouchEnd:
if (xf_input_is_duplicate(xfc, cookie.cc) == FALSE)
xf_input_touch_end(xfc, cookie.cc->data);
xf_input_save_last_event(xfc, cookie.cc);
break;
default:
xf_input_event(xfc, event, cookie.cc->data, cookie.cc->evtype);
break;
}
}
XFreeEventData(xfc->display, cookie.vc);
return 0;
}
static void xf_input_hide_cursor(xfContext* xfc)
{
#ifdef WITH_XCURSOR
if (!xfc->cursorHidden)
{
XcursorImage ci = WINPR_C_ARRAY_INIT;
XcursorPixel xp = 0;
static Cursor nullcursor = None;
xf_lock_x11(xfc);
ci.version = XCURSOR_IMAGE_VERSION;
ci.size = sizeof(ci);
ci.width = ci.height = 1;
ci.xhot = ci.yhot = 0;
ci.pixels = &xp;
nullcursor = XcursorImageLoadCursor(xfc->display, &ci);
if ((xfc->window) && (nullcursor != None))
XDefineCursor(xfc->display, xfc->window->handle, nullcursor);
xfc->cursorHidden = TRUE;
xf_unlock_x11(xfc);
}
#endif
}
static void xf_input_show_cursor(xfContext* xfc)
{
#ifdef WITH_XCURSOR
xf_lock_x11(xfc);
if (xfc->cursorHidden)
{
if (xfc->window)
{
if (!xfc->pointer)
XUndefineCursor(xfc->display, xfc->window->handle);
else
XDefineCursor(xfc->display, xfc->window->handle, xfc->pointer->cursor);
}
xfc->cursorHidden = FALSE;
}
xf_unlock_x11(xfc);
#endif
}
static int xf_input_touch_remote(xfContext* xfc, XIDeviceEvent* event, int evtype)
{
int x = 0;
int y = 0;
int touchId = 0;
RdpeiClientContext* rdpei = xfc->common.rdpei;
if (!rdpei)
return 0;
xf_input_hide_cursor(xfc);
touchId = event->detail;
x = (int)event->event_x;
y = (int)event->event_y;
xf_event_adjust_coordinates(xfc, &x, &y);
switch (evtype)
{
case XI_TouchBegin:
freerdp_client_handle_touch(&xfc->common, FREERDP_TOUCH_DOWN, touchId, 0, x, y);
break;
case XI_TouchUpdate:
freerdp_client_handle_touch(&xfc->common, FREERDP_TOUCH_MOTION, touchId, 0, x, y);
break;
case XI_TouchEnd:
freerdp_client_handle_touch(&xfc->common, FREERDP_TOUCH_UP, touchId, 0, x, y);
break;
default:
break;
}
return 0;
}
static BOOL xf_input_pen_remote(xfContext* xfc, XIDeviceEvent* event, int evtype, int deviceid)
{
int x = 0;
int y = 0;
RdpeiClientContext* rdpei = xfc->common.rdpei;
if (!rdpei)
return FALSE;
xf_input_hide_cursor(xfc);
x = (int)event->event_x;
y = (int)event->event_y;
xf_event_adjust_coordinates(xfc, &x, &y);
double pressure = 0.0;
double* val = event->valuators.values;
for (int i = 0; i < MIN(event->valuators.mask_len * 8, 3); i++)
{
if (XIMaskIsSet(event->valuators.mask, i))
{
double value = *val++;
if (i == 2)
pressure = value;
}
}
UINT32 flags = FREERDP_PEN_HAS_PRESSURE;
if ((evtype == XI_ButtonPress) || (evtype == XI_ButtonRelease))
{
WLog_DBG(TAG, "pen button %d", event->detail);
switch (event->detail)
{
case 1:
break;
case 3:
flags |= FREERDP_PEN_BARREL_PRESSED;
break;
default:
return FALSE;
}
}
switch (evtype)
{
case XI_ButtonPress:
flags |= FREERDP_PEN_PRESS;
if (!freerdp_client_handle_pen(&xfc->common, flags, deviceid, x, y, pressure))
return FALSE;
break;
case XI_Motion:
flags |= FREERDP_PEN_MOTION;
if (!freerdp_client_handle_pen(&xfc->common, flags, deviceid, x, y, pressure))
return FALSE;
break;
case XI_ButtonRelease:
flags |= FREERDP_PEN_RELEASE;
if (!freerdp_client_handle_pen(&xfc->common, flags, deviceid, x, y, pressure))
return FALSE;
break;
default:
break;
}
return TRUE;
}
static int xf_input_pens_unhover(xfContext* xfc)
{
WINPR_ASSERT(xfc);
freerdp_client_pen_cancel_all(&xfc->common);
return 0;
}
bool xf_use_rel_mouse(xfContext* xfc)
{
if (!freerdp_client_use_relative_mouse_events(&xfc->common))
return false;
if (!xfc->isCursorHidden)
return false;
return true;
}
int xf_input_event(xfContext* xfc, WINPR_ATTR_UNUSED const XEvent* xevent, XIDeviceEvent* event,
int evtype)
{
WINPR_ASSERT(xfc);
WINPR_ASSERT(xevent);
WINPR_ASSERT(event);
xfWindow* window = xfc->window;
if (window)
{
if (xf_floatbar_is_locked(window->floatbar))
return 0;
}
xf_input_show_cursor(xfc);
switch (evtype)
{
case XI_ButtonPress:
case XI_ButtonRelease:
xfc->xi_event = !xfc->common.mouse_grabbed || !xf_use_rel_mouse(xfc);
if (xfc->xi_event)
{
if (!xfc_is_floatbar_window(xfc, event->event) || (evtype != XI_ButtonPress))
{
xf_generic_ButtonEvent(xfc, (int)event->event_x, (int)event->event_y,
event->detail, event->event, xfc->remote_app,
evtype == XI_ButtonPress);
}
}
break;
case XI_Motion:
xfc->xi_event = !xfc->common.mouse_grabbed || !xf_use_rel_mouse(xfc);
if (xfc->xi_event)
{
xf_generic_MotionNotify(xfc, (int)event->event_x, (int)event->event_y, event->event,
xfc->remote_app);
}
break;
case XI_RawButtonPress:
case XI_RawButtonRelease:
xfc->xi_rawevent = xfc->common.mouse_grabbed && xf_use_rel_mouse(xfc);
if (xfc->xi_rawevent)
{
const XIRawEvent* ev = (const XIRawEvent*)event;
xf_generic_RawButtonEvent(xfc, ev->detail, xfc->remote_app,
evtype == XI_RawButtonPress);
}
break;
case XI_RawMotion:
xfc->xi_rawevent = xfc->common.mouse_grabbed && xf_use_rel_mouse(xfc);
if (xfc->xi_rawevent)
{
const XIRawEvent* ev = (const XIRawEvent*)event;
double x = 0.0;
double y = 0.0;
if (XIMaskIsSet(ev->valuators.mask, 0))
x = ev->raw_values[0];
if (XIMaskIsSet(ev->valuators.mask, 1))
y = ev->raw_values[1];
xf_generic_RawMotionNotify(xfc, (int)x, (int)y, event->event, xfc->remote_app);
}
break;
case XI_DeviceChanged:
{
const XIDeviceChangedEvent* ev = (const XIDeviceChangedEvent*)event;
if (ev->reason != XIDeviceChange)
break;
/*
* TODO:
* 1. Register only changed devices.
* 2. Both `XIDeviceChangedEvent` and `XIHierarchyEvent` have no target
* `Window` which is used to register xinput events. So assume
* `xfc->window` created by `xf_CreateDesktopWindow` is the same
* `Window` we registered.
*/
if (xfc->window)
register_input_events(xfc, xfc->window->handle);
}
break;
case XI_HierarchyChanged:
if (xfc->window)
register_input_events(xfc, xfc->window->handle);
break;
default:
WLog_WARN(TAG, "Unhandled event %d: Event was registered but is not handled!", evtype);
break;
}
return 0;
}
static int xf_input_handle_event_remote(xfContext* xfc, const XEvent* event)
{
union
{
const XGenericEventCookie* cc;
XGenericEventCookie* vc;
} cookie;
cookie.cc = &event->xcookie;
XGetEventData(xfc->display, cookie.vc);
if ((cookie.cc->type == GenericEvent) && (cookie.cc->extension == xfc->XInputOpcode))
{
switch (cookie.cc->evtype)
{
case XI_TouchBegin:
xf_input_pens_unhover(xfc);
/* fallthrough */
WINPR_FALLTHROUGH
case XI_TouchUpdate:
case XI_TouchEnd:
xf_input_touch_remote(xfc, cookie.cc->data, cookie.cc->evtype);
break;
case XI_ButtonPress:
case XI_Motion:
case XI_ButtonRelease:
{
WLog_DBG(TAG, "checking for pen");
XIDeviceEvent* deviceEvent = (XIDeviceEvent*)cookie.cc->data;
int deviceid = deviceEvent->deviceid;
if (freerdp_client_is_pen(&xfc->common, deviceid))
{
if (!xf_input_pen_remote(xfc, cookie.cc->data, cookie.cc->evtype, deviceid))
{
// XXX: don't show cursor
xf_input_event(xfc, event, cookie.cc->data, cookie.cc->evtype);
}
break;
}
}
/* fallthrough */
WINPR_FALLTHROUGH
default:
xf_input_pens_unhover(xfc);
xf_input_event(xfc, event, cookie.cc->data, cookie.cc->evtype);
break;
}
}
XFreeEventData(xfc->display, cookie.vc);
return 0;
}
#else
int xf_input_init(xfContext* xfc, Window window)
{
return 0;
}
#endif
int xf_input_handle_event(xfContext* xfc, const XEvent* event)
{
#ifdef WITH_XI
const rdpSettings* settings = nullptr;
WINPR_ASSERT(xfc);
settings = xfc->common.context.settings;
WINPR_ASSERT(settings);
if (freerdp_settings_get_bool(settings, FreeRDP_MultiTouchInput))
{
return xf_input_handle_event_remote(xfc, event);
}
else if (freerdp_settings_get_bool(settings, FreeRDP_MultiTouchGestures))
{
return xf_input_handle_event_local(xfc, event);
}
else
{
return xf_input_handle_event_local(xfc, event);
}
#else
return 0;
#endif
}

View File

@@ -0,0 +1,34 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Input
*
* Copyright 2013 Corey Clayton <can.of.tuna@gmail.com>
*
* 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.
*/
#ifndef FREERDP_CLIENT_X11_INPUT_H
#define FREERDP_CLIENT_X11_INPUT_H
#include "xf_client.h"
#include "xfreerdp.h"
#ifdef WITH_XI
#include <X11/extensions/XInput2.h>
#endif
int xf_input_init(xfContext* xfc, Window window);
int xf_input_handle_event(xfContext* xfc, const XEvent* event);
bool xf_use_rel_mouse(xfContext* xfc);
#endif /* FREERDP_CLIENT_X11_INPUT_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,47 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Keyboard Handling
*
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#ifndef FREERDP_CLIENT_X11_XF_KEYBOARD_H
#define FREERDP_CLIENT_X11_XF_KEYBOARD_H
#include <freerdp/locale/keyboard.h>
#include "xf_client.h"
#include "xfreerdp.h"
BOOL xf_keyboard_init(xfContext* xfc);
void xf_keyboard_free(xfContext* xfc);
BOOL xf_keyboard_action_script_init(xfContext* xfc);
void xf_keyboard_key_press(xfContext* xfc, const XKeyEvent* event, KeySym keysym);
void xf_keyboard_key_release(xfContext* xfc, const XKeyEvent* event, KeySym keysym);
void xf_keyboard_release_all_keypress(xfContext* xfc);
void xf_keyboard_focus_in(xfContext* xfc);
BOOL xf_keyboard_set_indicators(rdpContext* context, UINT16 led_flags);
BOOL xf_keyboard_set_ime_status(rdpContext* context, UINT16 imeId, UINT32 imeState,
UINT32 imeConvMode);
BOOL xf_ungrab(xfContext* xfc);
void xf_button_map_init(xfContext* xfc);
#endif /* FREERDP_CLIENT_X11_XF_KEYBOARD_H */

View File

@@ -0,0 +1,672 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Monitor Handling
*
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2017 David Fort <contact@hardening-consulting.com>
* Copyright 2018 Kai Harms <kharms@rangee.com>
*
* 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.
*/
#include <freerdp/config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <winpr/assert.h>
#include <winpr/cast.h>
#include <winpr/crt.h>
#include <freerdp/log.h>
#define TAG CLIENT_TAG("x11")
#ifdef WITH_XINERAMA
#include <X11/extensions/Xinerama.h>
#endif
#ifdef WITH_XRANDR
#include <X11/extensions/Xrandr.h>
#include <X11/extensions/randr.h>
#if (RANDR_MAJOR * 100 + RANDR_MINOR) >= 105
#define USABLE_XRANDR
#endif
#endif
#include "xf_monitor.h"
#include "xf_utils.h"
/* See MSDN Section on Multiple Display Monitors: http://msdn.microsoft.com/en-us/library/dd145071
*/
int xf_list_monitors(xfContext* xfc)
{
WINPR_UNUSED(xfc);
int major = 0;
int minor = 0;
int nmonitors = 0;
Display* display = XOpenDisplay(nullptr);
if (!display)
{
WLog_ERR(TAG, "failed to open X display");
return -1;
}
#if defined(USABLE_XRANDR)
if (XRRQueryExtension(display, &major, &minor) &&
(XRRQueryVersion(display, &major, &minor) == True) && (major * 100 + minor >= 105))
{
XRRMonitorInfo* monitors =
XRRGetMonitors(display, DefaultRootWindow(display), 1, &nmonitors);
for (int i = 0; i < nmonitors; i++)
{
printf(" %s [%d] %dx%d\t+%d+%d\n", monitors[i].primary ? "*" : " ", i,
monitors[i].width, monitors[i].height, monitors[i].x, monitors[i].y);
}
XRRFreeMonitors(monitors);
}
else
#endif
#ifdef WITH_XINERAMA
if (XineramaQueryExtension(display, &major, &minor))
{
if (XineramaIsActive(display))
{
XineramaScreenInfo* screen = XineramaQueryScreens(display, &nmonitors);
for (int i = 0; i < nmonitors; i++)
{
printf(" %s [%d] %hdx%hd\t+%hd+%hd\n", (i == 0) ? "*" : " ", i,
screen[i].width, screen[i].height, screen[i].x_org, screen[i].y_org);
}
XFree(screen);
}
}
else
#else
{
Screen* screen = ScreenOfDisplay(display, DefaultScreen(display));
printf(" * [0] %dx%d\t+0+0\n", WidthOfScreen(screen), HeightOfScreen(screen));
}
#endif
LogDynAndXCloseDisplay(xfc->log, display);
return 0;
}
static BOOL xf_is_monitor_id_active(xfContext* xfc, UINT32 id)
{
const rdpSettings* settings = nullptr;
WINPR_ASSERT(xfc);
settings = xfc->common.context.settings;
WINPR_ASSERT(settings);
const UINT32 NumMonitorIds = freerdp_settings_get_uint32(settings, FreeRDP_NumMonitorIds);
if (NumMonitorIds == 0)
return TRUE;
for (UINT32 index = 0; index < NumMonitorIds; index++)
{
const UINT32* cur = freerdp_settings_get_pointer_array(settings, FreeRDP_MonitorIds, index);
if (cur && (*cur == id))
return TRUE;
}
return FALSE;
}
BOOL xf_detect_monitors(xfContext* xfc, UINT32* pMaxWidth, UINT32* pMaxHeight)
{
BOOL rc = FALSE;
UINT32 monitor_index = 0;
BOOL primaryMonitorFound = FALSE;
int mouse_x = 0;
int mouse_y = 0;
int _dummy_i = 0;
Window _dummy_w = 0;
UINT32 current_monitor = 0;
Screen* screen = nullptr;
#if defined WITH_XINERAMA || defined WITH_XRANDR
int major = 0;
int minor = 0;
#endif
#if defined(USABLE_XRANDR)
XRRMonitorInfo* rrmonitors = nullptr;
BOOL useXRandr = FALSE;
#endif
if (!xfc || !pMaxWidth || !pMaxHeight || !xfc->common.context.settings)
return FALSE;
rdpSettings* settings = xfc->common.context.settings;
VIRTUAL_SCREEN* vscreen = &xfc->vscreen;
*pMaxWidth = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
*pMaxHeight = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
if (freerdp_settings_get_uint64(settings, FreeRDP_ParentWindowId) > 0)
{
xfc->workArea.x = 0;
xfc->workArea.y = 0;
xfc->workArea.width = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
xfc->workArea.height = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
return TRUE;
}
/* get mouse location */
if (!XQueryPointer(xfc->display, DefaultRootWindow(xfc->display), &_dummy_w, &_dummy_w,
&mouse_x, &mouse_y, &_dummy_i, &_dummy_i, (void*)&_dummy_i))
mouse_x = mouse_y = 0;
#if defined(USABLE_XRANDR)
if (XRRQueryExtension(xfc->display, &major, &minor) &&
(XRRQueryVersion(xfc->display, &major, &minor) == True) && (major * 100 + minor >= 105))
{
int nmonitors = 0;
rrmonitors = XRRGetMonitors(xfc->display, DefaultRootWindow(xfc->display), 1, &nmonitors);
if ((nmonitors < 0) || (nmonitors > 16))
vscreen->nmonitors = 0;
else
vscreen->nmonitors = (UINT32)nmonitors;
if (vscreen->nmonitors)
{
for (UINT32 i = 0; i < vscreen->nmonitors; i++)
{
MONITOR_INFO* cur_vscreen = &vscreen->monitors[i];
const XRRMonitorInfo* cur_monitor = &rrmonitors[i];
cur_vscreen->area.left = WINPR_ASSERTING_INT_CAST(UINT16, cur_monitor->x);
cur_vscreen->area.top = WINPR_ASSERTING_INT_CAST(UINT16, cur_monitor->y);
cur_vscreen->area.right =
WINPR_ASSERTING_INT_CAST(UINT16, cur_monitor->x + cur_monitor->width - 1);
cur_vscreen->area.bottom =
WINPR_ASSERTING_INT_CAST(UINT16, cur_monitor->y + cur_monitor->height - 1);
cur_vscreen->primary = cur_monitor->primary > 0;
}
}
useXRandr = TRUE;
}
else
#endif
#ifdef WITH_XINERAMA
if (XineramaQueryExtension(xfc->display, &major, &minor) && XineramaIsActive(xfc->display))
{
int nmonitors = 0;
XineramaScreenInfo* screenInfo = XineramaQueryScreens(xfc->display, &nmonitors);
if ((nmonitors < 0) || (nmonitors > 16))
vscreen->nmonitors = 0;
else
vscreen->nmonitors = (UINT32)nmonitors;
if (vscreen->nmonitors)
{
for (UINT32 i = 0; i < vscreen->nmonitors; i++)
{
MONITOR_INFO* monitor = &vscreen->monitors[i];
monitor->area.left = WINPR_ASSERTING_INT_CAST(uint16_t, screenInfo[i].x_org);
monitor->area.top = WINPR_ASSERTING_INT_CAST(uint16_t, screenInfo[i].y_org);
monitor->area.right = WINPR_ASSERTING_INT_CAST(
uint16_t, screenInfo[i].x_org + screenInfo[i].width - 1);
monitor->area.bottom = WINPR_ASSERTING_INT_CAST(
uint16_t, screenInfo[i].y_org + screenInfo[i].height - 1);
}
}
XFree(screenInfo);
}
else
#endif
{
/* Both XRandR and Xinerama are either not compiled in or are not working, do nothing.
*/
}
rdpMonitor* rdpmonitors = calloc(vscreen->nmonitors + 1, sizeof(rdpMonitor));
if (!rdpmonitors)
goto fail;
xfc->fullscreenMonitors.top = 0;
xfc->fullscreenMonitors.bottom = 0;
xfc->fullscreenMonitors.left = 0;
xfc->fullscreenMonitors.right = 0;
/* Determine which monitor that the mouse cursor is on */
if (vscreen->monitors)
{
for (UINT32 i = 0; i < vscreen->nmonitors; i++)
{
const MONITOR_INFO* monitor = &vscreen->monitors[i];
if ((mouse_x >= monitor->area.left) && (mouse_x <= monitor->area.right) &&
(mouse_y >= monitor->area.top) && (mouse_y <= monitor->area.bottom))
{
current_monitor = i;
break;
}
}
}
/*
Even for a single monitor, we need to calculate the virtual screen to support
window managers that do not implement all X window state hints.
If the user did not request multiple monitor or is using workarea
without remote app, we force the number of monitors be 1 so later
the rest of the client don't end up using more monitors than the user desires.
*/
if ((!freerdp_settings_get_bool(settings, FreeRDP_UseMultimon) &&
!freerdp_settings_get_bool(settings, FreeRDP_SpanMonitors)) ||
(freerdp_settings_get_bool(settings, FreeRDP_Workarea) &&
!freerdp_settings_get_bool(settings, FreeRDP_RemoteApplicationMode)))
{
/* If no monitors were specified on the command-line then set the current monitor as active
*/
if (freerdp_settings_get_uint32(settings, FreeRDP_NumMonitorIds) == 0)
{
UINT32 id = current_monitor;
if (!freerdp_settings_set_pointer_len(settings, FreeRDP_MonitorIds, &id, 1))
goto fail;
}
/* Always sets number of monitors from command-line to just 1.
* If the monitor is invalid then we will default back to current monitor
* later as a fallback. So, there is no need to validate command-line entry here.
*/
if (!freerdp_settings_set_uint32(settings, FreeRDP_NumMonitorIds, 1))
goto fail;
}
/* WORKAROUND: With Remote Application Mode - using NET_WM_WORKAREA
* causes issues with the ability to fully size the window vertically
* (the bottom of the window area is never updated). So, we just set
* the workArea to match the full Screen width/height.
*/
if (freerdp_settings_get_bool(settings, FreeRDP_RemoteApplicationMode) || !xf_GetWorkArea(xfc))
{
/*
if only 1 monitor is enabled, use monitor area
this is required in case of a screen composed of more than one monitor
but user did not enable multimonitor
*/
if ((freerdp_settings_get_uint32(settings, FreeRDP_NumMonitorIds) == 1) &&
(vscreen->nmonitors > current_monitor))
{
MONITOR_INFO* monitor = vscreen->monitors + current_monitor;
if (!monitor)
goto fail;
xfc->workArea.x = monitor->area.left;
xfc->workArea.y = monitor->area.top;
xfc->workArea.width = monitor->area.right - monitor->area.left + 1;
xfc->workArea.height = monitor->area.bottom - monitor->area.top + 1;
}
else
{
xfc->workArea.x = 0;
xfc->workArea.y = 0;
xfc->workArea.width = WINPR_ASSERTING_INT_CAST(uint32_t, WidthOfScreen(xfc->screen));
xfc->workArea.height = WINPR_ASSERTING_INT_CAST(uint32_t, HeightOfScreen(xfc->screen));
}
}
if (freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
{
*pMaxWidth = WINPR_ASSERTING_INT_CAST(uint32_t, WidthOfScreen(xfc->screen));
*pMaxHeight = WINPR_ASSERTING_INT_CAST(uint32_t, HeightOfScreen(xfc->screen));
}
else if (freerdp_settings_get_bool(settings, FreeRDP_Workarea))
{
*pMaxWidth = xfc->workArea.width;
*pMaxHeight = xfc->workArea.height;
}
else if (freerdp_settings_get_uint32(settings, FreeRDP_PercentScreen))
{
/* If we have specific monitor information then limit the PercentScreen value
* to only affect the current monitor vs. the entire desktop
*/
if (vscreen->nmonitors > 0)
{
if (!vscreen->monitors)
goto fail;
const MONITOR_INFO* vmonitor = &vscreen->monitors[current_monitor];
const RECTANGLE_16* area = &vmonitor->area;
*pMaxWidth = area->right - area->left + 1;
*pMaxHeight = area->bottom - area->top + 1;
if (freerdp_settings_get_bool(settings, FreeRDP_PercentScreenUseWidth))
*pMaxWidth = ((area->right - area->left + 1) *
freerdp_settings_get_uint32(settings, FreeRDP_PercentScreen)) /
100;
if (freerdp_settings_get_bool(settings, FreeRDP_PercentScreenUseHeight))
*pMaxHeight = ((area->bottom - area->top + 1) *
freerdp_settings_get_uint32(settings, FreeRDP_PercentScreen)) /
100;
}
else
{
*pMaxWidth = xfc->workArea.width;
*pMaxHeight = xfc->workArea.height;
if (freerdp_settings_get_bool(settings, FreeRDP_PercentScreenUseWidth))
*pMaxWidth = (xfc->workArea.width *
freerdp_settings_get_uint32(settings, FreeRDP_PercentScreen)) /
100;
if (freerdp_settings_get_bool(settings, FreeRDP_PercentScreenUseHeight))
*pMaxHeight = (xfc->workArea.height *
freerdp_settings_get_uint32(settings, FreeRDP_PercentScreen)) /
100;
}
}
else if (freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) &&
freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight))
{
*pMaxWidth = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
*pMaxHeight = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
}
/* Create array of all active monitors by taking into account monitors requested on the
* command-line */
{
size_t nmonitors = 0;
{
UINT32 nr = 0;
{
const UINT32* ids = freerdp_settings_get_pointer(settings, FreeRDP_MonitorIds);
if (ids)
nr = *ids;
}
for (UINT32 i = 0; i < vscreen->nmonitors; i++)
{
MONITOR_ATTRIBUTES* attrs = nullptr;
if (!xf_is_monitor_id_active(xfc, i))
continue;
if (!vscreen->monitors)
goto fail;
rdpMonitor* monitor = &rdpmonitors[nmonitors];
const RECTANGLE_16* area = &vscreen->monitors[i].area;
monitor->x =
WINPR_ASSERTING_INT_CAST(
int32_t,
area->left*(
freerdp_settings_get_bool(settings, FreeRDP_PercentScreenUseWidth)
? freerdp_settings_get_uint32(settings, FreeRDP_PercentScreen)
: 100)) /
100;
monitor->y =
WINPR_ASSERTING_INT_CAST(
int32_t,
area->top*(
freerdp_settings_get_bool(settings, FreeRDP_PercentScreenUseHeight)
? freerdp_settings_get_uint32(settings, FreeRDP_PercentScreen)
: 100)) /
100;
monitor->width =
WINPR_ASSERTING_INT_CAST(
int32_t,
(area->right - area->left + 1) *
(freerdp_settings_get_bool(settings, FreeRDP_PercentScreenUseWidth)
? freerdp_settings_get_uint32(settings, FreeRDP_PercentScreen)
: 100)) /
100;
monitor->height =
WINPR_ASSERTING_INT_CAST(
int32_t,
(area->bottom - area->top + 1) *
(freerdp_settings_get_bool(settings, FreeRDP_PercentScreenUseHeight)
? freerdp_settings_get_uint32(settings, FreeRDP_PercentScreen)
: 100)) /
100;
monitor->orig_screen = i;
#ifdef USABLE_XRANDR
if (useXRandr && rrmonitors)
{
Rotation rot = 0;
Rotation ret = 0;
attrs = &monitor->attributes;
attrs->physicalWidth = WINPR_ASSERTING_INT_CAST(uint32_t, rrmonitors[i].mwidth);
attrs->physicalHeight =
WINPR_ASSERTING_INT_CAST(uint32_t, rrmonitors[i].mheight);
ret = XRRRotations(xfc->display, WINPR_ASSERTING_INT_CAST(int, i), &rot);
switch (rot & ret)
{
case RR_Rotate_90:
attrs->orientation = ORIENTATION_PORTRAIT;
break;
case RR_Rotate_180:
attrs->orientation = ORIENTATION_LANDSCAPE_FLIPPED;
break;
case RR_Rotate_270:
attrs->orientation = ORIENTATION_PORTRAIT_FLIPPED;
break;
case RR_Rotate_0:
default:
attrs->orientation = ORIENTATION_LANDSCAPE;
break;
}
}
#endif
if (i == nr)
{
monitor->is_primary = TRUE;
primaryMonitorFound = TRUE;
}
nmonitors++;
}
}
/* If no monitor is active(bogus command-line monitor specification) - then lets try to
* fallback to go fullscreen on the current monitor only */
if ((nmonitors == 0) && (vscreen->nmonitors > 0))
{
if (!vscreen->monitors)
goto fail;
const MONITOR_INFO* vmonitor = &vscreen->monitors[current_monitor];
const RECTANGLE_16* area = &vmonitor->area;
const INT32 width = area->right - area->left + 1;
const INT32 height = area->bottom - area->top + 1;
const INT32 maxw =
((width < 0) || ((UINT32)width < *pMaxWidth)) ? width : (INT32)*pMaxWidth;
const INT32 maxh =
((height < 0) || ((UINT32)height < *pMaxHeight)) ? width : (INT32)*pMaxHeight;
rdpMonitor* monitor = &rdpmonitors[0];
if (!monitor)
goto fail;
monitor->x = area->left;
monitor->y = area->top;
monitor->width = maxw;
monitor->height = maxh;
monitor->orig_screen = current_monitor;
nmonitors = 1;
}
if (!freerdp_settings_set_uint32(settings, FreeRDP_MonitorCount,
WINPR_ASSERTING_INT_CAST(uint32_t, nmonitors)))
goto fail;
/* If we have specific monitor information */
if (freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount) > 0)
{
const rdpMonitor* cmonitor = &rdpmonitors[0];
if (!cmonitor)
goto fail;
/* Initialize bounding rectangle for all monitors */
int vX = cmonitor->x;
int vY = cmonitor->y;
int vR = vX + cmonitor->width;
int vB = vY + cmonitor->height;
const int32_t corig = WINPR_ASSERTING_INT_CAST(int32_t, cmonitor->orig_screen);
xfc->fullscreenMonitors.top = corig;
xfc->fullscreenMonitors.bottom = corig;
xfc->fullscreenMonitors.left = corig;
xfc->fullscreenMonitors.right = corig;
/* Calculate bounding rectangle around all monitors to be used AND
* also set the Xinerama indices which define left/top/right/bottom monitors.
*/
for (UINT32 i = 0; i < freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount); i++)
{
rdpMonitor* monitor = &rdpmonitors[i];
/* does the same as gdk_rectangle_union */
const int destX = MIN(vX, monitor->x);
const int destY = MIN(vY, monitor->y);
const int destR = MAX(vR, monitor->x + monitor->width);
const int destB = MAX(vB, monitor->y + monitor->height);
const int32_t orig = WINPR_ASSERTING_INT_CAST(int32_t, monitor->orig_screen);
if (vX != destX)
xfc->fullscreenMonitors.left = orig;
if (vY != destY)
xfc->fullscreenMonitors.top = orig;
if (vR != destR)
xfc->fullscreenMonitors.right = orig;
if (vB != destB)
xfc->fullscreenMonitors.bottom = orig;
vX = destX;
vY = destY;
vR = destR;
vB = destB;
}
vscreen->area.left = 0;
const int r = vR - vX - 1;
vscreen->area.right = WINPR_ASSERTING_INT_CAST(UINT16, r);
vscreen->area.top = 0;
const int b = vB - vY - 1;
vscreen->area.bottom = WINPR_ASSERTING_INT_CAST(UINT16, b);
if (freerdp_settings_get_bool(settings, FreeRDP_Workarea))
{
INT64 bottom = 1LL * xfc->workArea.height + xfc->workArea.y - 1LL;
vscreen->area.top = WINPR_ASSERTING_INT_CAST(UINT16, xfc->workArea.y);
vscreen->area.bottom = WINPR_ASSERTING_INT_CAST(UINT16, bottom);
}
if (!primaryMonitorFound)
{
/* If we have a command line setting we should use it */
if (freerdp_settings_get_uint32(settings, FreeRDP_NumMonitorIds) > 0)
{
/* The first monitor is the first in the setting which should be used */
UINT32* ids = freerdp_settings_get_pointer_array_writable(
settings, FreeRDP_MonitorIds, 0);
if (ids)
monitor_index = *ids;
}
else
{
/* This is the same as when we would trust the Xinerama results..
and set the monitor index to zero.
The monitor listed with /list:monitor on index zero is always the primary
*/
screen = DefaultScreenOfDisplay(xfc->display);
monitor_index =
WINPR_ASSERTING_INT_CAST(uint32_t, XScreenNumberOfScreen(screen));
}
UINT32 j = monitor_index;
rdpMonitor* pmonitor = &rdpmonitors[j];
/* If the "default" monitor is not 0,0 use it */
if ((pmonitor->x != 0) || (pmonitor->y != 0))
{
pmonitor->is_primary = TRUE;
}
else
{
/* Lets try to see if there is a monitor with a 0,0 coordinate and use it as a
* fallback*/
for (UINT32 i = 0;
i < freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount); i++)
{
rdpMonitor* monitor = &rdpmonitors[i];
if (!primaryMonitorFound && monitor->x == 0 && monitor->y == 0)
{
monitor->is_primary = TRUE;
primaryMonitorFound = TRUE;
}
}
}
}
/* Set the desktop width and height according to the bounding rectangle around the
* active monitors */
*pMaxWidth = MIN(*pMaxWidth, (UINT32)vscreen->area.right - vscreen->area.left + 1);
*pMaxHeight = MIN(*pMaxHeight, (UINT32)vscreen->area.bottom - vscreen->area.top + 1);
}
/* some 2008 server freeze at logon if we announce support for monitor layout PDU with
* #monitors < 2. So let's announce it only if we have more than 1 monitor.
*/
nmonitors = freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount);
if (nmonitors > 1)
{
if (!freerdp_settings_set_bool(settings, FreeRDP_SupportMonitorLayoutPdu, TRUE))
goto fail;
}
rc = freerdp_settings_set_monitor_def_array_sorted(settings, rdpmonitors, nmonitors);
}
fail:
#ifdef USABLE_XRANDR
if (rrmonitors)
XRRFreeMonitors(rrmonitors);
#endif
free(rdpmonitors);
return rc;
}

View File

@@ -0,0 +1,33 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Monitor Handling
*
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#ifndef FREERDP_CLIENT_X11_MONITOR_H
#define FREERDP_CLIENT_X11_MONITOR_H
#include <freerdp/api.h>
#include <freerdp/freerdp.h>
#include <freerdp/types.h>
#include "xf_types.h"
FREERDP_API int xf_list_monitors(xfContext* xfc);
FREERDP_API BOOL xf_detect_monitors(xfContext* xfc, UINT32* pMaxWidth, UINT32* pMaxHeight);
FREERDP_API void xf_monitors_free(xfContext* xfc);
#endif /* FREERDP_CLIENT_X11_MONITOR_H */

1420
third_party/FreeRDP/client/X11/xf_rail.c vendored Normal file

File diff suppressed because it is too large Load Diff

145
third_party/FreeRDP/client/X11/xf_rail.h vendored Normal file
View File

@@ -0,0 +1,145 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 RAIL
*
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#ifndef FREERDP_CLIENT_X11_RAIL_H
#define FREERDP_CLIENT_X11_RAIL_H
#include <freerdp/client/rail.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include "xf_types.h"
enum xf_localmove_state
{
LMS_NOT_ACTIVE,
LMS_STARTING,
LMS_ACTIVE,
LMS_TERMINATING
};
struct xf_localmove
{
int root_x;
int root_y;
int window_x;
int window_y;
enum xf_localmove_state state;
int direction;
};
typedef struct xf_localmove xfLocalMove;
struct xf_app_window
{
xfContext* xfc;
int x;
int y;
int width;
int height;
char* title;
UINT32 surfaceId;
UINT64 windowId;
UINT32 ownerWindowId;
UINT32 dwStyle;
UINT32 dwExStyle;
UINT32 showState;
INT32 clientOffsetX;
INT32 clientOffsetY;
UINT32 clientAreaWidth;
UINT32 clientAreaHeight;
INT32 windowOffsetX;
INT32 windowOffsetY;
INT32 windowClientDeltaX;
INT32 windowClientDeltaY;
UINT32 windowWidth;
UINT32 windowHeight;
UINT32 numWindowRects;
RECTANGLE_16* windowRects;
INT32 visibleOffsetX;
INT32 visibleOffsetY;
UINT32 numVisibilityRects;
RECTANGLE_16* visibilityRects;
UINT32 localWindowOffsetCorrX;
UINT32 localWindowOffsetCorrY;
UINT32 resizeMarginLeft;
UINT32 resizeMarginTop;
UINT32 resizeMarginRight;
UINT32 resizeMarginBottom;
GC gc;
int shmid;
Window handle;
Window* xfwin;
BOOL fullscreen;
BOOL decorations;
BOOL is_mapped;
BOOL is_transient;
xfLocalMove local_move;
BYTE rail_state;
BOOL maxVert;
BOOL maxHorz;
BOOL minimized;
BOOL rail_ignore_configure;
Pixmap pixmap;
XImage* image;
};
typedef struct xf_app_window xfAppWindow;
typedef struct xf_rail_icon_cache xfRailIconCache;
BOOL xf_rail_paint(xfContext* xfc, const RECTANGLE_16* rect);
BOOL xf_rail_paint_surface(xfContext* xfc, UINT64 windowId, const RECTANGLE_16* rect);
BOOL xf_rail_send_client_system_command(xfContext* xfc, UINT64 windowId, UINT16 command);
BOOL xf_rail_send_activate(xfContext* xfc, Window xwindow, BOOL enabled);
BOOL xf_rail_adjust_position(xfContext* xfc, xfAppWindow* appWindow);
BOOL xf_rail_end_local_move(xfContext* xfc, xfAppWindow* appWindow);
BOOL xf_rail_enable_remoteapp_mode(xfContext* xfc);
BOOL xf_rail_disable_remoteapp_mode(xfContext* xfc);
xfAppWindow* xf_rail_add_window(xfContext* xfc, UINT64 id, INT32 x, INT32 y, UINT32 width,
UINT32 height, UINT32 surfaceId);
#define xf_rail_return_window(window, alreadyLocked) \
xf_rail_return_windowFrom((window), (alreadyLocked), __FILE__, __func__, __LINE__)
void xf_rail_return_windowFrom(xfAppWindow* window, BOOL alreadyLocked, const char* file,
const char* fkt, size_t line);
#define xf_rail_get_window(xfc, id, alreadyLocked) \
xf_rail_get_windowFrom((xfc), (id), (alreadyLocked), __FILE__, __func__, __LINE__)
WINPR_ATTR_MALLOC(xf_rail_return_windowFrom, 1)
xfAppWindow* xf_rail_get_windowFrom(xfContext* xfc, UINT64 id, BOOL alreadyLocked, const char* file,
const char* fkt, size_t line);
BOOL xf_rail_del_window(xfContext* xfc, UINT64 id);
int xf_rail_init(xfContext* xfc, RailClientContext* rail);
int xf_rail_uninit(xfContext* xfc, RailClientContext* rail);
#endif /* FREERDP_CLIENT_X11_RAIL_H */

479
third_party/FreeRDP/client/X11/xf_tsmf.c vendored Normal file
View File

@@ -0,0 +1,479 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Video Redirection
*
* Copyright 2010-2011 Vic Lee
*
* 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.
*/
#include <freerdp/config.h>
#include <winpr/crt.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/extensions/XShm.h>
#include <freerdp/log.h>
#include <freerdp/client/tsmf.h>
#include "xf_tsmf.h"
#include "xf_utils.h"
#ifdef WITH_XV
#include <X11/extensions/Xv.h>
#include <X11/extensions/Xvlib.h>
static long xv_port = 0;
struct xf_xv_context
{
XvPortID xv_port;
Atom xv_colorkey_atom;
int xv_image_size;
int xv_shmid;
char* xv_shmaddr;
UINT32* xv_pixfmts;
};
typedef struct xf_xv_context xfXvContext;
#define TAG CLIENT_TAG("x11")
static BOOL xf_tsmf_is_format_supported(xfXvContext* xv, UINT32 pixfmt)
{
if (!xv->xv_pixfmts)
return FALSE;
for (int i = 0; xv->xv_pixfmts[i]; i++)
{
if (xv->xv_pixfmts[i] == pixfmt)
return TRUE;
}
return FALSE;
}
static int xf_tsmf_xv_video_frame_event(TsmfClientContext* tsmf, TSMF_VIDEO_FRAME_EVENT* event)
{
int x = 0;
int y = 0;
UINT32 width = 0;
UINT32 height = 0;
BYTE* data1 = nullptr;
BYTE* data2 = nullptr;
UINT32 pixfmt = 0;
UINT32 xvpixfmt = 0;
XvImage* image = nullptr;
int colorkey = 0;
int numRects = 0;
xfContext* xfc = nullptr;
xfXvContext* xv = nullptr;
XRectangle* xrects = nullptr;
XShmSegmentInfo shminfo;
BOOL converti420yv12 = FALSE;
if (!tsmf)
return -1;
xfc = (xfContext*)tsmf->custom;
if (!xfc)
return -1;
xv = (xfXvContext*)xfc->xv_context;
if (!xv)
return -1;
if (xv->xv_port == 0)
return -1001;
/* In case the player is minimized */
if (event->x < -2048 || event->y < -2048 || event->numVisibleRects == 0)
{
return -1002;
}
xrects = nullptr;
numRects = event->numVisibleRects;
if (numRects > 0)
{
xrects = (XRectangle*)calloc(numRects, sizeof(XRectangle));
if (!xrects)
return -1;
for (int i = 0; i < numRects; i++)
{
x = event->x + event->visibleRects[i].left;
y = event->y + event->visibleRects[i].top;
width = event->visibleRects[i].right - event->visibleRects[i].left;
height = event->visibleRects[i].bottom - event->visibleRects[i].top;
xrects[i].x = WINPR_ASSERTING_INT_CAST(short, x);
xrects[i].y = WINPR_ASSERTING_INT_CAST(short, y);
xrects[i].width = width;
xrects[i].height = height;
}
}
if (xv->xv_colorkey_atom != None)
{
XvGetPortAttribute(xfc->display, xv->xv_port, xv->xv_colorkey_atom, &colorkey);
LogDynAndXSetFunction(xfc->log, xfc->display, xfc->gc, GXcopy);
LogDynAndXSetFillStyle(xfc->log, xfc->display, xfc->gc, FillSolid);
LogDynAndXSetForeground(xfc->log, xfc->display, xfc->gc, colorkey);
if (event->numVisibleRects < 1)
{
LogDynAndXSetClipMask(xfc->log, xfc->display, xfc->gc, None);
}
else
{
XFillRectangles(xfc->display, xfc->window->handle, xfc->gc, xrects, numRects);
}
}
else
{
LogDynAndXSetFunction(xfc->log, xfc->display, xfc->gc, GXcopy);
LogDynAndXSetFillStyle(xfc->log, xfc->display, xfc->gc, FillSolid);
if (event->numVisibleRects < 1)
{
LogDynAndXSetClipMask(xfc->log, xfc->display, xfc->gc, None);
}
else
{
XSetClipRectangles(xfc->display, xfc->gc, 0, 0, xrects, numRects, YXBanded);
}
}
pixfmt = event->framePixFmt;
if (xf_tsmf_is_format_supported(xv, pixfmt))
{
xvpixfmt = pixfmt;
}
else if (pixfmt == RDP_PIXFMT_I420 && xf_tsmf_is_format_supported(xv, RDP_PIXFMT_YV12))
{
xvpixfmt = RDP_PIXFMT_YV12;
converti420yv12 = TRUE;
}
else if (pixfmt == RDP_PIXFMT_YV12 && xf_tsmf_is_format_supported(xv, RDP_PIXFMT_I420))
{
xvpixfmt = RDP_PIXFMT_I420;
converti420yv12 = TRUE;
}
else
{
WLog_DBG(TAG, "pixel format 0x%" PRIX32 " not supported by hardware.", pixfmt);
free(xrects);
return -1003;
}
image = XvShmCreateImage(xfc->display, xv->xv_port, WINPR_ASSERTING_INT_CAST(int, xvpixfmt), 0,
event->frameWidth, event->frameHeight, &shminfo);
if (xv->xv_image_size != image->data_size)
{
if (xv->xv_image_size > 0)
{
shmdt(xv->xv_shmaddr);
shmctl(xv->xv_shmid, IPC_RMID, nullptr);
}
xv->xv_image_size = image->data_size;
xv->xv_shmid = shmget(IPC_PRIVATE, image->data_size, IPC_CREAT | 0777);
xv->xv_shmaddr = shmat(xv->xv_shmid, 0, 0);
}
shminfo.shmid = xv->xv_shmid;
shminfo.shmaddr = image->data = xv->xv_shmaddr;
shminfo.readOnly = FALSE;
if (!XShmAttach(xfc->display, &shminfo))
{
XFree(image);
free(xrects);
WLog_DBG(TAG, "XShmAttach failed.");
return -1004;
}
/* The video driver may align each line to a different size
and we need to convert our original image data. */
switch (pixfmt)
{
case RDP_PIXFMT_I420:
case RDP_PIXFMT_YV12:
/* Y */
if (image->pitches[0] == event->frameWidth)
{
CopyMemory(image->data + image->offsets[0], event->frameData,
1ULL * event->frameWidth * event->frameHeight);
}
else
{
for (int i = 0; i < event->frameHeight; i++)
{
CopyMemory(image->data + 1ULL * image->offsets[0] +
1ULL * i * image->pitches[0],
event->frameData + 1ULL * i * event->frameWidth, event->frameWidth);
}
}
/* UV */
/* Conversion between I420 and YV12 is to simply swap U and V */
if (!converti420yv12)
{
data1 = event->frameData + 1ULL * event->frameWidth * event->frameHeight;
data2 = event->frameData + 1ULL * event->frameWidth * event->frameHeight +
1ULL * event->frameWidth * event->frameHeight / 4;
}
else
{
data2 = event->frameData + 1ULL * event->frameWidth * event->frameHeight;
data1 = event->frameData + 1ULL * event->frameWidth * event->frameHeight +
1ULL * event->frameWidth * event->frameHeight / 4;
image->id = pixfmt == RDP_PIXFMT_I420 ? RDP_PIXFMT_YV12 : RDP_PIXFMT_I420;
}
if (image->pitches[1] * 2 == event->frameWidth)
{
CopyMemory(image->data + image->offsets[1], data1,
event->frameWidth * event->frameHeight / 4);
CopyMemory(image->data + image->offsets[2], data2,
event->frameWidth * event->frameHeight / 4);
}
else
{
for (int i = 0; i < event->frameHeight / 2; i++)
{
CopyMemory(image->data + 1ULL * image->offsets[1] +
1ULL * i * image->pitches[1],
data1 + 1ULL * i * event->frameWidth / 2, event->frameWidth / 2);
CopyMemory(image->data + 1ULL * image->offsets[2] +
1ULL * i * image->pitches[2],
data2 + 1ULL * i * event->frameWidth / 2, event->frameWidth / 2);
}
}
break;
default:
if (image->data_size < 0)
{
free(xrects);
return -2000;
}
else
{
const size_t size = ((UINT32)image->data_size <= event->frameSize)
? (UINT32)image->data_size
: event->frameSize;
CopyMemory(image->data, event->frameData, size);
}
break;
}
XvShmPutImage(xfc->display, xv->xv_port, xfc->window->handle, xfc->gc, image, 0, 0,
image->width, image->height, event->x, event->y, event->width, event->height,
FALSE);
if (xv->xv_colorkey_atom == None)
LogDynAndXSetClipMask(xfc->log, xfc->display, xfc->gc, None);
LogDynAndXSync(xfc->log, xfc->display, FALSE);
XShmDetach(xfc->display, &shminfo);
XFree(image);
free(xrects);
return 1;
}
static int xf_tsmf_xv_init(xfContext* xfc, TsmfClientContext* tsmf)
{
int ret = 0;
unsigned int version = 0;
unsigned int release = 0;
unsigned int event_base = 0;
unsigned int error_base = 0;
unsigned int request_base = 0;
unsigned int num_adaptors = 0;
xfXvContext* xv = nullptr;
XvAdaptorInfo* ai = nullptr;
XvAttribute* attr = nullptr;
XvImageFormatValues* fo = nullptr;
if (xfc->xv_context)
return 1; /* context already created */
xv = (xfXvContext*)calloc(1, sizeof(xfXvContext));
if (!xv)
return -1;
xfc->xv_context = xv;
xv->xv_colorkey_atom = None;
xv->xv_image_size = 0;
xv->xv_port = xv_port;
if (!XShmQueryExtension(xfc->display))
{
WLog_DBG(TAG, "no xshm available.");
return -1;
}
ret =
XvQueryExtension(xfc->display, &version, &release, &request_base, &event_base, &error_base);
if (ret != Success)
{
WLog_DBG(TAG, "XvQueryExtension failed %d.", ret);
return -1;
}
WLog_DBG(TAG, "version %u release %u", version, release);
ret = XvQueryAdaptors(xfc->display, DefaultRootWindow(xfc->display), &num_adaptors, &ai);
if (ret != Success)
{
WLog_DBG(TAG, "XvQueryAdaptors failed %d.", ret);
return -1;
}
for (unsigned int i = 0; i < num_adaptors; i++)
{
WLog_DBG(TAG, "adapter port %lu-%lu (%s)", ai[i].base_id,
ai[i].base_id + ai[i].num_ports - 1, ai[i].name);
if (xv->xv_port == 0 && i == num_adaptors - 1)
xv->xv_port = ai[i].base_id;
}
if (num_adaptors > 0)
XvFreeAdaptorInfo(ai);
if (xv->xv_port == 0)
{
WLog_DBG(TAG, "no adapter selected, video frames will not be processed.");
return -1;
}
WLog_DBG(TAG, "selected %ld", xv->xv_port);
attr = XvQueryPortAttributes(xfc->display, xv->xv_port, &ret);
unsigned int i = 0;
for (; i < (unsigned int)ret; i++)
{
if (strcmp(attr[i].name, "XV_COLORKEY") == 0)
{
static wLog* log = nullptr;
if (!log)
log = WLog_Get(TAG);
xv->xv_colorkey_atom = Logging_XInternAtom(log, xfc->display, "XV_COLORKEY", FALSE);
XvSetPortAttribute(xfc->display, xv->xv_port, xv->xv_colorkey_atom,
attr[i].min_value + 1);
break;
}
}
XFree(attr);
WLog_DBG(TAG, "xf_tsmf_init: pixel format ");
fo = XvListImageFormats(xfc->display, xv->xv_port, &ret);
if (ret > 0)
{
xv->xv_pixfmts = (UINT32*)calloc((ret + 1), sizeof(UINT32));
size_t x = 0;
for (; x < (size_t)ret; x++)
{
xv->xv_pixfmts[x] = fo[x].id;
WLog_DBG(TAG, "%c%c%c%c ", ((char*)(xv->xv_pixfmts + x))[0],
((char*)(xv->xv_pixfmts + x))[1], ((char*)(xv->xv_pixfmts + x))[2],
((char*)(xv->xv_pixfmts + x))[3]);
}
xv->xv_pixfmts[x] = 0;
}
XFree(fo);
if (tsmf)
{
xfc->tsmf = tsmf;
tsmf->custom = (void*)xfc;
tsmf->FrameEvent = xf_tsmf_xv_video_frame_event;
}
return 1;
}
static int xf_tsmf_xv_uninit(xfContext* xfc, TsmfClientContext* tsmf)
{
xfXvContext* xv = (xfXvContext*)xfc->xv_context;
WINPR_UNUSED(tsmf);
if (xv)
{
if (xv->xv_image_size > 0)
{
shmdt(xv->xv_shmaddr);
shmctl(xv->xv_shmid, IPC_RMID, nullptr);
}
if (xv->xv_pixfmts)
{
free(xv->xv_pixfmts);
xv->xv_pixfmts = nullptr;
}
free(xv);
xfc->xv_context = nullptr;
}
if (xfc->tsmf)
{
xfc->tsmf->custom = nullptr;
xfc->tsmf = nullptr;
}
return 1;
}
#endif
int xf_tsmf_init(xfContext* xfc, TsmfClientContext* tsmf)
{
#ifdef WITH_XV
return xf_tsmf_xv_init(xfc, tsmf);
#else
return 1;
#endif
}
int xf_tsmf_uninit(xfContext* xfc, TsmfClientContext* tsmf)
{
#ifdef WITH_XV
return xf_tsmf_xv_uninit(xfc, tsmf);
#else
return 1;
#endif
}

View File

@@ -0,0 +1,29 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Video Redirection
*
* Copyright 2010-2011 Vic Lee
*
* 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.
*/
#ifndef FREERDP_CLIENT_X11_TSMF_H
#define FREERDP_CLIENT_X11_TSMF_H
#include "xf_client.h"
#include "xfreerdp.h"
int xf_tsmf_init(xfContext* xfc, TsmfClientContext* tsmf);
int xf_tsmf_uninit(xfContext* xfc, TsmfClientContext* tsmf);
#endif /* FREERDP_CLIENT_X11_TSMF_H */

View File

@@ -0,0 +1,42 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Client
*
* Copyright 2026 Thincast Technologies GmbH
* Copyright 2026 Armin Novak <armin.novak@thincast.com>
*
* 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.
*/
#pragma once
#include <winpr/wtypes.h>
#include <freerdp/types.h>
/* Forward declarations */
typedef struct xf_context xfContext;
typedef struct
{
RECTANGLE_16 area;
RECTANGLE_16 workarea;
BOOL primary;
} MONITOR_INFO;
typedef struct
{
UINT32 nmonitors;
RECTANGLE_16 area;
RECTANGLE_16 workarea;
MONITOR_INFO* monitors;
} VIRTUAL_SCREEN;

View File

@@ -0,0 +1,769 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 helper utilities
*
* Copyright 2023 Armin Novak <armin.novak@thincast.com>
* Copyringht 2023 Thincast Technologies GmbH
*
* 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.
*/
#include <string.h>
#include <winpr/assert.h>
#include <winpr/wtypes.h>
#include <winpr/path.h>
#include "xf_utils.h"
#include "xfreerdp.h"
#include <freerdp/utils/helpers.h>
#include <freerdp/log.h>
#define TAG CLIENT_TAG("xfreerdp.utils")
static const DWORD log_level = WLOG_TRACE;
static const char* error_to_string(wLog* log, Display* display, int error, char* buffer,
size_t size)
{
WINPR_ASSERT(size <= INT32_MAX);
const int rc = XGetErrorText(display, error, buffer, (int)size);
if (rc != Success)
WLog_Print(log, WLOG_WARN, "XGetErrorText returned %d", rc);
return buffer;
}
WINPR_ATTR_FORMAT_ARG(6, 7)
static void write_log(wLog* log, DWORD level, const char* fname, const char* fkt, size_t line,
WINPR_FORMAT_ARG const char* fmt, ...)
{
va_list ap = WINPR_C_ARRAY_INIT;
va_start(ap, fmt);
WLog_PrintTextMessageVA(log, level, line, fname, fkt, fmt, ap);
va_end(ap);
}
static BOOL ignore_code(int rc, size_t count, va_list ap)
{
for (size_t x = 0; x < count; x++)
{
const int val = va_arg(ap, int);
if (rc == val)
return TRUE;
}
return FALSE;
}
/* libx11 return codes are not really well documented, so checked against
* https://gitlab.freedesktop.org/xorg/lib/libx11.git */
static int write_result_log_va(wLog* log, DWORD level, const char* fname, const char* fkt,
size_t line, Display* display, char* name, int rc, size_t count,
va_list ap)
{
const BOOL ignore = ignore_code(rc, count, ap);
if (!ignore)
{
char buffer[128] = WINPR_C_ARRAY_INIT;
if (WLog_IsLevelActive(log, level))
{
WLog_PrintTextMessage(log, level, line, fname, fkt, "%s returned %s", name,
error_to_string(log, display, rc, buffer, sizeof(buffer)));
}
}
return rc;
}
static int write_result_log_expect_success(wLog* log, DWORD level, const char* fname,
const char* fkt, size_t line, Display* display,
char* name, int rc)
{
if (rc != Success)
{
va_list ap = WINPR_C_ARRAY_INIT;
(void)write_result_log_va(log, level, fname, fkt, line, display, name, rc, 0, ap);
}
return rc;
}
static int write_result_log_expect_one(wLog* log, DWORD level, const char* fname, const char* fkt,
size_t line, Display* display, char* name, int rc)
{
if (rc != 1)
{
va_list ap = WINPR_C_ARRAY_INIT;
(void)write_result_log_va(log, level, fname, fkt, line, display, name, rc, 0, ap);
}
return rc;
}
char* Safe_XGetAtomNameEx(wLog* log, Display* display, Atom atom, const char* varname)
{
WLog_Print(log, log_level, "XGetAtomName(%s, 0x%08lx)", varname, atom);
if (atom == None)
return strdup("Atom_None");
return XGetAtomName(display, atom);
}
Atom Logging_XInternAtom(wLog* log, Display* display, _Xconst char* atom_name, Bool only_if_exists)
{
Atom atom = XInternAtom(display, atom_name, only_if_exists);
if (WLog_IsLevelActive(log, log_level))
{
WLog_Print(log, log_level, "XInternAtom(%p, %s, %s) -> 0x%08" PRIx32, (void*)display,
atom_name, only_if_exists ? "True" : "False",
WINPR_CXX_COMPAT_CAST(UINT32, atom));
}
return atom;
}
const char* x11_error_to_string(xfContext* xfc, int error, char* buffer, size_t size)
{
WINPR_ASSERT(xfc);
return error_to_string(xfc->log, xfc->display, error, buffer, size);
}
int LogDynAndXChangeProperty_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w, Atom property, Atom type, int format,
int mode, const unsigned char* data, int nelements)
{
if (WLog_IsLevelActive(log, log_level))
{
char* propstr = Safe_XGetAtomName(log, display, property);
char* typestr = Safe_XGetAtomName(log, display, type);
write_log(log, log_level, file, fkt, line,
"XChangeProperty(%p, %lu, %s [%lu], %s [%lu], %d, %d, %p, %d)", (void*)display, w,
propstr, property, typestr, type, format, mode, (const void*)data, nelements);
XFree(propstr);
XFree(typestr);
}
const int rc = XChangeProperty(display, w, property, type, format, mode, data, nelements);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XChangeProperty",
rc);
}
int LogDynAndXDeleteProperty_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w, Atom property)
{
if (WLog_IsLevelActive(log, log_level))
{
char* propstr = Safe_XGetAtomName(log, display, property);
write_log(log, log_level, file, fkt, line, "XDeleteProperty(%p, %lu, %s [%lu])",
(void*)display, w, propstr, property);
XFree(propstr);
}
const int rc = XDeleteProperty(display, w, property);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XDeleteProperty",
rc);
}
int LogDynAndXConvertSelection_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Atom selection, Atom target, Atom property,
Window requestor, Time time)
{
if (WLog_IsLevelActive(log, log_level))
{
char* selectstr = Safe_XGetAtomName(log, display, selection);
char* targetstr = Safe_XGetAtomName(log, display, target);
char* propstr = Safe_XGetAtomName(log, display, property);
write_log(log, log_level, file, fkt, line,
"XConvertSelection(%p, %s [%lu], %s [%lu], %s [%lu], %lu, %lu)", (void*)display,
selectstr, selection, targetstr, target, propstr, property, requestor, time);
XFree(propstr);
XFree(targetstr);
XFree(selectstr);
}
const int rc = XConvertSelection(display, selection, target, property, requestor, time);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display,
"XConvertSelection", rc);
}
int LogDynAndXGetWindowProperty_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w, Atom property, long long_offset,
long long_length, int c_delete, Atom req_type,
Atom* actual_type_return, int* actual_format_return,
unsigned long* nitems_return, unsigned long* bytes_after_return,
unsigned char** prop_return)
{
if (WLog_IsLevelActive(log, log_level))
{
char* propstr = Safe_XGetAtomName(log, display, property);
char* req_type_str = Safe_XGetAtomName(log, display, req_type);
write_log(
log, log_level, file, fkt, line,
"XGetWindowProperty(%p, %lu, %s [%lu], %ld, %ld, %d, %s [%lu], %p, %p, %p, %p, %p)",
(void*)display, w, propstr, property, long_offset, long_length, c_delete, req_type_str,
req_type, (void*)actual_type_return, (void*)actual_format_return, (void*)nitems_return,
(void*)bytes_after_return, (void*)prop_return);
XFree(propstr);
XFree(req_type_str);
}
const int rc = XGetWindowProperty(display, w, property, long_offset, long_length, c_delete,
req_type, actual_type_return, actual_format_return,
nitems_return, bytes_after_return, prop_return);
return write_result_log_expect_success(log, WLOG_WARN, file, fkt, line, display,
"XGetWindowProperty", rc);
}
BOOL IsGnome(void)
{
// NOLINTNEXTLINE(concurrency-mt-unsafe)
char* env = getenv("DESKTOP_SESSION");
return (env != nullptr && strcmp(env, "gnome") == 0);
}
BOOL run_action_script(xfContext* xfc, const char* what, const char* arg, fn_action_script_run fkt,
void* user)
{
BOOL rc = FALSE;
FILE* keyScript = nullptr;
WINPR_ASSERT(xfc);
rdpSettings* settings = xfc->common.context.settings;
WINPR_ASSERT(settings);
const char* ActionScript = freerdp_settings_get_string(settings, FreeRDP_ActionScript);
xfc->actionScriptExists = winpr_PathFileExists(ActionScript);
if (!xfc->actionScriptExists)
{
WLog_DBG(TAG, "[ActionScript] no such script '%s'", ActionScript);
goto fail;
}
{
char command[2048] = WINPR_C_ARRAY_INIT;
(void)sprintf_s(command, sizeof(command), "%s %s", ActionScript, what);
keyScript = popen(command, "r");
if (!keyScript)
{
WLog_ERR(TAG, "[ActionScript] Failed to execute '%s'", command);
goto fail;
}
{
BOOL read_data = FALSE;
char buffer[2048] = WINPR_C_ARRAY_INIT;
while (fgets(buffer, sizeof(buffer), keyScript) != nullptr)
{
char* context = nullptr;
(void)strtok_s(buffer, "\n", &context);
if (fkt)
{
if (!fkt(xfc, buffer, strnlen(buffer, sizeof(buffer)), user, what, arg))
goto fail;
}
read_data = TRUE;
}
rc = read_data;
}
if (!rc)
WLog_ERR(TAG, "[ActionScript] No data returned from command '%s'", command);
}
fail:
if (keyScript)
pclose(keyScript);
const BOOL res = rc || !xfc->actionScriptExists;
if (!rc)
xfc->actionScriptExists = FALSE;
return res;
}
int LogDynAndXCopyArea_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Pixmap src, Window dest, GC gc, int src_x, int src_y,
unsigned int width, unsigned int height, int dest_x, int dest_y)
{
if (WLog_IsLevelActive(log, log_level))
{
XWindowAttributes attr = WINPR_C_ARRAY_INIT;
const Status rc = XGetWindowAttributes(display, dest, &attr);
write_log(log, log_level, file, fkt, line,
"XCopyArea(%p, src: {%lu}, dest: [%d]{%lu, %lu, %d}, gc: {%p}, src_x: {%d}, "
"src_y: {%d}, "
"width: {%u}, "
"height: {%u}, dest_x: {%d}, dest_y: {%d})",
(void*)display, src, rc, dest, attr.root, attr.depth, (void*)gc, src_x, src_y,
width, height, dest_x, dest_y);
}
if ((width == 0) || (height == 0))
{
const DWORD lvl = WLOG_WARN;
if (WLog_IsLevelActive(log, lvl))
write_log(log, lvl, file, fkt, line, "XCopyArea(width=%u, height=%u) !", width, height);
return Success;
}
const int rc = XCopyArea(display, src, dest, gc, src_x, src_y, width, height, dest_x, dest_y);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XCopyArea", rc);
}
int LogDynAndXPutImage_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Drawable d, GC gc, XImage* image, int src_x, int src_y,
int dest_x, int dest_y, unsigned int width, unsigned int height)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line,
"XPutImage(%p, d: {%lu}, gc: {%p}, image: [%p]{%d}, src_x: {%d}, src_y: {%d}, "
"dest_x: {%d}, "
"dest_y: {%d}, width: {%u}, "
"height: {%u})",
(void*)display, d, (void*)gc, (void*)image, image ? image->depth : -1, src_x,
src_y, dest_x, dest_y, width, height);
}
if ((width == 0) || (height == 0))
{
const DWORD lvl = WLOG_WARN;
if (WLog_IsLevelActive(log, lvl))
write_log(log, lvl, file, fkt, line, "XPutImage(width=%u, height=%u) !", width, height);
return Success;
}
const int rc = XPutImage(display, d, gc, image, src_x, src_y, dest_x, dest_y, width, height);
return write_result_log_expect_success(log, WLOG_WARN, file, fkt, line, display, "XPutImage",
rc);
}
/* be careful here.
* XSendEvent returns Status, but implementation always returns 1
*/
Status LogDynAndXSendEvent_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w, int propagate, long event_mask,
XEvent* event_send)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line,
"XSendEvent(d: {%p}, w: {%lu}, propagate: {%d}, event_mask: {%ld}, "
"event_send: [%p]{TODO})",
(void*)display, w, propagate, event_mask, (void*)event_send);
}
const int rc = XSendEvent(display, w, propagate, event_mask, event_send);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XSendEvent", rc);
}
int LogDynAndXFlush_ex(wLog* log, const char* file, const char* fkt, size_t line, Display* display)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XFlush(%p)", (void*)display);
}
const int rc = XFlush(display);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XFlush", rc);
}
Window LogDynAndXGetSelectionOwner_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Atom selection)
{
if (WLog_IsLevelActive(log, log_level))
{
char* selectionstr = Safe_XGetAtomName(log, display, selection);
write_log(log, log_level, file, fkt, line, "XGetSelectionOwner(%p, %s)", (void*)display,
selectionstr);
XFree(selectionstr);
}
return XGetSelectionOwner(display, selection);
}
int LogDynAndXDestroyWindow_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window window)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XDestroyWindow(%p, %lu)", (void*)display,
window);
}
const int rc = XDestroyWindow(display, window);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XDestroyWindow",
rc);
}
int LogDynAndXSync_ex(wLog* log, const char* file, const char* fkt, size_t line, Display* display,
Bool discard)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XSync(%p, %d)", (void*)display, discard);
}
const int rc = XSync(display, discard);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XSync", rc);
}
int LogDynAndXChangeWindowAttributes_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window window, unsigned long valuemask,
XSetWindowAttributes* attributes)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XChangeWindowAttributes(%p, %lu, 0x%08lu, %p)",
(void*)display, window, valuemask, (void*)attributes);
}
const int rc = XChangeWindowAttributes(display, window, valuemask, attributes);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display,
"XChangeWindowAttributes", rc);
}
int LogDynAndXSetTransientForHint_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window window, Window prop_window)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XSetTransientForHint(%p, %lu, %lu)",
(void*)display, window, prop_window);
}
const int rc = XSetTransientForHint(display, window, prop_window);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display,
"XSetTransientForHint", rc);
}
int LogDynAndXCloseDisplay_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XCloseDisplay(%p)", (void*)display);
}
const int rc = XCloseDisplay(display);
return write_result_log_expect_success(log, WLOG_WARN, file, fkt, line, display,
"XCloseDisplay", rc);
}
XImage* LogDynAndXCreateImage_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Visual* visual, unsigned int depth, int format,
int offset, char* data, unsigned int width, unsigned int height,
int bitmap_pad, int bytes_per_line)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XCreateImage(%p)", (void*)display);
}
return XCreateImage(display, visual, depth, format, offset, data, width, height, bitmap_pad,
bytes_per_line);
}
Window LogDynAndXCreateWindow_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window parent, int x, int y, unsigned int width,
unsigned int height, unsigned int border_width, int depth,
unsigned int c_class, Visual* visual, unsigned long valuemask,
XSetWindowAttributes* attributes)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XCreateWindow(%p)", (void*)display);
}
return XCreateWindow(display, parent, x, y, width, height, border_width, depth, c_class, visual,
valuemask, attributes);
}
GC LogDynAndXCreateGC_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Drawable d, unsigned long valuemask, XGCValues* values)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XCreateGC(%p)", (void*)display);
}
return XCreateGC(display, d, valuemask, values);
}
int LogDynAndXFreeGC_ex(wLog* log, const char* file, const char* fkt, size_t line, Display* display,
GC gc)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XFreeGC(%p)", (void*)display);
}
const int rc = XFreeGC(display, gc);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XFreeGC", rc);
}
Pixmap LogDynAndXCreatePixmap_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Drawable d, unsigned int width,
unsigned int height, unsigned int depth)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XCreatePixmap(%p, 0x%08lu, %u, %u, %u)",
(void*)display, d, width, height, depth);
}
return XCreatePixmap(display, d, width, height, depth);
}
int LogDynAndXFreePixmap_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Pixmap pixmap)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XFreePixmap(%p)", (void*)display);
}
const int rc = XFreePixmap(display, pixmap);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XFreePixmap", rc);
}
int LogDynAndXSetSelectionOwner_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Atom selection, Window owner, Time time)
{
if (WLog_IsLevelActive(log, log_level))
{
char* selectionstr = Safe_XGetAtomName(log, display, selection);
write_log(log, log_level, file, fkt, line, "XSetSelectionOwner(%p, %s, 0x%08lu, %lu)",
(void*)display, selectionstr, owner, time);
XFree(selectionstr);
}
const int rc = XSetSelectionOwner(display, selection, owner, time);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display,
"XSetSelectionOwner", rc);
}
int LogDynAndXSetForeground_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, GC gc, unsigned long foreground)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XSetForeground(%p, %p, 0x%08lu)",
(void*)display, (void*)gc, foreground);
}
const int rc = XSetForeground(display, gc, foreground);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XSetForeground",
rc);
}
int LogDynAndXMoveWindow_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w, int x, int y)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XMoveWindow(%p, 0x%08lu, %d, %d)",
(void*)display, w, x, y);
}
const int rc = XMoveWindow(display, w, x, y);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XMoveWindow", rc);
}
int LogDynAndXSetFillStyle_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, GC gc, int fill_style)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XSetFillStyle(%p, %p, %d)", (void*)display,
(void*)gc, fill_style);
}
const int rc = XSetFillStyle(display, gc, fill_style);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XSetFillStyle",
rc);
}
int LogDynAndXSetFunction_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, GC gc, int function)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XSetFunction(%p, %p, %d)", (void*)display,
(void*)gc, function);
}
const int rc = XSetFunction(display, gc, function);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XSetFunction",
rc);
}
int LogDynAndXRaiseWindow_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XRaiseWindow(%p, %lu)", (void*)display, w);
}
const int rc = XRaiseWindow(display, w);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XRaiseWindow",
rc);
}
int LogDynAndXMapWindow_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XMapWindow(%p, %lu)", (void*)display, w);
}
const int rc = XMapWindow(display, w);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XMapWindow", rc);
}
int LogDynAndXUnmapWindow_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XUnmapWindow(%p, %lu)", (void*)display, w);
}
const int rc = XUnmapWindow(display, w);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XUnmapWindow",
rc);
}
int LogDynAndXMoveResizeWindow_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w, int x, int y, unsigned int width,
unsigned int height)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XMoveResizeWindow(%p, %lu, %d, %d, %u, %u)",
(void*)display, w, x, y, width, height);
}
const int rc = XMoveResizeWindow(display, w, x, y, width, height);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display,
"XMoveResizeWindow", rc);
}
Status LogDynAndXWithdrawWindow_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w, int screen_number)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XWithdrawWindow(%p, %lu, %d)", (void*)display,
w, screen_number);
}
const Status rc = XWithdrawWindow(display, w, screen_number);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XWithdrawWindow",
rc);
}
int LogDynAndXResizeWindow_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w, unsigned int width, unsigned int height)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XResizeWindow(%p, %lu, %u, %u)", (void*)display,
w, width, height);
}
const int rc = XResizeWindow(display, w, width, height);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XResizeWindow",
rc);
}
int LogDynAndXClearWindow_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XClearWindow(%p, %lu)", (void*)display, w);
}
const int rc = XClearWindow(display, w);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XClearWindow",
rc);
}
int LogDynAndXSetBackground_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, GC gc, unsigned long background)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XSetBackground(%p, %p, %lu)", (void*)display,
(void*)gc, background);
}
const int rc = XSetBackground(display, gc, background);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XSetBackground",
rc);
}
int LogDynAndXSetClipMask_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, GC gc, Pixmap pixmap)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XSetClipMask(%p, %p, %lu)", (void*)display,
(void*)gc, pixmap);
}
const int rc = XSetClipMask(display, gc, pixmap);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XSetClipMask",
rc);
}
int LogDynAndXFillRectangle_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w, GC gc, int x, int y, unsigned int width,
unsigned int height)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XFillRectangle(%p, %lu, %p, %d, %d, %u, %u)",
(void*)display, w, (void*)gc, x, y, width, height);
}
const int rc = XFillRectangle(display, w, gc, x, y, width, height);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XFillRectangle",
rc);
}
int LogDynAndXSetRegion_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, GC gc, Region r)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XSetRegion(%p, %p, %p)", (void*)display,
(void*)gc, (void*)r);
}
const int rc = XSetRegion(display, gc, r);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XSetRegion", rc);
}
int LogDynAndXReparentWindow_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w, Window parent, int x, int y)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XReparentWindow(%p, %lu, %lu, %d, %d)",
(void*)display, w, parent, x, y);
}
const int rc = XReparentWindow(display, w, parent, x, y);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XReparentWindow",
rc);
}
char* getConfigOption(BOOL system, const char* option)
{
char* res = nullptr;
WINPR_JSON* file = freerdp_GetJSONConfigFile(system, "xfreerdp.json");
if (!file)
return nullptr;
WINPR_JSON* obj = WINPR_JSON_GetObjectItemCaseSensitive(file, option);
if (obj)
{
const char* val = WINPR_JSON_GetStringValue(obj);
if (val)
res = _strdup(val);
}
WINPR_JSON_Delete(file);
return res;
}
int LogDynAndXRestackWindows_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window* windows, int nwindows)
{
if (WLog_IsLevelActive(log, log_level))
{
write_log(log, log_level, file, fkt, line, "XRestackWindows(%p, %p, %d)", (void*)display,
(const void*)windows, nwindows);
}
const int rc = XRestackWindows(display, windows, nwindows);
return write_result_log_expect_one(log, WLOG_WARN, file, fkt, line, display, "XRestackWindows",
rc);
}

View File

@@ -0,0 +1,300 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 helper utilities
*
* Copyright 2023 Armin Novak <armin.novak@thincast.com>
* Copyringht 2023 Thincast Technologies GmbH
*
* 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.
*/
#pragma once
#include <winpr/wlog.h>
#include <winpr/wtypes.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "xfreerdp.h"
const char* x11_error_to_string(xfContext* xfc, int error, char* buffer, size_t size);
#define X_GET_ATOM_VAR_NAME(x) #x
#define Safe_XGetAtomName(log, display, atom) \
Safe_XGetAtomNameEx((log), (display), (atom), X_GET_ATOM_VAR_NAME(atom))
char* Safe_XGetAtomNameEx(wLog* log, Display* display, Atom atom, const char* varname);
Atom Logging_XInternAtom(wLog* log, Display* display, _Xconst char* atom_name, Bool only_if_exists);
typedef BOOL (*fn_action_script_run)(xfContext* xfc, const char* buffer, size_t size, void* user,
const char* what, const char* arg);
BOOL run_action_script(xfContext* xfc, const char* what, const char* arg, fn_action_script_run fkt,
void* user);
#define LogDynAndXCreatePixmap(log, display, d, width, height, depth) \
LogDynAndXCreatePixmap_ex((log), __FILE__, __func__, __LINE__, (display), (d), (width), \
(height), (depth))
Pixmap LogDynAndXCreatePixmap_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Drawable d, unsigned int width,
unsigned int height, unsigned int depth);
#define LogDynAndXFreePixmap(log, display, pixmap) \
LogDynAndXFreePixmap_ex(log, __FILE__, __func__, __LINE__, (display), (pixmap))
int LogDynAndXFreePixmap_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Pixmap pixmap);
#define LogDynAndXCreateWindow(log, display, parent, x, y, width, height, border_width, depth, \
class, visual, valuemask, attributes) \
LogDynAndXCreateWindow_ex((log), __FILE__, __func__, __LINE__, (display), (parent), (x), (y), \
(width), (height), (border_width), (depth), (class), (visual), \
(valuemask), (attributes))
Window LogDynAndXCreateWindow_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window parent, int x, int y, unsigned int width,
unsigned int height, unsigned int border_width, int depth,
unsigned int c_class, Visual* visual, unsigned long valuemask,
XSetWindowAttributes* attributes);
#define LogDynAndXRaiseWindow(log, display, w) \
LogDynAndXRaiseWindow_ex((log), __FILE__, __func__, __LINE__, (display), (w))
int LogDynAndXRaiseWindow_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w);
#define LogDynAndXMapWindow(log, display, w) \
LogDynAndXMapWindow_ex((log), __FILE__, __func__, __LINE__, (display), (w))
int LogDynAndXMapWindow_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w);
#define LogDynAndXUnmapWindow(log, display, w) \
LogDynAndXUnmapWindow_ex((log), __FILE__, __func__, __LINE__, (display), (w))
int LogDynAndXUnmapWindow_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w);
#define LogDynAndXMoveResizeWindow(log, display, w, x, y, width, height) \
LogDynAndXMoveResizeWindow_ex((log), __FILE__, __func__, __LINE__, (display), (w), (x), (y), \
(width), (height))
int LogDynAndXMoveResizeWindow_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w, int x, int y, unsigned int width,
unsigned int height);
#define LogDynAndXWithdrawWindow(log, display, w, screen_number) \
LogDynAndXWithdrawWindow_ex((log), __FILE__, __func__, __LINE__, (display), (w), \
(screen_number))
Status LogDynAndXWithdrawWindow_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w, int screen_number);
#define LogDynAndXMoveWindow(log, display, w, x, y) \
LogDynAndXMoveWindow_ex((log), __FILE__, __func__, __LINE__, (display), (w), (x), (y))
int LogDynAndXMoveWindow_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w, int x, int y);
#define LogDynAndXResizeWindow(log, display, w, width, height) \
LogDynAndXResizeWindow_ex((log), __FILE__, __func__, __LINE__, (display), (w), (width), \
(height))
int LogDynAndXResizeWindow_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w, unsigned int width, unsigned int height);
#define LogDynAndXClearWindow(log, display, w) \
LogDynAndXClearWindow_ex((log), __FILE__, __func__, __LINE__, (display), (w))
int LogDynAndXClearWindow_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w);
#define LogDynAndXGetWindowProperty(log, display, w, property, long_offset, long_length, delete, \
req_type, actual_type_return, actual_format_return, \
nitems_return, bytes_after_return, prop_return) \
LogDynAndXGetWindowProperty_ex((log), __FILE__, __func__, __LINE__, (display), (w), \
(property), (long_offset), (long_length), (delete), (req_type), \
(actual_type_return), (actual_format_return), (nitems_return), \
(bytes_after_return), (prop_return))
int LogDynAndXGetWindowProperty_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w, Atom property, long long_offset,
long long_length, Bool c_delete, Atom req_type,
Atom* actual_type_return, int* actual_format_return,
unsigned long* nitems_return, unsigned long* bytes_after_return,
unsigned char** prop_return);
#define LogDynAndXReparentWindow(log, display, w, parent, x, y) \
LogDynAndXReparentWindow_ex((log), __FILE__, __func__, __LINE__, (display), (w), (parent), \
(x), (y))
int LogDynAndXReparentWindow_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w, Window parent, int x, int y);
#define LogDynAndXChangeProperty(log, display, w, property, type, format, mode, data, nelements) \
LogDynAndXChangeProperty_ex((log), __FILE__, __func__, __LINE__, (display), (w), (property), \
(type), (format), (mode), (data), (nelements))
int LogDynAndXChangeProperty_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w, Atom property, Atom type, int format,
int mode, _Xconst unsigned char* data, int nelements);
#define LogDynAndXDeleteProperty(log, display, w, property) \
LogDynAndXDeleteProperty_ex((log), __FILE__, __func__, __LINE__, (display), (w), (property))
int LogDynAndXDeleteProperty_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w, Atom property);
#define LogDynAndXConvertSelection(log, display, selection, target, property, requestor, time) \
LogDynAndXConvertSelection_ex((log), __FILE__, __func__, __LINE__, (display), (selection), \
(target), (property), (requestor), (time))
int LogDynAndXConvertSelection_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Atom selection, Atom target, Atom property,
Window requestor, Time time);
#define LogDynAndXCreateGC(log, display, d, valuemask, values) \
LogDynAndXCreateGC_ex(log, __FILE__, __func__, __LINE__, (display), (d), (valuemask), (values))
GC LogDynAndXCreateGC_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Drawable d, unsigned long valuemask, XGCValues* values);
#define LogDynAndXFreeGC(log, display, gc) \
LogDynAndXFreeGC_ex(log, __FILE__, __func__, __LINE__, (display), (gc))
int LogDynAndXFreeGC_ex(wLog* log, const char* file, const char* fkt, size_t line, Display* display,
GC gc);
#define LogDynAndXCreateImage(log, display, visual, depth, format, offset, data, width, height, \
bitmap_pad, bytes_per_line) \
LogDynAndXCreateImage_ex(log, __FILE__, __func__, __LINE__, (display), (visual), (depth), \
(format), (offset), (data), (width), (height), (bitmap_pad), \
(bytes_per_line))
XImage* LogDynAndXCreateImage_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Visual* visual, unsigned int depth, int format,
int offset, char* data, unsigned int width, unsigned int height,
int bitmap_pad, int bytes_per_line);
#define LogDynAndXPutImage(log, display, d, gc, image, src_x, src_y, dest_x, dest_y, width, \
height) \
LogDynAndXPutImage_ex(log, __FILE__, __func__, __LINE__, (display), (d), (gc), (image), \
(src_x), (src_y), (dest_x), (dest_y), (width), (height))
int LogDynAndXPutImage_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Drawable d, GC gc, XImage* image, int src_x, int src_y,
int dest_x, int dest_y, unsigned int width, unsigned int height);
#define LogDynAndXCopyArea(log, display, src, dest, gc, src_x, src_y, width, height, dest_x, \
dest_y) \
LogDynAndXCopyArea_ex(log, __FILE__, __func__, __LINE__, (display), (src), (dest), (gc), \
(src_x), (src_y), (width), (height), (dest_x), (dest_y))
extern int LogDynAndXCopyArea_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Pixmap src, Window dest, GC gc, int src_x,
int src_y, unsigned int width, unsigned int height, int dest_x,
int dest_y);
#define LogDynAndXSendEvent(log, display, w, propagate, event_mask, event_send) \
LogDynAndXSendEvent_ex(log, __FILE__, __func__, __LINE__, (display), (w), (propagate), \
(event_mask), (event_send))
extern Status LogDynAndXSendEvent_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w, Bool propagate, long event_mask,
XEvent* event_send);
#define LogDynAndXFlush(log, display) \
LogDynAndXFlush_ex(log, __FILE__, __func__, __LINE__, (display))
extern Status LogDynAndXFlush_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display);
#define LogDynAndXSync(log, display, discard) \
LogDynAndXSync_ex(log, __FILE__, __func__, __LINE__, (display), (discard))
extern int LogDynAndXSync_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Bool discard);
#define LogDynAndXGetSelectionOwner(log, display, selection) \
LogDynAndXGetSelectionOwner_ex(log, __FILE__, __func__, __LINE__, (display), (selection))
extern Window LogDynAndXGetSelectionOwner_ex(wLog* log, const char* file, const char* fkt,
size_t line, Display* display, Atom selection);
#define LogDynAndXSetSelectionOwner(log, display, selection, owner, time) \
LogDynAndXSetSelectionOwner_ex(log, __FILE__, __func__, __LINE__, (display), (selection), \
(owner), (time))
extern int LogDynAndXSetSelectionOwner_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Atom selection, Window owner,
Time time);
#define LogDynAndXDestroyWindow(log, display, window) \
LogDynAndXDestroyWindow_ex(log, __FILE__, __func__, __LINE__, (display), (window))
extern int LogDynAndXDestroyWindow_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window window);
#define LogDynAndXChangeWindowAttributes(log, display, window, valuemask, attributes) \
LogDynAndXChangeWindowAttributes_ex(log, __FILE__, __func__, __LINE__, (display), (window), \
(valuemask), (attributes))
extern int LogDynAndXChangeWindowAttributes_ex(wLog* log, const char* file, const char* fkt,
size_t line, Display* display, Window window,
unsigned long valuemask,
XSetWindowAttributes* attributes);
#define LogDynAndXSetTransientForHint(log, display, window, prop_window) \
LogDynAndXSetTransientForHint_ex(log, __FILE__, __func__, __LINE__, (display), (window), \
(prop_window))
extern int LogDynAndXSetTransientForHint_ex(wLog* log, const char* file, const char* fkt,
size_t line, Display* display, Window window,
Window prop_window);
#define LogDynAndXCloseDisplay(log, display) \
LogDynAndXCloseDisplay_ex(log, __FILE__, __func__, __LINE__, (display))
extern int LogDynAndXCloseDisplay_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display);
#define LogDynAndXSetClipMask(log, display, gc, pixmap) \
LogDynAndXSetClipMask_ex(log, __FILE__, __func__, __LINE__, (display), (gc), (pixmap))
extern int LogDynAndXSetClipMask_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, GC gc, Pixmap pixmap);
#define LogDynAndXSetRegion(log, display, gc, r) \
LogDynAndXSetRegion_ex(log, __FILE__, __func__, __LINE__, (display), (gc), (r))
extern int LogDynAndXSetRegion_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, GC gc, Region r);
#define LogDynAndXSetBackground(log, display, gc, background) \
LogDynAndXSetBackground_ex(log, __FILE__, __func__, __LINE__, (display), (gc), (background))
extern int LogDynAndXSetBackground_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, GC gc, unsigned long background);
#define LogDynAndXSetForeground(log, display, gc, foreground) \
LogDynAndXSetForeground_ex(log, __FILE__, __func__, __LINE__, (display), (gc), (foreground))
extern int LogDynAndXSetForeground_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, GC gc, unsigned long foreground);
#define LogDynAndXSetFillStyle(log, display, gc, fill_style) \
LogDynAndXSetFillStyle_ex(log, __FILE__, __func__, __LINE__, (display), (gc), (fill_style))
extern int LogDynAndXSetFillStyle_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, GC gc, int fill_style);
#define LogDynAndXFillRectangle(log, display, w, gc, x, y, width, height) \
LogDynAndXFillRectangle_ex(log, __FILE__, __func__, __LINE__, (display), (w), (gc), (x), (y), \
(width), (height))
extern int LogDynAndXFillRectangle_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window w, GC gc, int x, int y,
unsigned int width, unsigned int height);
#define LogDynAndXSetFunction(log, display, gc, function) \
LogDynAndXSetFunction_ex(log, __FILE__, __func__, __LINE__, (display), (gc), (function))
extern int LogDynAndXSetFunction_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, GC gc, int function);
#define LogDynAndXRestackWindows(log, display, windows, count) \
LogDynAndXRestackWindows_ex(log, __FILE__, __func__, __LINE__, (display), (windows), (count))
extern int LogDynAndXRestackWindows_ex(wLog* log, const char* file, const char* fkt, size_t line,
Display* display, Window* windows, int nwindows);
BOOL IsGnome(void);
char* getConfigOption(BOOL system, const char* option);
const char* request_code_2_str(int code);

View File

@@ -0,0 +1,141 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Video Optimized Remoting Virtual Channel Extension for X11
*
* Copyright 2017 David Fort <contact@hardening-consulting.com>
*
* 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.
*/
#include <winpr/assert.h>
#include <freerdp/client/geometry.h>
#include <freerdp/client/video.h>
#include <freerdp/gdi/video.h>
#include "xf_video.h"
#include "xf_utils.h"
#include <freerdp/log.h>
#define TAG CLIENT_TAG("video")
typedef struct
{
VideoSurface base;
XImage* image;
} xfVideoSurface;
static VideoSurface* xfVideoCreateSurface(VideoClientContext* video, UINT32 x, UINT32 y,
UINT32 width, UINT32 height)
{
xfContext* xfc = nullptr;
xfVideoSurface* ret = nullptr;
WINPR_ASSERT(video);
ret = (xfVideoSurface*)VideoClient_CreateCommonContext(sizeof(xfContext), x, y, width, height);
if (!ret)
return nullptr;
xfc = (xfContext*)video->custom;
WINPR_ASSERT(xfc);
ret->image = LogDynAndXCreateImage(xfc->log, xfc->display, xfc->visual,
WINPR_ASSERTING_INT_CAST(uint32_t, xfc->depth), ZPixmap, 0,
(char*)ret->base.data, width, height, 8,
WINPR_ASSERTING_INT_CAST(int, ret->base.scanline));
if (!ret->image)
{
WLog_ERR(TAG, "unable to create surface image");
VideoClient_DestroyCommonContext(&ret->base);
return nullptr;
}
return &ret->base;
}
static BOOL xfVideoShowSurface(VideoClientContext* video, const VideoSurface* surface,
WINPR_ATTR_UNUSED UINT32 destinationWidth,
WINPR_ATTR_UNUSED UINT32 destinationHeight)
{
const xfVideoSurface* xfSurface = (const xfVideoSurface*)surface;
xfContext* xfc = nullptr;
const rdpSettings* settings = nullptr;
WINPR_ASSERT(video);
WINPR_ASSERT(xfSurface);
xfc = video->custom;
WINPR_ASSERT(xfc);
settings = xfc->common.context.settings;
WINPR_ASSERT(settings);
#ifdef WITH_XRENDER
if (freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) ||
freerdp_settings_get_bool(settings, FreeRDP_MultiTouchGestures))
{
LogDynAndXPutImage(xfc->log, xfc->display, xfc->primary, xfc->gc, xfSurface->image, 0, 0,
WINPR_ASSERTING_INT_CAST(int, surface->x),
WINPR_ASSERTING_INT_CAST(int, surface->y), surface->w, surface->h);
xf_draw_screen(xfc, WINPR_ASSERTING_INT_CAST(int32_t, surface->x),
WINPR_ASSERTING_INT_CAST(int32_t, surface->y),
WINPR_ASSERTING_INT_CAST(int32_t, surface->w),
WINPR_ASSERTING_INT_CAST(int32_t, surface->h));
}
else
#endif
{
LogDynAndXPutImage(xfc->log, xfc->display, xfc->drawable, xfc->gc, xfSurface->image, 0, 0,
WINPR_ASSERTING_INT_CAST(int, surface->x),
WINPR_ASSERTING_INT_CAST(int, surface->y), surface->w, surface->h);
}
return TRUE;
}
static BOOL xfVideoDeleteSurface(VideoClientContext* video, VideoSurface* surface)
{
xfVideoSurface* xfSurface = (xfVideoSurface*)surface;
WINPR_UNUSED(video);
if (xfSurface)
XFree(xfSurface->image);
VideoClient_DestroyCommonContext(surface);
return TRUE;
}
void xf_video_control_init(xfContext* xfc, VideoClientContext* video)
{
WINPR_ASSERT(xfc);
WINPR_ASSERT(video);
gdi_video_control_init(xfc->common.context.gdi, video);
/* X11 needs to be able to handle 32bpp colors directly. */
if (xfc->depth >= 24)
{
video->custom = xfc;
video->createSurface = xfVideoCreateSurface;
video->showSurface = xfVideoShowSurface;
video->deleteSurface = xfVideoDeleteSurface;
}
}
void xf_video_control_uninit(xfContext* xfc, VideoClientContext* video)
{
WINPR_ASSERT(xfc);
gdi_video_control_uninit(xfc->common.context.gdi, video);
}

View File

@@ -0,0 +1,37 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Video Optimized Remoting Virtual Channel Extension for X11
*
* Copyright 2017 David Fort <contact@hardening-consulting.com>
*
* 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.
*/
#ifndef CLIENT_X11_XF_VIDEO_H_
#define CLIENT_X11_XF_VIDEO_H_
#include <freerdp/client/video.h>
#include "xf_types.h"
typedef struct s_xfVideoContext xfVideoContext;
void xf_video_control_init(xfContext* xfc, VideoClientContext* video);
void xf_video_control_uninit(xfContext* xfc, VideoClientContext* video);
void xf_video_free(xfVideoContext* context);
WINPR_ATTR_MALLOC(xf_video_free, 1)
WINPR_ATTR_NODISCARD
xfVideoContext* xf_video_new(xfContext* xfc);
#endif /* CLIENT_X11_XF_VIDEO_H_ */

1647
third_party/FreeRDP/client/X11/xf_window.c vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,140 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Windows
*
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#ifndef FREERDP_CLIENT_X11_WINDOW_H
#define FREERDP_CLIENT_X11_WINDOW_H
#include <X11/Xlib.h>
#include <winpr/platform.h>
#include <freerdp/freerdp.h>
#include <freerdp/gdi/gfx.h>
#include "xf_types.h"
#include "xf_rail.h"
#include "xf_floatbar.h"
typedef struct xf_window xfWindow;
// Extended ICCM flags http://standards.freedesktop.org/wm-spec/wm-spec-latest.html
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO
#define NET_WM_MOVERESIZE_SIZE_TOPLEFT 0
#define NET_WM_MOVERESIZE_SIZE_TOP 1
#define NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2
#define NET_WM_MOVERESIZE_SIZE_RIGHT 3
#define NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
#define NET_WM_MOVERESIZE_SIZE_BOTTOM 5
#define NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
#define NET_WM_MOVERESIZE_SIZE_LEFT 7
#define NET_WM_MOVERESIZE_MOVE 8 /* movement only */
#define NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 /* size via keyboard */
#define NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */
#define NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */
#define NET_WM_STATE_REMOVE 0 /* remove/unset property */
#define NET_WM_STATE_ADD 1 /* add/set property */
#define NET_WM_STATE_TOGGLE 2 /* toggle property */
WINPR_PRAGMA_DIAG_POP
struct xf_window
{
GC gc;
int left;
int top;
int right;
int bottom;
int width;
int height;
int shmid;
Window handle;
Window* xfwin;
xfFloatbar* floatbar;
BOOL decorations;
BOOL is_mapped;
BOOL is_transient;
};
void xf_ewmhints_init(xfContext* xfc);
BOOL xf_GetWorkArea(xfContext* xfc);
void xf_SetWindowFullscreen(xfContext* xfc, xfWindow* window, BOOL fullscreen);
void xf_SetWindowMinimized(xfContext* xfc, xfWindow* window);
void xf_SetWindowDecorations(xfContext* xfc, Window window, BOOL show);
void xf_SetWindowUnlisted(xfContext* xfc, Window window);
void xf_DestroyDesktopWindow(xfContext* xfc, xfWindow* window);
WINPR_ATTR_MALLOC(xf_DestroyDesktopWindow, 2)
xfWindow* xf_CreateDesktopWindow(xfContext* xfc, char* name, int width, int height);
void xf_ResizeDesktopWindow(xfContext* xfc, xfWindow* window, int width, int height);
Window xf_CreateDummyWindow(xfContext* xfc);
void xf_DestroyDummyWindow(xfContext* xfc, Window window);
BOOL xf_GetWindowProperty(xfContext* xfc, Window window, Atom property, int length,
unsigned long* nitems, unsigned long* bytes, BYTE** prop);
void xf_SendClientEvent(xfContext* xfc, Window window, Atom atom, unsigned int numArgs, ...);
BOOL xf_AppWindowCreate(xfContext* xfc, xfAppWindow* appWindow);
int xf_AppWindowInit(xfContext* xfc, xfAppWindow* appWindow);
BOOL xf_AppWindowResize(xfContext* xfc, xfAppWindow* appWindow);
void xf_SetWindowText(xfContext* xfc, xfAppWindow* appWindow, const char* name);
void xf_MoveWindow(xfContext* xfc, xfAppWindow* appWindow, int x, int y, int width, int height);
void xf_ShowWindow(xfContext* xfc, xfAppWindow* appWindow, BYTE state);
// void xf_SetWindowIcon(xfContext* xfc, xfAppWindow* appWindow, rdpIcon* icon);
void xf_SetWindowRects(xfContext* xfc, xfAppWindow* appWindow, RECTANGLE_16* rects, int nrects);
void xf_SetWindowVisibilityRects(xfContext* xfc, xfAppWindow* appWindow, UINT32 rectsOffsetX,
UINT32 rectsOffsetY, RECTANGLE_16* rects, int nrects);
void xf_SetWindowStyle(xfContext* xfc, xfAppWindow* appWindow, UINT32 style, UINT32 ex_style);
void xf_SetWindowActions(xfContext* xfc, xfAppWindow* appWindow);
void xf_UpdateWindowArea(xfContext* xfc, xfAppWindow* appWindow, int x, int y, int width,
int height);
UINT xf_AppUpdateWindowFromSurface(xfContext* xfc, gdiGfxSurface* surface);
void xf_AppWindowDestroyImage(xfAppWindow* appWindow);
void xf_DestroyWindow(xfContext* xfc, xfAppWindow* appWindow);
void xf_SetWindowMinMaxInfo(xfContext* xfc, xfAppWindow* appWindow, int maxWidth, int maxHeight,
int maxPosX, int maxPosY, int minTrackWidth, int minTrackHeight,
int maxTrackWidth, int maxTrackHeight);
void xf_StartLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow, int direction, int x, int y);
void xf_EndLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow);
#define xf_AppWindowFromX11Window(xfc, wnd) \
xf_AppWindowFromX11WindowFrom((xfc), (wnd), __FILE__, __func__, __LINE__)
WINPR_ATTR_MALLOC(xf_rail_return_windowFrom, 1)
xfAppWindow* xf_AppWindowFromX11WindowFrom(xfContext* xfc, Window wnd, const char* file,
const char* fkt, size_t line);
#define xf_AppWindowsLock(xfc) xfAppWindowsLockFrom((xfc), __FILE__, __func__, __LINE__)
void xfAppWindowsLockFrom(xfContext* xfc, const char* file, const char* fkt, size_t line);
#define xf_AppWindowsUnlock(xfc) xfAppWindowsUnlockFrom((xfc), __FILE__, __func__, __LINE__)
void xfAppWindowsUnlockFrom(xfContext* xfc, const char* file, const char* fkt, size_t line);
const char* window_styles_to_string(UINT32 style, char* buffer, size_t length);
const char* window_styles_ex_to_string(UINT32 styleEx, char* buffer, size_t length);
#endif /* FREERDP_CLIENT_X11_WINDOW_H */

View File

@@ -0,0 +1,157 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 helper utilities
*
* Copyright 2025 Armin Novak <armin.novak@thincast.com>
* Copyringht 2025 Thincast Technologies GmbH
*
* 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.
*/
// Do not include! X11 has conflicting defines #include "xf_utils.h"
const char* request_code_2_str(int code);
#include <X11/Xproto.h>
const char* request_code_2_str(int code)
{
#define CASE2STR(x) \
case x: \
return #x
switch (code)
{
CASE2STR(X_CreateWindow);
CASE2STR(X_ChangeWindowAttributes);
CASE2STR(X_GetWindowAttributes);
CASE2STR(X_DestroyWindow);
CASE2STR(X_DestroySubwindows);
CASE2STR(X_ChangeSaveSet);
CASE2STR(X_ReparentWindow);
CASE2STR(X_MapWindow);
CASE2STR(X_MapSubwindows);
CASE2STR(X_UnmapWindow);
CASE2STR(X_UnmapSubwindows);
CASE2STR(X_ConfigureWindow);
CASE2STR(X_CirculateWindow);
CASE2STR(X_GetGeometry);
CASE2STR(X_QueryTree);
CASE2STR(X_InternAtom);
CASE2STR(X_GetAtomName);
CASE2STR(X_ChangeProperty);
CASE2STR(X_DeleteProperty);
CASE2STR(X_GetProperty);
CASE2STR(X_ListProperties);
CASE2STR(X_SetSelectionOwner);
CASE2STR(X_GetSelectionOwner);
CASE2STR(X_ConvertSelection);
CASE2STR(X_SendEvent);
CASE2STR(X_GrabPointer);
CASE2STR(X_UngrabPointer);
CASE2STR(X_GrabButton);
CASE2STR(X_UngrabButton);
CASE2STR(X_ChangeActivePointerGrab);
CASE2STR(X_GrabKeyboard);
CASE2STR(X_UngrabKeyboard);
CASE2STR(X_GrabKey);
CASE2STR(X_UngrabKey);
CASE2STR(X_AllowEvents);
CASE2STR(X_GrabServer);
CASE2STR(X_UngrabServer);
CASE2STR(X_QueryPointer);
CASE2STR(X_GetMotionEvents);
CASE2STR(X_TranslateCoords);
CASE2STR(X_WarpPointer);
CASE2STR(X_SetInputFocus);
CASE2STR(X_GetInputFocus);
CASE2STR(X_QueryKeymap);
CASE2STR(X_OpenFont);
CASE2STR(X_CloseFont);
CASE2STR(X_QueryFont);
CASE2STR(X_QueryTextExtents);
CASE2STR(X_ListFonts);
CASE2STR(X_ListFontsWithInfo);
CASE2STR(X_SetFontPath);
CASE2STR(X_GetFontPath);
CASE2STR(X_CreatePixmap);
CASE2STR(X_FreePixmap);
CASE2STR(X_CreateGC);
CASE2STR(X_ChangeGC);
CASE2STR(X_CopyGC);
CASE2STR(X_SetDashes);
CASE2STR(X_SetClipRectangles);
CASE2STR(X_FreeGC);
CASE2STR(X_ClearArea);
CASE2STR(X_CopyArea);
CASE2STR(X_CopyPlane);
CASE2STR(X_PolyPoint);
CASE2STR(X_PolyLine);
CASE2STR(X_PolySegment);
CASE2STR(X_PolyRectangle);
CASE2STR(X_PolyArc);
CASE2STR(X_FillPoly);
CASE2STR(X_PolyFillRectangle);
CASE2STR(X_PolyFillArc);
CASE2STR(X_PutImage);
CASE2STR(X_GetImage);
CASE2STR(X_PolyText8);
CASE2STR(X_PolyText16);
CASE2STR(X_ImageText8);
CASE2STR(X_ImageText16);
CASE2STR(X_CreateColormap);
CASE2STR(X_FreeColormap);
CASE2STR(X_CopyColormapAndFree);
CASE2STR(X_InstallColormap);
CASE2STR(X_UninstallColormap);
CASE2STR(X_ListInstalledColormaps);
CASE2STR(X_AllocColor);
CASE2STR(X_AllocNamedColor);
CASE2STR(X_AllocColorCells);
CASE2STR(X_AllocColorPlanes);
CASE2STR(X_FreeColors);
CASE2STR(X_StoreColors);
CASE2STR(X_StoreNamedColor);
CASE2STR(X_QueryColors);
CASE2STR(X_LookupColor);
CASE2STR(X_CreateCursor);
CASE2STR(X_CreateGlyphCursor);
CASE2STR(X_FreeCursor);
CASE2STR(X_RecolorCursor);
CASE2STR(X_QueryBestSize);
CASE2STR(X_QueryExtension);
CASE2STR(X_ListExtensions);
CASE2STR(X_ChangeKeyboardMapping);
CASE2STR(X_GetKeyboardMapping);
CASE2STR(X_ChangeKeyboardControl);
CASE2STR(X_GetKeyboardControl);
CASE2STR(X_Bell);
CASE2STR(X_ChangePointerControl);
CASE2STR(X_GetPointerControl);
CASE2STR(X_SetScreenSaver);
CASE2STR(X_GetScreenSaver);
CASE2STR(X_ChangeHosts);
CASE2STR(X_ListHosts);
CASE2STR(X_SetAccessControl);
CASE2STR(X_SetCloseDownMode);
CASE2STR(X_KillClient);
CASE2STR(X_RotateProperties);
CASE2STR(X_ForceScreenSaver);
CASE2STR(X_SetPointerMapping);
CASE2STR(X_GetPointerMapping);
CASE2STR(X_SetModifierMapping);
CASE2STR(X_GetModifierMapping);
CASE2STR(X_NoOperation);
default:
return "UNKNOWN";
}
}

View File

@@ -0,0 +1,422 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Client
*
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2016 Thincast Technologies GmbH
* Copyright 2016 Armin Novak <armin.novak@thincast.com>
*
* 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.
*/
#ifndef FREERDP_CLIENT_X11_FREERDP_H
#define FREERDP_CLIENT_X11_FREERDP_H
#include <freerdp/config.h>
#include "xf_types.h"
#include "xf_disp.h"
#include "xf_cliprdr.h"
#include "xf_video.h"
#include "xf_rail.h"
#ifdef WITH_XCURSOR
#include <X11/Xcursor/Xcursor.h>
#endif
#ifdef WITH_XI
#include <X11/extensions/XInput2.h>
#endif
#include <freerdp/api.h>
#include "xf_window.h"
#include "xf_monitor.h"
#include "xf_channels.h"
#if defined(CHANNEL_TSMF_CLIENT)
#include <freerdp/client/tsmf.h>
#endif
#include <freerdp/gdi/gdi.h>
#include <freerdp/codec/rfx.h>
#include <freerdp/codec/nsc.h>
#include <freerdp/codec/clear.h>
#include <freerdp/codec/color.h>
#include <freerdp/codec/bitmap.h>
#include <freerdp/codec/h264.h>
#include <freerdp/codec/progressive.h>
#include <freerdp/codec/region.h>
#include <freerdp/locale/keyboard.h>
#include <freerdp/client.h>
#if !defined(XcursorUInt)
typedef unsigned int XcursorUInt;
#endif
#if !defined(XcursorPixel)
typedef XcursorUInt XcursorPixel;
#endif
struct xf_FullscreenMonitors
{
INT32 top;
INT32 bottom;
INT32 left;
INT32 right;
};
typedef struct xf_FullscreenMonitors xfFullscreenMonitors;
struct xf_WorkArea
{
INT32 x;
INT32 y;
UINT32 width;
UINT32 height;
};
typedef struct xf_WorkArea xfWorkArea;
struct xf_pointer
{
rdpPointer pointer;
XcursorPixel* cursorPixels;
UINT32 nCursors;
UINT32 mCursors;
UINT32* cursorWidths;
UINT32* cursorHeights;
Cursor* cursors;
Cursor cursor;
};
typedef struct xf_pointer xfPointer;
struct xf_bitmap
{
rdpBitmap bitmap;
Pixmap pixmap;
XImage* image;
};
typedef struct xf_bitmap xfBitmap;
struct xf_glyph
{
rdpGlyph glyph;
Pixmap pixmap;
};
typedef struct xf_glyph xfGlyph;
/* Number of buttons that are mapped from X11 to RDP button events. */
#define NUM_BUTTONS_MAPPED 11
typedef struct
{
UINT32 button;
UINT16 flags;
} button_map;
#if defined(WITH_XI)
#define MAX_CONTACTS 20
typedef struct touch_contact
{
int id;
int count;
double pos_x;
double pos_y;
double last_x;
double last_y;
} touchContact;
#endif
struct xf_context
{
rdpClientContext common;
GC gc;
int xfds;
int depth;
GC gc_mono;
BOOL invert;
Screen* screen;
XImage* image;
Pixmap primary;
Pixmap drawing;
Visual* visual;
Display* display;
Drawable drawable;
Pixmap bitmap_mono;
Colormap colormap;
int screen_number;
int scanline_pad;
BOOL big_endian;
BOOL fullscreen;
BOOL decorations;
BOOL grab_keyboard;
BOOL unobscured;
BOOL debug;
HANDLE x11event;
xfWindow* window;
xfAppWindow* appWindow;
xfPointer* pointer;
xfWorkArea workArea;
xfFullscreenMonitors fullscreenMonitors;
int current_desktop;
BOOL remote_app;
HANDLE mutex;
BOOL UseXThreads;
BOOL cursorHidden;
UINT32 bitmap_size;
BYTE* bitmap_buffer;
BOOL frame_begin;
int XInputOpcode;
int savedWidth;
int savedHeight;
int savedPosX;
int savedPosY;
#ifdef WITH_XRENDER
int scaledWidth;
int scaledHeight;
int offset_x;
int offset_y;
#endif
BOOL focused;
BOOL mouse_active;
BOOL fullscreen_toggle;
BOOL KeyboardState[256];
XModifierKeymap* modifierMap;
wArrayList* keyCombinations;
wArrayList* xevents;
BOOL actionScriptExists;
int attribs_mask;
XSetWindowAttributes attribs;
BOOL complex_regions;
VIRTUAL_SCREEN vscreen;
#if defined(CHANNEL_TSMF_CLIENT)
void* xv_context;
#endif
Atom* supportedAtoms;
unsigned long supportedAtomCount;
Atom UTF8_STRING;
Atom XWAYLAND_MAY_GRAB_KEYBOARD;
Atom NET_WM_ICON;
Atom MOTIF_WM_HINTS;
Atom NET_NUMBER_OF_DESKTOPS;
Atom NET_CURRENT_DESKTOP;
Atom NET_WORKAREA;
Atom NET_SUPPORTED;
Atom NET_SUPPORTING_WM_CHECK;
Atom NET_WM_STATE;
Atom NET_WM_STATE_MODAL;
Atom NET_WM_STATE_STICKY;
Atom NET_WM_STATE_MAXIMIZED_VERT;
Atom NET_WM_STATE_MAXIMIZED_HORZ;
Atom NET_WM_STATE_SHADED;
Atom NET_WM_STATE_SKIP_TASKBAR;
Atom NET_WM_STATE_SKIP_PAGER;
Atom NET_WM_STATE_HIDDEN;
Atom NET_WM_STATE_FULLSCREEN;
Atom NET_WM_STATE_ABOVE;
Atom NET_WM_STATE_BELOW;
Atom NET_WM_STATE_DEMANDS_ATTENTION;
Atom NET_WM_FULLSCREEN_MONITORS;
Atom NET_WM_NAME;
Atom NET_WM_PID;
Atom NET_WM_WINDOW_TYPE;
Atom NET_WM_WINDOW_TYPE_NORMAL;
Atom NET_WM_WINDOW_TYPE_DIALOG;
Atom NET_WM_WINDOW_TYPE_UTILITY;
Atom NET_WM_WINDOW_TYPE_POPUP;
Atom NET_WM_WINDOW_TYPE_POPUP_MENU;
Atom NET_WM_WINDOW_TYPE_DROPDOWN_MENU;
Atom NET_WM_MOVERESIZE;
Atom NET_MOVERESIZE_WINDOW;
Atom WM_STATE;
Atom WM_PROTOCOLS;
Atom WM_DELETE_WINDOW;
/* Allow actions */
Atom NET_WM_ALLOWED_ACTIONS;
Atom NET_WM_ACTION_CLOSE;
Atom NET_WM_ACTION_MINIMIZE;
Atom NET_WM_ACTION_MOVE;
Atom NET_WM_ACTION_RESIZE;
Atom NET_WM_ACTION_MAXIMIZE_HORZ;
Atom NET_WM_ACTION_MAXIMIZE_VERT;
Atom NET_WM_ACTION_FULLSCREEN;
Atom NET_WM_ACTION_CHANGE_DESKTOP;
/* Channels */
#if defined(CHANNEL_TSMF_CLIENT)
TsmfClientContext* tsmf;
#endif
xfClipboard* clipboard;
CliprdrClientContext* cliprdr;
xfVideoContext* xfVideo;
xfDispContext* xfDisp;
RailClientContext* rail;
wHashTable* railWindows;
xfRailIconCache* railIconCache;
#if defined(WITH_VERBOSE_WINPR_ASSERT)
BOOL isRailWindowsLocked;
#endif
BOOL xkbAvailable;
BOOL xrenderAvailable;
/* value to be sent over wire for each logical client mouse button */
button_map button_map[NUM_BUTTONS_MAPPED];
BYTE savedMaximizedState;
UINT32 locked;
BOOL wasRightCtrlAlreadyPressed;
BOOL ungrabKeyboardWithRightCtrl;
#if defined(WITH_XI)
touchContact contacts[MAX_CONTACTS];
int active_contacts;
int lastEvType;
XIDeviceEvent lastEvent;
double firstDist;
double lastDist;
double z_vector;
double px_vector;
double py_vector;
#endif
BOOL xi_rawevent;
BOOL xi_event;
HANDLE pipethread;
wLog* log;
FREERDP_REMAP_TABLE* remap_table;
DWORD X11_KEYCODE_TO_VIRTUAL_SCANCODE[256];
bool isCursorHidden;
bool isActionScriptAllowed;
};
BOOL xf_create_window(xfContext* xfc);
void xf_destroy_window(xfContext* xfc);
BOOL xf_create_image(xfContext* xfc);
void xf_toggle_fullscreen(xfContext* xfc);
void xf_minimize(xfContext* xfc);
enum XF_EXIT_CODE
{
/* section 0-15: protocol-independent codes */
XF_EXIT_SUCCESS = 0,
XF_EXIT_DISCONNECT = 1,
XF_EXIT_LOGOFF = 2,
XF_EXIT_IDLE_TIMEOUT = 3,
XF_EXIT_LOGON_TIMEOUT = 4,
XF_EXIT_CONN_REPLACED = 5,
XF_EXIT_OUT_OF_MEMORY = 6,
XF_EXIT_CONN_DENIED = 7,
XF_EXIT_CONN_DENIED_FIPS = 8,
XF_EXIT_USER_PRIVILEGES = 9,
XF_EXIT_FRESH_CREDENTIALS_REQUIRED = 10,
XF_EXIT_DISCONNECT_BY_USER = 11,
/* section 16-31: license error set */
XF_EXIT_LICENSE_INTERNAL = 16,
XF_EXIT_LICENSE_NO_LICENSE_SERVER = 17,
XF_EXIT_LICENSE_NO_LICENSE = 18,
XF_EXIT_LICENSE_BAD_CLIENT_MSG = 19,
XF_EXIT_LICENSE_HWID_DOESNT_MATCH = 20,
XF_EXIT_LICENSE_BAD_CLIENT = 21,
XF_EXIT_LICENSE_CANT_FINISH_PROTOCOL = 22,
XF_EXIT_LICENSE_CLIENT_ENDED_PROTOCOL = 23,
XF_EXIT_LICENSE_BAD_CLIENT_ENCRYPTION = 24,
XF_EXIT_LICENSE_CANT_UPGRADE = 25,
XF_EXIT_LICENSE_NO_REMOTE_CONNECTIONS = 26,
/* section 32-127: RDP protocol error set */
XF_EXIT_RDP = 32,
/* section 128-254: xfreerdp specific exit codes */
XF_EXIT_PARSE_ARGUMENTS = 128,
XF_EXIT_MEMORY = 129,
XF_EXIT_PROTOCOL = 130,
XF_EXIT_CONN_FAILED = 131,
XF_EXIT_AUTH_FAILURE = 132,
XF_EXIT_NEGO_FAILURE = 133,
XF_EXIT_LOGON_FAILURE = 134,
XF_EXIT_ACCOUNT_LOCKED_OUT = 135,
XF_EXIT_PRE_CONNECT_FAILED = 136,
XF_EXIT_CONNECT_UNDEFINED = 137,
XF_EXIT_POST_CONNECT_FAILED = 138,
XF_EXIT_DNS_ERROR = 139,
XF_EXIT_DNS_NAME_NOT_FOUND = 140,
XF_EXIT_CONNECT_FAILED = 141,
XF_EXIT_MCS_CONNECT_INITIAL_ERROR = 142,
XF_EXIT_TLS_CONNECT_FAILED = 143,
XF_EXIT_INSUFFICIENT_PRIVILEGES = 144,
XF_EXIT_CONNECT_CANCELLED = 145,
XF_EXIT_CONNECT_TRANSPORT_FAILED = 147,
XF_EXIT_CONNECT_PASSWORD_EXPIRED = 148,
XF_EXIT_CONNECT_PASSWORD_MUST_CHANGE = 149,
XF_EXIT_CONNECT_KDC_UNREACHABLE = 150,
XF_EXIT_CONNECT_ACCOUNT_DISABLED = 151,
XF_EXIT_CONNECT_PASSWORD_CERTAINLY_EXPIRED = 152,
XF_EXIT_CONNECT_CLIENT_REVOKED = 153,
XF_EXIT_CONNECT_WRONG_PASSWORD = 154,
XF_EXIT_CONNECT_ACCESS_DENIED = 155,
XF_EXIT_CONNECT_ACCOUNT_RESTRICTION = 156,
XF_EXIT_CONNECT_ACCOUNT_EXPIRED = 157,
XF_EXIT_CONNECT_LOGON_TYPE_NOT_GRANTED = 158,
XF_EXIT_CONNECT_NO_OR_MISSING_CREDENTIALS = 159,
XF_EXIT_CONNECT_TARGET_BOOTING = 160,
XF_EXIT_CODE_LAST = XF_EXIT_CONNECT_TARGET_BOOTING,
XF_EXIT_UNKNOWN = 255,
};
#define xf_lock_x11(xfc) xf_lock_x11_(xfc, __func__)
#define xf_unlock_x11(xfc) xf_unlock_x11_(xfc, __func__)
void xf_lock_x11_(xfContext* xfc, const char* fkt);
void xf_unlock_x11_(xfContext* xfc, const char* fkt);
BOOL xf_picture_transform_required(xfContext* xfc);
#define xf_draw_screen(_xfc, _x, _y, _w, _h) \
xf_draw_screen_((_xfc), (_x), (_y), (_w), (_h), __func__, __FILE__, __LINE__)
void xf_draw_screen_(xfContext* xfc, int x, int y, int w, int h, const char* fkt, const char* file,
int line);
BOOL xf_keyboard_update_modifier_map(xfContext* xfc);
int xf_exit_code_from_disconnect_reason(DWORD reason);
#endif /* FREERDP_CLIENT_X11_FREERDP_H */

View File

@@ -0,0 +1,870 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RDP Keyboard layout ID detection from common X11 xkb keyboard layout names
*
* Copyright 2009-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <freerdp/config.h>
#include "xkb_layout_ids.h"
#include <string.h>
#include <winpr/crt.h>
#include "xf_debug.h"
#include <freerdp/locale/keyboard.h>
typedef struct
{
const char* variant; /* XKB Keyboard layout variant */
INT64 keyboardLayoutID; /* Keyboard Layout ID */
} XKB_VARIANT;
typedef struct
{
const char* layout; /* XKB Keyboard layout */
INT64 keyboardLayoutID; /* Keyboard Layout ID */
const XKB_VARIANT* variants;
} XKB_LAYOUT;
/* Those have been generated automatically and are waiting to be filled by hand */
/* USA */
static const XKB_VARIANT us_variants[] = {
{ "chr", 0 }, /* Cherokee */
{ "euro", 0 }, /* With EuroSign on 5 */
{ "intl", KBD_UNITED_STATES_INTERNATIONAL }, /* International (with dead keys) */
{ "alt-intl",
KBD_UNITED_STATES_INTERNATIONAL }, /* Alternative international (former us_intl) */
{ "colemak", 0 }, /* Colemak */
{ "dvorak", KBD_UNITED_STATES_DVORAK }, /* Dvorak */
{ "dvorak-intl", KBD_UNITED_STATES_DVORAK }, /* Dvorak international */
{ "dvorak-l", KBD_UNITED_STATES_DVORAK_FOR_LEFT_HAND }, /* Left handed Dvorak */
{ "dvorak-r", KBD_UNITED_STATES_DVORAK_FOR_RIGHT_HAND }, /* Right handed Dvorak */
{ "dvorak-classic", KBD_UNITED_STATES_DVORAK }, /* Classic Dvorak */
{ "dvp", KBD_UNITED_STATES_DVORAK_PROGRAMMER }, /* Programmer Dvorak */
{ "rus", 0 }, /* Russian phonetic */
{ "mac", KBD_US }, /* Macintosh */
{ "altgr-intl", KBD_UNITED_STATES_INTERNATIONAL }, /* International (AltGr dead keys) */
{ "olpc2", KBD_US }, /* Group toggle on multiply/divide key */
{ "", 0 },
};
/* Afghanistan */
static const XKB_VARIANT af_variants[] = {
{ "ps", KBD_PASHTO }, /* Pashto */
{ "uz", KBD_UZBEK_CYRILLIC }, /* Southern Uzbek */
{ "olpc-ps", KBD_PASHTO }, /* OLPC Pashto */
{ "olpc-fa", 0 }, /* OLPC Dari */
{ "olpc-uz", KBD_UZBEK_CYRILLIC }, /* OLPC Southern Uzbek */
{ "", 0 },
};
/* Arabic */
static const XKB_VARIANT ara_variants[] = {
{ "azerty", KBD_ARABIC_102_AZERTY }, /* azerty */
{ "azerty_digits", KBD_ARABIC_102_AZERTY }, /* azerty/digits */
{ "digits", KBD_ARABIC_102_AZERTY }, /* digits */
{ "qwerty", KBD_ARABIC_101 }, /* qwerty */
{ "qwerty_digits", KBD_ARABIC_101 }, /* qwerty/digits */
{ "buckwalter", KBD_US_ENGLISH_TABLE_FOR_IBM_ARABIC_238_L }, /* Buckwalter */
{ "", 0 },
};
/* Armenia */
static const XKB_VARIANT am_variants[] = {
{ "phonetic", 0 }, /* Phonetic */
{ "phonetic-alt", 0 }, /* Alternative Phonetic */
{ "eastern", KBD_ARMENIAN_EASTERN }, /* Eastern */
{ "western", KBD_ARMENIAN_WESTERN }, /* Western */
{ "eastern-alt", KBD_ARMENIAN_EASTERN }, /* Alternative Eastern */
{ "", 0 },
};
/* Azerbaijan */
static const XKB_VARIANT az_variants[] = {
{ "cyrillic", KBD_AZERI_CYRILLIC }, /* Cyrillic */
{ "", 0 },
};
/* Belarus */
static const XKB_VARIANT by_variants[] = {
{ "winkeys", KBD_BELARUSIAN }, /* Winkeys */
{ "latin", KBD_BELARUSIAN }, /* Latin */
{ "", 0 },
};
/* Belgium */
static const XKB_VARIANT be_variants[] = {
{ "oss", KBD_BELGIAN_FRENCH }, /* Alternative */
{ "oss_latin9", KBD_BELGIAN_FRENCH }, /* Alternative, latin-9 only */
{ "oss_sundeadkeys", KBD_BELGIAN_PERIOD }, /* Alternative, Sun dead keys */
{ "iso-alternate", KBD_BELGIAN_COMMA }, /* ISO Alternate */
{ "nodeadkeys", KBD_BELGIAN_COMMA }, /* Eliminate dead keys */
{ "sundeadkeys", KBD_BELGIAN_PERIOD }, /* Sun dead keys */
{ "wang", KBD_BELGIAN_FRENCH }, /* Wang model 724 azerty */
{ "", 0 },
};
/* Bangladesh */
static const XKB_VARIANT bd_variants[] = {
{ "probhat", KBD_BENGALI_INSCRIPT }, /* Probhat */
{ "", 0 },
};
/* India */
static const XKB_VARIANT in_variants[] = {
{ "ben", KBD_BENGALI }, /* Bengali */
{ "ben_probhat", KBD_BENGALI_INSCRIPT }, /* Bengali Probhat */
{ "guj", KBD_GUJARATI }, /* Gujarati */
{ "guru", 0 }, /* Gurmukhi */
{ "jhelum", 0 }, /* Gurmukhi Jhelum */
{ "kan", KBD_KANNADA }, /* Kannada */
{ "mal", KBD_MALAYALAM }, /* Malayalam */
{ "mal_lalitha", KBD_MALAYALAM }, /* Malayalam Lalitha */
{ "ori", 0 }, /* Oriya */
{ "tam_unicode", KBD_TAMIL }, /* Tamil Unicode */
{ "tam_TAB", KBD_TAMIL }, /* Tamil TAB Typewriter */
{ "tam_TSCII", KBD_TAMIL }, /* Tamil TSCII Typewriter */
{ "tam", KBD_TAMIL }, /* Tamil */
{ "tel", KBD_TELUGU }, /* Telugu */
{ "urd-phonetic", KBD_URDU }, /* Urdu, Phonetic */
{ "urd-phonetic3", KBD_URDU }, /* Urdu, Alternative phonetic */
{ "urd-winkeys", KBD_URDU }, /* Urdu, Winkeys */
{ "bolnagri", KBD_HINDI_TRADITIONAL }, /* Hindi Bolnagri */
{ "hin-wx", KBD_HINDI_TRADITIONAL }, /* Hindi Wx */
{ "", 0 },
};
/* Bosnia and Herzegovina */
static const XKB_VARIANT ba_variants[] = {
{ "alternatequotes", KBD_BOSNIAN }, /* Use guillemets for quotes */
{ "unicode", KBD_BOSNIAN }, /* Use Bosnian digraphs */
{ "unicodeus", KBD_BOSNIAN }, /* US keyboard with Bosnian digraphs */
{ "us", KBD_BOSNIAN_CYRILLIC }, /* US keyboard with Bosnian letters */
{ "", 0 },
};
/* Brazil */
static const XKB_VARIANT br_variants[] = {
{ "nodeadkeys", KBD_PORTUGUESE_BRAZILIAN_ABNT2 }, /* Eliminate dead keys */
{ "dvorak", KBD_UNITED_STATES_DVORAK }, /* Dvorak */
{ "nativo", KBD_PORTUGUESE_BRAZILIAN_ABNT2 }, /* Nativo */
{ "nativo-us", KBD_PORTUGUESE_BRAZILIAN_ABNT2 }, /* Nativo for USA keyboards */
{ "nativo-epo", KBD_PORTUGUESE_BRAZILIAN_ABNT2 }, /* Nativo for Esperanto */
{ "", 0 },
};
/* Bulgaria */
static const XKB_VARIANT bg_variants[] = {
{ "phonetic", KBD_BULGARIAN_LATIN }, /* Traditional Phonetic */
{ "bas_phonetic", KBD_BULGARIAN_LATIN }, /* Standard Phonetic */
{ "", 0 },
};
/* Morocco */
static const XKB_VARIANT ma_variants[] = {
{ "french", KBD_FRENCH }, /* French */
{ "tifinagh", 0 }, /* Tifinagh */
{ "tifinagh-alt", 0 }, /* Tifinagh Alternative */
{ "tifinagh-alt-phonetic", 0 }, /* Tifinagh Alternative Phonetic */
{ "tifinagh-extended", 0 }, /* Tifinagh Extended */
{ "tifinagh-phonetic", 0 }, /* Tifinagh Phonetic */
{ "tifinagh-extended-phonetic", 0 }, /* Tifinagh Extended Phonetic */
{ "", 0 },
};
/* Canada */
static const XKB_VARIANT ca_variants[] = {
{ "fr", KBD_CANADIAN_FRENCH }, /* French Dvorak */
{ "fr-dvorak", KBD_UNITED_STATES_DVORAK }, /* French Dvorak */
{ "fr-legacy", KBD_CANADIAN_FRENCH_LEGACY }, /* French (legacy) */
{ "multix", KBD_CANADIAN_MULTILINGUAL_STANDARD }, /* Multilingual */
{ "multi", KBD_CANADIAN_MULTILINGUAL_STANDARD }, /* Multilingual, first part */
{ "multi-2gr", KBD_CANADIAN_MULTILINGUAL_STANDARD }, /* Multilingual, second part */
{ "ike", KBD_INUKTITUT_LATIN }, /* Inuktitut */
{ "shs" /* codespell:ignore shs */, 0 }, /* Secwepemctsin */
{ "kut", 0 }, /* Ktunaxa */
{ "", 0 },
};
/* China */
static const XKB_VARIANT cn_variants[] = {
{ "tib", 0 }, /* Tibetan */
{ "tib_asciinum", 0 }, /* Tibetan (with ASCII numerals) */
{ "", 0 },
};
/* Croatia */
static const XKB_VARIANT hr_variants[] = {
{ "alternatequotes", KBD_CROATIAN }, /* Use guillemets for quotes */
{ "unicode", KBD_CROATIAN }, /* Use Croatian digraphs */
{ "unicodeus", KBD_CROATIAN }, /* US keyboard with Croatian digraphs */
{ "us", KBD_CROATIAN }, /* US keyboard with Croatian letters */
{ "", 0 },
};
/* Czechia */
static const XKB_VARIANT cz_variants[] = {
{ "bksl", KBD_CZECH_PROGRAMMERS }, /* With &lt;\|&gt; key */
{ "qwerty", KBD_CZECH_QWERTY }, /* qwerty */
{ "qwerty_bksl", KBD_CZECH_QWERTY }, /* qwerty, extended Backslash */
{ "ucw", KBD_CZECH }, /* UCW layout (accented letters only) */
{ "", 0 },
};
/* Denmark */
static const XKB_VARIANT dk_variants[] = {
{ "nodeadkeys", KBD_DANISH }, /* Eliminate dead keys */
{ "mac", KBD_DANISH }, /* Macintosh */
{ "mac_nodeadkeys", KBD_DANISH }, /* Macintosh, eliminate dead keys */
{ "dvorak", KBD_UNITED_STATES_DVORAK }, /* Dvorak */
{ "", 0 },
};
/* Netherlands */
static const XKB_VARIANT nl_variants[] = {
{ "sundeadkeys", KBD_SWISS_FRENCH }, /* Sun dead keys */
{ "mac", KBD_SWISS_FRENCH }, /* Macintosh */
{ "std", KBD_SWISS_FRENCH }, /* Standard */
{ "", 0 },
};
/* Estonia */
static const XKB_VARIANT ee_variants[] = {
{ "nodeadkeys", KBD_US }, /* Eliminate dead keys */
{ "dvorak", KBD_UNITED_STATES_DVORAK }, /* Dvorak */
{ "us", KBD_UNITED_STATES_INTERNATIONAL }, /* US keyboard with Estonian letters */
{ "", 0 },
};
/* Iran */
static const XKB_VARIANT ir_variants[] = {
{ "pro", 0 }, /* Pro */
{ "keypad", 0 }, /* Keypad */
{ "pro_keypad", 0 }, /* Pro Keypad */
{ "ku", 0 }, /* Kurdish, Latin Q */
{ "ku_f", 0 }, /* Kurdish, (F) */
{ "ku_alt", 0 }, /* Kurdish, Latin Alt-Q */
{ "ku_ara", 0 }, /* Kurdish, Arabic-Latin */
{ "", 0 },
};
/* Iraq */
static const XKB_VARIANT iq_variants[] = {
{ "ku", 0 }, /* Kurdish, Latin Q */
{ "ku_f", 0 }, /* Kurdish, (F) */
{ "ku_alt", 0 }, /* Kurdish, Latin Alt-Q */
{ "ku_ara", 0 }, /* Kurdish, Arabic-Latin */
{ "", 0 },
};
/* Faroe Islands */
static const XKB_VARIANT fo_variants[] = {
{ "nodeadkeys", 0 }, /* Eliminate dead keys */
{ "", 0 },
};
/* Finland */
static const XKB_VARIANT fi_variants[] = {
{ "nodeadkeys", 0 }, /* Eliminate dead keys */
{ "smi", 0 }, /* Northern Saami */
{ "classic", 0 }, /* Classic */
{ "mac", 0 }, /* Macintosh */
{ "", 0 },
};
/* France */
static const XKB_VARIANT fr_variants[] = {
{ "nodeadkeys", 0 }, /* Eliminate dead keys */
{ "sundeadkeys", 0 }, /* Sun dead keys */
{ "oss", 0 }, /* Alternative */
{ "oss_latin9", 0 }, /* Alternative, latin-9 only */
{ "oss_nodeadkeys", 0 }, /* Alternative, eliminate dead keys */
{ "oss_sundeadkeys", 0 }, /* Alternative, Sun dead keys */
{ "latin9", 0 }, /* (Legacy) Alternative */
{ "latin9_nodeadkeys", 0 }, /* (Legacy) Alternative, eliminate dead keys */
{ "latin9_sundeadkeys", 0 }, /* (Legacy) Alternative, Sun dead keys */
{ "bepo", KBD_FRENCH_BEPO }, /* Bepo, ergonomic, Dvorak way */
{ "bepo_latin9", 0 }, /* Bepo, ergonomic, Dvorak way, latin-9 only */
{ "dvorak", 0 }, /* Dvorak */
{ "mac", 0 }, /* Macintosh */
{ "bre", 0 }, /* Breton */
{ "oci", 0 }, /* Occitan */
{ "geo", 0 }, /* Georgian AZERTY Tskapo */
{ "", 0 },
};
/* Ghana */
static const XKB_VARIANT gh_variants[] = {
{ "generic", 0 }, /* Multilingual */
{ "akan", 0 }, /* Akan */
{ "ewe", 0 }, /* Ewe */
{ "fula", 0 }, /* Fula */
{ "ga", 0 }, /* Ga */
{ "hausa", 0 }, /* Hausa */
{ "", 0 },
};
/* Georgia */
static const XKB_VARIANT ge_variants[] = {
{ "ergonomic", 0 }, /* Ergonomic */
{ "mess", 0 }, /* MESS */
{ "ru", 0 }, /* Russian */
{ "os", 0 }, /* Ossetian */
{ "", 0 },
};
/* Germany */
static const XKB_VARIANT de_variants[] = {
{ "deadacute", KBD_GERMAN }, /* Dead acute */
{ "deadgraveacute", KBD_GERMAN }, /* Dead grave acute */
{ "nodeadkeys", KBD_GERMAN }, /* Eliminate dead keys */
{ "ro", KBD_GERMAN }, /* Romanian keyboard with German letters */
{ "ro_nodeadkeys",
KBD_GERMAN }, /* Romanian keyboard with German letters, eliminate dead keys */
{ "dvorak", KBD_UNITED_STATES_DVORAK }, /* Dvorak */
{ "sundeadkeys", KBD_GERMAN }, /* Sun dead keys */
{ "neo", KBD_GERMAN_NEO }, /* Neo 2 */
{ "mac", KBD_GERMAN }, /* Macintosh */
{ "mac_nodeadkeys", KBD_GERMAN }, /* Macintosh, eliminate dead keys */
{ "dsb", KBD_GERMAN }, /* Lower Sorbian */
{ "dsb_qwertz", KBD_GERMAN }, /* Lower Sorbian (qwertz) */
{ "qwerty", KBD_GERMAN_IBM }, /* qwerty */
{ "", 0 },
};
/* Greece */
static const XKB_VARIANT gr_variants[] = {
{ "simple", KBD_GREEK_220 }, /* Simple */
{ "extended", KBD_GREEK_319 }, /* Extended */
{ "nodeadkeys", KBD_GREEK_319 }, /* Eliminate dead keys */
{ "polytonic", KBD_GREEK_POLYTONIC }, /* Polytonic */
{ "", 0 },
};
/* Hungary */
static const XKB_VARIANT hu_variants[] = {
{ "standard", KBD_HUNGARIAN_101_KEY }, /* Standard */
{ "nodeadkeys", KBD_HUNGARIAN_101_KEY }, /* Eliminate dead keys */
{ "qwerty", KBD_HUNGARIAN_101_KEY }, /* qwerty */
{ "101_qwertz_comma_dead", KBD_HUNGARIAN_101_KEY }, /* 101/qwertz/comma/Dead keys */
{ "101_qwertz_comma_nodead", KBD_HUNGARIAN_101_KEY }, /* 101/qwertz/comma/Eliminate dead keys */
{ "101_qwertz_dot_dead", KBD_HUNGARIAN_101_KEY }, /* 101/qwertz/dot/Dead keys */
{ "101_qwertz_dot_nodead", KBD_HUNGARIAN_101_KEY }, /* 101/qwertz/dot/Eliminate dead keys */
{ "101_qwerty_comma_dead", KBD_HUNGARIAN_101_KEY }, /* 101/qwerty/comma/Dead keys */
{ "101_qwerty_comma_nodead", KBD_HUNGARIAN_101_KEY }, /* 101/qwerty/comma/Eliminate dead keys */
{ "101_qwerty_dot_dead", KBD_HUNGARIAN_101_KEY }, /* 101/qwerty/dot/Dead keys */
{ "101_qwerty_dot_nodead", KBD_HUNGARIAN_101_KEY }, /* 101/qwerty/dot/Eliminate dead keys */
{ "102_qwertz_comma_dead", KBD_HUNGARIAN_101_KEY }, /* 102/qwertz/comma/Dead keys */
{ "102_qwertz_comma_nodead", KBD_HUNGARIAN_101_KEY }, /* 102/qwertz/comma/Eliminate dead keys */
{ "102_qwertz_dot_dead", KBD_HUNGARIAN_101_KEY }, /* 102/qwertz/dot/Dead keys */
{ "102_qwertz_dot_nodead", KBD_HUNGARIAN_101_KEY }, /* 102/qwertz/dot/Eliminate dead keys */
{ "102_qwerty_comma_dead", KBD_HUNGARIAN_101_KEY }, /* 102/qwerty/comma/Dead keys */
{ "102_qwerty_comma_nodead", KBD_HUNGARIAN_101_KEY }, /* 102/qwerty/comma/Eliminate dead keys */
{ "102_qwerty_dot_dead", KBD_HUNGARIAN_101_KEY }, /* 102/qwerty/dot/Dead keys */
{ "102_qwerty_dot_nodead", KBD_HUNGARIAN_101_KEY }, /* 102/qwerty/dot/Eliminate dead keys */
{ "", 0 },
};
/* Iceland */
static const XKB_VARIANT is_variants[] = {
{ "Sundeadkeys", KBD_ICELANDIC }, /* Sun dead keys */
{ "nodeadkeys", KBD_ICELANDIC }, /* Eliminate dead keys */
{ "mac", KBD_ICELANDIC }, /* Macintosh */
{ "dvorak", KBD_UNITED_STATES_DVORAK }, /* Dvorak */
{ "", 0 },
};
/* Israel */
static const XKB_VARIANT il_variants[] = {
{ "lyx", KBD_HEBREW }, /* lyx */
{ "phonetic", KBD_HEBREW }, /* Phonetic */
{ "biblical", KBD_HEBREW }, /* Biblical Hebrew (Tiro) */
{ "", 0 },
};
/* Italy */
static const XKB_VARIANT it_variants[] = {
{ "nodeadkeys", KBD_ITALIAN_142 }, /* Eliminate dead keys */
{ "mac", KBD_ITALIAN }, /* Macintosh */
{ "geo", KBD_GEORGIAN }, /* Georgian */
{ "", 0 },
};
/* Japan */
static const XKB_VARIANT jp_variants[] = {
{ "kana", KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002 }, /* Kana */
{ "OADG109A", KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002 }, /* OADG 109A */
{ "", 0 },
};
/* Kyrgyzstan */
static const XKB_VARIANT kg_variants[] = {
{ "phonetic", KBD_KYRGYZ_CYRILLIC }, /* Phonetic */
{ "", 0 },
};
/* Kazakhstan */
static const XKB_VARIANT kz_variants[] = {
{ "ruskaz", KBD_KAZAKH }, /* Russian with Kazakh */
{ "kazrus", KBD_KAZAKH }, /* Kazakh with Russian */
{ "", 0 },
};
/* Latin America */
static const XKB_VARIANT latam_variants[] = {
{ "nodeadkeys", KBD_LATIN_AMERICAN }, /* Eliminate dead keys */
{ "deadtilde", KBD_LATIN_AMERICAN }, /* Include dead tilde */
{ "sundeadkeys", KBD_LATIN_AMERICAN }, /* Sun dead keys */
{ "", 0 },
};
/* Lithuania */
static const XKB_VARIANT lt_variants[] = {
{ "std", KBD_LITHUANIAN }, /* Standard */
{ "us", KBD_LITHUANIAN_IBM }, /* US keyboard with Lithuanian letters */
{ "ibm", KBD_LITHUANIAN_IBM }, /* IBM (LST 1205-92) */
{ "lekp", KBD_LITHUANIAN }, /* LEKP */
{ "lekpa", KBD_LITHUANIAN }, /* LEKPa */
{ "balticplus", KBD_LITHUANIAN }, /* Baltic+ */
{ "", 0 },
};
/* Latvia */
static const XKB_VARIANT lv_variants[] = {
{ "apostrophe", KBD_LATVIAN }, /* Apostrophe (') variant */
{ "tilde", KBD_LATVIAN }, /* Tilde (~) variant */
{ "fkey", KBD_LATVIAN }, /* F-letter (F) variant */
{ "", 0 },
};
/* Montenegro */
static const XKB_VARIANT me_variants[] = {
{ "cyrillic", 0 }, /* Cyrillic */
{ "cyrillicyz", 0 }, /* Cyrillic, Z and ZHE swapped */
{ "latinunicode", 0 }, /* Latin unicode */
{ "latinyz", 0 }, /* Latin qwerty */
{ "latinunicodeyz", 0 }, /* Latin unicode qwerty */
{ "cyrillicalternatequotes", 0 }, /* Cyrillic with guillemets */
{ "latinalternatequotes", 0 }, /* Latin with guillemets */
{ "", 0 },
};
/* Macedonia */
static const XKB_VARIANT mk_variants[] = {
{ "nodeadkeys", KBD_FYRO_MACEDONIAN }, /* Eliminate dead keys */
{ "", 0 },
};
/* Malta */
static const XKB_VARIANT mt_variants[] = {
{ "us", KBD_MALTESE_48_KEY }, /* Maltese keyboard with US layout */
{ "", 0 },
};
/* Norway */
static const XKB_VARIANT no_variants[] = {
{ "nodeadkeys", KBD_NORWEGIAN }, /* Eliminate dead keys */
{ "dvorak", KBD_UNITED_STATES_DVORAK }, /* Dvorak */
{ "smi", KBD_NORWEGIAN_WITH_SAMI }, /* Northern Saami */
{ "smi_nodeadkeys", KBD_SAMI_EXTENDED_NORWAY }, /* Northern Saami, eliminate dead keys */
{ "mac", KBD_NORWEGIAN }, /* Macintosh */
{ "mac_nodeadkeys", KBD_SAMI_EXTENDED_NORWAY }, /* Macintosh, eliminate dead keys */
{ "", 0 },
};
/* Poland */
static const XKB_VARIANT pl_variants[] = {
{ "qwertz", KBD_POLISH_214 }, /* qwertz */
{ "dvorak", KBD_UNITED_STATES_DVORAK }, /* Dvorak */
{ "dvorak_quotes", KBD_UNITED_STATES_DVORAK }, /* Dvorak, Polish quotes on quotemark key */
{ "dvorak_altquotes", KBD_UNITED_STATES_DVORAK }, /* Dvorak, Polish quotes on key 1 */
{ "csb", 0 }, /* Kashubian */
{ "ru_phonetic_dvorak", KBD_UNITED_STATES_DVORAK }, /* Russian phonetic Dvorak */
{ "", 0 },
};
/* Portugal */
static const XKB_VARIANT pt_variants[] = {
{ "nodeadkeys", KBD_PORTUGUESE }, /* Eliminate dead keys */
{ "sundeadkeys", KBD_PORTUGUESE }, /* Sun dead keys */
{ "mac", KBD_PORTUGUESE }, /* Macintosh */
{ "mac_nodeadkeys", KBD_PORTUGUESE }, /* Macintosh, eliminate dead keys */
{ "mac_sundeadkeys", KBD_PORTUGUESE }, /* Macintosh, Sun dead keys */
{ "nativo", KBD_PORTUGUESE }, /* Nativo */
{ "nativo-us", KBD_PORTUGUESE }, /* Nativo for USA keyboards */
{ "nativo-epo", KBD_PORTUGUESE }, /* Nativo for Esperanto */
{ "", 0 },
};
/* Romania */
static const XKB_VARIANT ro_variants[] = {
{ "cedilla", KBD_ROMANIAN }, /* Cedilla */
{ "std", KBD_ROMANIAN }, /* Standard */
{ "std_cedilla", KBD_ROMANIAN }, /* Standard (Cedilla) */
{ "winkeys", KBD_ROMANIAN }, /* Winkeys */
{ "crh_f", KBD_TURKISH_F }, /* Crimean Tatar (Turkish F) */
{ "crh_alt", KBD_TURKISH_Q }, /* Crimean Tatar (Turkish Alt-Q) */
{ "crh_dobruca1", KBD_TATAR }, /* Crimean Tatar (Dobruca-1 Q) */
{ "crh_dobruca2", KBD_TATAR }, /* Crimean Tatar (Dobruca-2 Q) */
{ "", 0 },
};
/* Russia */
static const XKB_VARIANT ru_variants[] = {
{ "phonetic", KBD_RUSSIAN }, /* Phonetic */
{ "phonetic_winkeys", KBD_RUSSIAN }, /* Phonetic Winkeys */
{ "typewriter", KBD_RUSSIAN_TYPEWRITER }, /* Typewriter */
{ "legacy", KBD_RUSSIAN }, /* Legacy */
{ "tt", KBD_TATAR }, /* Tatar */
{ "os_legacy", 0 }, /* Ossetian, legacy */
{ "os_winkeys", 0 }, /* Ossetian, Winkeys */
{ "cv", 0 }, /* Chuvash */
{ "cv_latin", 0 }, /* Chuvash Latin */
{ "udm", 0 }, /* Udmurt */
{ "kom", 0 }, /* Komi */
{ "sah", 0 }, /* Yakut */
{ "xal", 0 }, /* Kalmyk */
{ "dos", 0 }, /* DOS */
{ "", 0 },
};
/* Serbia */
static const XKB_VARIANT rs_variants[] = {
{ "yz", KBD_SERBIAN_CYRILLIC }, /* Z and ZHE swapped */
{ "latin", KBD_SERBIAN_LATIN }, /* Latin */
{ "latinunicode", KBD_SERBIAN_LATIN }, /* Latin Unicode */
{ "latinyz", KBD_SERBIAN_LATIN }, /* Latin qwerty */
{ "latinunicodeyz", KBD_SERBIAN_LATIN }, /* Latin Unicode qwerty */
{ "alternatequotes", KBD_SERBIAN_CYRILLIC }, /* With guillemets */
{ "latinalternatequotes", KBD_SERBIAN_LATIN }, /* Latin with guillemets */
{ "", 0 },
};
/* Slovenia */
static const XKB_VARIANT si_variants[] = {
{ "alternatequotes", KBD_SLOVENIAN }, /* Use guillemets for quotes */
{ "us", KBD_UNITED_STATES_INTERNATIONAL }, /* US keyboard with Slovenian letters */
{ "", 0 },
};
/* Slovakia */
static const XKB_VARIANT sk_variants[] = {
{ "bksl", KBD_SLOVAK }, /* Extended Backslash */
{ "qwerty", KBD_SLOVAK_QWERTY }, /* qwerty */
{ "qwerty_bksl", KBD_SLOVAK_QWERTY }, /* qwerty, extended Backslash */
{ "", 0 },
};
/* Spain */
static const XKB_VARIANT es_variants[] = {
{ "nodeadkeys", KBD_SPANISH_VARIATION }, /* Eliminate dead keys */
{ "deadtilde", KBD_SPANISH_VARIATION }, /* Include dead tilde */
{ "sundeadkeys", KBD_SPANISH }, /* Sun dead keys */
{ "dvorak", KBD_UNITED_STATES_DVORAK }, /* Dvorak */
{ "ast", KBD_SPANISH_VARIATION }, /* Asturian variant with bottom-dot H and bottom-dot L */
{ "cat", KBD_SPANISH_VARIATION }, /* Catalan variant with middle-dot L */
{ "mac", KBD_SPANISH }, /* Macintosh */
{ "", 0 },
};
/* Sweden */
static const XKB_VARIANT se_variants[] = {
{ "nodeadkeys", KBD_SWEDISH }, /* Eliminate dead keys */
{ "dvorak", KBD_UNITED_STATES_DVORAK }, /* Dvorak */
{ "rus", KBD_RUSSIAN }, /* Russian phonetic */
{ "rus_nodeadkeys", KBD_RUSSIAN }, /* Russian phonetic, eliminate dead keys */
{ "smi", KBD_SWEDISH_WITH_SAMI }, /* Northern Saami */
{ "mac", KBD_SWEDISH }, /* Macintosh */
{ "svdvorak", KBD_UNITED_STATES_DVORAK }, /* Svdvorak */
{ "", 0 },
};
/* Switzerland */
static const XKB_VARIANT ch_variants[] = {
{ "de_nodeadkeys", KBD_SWISS_GERMAN }, /* German, eliminate dead keys */
{ "de_sundeadkeys", KBD_SWISS_GERMAN }, /* German, Sun dead keys */
{ "fr", KBD_SWISS_FRENCH }, /* French */
{ "fr_nodeadkeys", KBD_SWISS_FRENCH }, /* French, eliminate dead keys */
{ "fr_sundeadkeys", KBD_SWISS_FRENCH }, /* French, Sun dead keys */
{ "fr_mac", KBD_SWISS_FRENCH }, /* French (Macintosh) */
{ "de_mac", KBD_SWISS_GERMAN }, /* German (Macintosh) */
{ "", 0 },
};
/* Syria */
static const XKB_VARIANT sy_variants[] = {
{ "syc", KBD_SYRIAC }, /* Syriac */
{ "syc_phonetic", KBD_SYRIAC_PHONETIC }, /* Syriac phonetic */
{ "ku", 0 }, /* Kurdish, Latin Q */
{ "ku_f", 0 }, /* Kurdish, (F) */
{ "ku_alt", 0 }, /* Kurdish, Latin Alt-Q */
{ "", 0 },
};
/* Tajikistan */
static const XKB_VARIANT tj_variants[] = {
{ "legacy", 0 }, /* Legacy */
{ "", 0 },
};
/* Sri Lanka */
static const XKB_VARIANT lk_variants[] = {
{ "tam_unicode", KBD_TAMIL }, /* Tamil Unicode */
{ "tam_TAB", KBD_TAMIL }, /* Tamil TAB Typewriter */
{ "", 0 },
};
/* Thailand */
static const XKB_VARIANT th_variants[] = {
{ "tis", KBD_THAI_KEDMANEE_NON_SHIFTLOCK }, /* TIS-820.2538 */
{ "pat", KBD_THAI_PATTACHOTE }, /* Pattachote */
{ "", 0 },
};
/* Turkey */
static const XKB_VARIANT tr_variants[] = {
{ "f", KBD_TURKISH_F }, /* (F) */
{ "alt", KBD_TURKISH_Q }, /* Alt-Q */
{ "sundeadkeys", KBD_TURKISH_F }, /* Sun dead keys */
{ "ku", 0 }, /* Kurdish, Latin Q */
{ "ku_f", 0 }, /* Kurdish, (F) */
{ "ku_alt", 0 }, /* Kurdish, Latin Alt-Q */
{ "intl", KBD_TURKISH_F }, /* International (with dead keys) */
{ "crh", KBD_TATAR }, /* Crimean Tatar (Turkish Q) */
{ "crh_f", KBD_TURKISH_F }, /* Crimean Tatar (Turkish F) */
{ "crh_alt", KBD_TURKISH_Q }, /* Crimean Tatar (Turkish Alt-Q) */
{ "", 0 },
};
/* Ukraine */
static const XKB_VARIANT ua_variants[] = {
{ "phonetic", KBD_UKRAINIAN }, /* Phonetic */
{ "typewriter", KBD_UKRAINIAN }, /* Typewriter */
{ "winkeys", KBD_UKRAINIAN }, /* Winkeys */
{ "legacy", KBD_UKRAINIAN }, /* Legacy */
{ "rstu", KBD_UKRAINIAN }, /* Standard RSTU */
{ "rstu_ru", KBD_UKRAINIAN }, /* Standard RSTU on Russian layout */
{ "homophonic", KBD_UKRAINIAN }, /* Homophonic */
{ "crh", KBD_TATAR }, /* Crimean Tatar (Turkish Q) */
{ "crh_f", KBD_TURKISH_F }, /* Crimean Tatar (Turkish F) */
{ "crh_alt", KBD_TURKISH_Q }, /* Crimean Tatar (Turkish Alt-Q) */
{ "", 0 },
};
/* United Kingdom */
static const XKB_VARIANT gb_variants[] = {
{ "extd", KBD_UNITED_KINGDOM_EXTENDED }, /* Extended - Winkeys */
{ "intl", KBD_UNITED_KINGDOM_EXTENDED }, /* International (with dead keys) */
{ "dvorak", KBD_UNITED_STATES_DVORAK }, /* Dvorak */
{ "dvorakukp", KBD_UNITED_STATES_DVORAK }, /* Dvorak (UK Punctuation) */
{ "mac", KBD_UNITED_KINGDOM }, /* Macintosh */
{ "colemak", 0 }, /* Colemak */
{ "", 0 },
};
/* Uzbekistan */
static const XKB_VARIANT uz_variants[] = {
{ "latin", 0 }, /* Latin */
{ "crh", KBD_TATAR }, /* Crimean Tatar (Turkish Q) */
{ "crh_f", KBD_TURKISH_F }, /* Crimean Tatar (Turkish F) */
{ "crh_alt", KBD_TURKISH_Q }, /* Crimean Tatar (Turkish Alt-Q) */
{ "", 0 },
};
/* Korea, Republic of */
static const XKB_VARIANT kr_variants[] = {
{ "kr104", KBD_KOREAN_INPUT_SYSTEM_IME_2000 }, /* 101/104 key Compatible */
{ "", 0 },
};
/* Ireland */
static const XKB_VARIANT ie_variants[] = {
{ "CloGaelach", KBD_GAELIC }, /* CloGaelach */
{ "UnicodeExpert", KBD_GAELIC }, /* UnicodeExpert */
{ "ogam", KBD_GAELIC }, /* Ogham */
{ "ogam_is434", KBD_GAELIC }, /* Ogham IS434 */
{ "", 0 },
};
/* Pakistan */
static const XKB_VARIANT pk_variants[] = {
{ "urd-crulp", 0 }, /* CRULP */
{ "urd-nla", 0 }, /* NLA */
{ "ara", KBD_ARABIC_101 }, /* Arabic */
{ "", 0 },
};
/* Esperanto */
static const XKB_VARIANT epo_variants[] = {
{ "legacy", 0 }, /* displaced semicolon and quote (obsolete) */
{ "", 0 },
};
/* Nigeria */
static const XKB_VARIANT ng_variants[] = {
{ "igbo", 0 }, /* Igbo */
{ "yoruba", 0 }, /* Yoruba */
{ "hausa", 0 }, /* Hausa */
{ "", 0 },
};
/* Braille */
static const XKB_VARIANT brai_variants[] = {
{ "left_hand", 0 }, /* Left hand */
{ "right_hand", 0 }, /* Right hand */
{ "", 0 },
};
/* Turkmenistan */
static const XKB_VARIANT tm_variants[] = {
{ "alt", KBD_TURKISH_Q }, /* Alt-Q */
{ "", 0 },
};
static const XKB_LAYOUT xkbLayouts[] = {
{ "us", KBD_US, us_variants }, /* USA */
{ "ad", 0, nullptr }, /* Andorra */
{ "af", KBD_FARSI, af_variants }, /* Afghanistan */
{ "ara", KBD_ARABIC_101, ara_variants }, /* Arabic */
{ "al", 0, nullptr }, /* Albania */
{ "am", KBD_ARMENIAN_EASTERN, am_variants }, /* Armenia */
{ "az", KBD_AZERI_CYRILLIC, az_variants }, /* Azerbaijan */
{ "by", KBD_BELARUSIAN, by_variants }, /* Belarus */
{ "be", KBD_BELGIAN_FRENCH, be_variants }, /* Belgium */
{ "bd", KBD_BENGALI, bd_variants }, /* Bangladesh */
{ "in", KBD_HINDI_TRADITIONAL, in_variants }, /* India */
{ "ba", KBD_CROATIAN, ba_variants }, /* Bosnia and Herzegovina */
{ "br", KBD_PORTUGUESE_BRAZILIAN_ABNT, br_variants }, /* Brazil */
{ "bg", KBD_BULGARIAN_LATIN, bg_variants }, /* Bulgaria */
{ "ma", KBD_FRENCH, ma_variants }, /* Morocco */
{ "mm", 0, nullptr }, /* Myanmar */
{ "ca", KBD_US, ca_variants }, /* Canada */
{ "cd", 0, nullptr }, /* Congo, Democratic Republic of the */
{ "cn", KBD_CHINESE_TRADITIONAL_PHONETIC, cn_variants }, /* China */
{ "hr", KBD_CROATIAN, hr_variants }, /* Croatia */
{ "cz", KBD_CZECH, cz_variants }, /* Czechia */
{ "dk", KBD_DANISH, dk_variants }, /* Denmark */
{ "nl", KBD_DUTCH, nl_variants }, /* Netherlands */
{ "bt", 0, nullptr }, /* Bhutan */
{ "ee", KBD_ESTONIAN, ee_variants }, /* Estonia */
{ "ir", 0, ir_variants }, /* Iran */
{ "iq", 0, iq_variants }, /* Iraq */
{ "fo", 0, fo_variants }, /* Faroe Islands */
{ "fi", KBD_FINNISH, fi_variants }, /* Finland */
{ "fr", KBD_FRENCH, fr_variants }, /* France */
{ "gh", 0, gh_variants }, /* Ghana */
{ "gn", 0, nullptr }, /* Guinea */
{ "ge", KBD_GEORGIAN, ge_variants }, /* Georgia */
{ "at", KBD_GERMAN, de_variants }, /* Austria */
{ "de", KBD_GERMAN, de_variants }, /* Germany */
{ "gr", KBD_GREEK, gr_variants }, /* Greece */
{ "hu", KBD_HUNGARIAN, hu_variants }, /* Hungary */
{ "is", KBD_ICELANDIC, is_variants }, /* Iceland */
{ "il", KBD_HEBREW, il_variants }, /* Israel */
{ "it", KBD_ITALIAN, it_variants }, /* Italy */
{ "jp", KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002, jp_variants }, /* Japan */
{ "kg", 0, kg_variants }, /* Kyrgyzstan */
{ "kh", 0, nullptr }, /* Cambodia */
{ "kz", KBD_KAZAKH, kz_variants }, /* Kazakhstan */
{ "la", 0, nullptr }, /* Laos */
{ "latam", KBD_LATIN_AMERICAN, latam_variants }, /* Latin America */
{ "lt", KBD_LITHUANIAN, lt_variants }, /* Lithuania */
{ "lv", KBD_LATVIAN, lv_variants }, /* Latvia */
{ "mao", KBD_MAORI, nullptr }, /* Maori */
{ "me", KBD_SERBIAN_LATIN, me_variants }, /* Montenegro */
{ "mk", KBD_FYRO_MACEDONIAN, mk_variants }, /* Macedonia */
{ "mt", KBD_MALTESE_48_KEY, mt_variants }, /* Malta */
{ "mn", KBD_MONGOLIAN_CYRILLIC, nullptr }, /* Mongolia */
{ "no", KBD_NORWEGIAN, no_variants }, /* Norway */
{ "pl", KBD_POLISH_PROGRAMMERS, pl_variants }, /* Poland */
{ "pt", KBD_PORTUGUESE, pt_variants }, /* Portugal */
{ "ro", KBD_ROMANIAN, ro_variants }, /* Romania */
{ "ru", KBD_RUSSIAN, ru_variants }, /* Russia */
{ "rs", KBD_SERBIAN_LATIN, rs_variants }, /* Serbia */
{ "si", KBD_SLOVENIAN, si_variants }, /* Slovenia */
{ "sk", KBD_SLOVAK, sk_variants }, /* Slovakia */
{ "es", KBD_SPANISH, es_variants }, /* Spain */
{ "se", KBD_SWEDISH, se_variants }, /* Sweden */
{ "ch", KBD_SWISS_GERMAN, ch_variants }, /* Switzerland */
{ "sy", KBD_SYRIAC, sy_variants }, /* Syria */
{ "tj", 0, tj_variants }, /* Tajikistan */
{ "lk", 0, lk_variants }, /* Sri Lanka */
{ "th", KBD_THAI_KEDMANEE, th_variants }, /* Thailand */
{ "tr", KBD_TURKISH_Q, tr_variants }, /* Turkey */
{ "ua", KBD_UKRAINIAN, ua_variants }, /* Ukraine */
{ "gb", KBD_UNITED_KINGDOM, gb_variants }, /* United Kingdom */
{ "uz", KBD_UZBEK_CYRILLIC, uz_variants }, /* Uzbekistan */
{ "vn", KBD_VIETNAMESE, nullptr }, /* Vietnam */
{ "kr", KBD_KOREAN_INPUT_SYSTEM_IME_2000, kr_variants }, /* Korea, Republic of */
{ "ie", KBD_UNITED_KINGDOM, ie_variants }, /* Ireland */
{ "pk", 0, pk_variants }, /* Pakistan */
{ "mv", 0, nullptr }, /* Maldives */
{ "za", KBD_US, nullptr }, /* South Africa */
{ "epo", 0, epo_variants }, /* Esperanto */
{ "np", KBD_NEPALI, nullptr }, /* Nepal */
{ "ng", 0, ng_variants }, /* Nigeria */
{ "et", 0, nullptr }, /* Ethiopia */
{ "sn", 0, nullptr }, /* Senegal */
{ "brai", 0, brai_variants }, /* Braille */
{ "tm", KBD_TURKISH_Q, tm_variants }, /* Turkmenistan */
};
static uint32_t convert(int64_t val)
{
WINPR_ASSERT(val <= UINT32_MAX);
WINPR_ASSERT(val >= INT32_MIN);
return WINPR_CXX_COMPAT_CAST(uint32_t, val);
}
static UINT32 find_keyboard_layout_variant(const XKB_LAYOUT* layout, const char* variant)
{
WINPR_ASSERT(layout);
WINPR_ASSERT(variant);
const XKB_VARIANT* variants = layout->variants;
if (variants)
{
const XKB_VARIANT* var = variants;
while (var->variant && (strlen(var->variant) != 0))
{
if (strcmp(var->variant, variant) == 0)
return convert(var->keyboardLayoutID);
var++;
}
}
return convert(layout->keyboardLayoutID);
}
UINT32 xf_find_keyboard_layout_in_xorg_rules(const char* layout, const char* variant)
{
if ((layout == nullptr) || (variant == nullptr))
return 0;
DEBUG_X11("xkbLayout: %s\txkbVariant: %s", layout, variant);
for (size_t i = 0; i < ARRAYSIZE(xkbLayouts); i++)
{
const XKB_LAYOUT* cur = &xkbLayouts[i];
if (strcmp(cur->layout, layout) == 0)
return find_keyboard_layout_variant(cur, variant);
}
return 0;
}

View File

@@ -0,0 +1,28 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RDP Keyboard layout ID detection from common X11 xkb keyboard layout names
*
* Copyright 2009-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#ifndef FREERDP_LIB_LOCALE_XKB_LAYOUT_IDS_H
#define FREERDP_LIB_LOCALE_XKB_LAYOUT_IDS_H
#include <freerdp/types.h>
#include <freerdp/api.h>
FREERDP_LOCAL UINT32 xf_find_keyboard_layout_in_xorg_rules(const char* layout, const char* variant);
#endif /* FREERDP_LIB_LOCALE_XKB_LAYOUT_IDS_H */