[client,X11] fix ActionScript

* Unify script calls
* Update documentation
* Fix home directory
This commit is contained in:
Armin Novak 2024-07-24 15:38:43 +02:00 committed by akallabeth
parent 8b8a19868a
commit 44b07721ec
No known key found for this signature in database
GPG Key ID: A49454A3FC909FD5
10 changed files with 267 additions and 110 deletions

View File

@ -77,13 +77,6 @@ if (NOT SDL2_FOUND AND NOT SDL3_FOUND)
message(FATAL_ERROR "No SDL library detected, giving up. Install SDL2 or SDL3 development package to fix")
endif()
# Configuration settings for manpages
if (NOT WITH_FULL_CONFIG_PATH AND "${VENDOR}" STREQUAL "${PRODUCT}")
string(TOLOWER "${VENDOR}" VENDOR_PRODUCT)
else()
set(VENDOR_PRODUCT "${VENDOR}/${PRODUCT}")
endif()
add_subdirectory(common)
include_directories(common)

View File

@ -5,4 +5,4 @@ set(DEPS
xfreerdp-envvar.1.xml
)
generate_and_install_freerdp_man_from_xml(${MODULE_NAME} "1" ${DEPS})
generate_and_install_freerdp_man_from_xml(${MODULE_NAME} "1" "${DEPS}")

View File

@ -2,6 +2,52 @@
<title>Examples</title>
<variablelist>
<varlistentry>
<programlisting><![CDATA[
#!/bin/bash
# we got a key combination
if [ "$1" = "key" ];
then
# we only got one argument 'key'
# 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 'key-local' 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
]]></programlisting>
<listitem>
<para>Example action script for key events, listing <replaceable>ctrl+alt+f1</replaceable> to be handled by local window manager and <replaceable>ctrl+alt+f2</replaceable> executing a script</para>
<para>The return value of the program determines if the key is handled locally or remotely (0 for local, &gt; 0 for remote, &lt; 0 for errors)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>xfreerdp connection.rdp /p:Pwd123! /f</command></term>
<listitem>
<para>Connect in fullscreen mode using a stored configuration <replaceable>connection.rdp</replaceable> and the password <replaceable>Pwd123!</replaceable></para>

View File

@ -17,9 +17,9 @@
<term>Action Script</term>
<listitem><para>executes a predefined script on key press.</para></listitem>
<listitem><para>Should the script not exist it is ignored.</para></listitem>
<listitem><para>Scripts can be provided at the default location ~/.config/freerdp/action.sh or as command line argument /action:script:&lt;path&gt;.</para></listitem>
<listitem><para>Scripts can be provided at the default location <replaceable>$XDG_CONFIG_HOME/@VENDOR_PRODUCT@/action.sh</replaceable> or as command line argument <replaceable>/action:script:&lt;path&gt;</replaceable>.</para></listitem>
<listitem><para>The script will receive the current key combination as argument.</para></listitem>
<listitem><para>The 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.</para></listitem>
<listitem><para>The output of the script is parsed for <replaceable>key-local</replaceable> which tells that the script used the key combination, otherwise the combination is forwarded to the remote.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@ -26,6 +26,7 @@
#include <string.h>
#include <winpr/assert.h>
#include <winpr/path.h>
#include <freerdp/log.h>
#include <freerdp/locale/keyboard.h>
@ -172,14 +173,28 @@ const char* x11_event_string(int event)
} while (0)
#endif
static BOOL xf_action_script_append(xfContext* xfc, const char* buffer, size_t size, void* user,
const char* what, const char* arg)
{
WINPR_ASSERT(xfc);
WINPR_UNUSED(what);
WINPR_UNUSED(arg);
if (buffer || (size == 0))
return TRUE;
if (!ArrayList_Append(xfc->xevents, buffer))
{
ArrayList_Clear(xfc->xevents);
return FALSE;
}
return TRUE;
}
BOOL xf_event_action_script_init(xfContext* xfc)
{
wObject* obj = NULL;
FILE* actionScript = NULL;
char buffer[1024] = { 0 };
char command[1024] = { 0 };
const rdpSettings* settings = NULL;
const char* ActionScript = NULL;
WINPR_ASSERT(xfc);
@ -195,28 +210,10 @@ BOOL xf_event_action_script_init(xfContext* xfc)
WINPR_ASSERT(obj);
obj->fnObjectNew = winpr_ObjectStringClone;
obj->fnObjectFree = winpr_ObjectStringFree;
ActionScript = freerdp_settings_get_string(settings, FreeRDP_ActionScript);
sprintf_s(command, sizeof(command), "%s xevent", ActionScript);
actionScript = popen(command, "r");
if (!actionScript)
if (!run_action_script(xfc, "xevent", NULL, xf_action_script_append, NULL))
return FALSE;
while (fgets(buffer, sizeof(buffer), actionScript))
{
char* context = NULL;
strtok_s(buffer, "\n", &context);
if (!ArrayList_Append(xfc->xevents, buffer))
{
pclose(actionScript);
ArrayList_Free(xfc->xevents);
xfc->xevents = NULL;
return FALSE;
}
}
pclose(actionScript);
return TRUE;
}
@ -229,16 +226,59 @@ void xf_event_action_script_free(xfContext* xfc)
}
}
static BOOL action_script_run(xfContext* xfc, const char* buffer, size_t size, void* user,
const char* what, const char* arg)
{
WINPR_UNUSED(xfc);
WINPR_UNUSED(what);
WINPR_UNUSED(arg);
WINPR_ASSERT(user);
int* pstatus = user;
if (size == 0)
{
WLog_WARN(TAG, "ActionScript xevent: script did not return data");
return FALSE;
}
if (winpr_PathFileExists(buffer))
{
char* cmd = NULL;
size_t cmdlen = 0;
winpr_asprintf(&cmd, &cmdlen, "%s %s %s", buffer, what, arg);
if (!cmd)
return FALSE;
FILE* fp = popen(cmd, "w");
free(cmd);
if (!fp)
{
WLog_ERR(TAG, "Failed to execute '%s'", buffer);
return FALSE;
}
*pstatus = pclose(fp);
if (*pstatus < 0)
{
WLog_ERR(TAG, "Command '%s' returned %d", buffer, *pstatus);
return FALSE;
}
}
else
{
WLog_WARN(TAG, "ActionScript xevent: No such file '%s'", buffer);
return FALSE;
}
return TRUE;
}
static BOOL xf_event_execute_action_script(xfContext* xfc, const XEvent* event)
{
int count = 0;
size_t count = 0;
char* name = NULL;
FILE* actionScript = NULL;
BOOL match = FALSE;
const char* xeventName = NULL;
const char* ActionScript = NULL;
char buffer[1024] = { 0 };
char command[1024] = { 0 };
if (!xfc->actionScriptExists || !xfc->xevents || !xfc->window)
return FALSE;
@ -249,7 +289,7 @@ static BOOL xf_event_execute_action_script(xfContext* xfc, const XEvent* event)
xeventName = x11_event_string(event->type);
count = ArrayList_Count(xfc->xevents);
for (int index = 0; index < count; index++)
for (size_t index = 0; index < count; index++)
{
name = (char*)ArrayList_GetItem(xfc->xevents, index);
@ -263,21 +303,12 @@ static BOOL xf_event_execute_action_script(xfContext* xfc, const XEvent* event)
if (!match)
return FALSE;
ActionScript = freerdp_settings_get_string(xfc->common.context.settings, FreeRDP_ActionScript);
sprintf_s(command, sizeof(command), "%s xevent %s %lu", ActionScript, xeventName,
(unsigned long)xfc->window->handle);
actionScript = popen(command, "r");
if (!actionScript)
char command[2048] = { 0 };
char arg[2048] = { 0 };
_snprintf(command, sizeof(command), "xevent %s", xeventName);
_snprintf(arg, sizeof(arg), "%lu", (unsigned long)xfc->window->handle);
if (!run_action_script(xfc, command, arg, action_script_run, NULL))
return FALSE;
while (fgets(buffer, sizeof(buffer), actionScript))
{
char* context = NULL;
strtok_s(buffer, "\n", &context);
}
pclose(actionScript);
return TRUE;
}

View File

@ -19,6 +19,7 @@
#include <freerdp/config.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -41,6 +42,8 @@
#include "xf_keyboard.h"
#include "xf_utils.h"
#include <freerdp/log.h>
#define TAG CLIENT_TAG("x11")
@ -78,25 +81,26 @@ static void xf_keyboard_clear(xfContext* xfc)
ZeroMemory(xfc->KeyboardState, sizeof(xfc->KeyboardState));
}
static BOOL xf_action_script_append(xfContext* xfc, const char* buffer, size_t size, void* user,
const char* what, const char* arg)
{
WINPR_ASSERT(xfc);
WINPR_UNUSED(what);
WINPR_UNUSED(arg);
if (!buffer || (size == 0))
return TRUE;
return ArrayList_Append(xfc->keyCombinations, buffer);
}
static BOOL xf_keyboard_action_script_init(xfContext* xfc)
{
wObject* obj = NULL;
FILE* keyScript = NULL;
char buffer[1024] = { 0 };
char command[1024] = { 0 };
const rdpSettings* settings = NULL;
const char* ActionScript = NULL;
WINPR_ASSERT(xfc);
settings = xfc->common.context.settings;
WINPR_ASSERT(settings);
ActionScript = freerdp_settings_get_string(settings, FreeRDP_ActionScript);
xfc->actionScriptExists = winpr_PathFileExists(ActionScript);
if (!xfc->actionScriptExists)
return FALSE;
xfc->keyCombinations = ArrayList_New(TRUE);
if (!xfc->keyCombinations)
@ -106,30 +110,10 @@ static BOOL xf_keyboard_action_script_init(xfContext* xfc)
WINPR_ASSERT(obj);
obj->fnObjectNew = winpr_ObjectStringClone;
obj->fnObjectFree = winpr_ObjectStringFree;
sprintf_s(command, sizeof(command), "%s key", ActionScript);
keyScript = popen(command, "r");
if (!keyScript)
{
xfc->actionScriptExists = FALSE;
if (!run_action_script(xfc, "key", NULL, xf_action_script_append, NULL))
return FALSE;
}
while (fgets(buffer, sizeof(buffer), keyScript) != NULL)
{
char* context = NULL;
strtok_s(buffer, "\n", &context);
if (!ArrayList_Append(xfc->keyCombinations, buffer))
{
ArrayList_Free(xfc->keyCombinations);
xfc->actionScriptExists = FALSE;
pclose(keyScript);
return FALSE;
}
}
pclose(keyScript);
return xf_event_action_script_init(xfc);
}
@ -452,11 +436,51 @@ void xf_keyboard_focus_in(xfContext* xfc)
}
}
static BOOL action_script_run(xfContext* xfc, const char* buffer, size_t size, void* user,
const char* what, const char* arg)
{
WINPR_UNUSED(xfc);
WINPR_UNUSED(what);
WINPR_UNUSED(arg);
WINPR_ASSERT(user);
int* pstatus = user;
if (size == 0)
{
WLog_WARN(TAG, "ActionScript key: script did not return data");
return FALSE;
}
if (strcmp(buffer, "key-local") == 0)
*pstatus = 0;
else if (winpr_PathFileExists(buffer))
{
FILE* fp = popen(buffer, "w");
if (!fp)
{
WLog_ERR(TAG, "Failed to execute '%s'", buffer);
return FALSE;
}
*pstatus = pclose(fp);
if (*pstatus < 0)
{
WLog_ERR(TAG, "Command '%s' returned %d", buffer, *pstatus);
return FALSE;
}
}
else
{
WLog_WARN(TAG, "ActionScript key: no such file '%s'", buffer);
return FALSE;
}
return TRUE;
}
static int xf_keyboard_execute_action_script(xfContext* xfc, XF_MODIFIER_KEYS* mod, KeySym keysym)
{
int status = 1;
BOOL match = FALSE;
char buffer[1024] = { 0 };
char command[2048] = { 0 };
char combination[1024] = { 0 };
@ -488,7 +512,10 @@ static int xf_keyboard_execute_action_script(xfContext* xfc, XF_MODIFIER_KEYS* m
if (mod->Super)
winpr_str_append("Super", combination, sizeof(combination), "+");
winpr_str_append(keyStr, combination, sizeof(combination), NULL);
winpr_str_append(keyStr, combination, sizeof(combination), "+");
for (size_t i = 0; i < strnlen(combination, sizeof(combination)); i++)
combination[i] = tolower(combination[i]);
const size_t count = ArrayList_Count(xfc->keyCombinations);
@ -506,26 +533,10 @@ static int xf_keyboard_execute_action_script(xfContext* xfc, XF_MODIFIER_KEYS* m
if (!match)
return 1;
const char* ActionScript =
freerdp_settings_get_string(xfc->common.context.settings, FreeRDP_ActionScript);
sprintf_s(command, sizeof(command), "%s key %s", ActionScript, combination);
FILE* keyScript = popen(command, "r");
if (!keyScript)
sprintf_s(command, sizeof(command), "key %s", combination);
if (!run_action_script(xfc, command, NULL, action_script_run, &status))
return -1;
while (fgets(buffer, sizeof(buffer), keyScript) != NULL)
{
char* context = NULL;
strtok_s(buffer, "\n", &context);
if (strcmp(buffer, "key-local") == 0)
status = 0;
}
if (pclose(keyScript) == -1)
status = -1;
return status;
}
@ -569,10 +580,11 @@ BOOL xf_keyboard_handle_special_keys(xfContext* xfc, KeySym keysym)
xfc->ungrabKeyboardWithRightCtrl = FALSE;
}
if (!xf_keyboard_execute_action_script(xfc, &mod, keysym))
{
const int rc = xf_keyboard_execute_action_script(xfc, &mod, keysym);
if (rc < 0)
return FALSE;
if (rc == 0)
return TRUE;
}
if (!xfc->remote_app && xfc->fullscreen_toggle)
{

View File

@ -21,8 +21,14 @@
#include <string.h>
#include <winpr/assert.h>
#include <winpr/wtypes.h>
#include <winpr/path.h>
#include "xf_utils.h"
#include "xfreerdp.h"
#include <freerdp/log.h>
#define TAG CLIENT_TAG("xfreerdp.utils")
static const DWORD log_level = WLOG_TRACE;
@ -158,3 +164,54 @@ BOOL IsGnome(void)
char* env = getenv("DESKTOP_SESSION");
return (env != NULL && 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 = NULL;
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)
goto fail;
char command[2048] = { 0 };
sprintf_s(command, sizeof(command), "%s %s", ActionScript, what);
keyScript = popen(command, "r");
if (!keyScript)
{
WLog_ERR(TAG, "Failed to execute '%s'", command);
goto fail;
}
BOOL read_data = FALSE;
char buffer[2048] = { 0 };
while (fgets(buffer, sizeof(buffer), keyScript) != NULL)
{
char* context = NULL;
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;
fail:
if (!rc)
xfc->actionScriptExists = FALSE;
if (keyScript)
pclose(keyScript);
return rc;
}

View File

@ -23,9 +23,15 @@
#include <winpr/wtypes.h>
#include <X11/Xlib.h>
#include "xfreerdp.h"
char* Safe_XGetAtomName(wLog* log, Display* display, Atom atom);
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 LogTagAndXGetWindowProperty(tag, display, w, property, long_offset, long_length, delete, \
req_type, actual_type_return, actual_format_return, \
nitems_return, bytes_after_return, prop_return) \

View File

@ -237,3 +237,10 @@ if (BUILD_FUZZERS)
endif()
option(WITH_FULL_CONFIG_PATH "Use <appdata>/Vendor/Product instead of <appdata>/product (lowercase, only if vendor equals product) as config directory" OFF)
# Configuration settings for manpages
if (NOT WITH_FULL_CONFIG_PATH AND "${VENDOR}" STREQUAL "${PRODUCT}")
string(TOLOWER "${VENDOR}" VENDOR_PRODUCT)
else()
set(VENDOR_PRODUCT "${VENDOR}/${PRODUCT}")
endif()

View File

@ -772,6 +772,13 @@ rdpSettings* freerdp_settings_new(DWORD flags)
char* config = freerdp_settings_get_config_path();
rc = freerdp_settings_set_string(settings, FreeRDP_ConfigPath, config);
if (rc)
{
char* action = GetCombinedPath(config, "action.sh");
rc = freerdp_settings_set_string(settings, FreeRDP_ActionScript, action);
free(action);
}
free(config);
if (!rc)
goto out_fail;
@ -779,8 +786,6 @@ rdpSettings* freerdp_settings_new(DWORD flags)
settings_load_hkey_local_machine(settings);
if (!freerdp_settings_set_string(settings, FreeRDP_ActionScript, "~/.config/freerdp/action.sh"))
goto out_fail;
if (!freerdp_settings_set_bool(settings, FreeRDP_SmartcardLogon, FALSE))
goto out_fail;
if (!freerdp_settings_set_uint32(settings, FreeRDP_TlsSecLevel, 1))