GUACAMOLE-1204: Add support for including touch events within session recordings.

This commit is contained in:
Michael Jumper 2021-02-06 15:10:50 -08:00
parent 5eb2867733
commit d16ba33dee
12 changed files with 179 additions and 2 deletions

View File

@ -71,6 +71,15 @@ typedef struct guac_common_recording {
*/ */
int include_mouse; int include_mouse;
/**
* Non-zero if multi-touch events should be included in the session
* recording, zero otherwise. Depending on whether the remote desktop will
* automatically provide graphical feedback for touches, including touch
* events may be necessary for multi-touch interactions to be rendered in
* any resulting video.
*/
int include_touch;
/** /**
* Non-zero if keys pressed and released should be included in the session * Non-zero if keys pressed and released should be included in the session
* recording, zero otherwise. Including key events within the recording may * recording, zero otherwise. Including key events within the recording may
@ -119,6 +128,13 @@ typedef struct guac_common_recording {
* otherwise. Including mouse state is necessary for the mouse cursor to be * otherwise. Including mouse state is necessary for the mouse cursor to be
* rendered in any resulting video. * rendered in any resulting video.
* *
* @param include_touch
* Non-zero if touch events should be included in the session recording,
* zero otherwise. Depending on whether the remote desktop will
* automatically provide graphical feedback for touches, including touch
* events may be necessary for multi-touch interactions to be rendered in
* any resulting video.
*
* @param include_keys * @param include_keys
* Non-zero if keys pressed and released should be included in the session * Non-zero if keys pressed and released should be included in the session
* recording, zero otherwise. Including key events within the recording may * recording, zero otherwise. Including key events within the recording may
@ -133,7 +149,8 @@ typedef struct guac_common_recording {
*/ */
guac_common_recording* guac_common_recording_create(guac_client* client, guac_common_recording* guac_common_recording_create(guac_client* client,
const char* path, const char* name, int create_path, const char* path, const char* name, int create_path,
int include_output, int include_mouse, int include_keys); int include_output, int include_mouse, int include_touch,
int include_keys);
/** /**
* Frees the resources associated with the given in-progress recording. Note * Frees the resources associated with the given in-progress recording. Note
@ -174,6 +191,44 @@ void guac_common_recording_free(guac_common_recording* recording);
void guac_common_recording_report_mouse(guac_common_recording* recording, void guac_common_recording_report_mouse(guac_common_recording* recording,
int x, int y, int button_mask); int x, int y, int button_mask);
/**
* Reports the current state of a touch contact within the recording.
*
* @param recording
* The guac_common_recording associated with the touch contact that
* has changed state.
*
* @param id
* An arbitrary integer ID which uniquely identifies this contact relative
* to other active contacts.
*
* @param x
* The X coordinate of the center of the touch contact.
*
* @param y
* The Y coordinate of the center of the touch contact.
*
* @param x_radius
* The X radius of the ellipse covering the general area of the touch
* contact, in pixels.
*
* @param y_radius
* The Y radius of the ellipse covering the general area of the touch
* contact, in pixels.
*
* @param angle
* The rough angle of clockwise rotation of the general area of the touch
* contact, in degrees.
*
* @param force
* The relative force exerted by the touch contact, where 0 is no force
* (the touch has been lifted) and 1 is maximum force (the maximum amount
* of force representable by the device).
*/
void guac_common_recording_report_touch(guac_common_recording* recording,
int id, int x, int y, int x_radius, int y_radius,
double angle, double force);
/** /**
* Reports a change in the state of an individual key within the recording. * Reports a change in the state of an individual key within the recording.
* *

View File

@ -137,7 +137,8 @@ static int guac_common_recording_open(const char* path,
guac_common_recording* guac_common_recording_create(guac_client* client, guac_common_recording* guac_common_recording_create(guac_client* client,
const char* path, const char* name, int create_path, const char* path, const char* name, int create_path,
int include_output, int include_mouse, int include_keys) { int include_output, int include_mouse, int include_touch,
int include_keys) {
char filename[GUAC_COMMON_RECORDING_MAX_NAME_LENGTH]; char filename[GUAC_COMMON_RECORDING_MAX_NAME_LENGTH];
@ -165,6 +166,7 @@ guac_common_recording* guac_common_recording_create(guac_client* client,
recording->socket = guac_socket_open(fd); recording->socket = guac_socket_open(fd);
recording->include_output = include_output; recording->include_output = include_output;
recording->include_mouse = include_mouse; recording->include_mouse = include_mouse;
recording->include_touch = include_touch;
recording->include_keys = include_keys; recording->include_keys = include_keys;
/* Replace client socket with wrapped recording socket only if including /* Replace client socket with wrapped recording socket only if including
@ -203,6 +205,17 @@ void guac_common_recording_report_mouse(guac_common_recording* recording,
} }
void guac_common_recording_report_touch(guac_common_recording* recording,
int id, int x, int y, int x_radius, int y_radius,
double angle, double force) {
/* Report touches only if recording should contain touch events */
if (recording->include_touch)
guac_protocol_send_touch(recording->socket, id, x, y,
x_radius, y_radius, angle, force, guac_timestamp_current());
}
void guac_common_recording_report_key(guac_common_recording* recording, void guac_common_recording_report_key(guac_common_recording* recording,
int keysym, int pressed) { int keysym, int pressed) {

View File

@ -209,6 +209,53 @@ int vguac_protocol_send_log(guac_socket* socket, const char* format,
int guac_protocol_send_mouse(guac_socket* socket, int x, int y, int guac_protocol_send_mouse(guac_socket* socket, int x, int y,
int button_mask, guac_timestamp timestamp); int button_mask, guac_timestamp timestamp);
/**
* Sends a touch instruction over the given guac_socket connection.
*
* If an error occurs sending the instruction, a non-zero value is
* returned, and guac_error is set appropriately.
*
* @param socket
* The guac_socket connection to use.
*
* @param id
* An arbitrary integer ID which uniquely identifies this contact relative
* to other active contacts.
*
* @param x
* The X coordinate of the center of the touch contact.
*
* @param y
* The Y coordinate of the center of the touch contact.
*
* @param x_radius
* The X radius of the ellipse covering the general area of the touch
* contact, in pixels.
*
* @param y_radius
* The Y radius of the ellipse covering the general area of the touch
* contact, in pixels.
*
* @param angle
* The rough angle of clockwise rotation of the general area of the touch
* contact, in degrees.
*
* @param force
* The relative force exerted by the touch contact, where 0 is no force
* (the touch has been lifted) and 1 is maximum force (the maximum amount
* of force representable by the device).
*
* @param timestamp
* The server timestamp (in milliseconds) at the point in time this touch
* event was acknowledged.
*
* @return
* Zero on success, non-zero on error.
*/
int guac_protocol_send_touch(guac_socket* socket, int id, int x, int y,
int x_radius, int y_radius, double angle, double force,
guac_timestamp timestamp);
/** /**
* Sends a nest instruction over the given guac_socket connection. * Sends a nest instruction over the given guac_socket connection.
* *

View File

@ -820,6 +820,37 @@ int guac_protocol_send_mouse(guac_socket* socket, int x, int y,
} }
int guac_protocol_send_touch(guac_socket* socket, int id, int x, int y,
int x_radius, int y_radius, double angle, double force,
guac_timestamp timestamp) {
int ret_val;
guac_socket_instruction_begin(socket);
ret_val =
guac_socket_write_string(socket, "5.touch,")
|| __guac_socket_write_length_int(socket, id)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_int(socket, x)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_int(socket, y)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_int(socket, x_radius)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_int(socket, y_radius)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_double(socket, angle)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_double(socket, force)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_int(socket, timestamp)
|| guac_socket_write_string(socket, ";");
guac_socket_instruction_end(socket);
return ret_val;
}
int guac_protocol_send_move(guac_socket* socket, const guac_layer* layer, int guac_protocol_send_move(guac_socket* socket, const guac_layer* layer,
const guac_layer* parent, int x, int y, int z) { const guac_layer* parent, int x, int y, int z) {

View File

@ -234,6 +234,7 @@ void* guac_kubernetes_client_thread(void* data) {
settings->create_recording_path, settings->create_recording_path,
!settings->recording_exclude_output, !settings->recording_exclude_output,
!settings->recording_exclude_mouse, !settings->recording_exclude_mouse,
0, /* Touch events not supported */
settings->recording_include_keys); settings->recording_include_keys);
} }

View File

@ -136,6 +136,11 @@ int guac_rdp_user_touch_handler(guac_user* user, int id, int x, int y,
if (rdp_inst == NULL) if (rdp_inst == NULL)
goto complete; goto complete;
/* Report touch event within recording */
if (rdp_client->recording != NULL)
guac_common_recording_report_touch(rdp_client->recording, id, x, y,
x_radius, y_radius, angle, force);
/* Forward touch event along RDPEI channel */ /* Forward touch event along RDPEI channel */
guac_rdp_rdpei_touch_update(rdp_client->rdpei, id, x, y, force); guac_rdp_rdpei_touch_update(rdp_client->rdpei, id, x, y, force);

View File

@ -426,6 +426,7 @@ static int guac_rdp_handle_connection(guac_client* client) {
settings->create_recording_path, settings->create_recording_path,
!settings->recording_exclude_output, !settings->recording_exclude_output,
!settings->recording_exclude_mouse, !settings->recording_exclude_mouse,
!settings->recording_exclude_touch,
settings->recording_include_keys); settings->recording_include_keys);
} }

View File

@ -104,6 +104,7 @@ const char* GUAC_RDP_CLIENT_ARGS[] = {
"recording-name", "recording-name",
"recording-exclude-output", "recording-exclude-output",
"recording-exclude-mouse", "recording-exclude-mouse",
"recording-exclude-touch",
"recording-include-keys", "recording-include-keys",
"create-recording-path", "create-recording-path",
"resize-method", "resize-method",
@ -500,6 +501,13 @@ enum RDP_ARGS_IDX {
*/ */
IDX_RECORDING_EXCLUDE_MOUSE, IDX_RECORDING_EXCLUDE_MOUSE,
/**
* Whether changes to touch contact state should NOT be included in the
* session recording. Touch state is included by default, as it may be
* necessary for touch interactions to be rendered in any resulting video.
*/
IDX_RECORDING_EXCLUDE_TOUCH,
/** /**
* Whether keys pressed and released should be included in the session * Whether keys pressed and released should be included in the session
* recording. Key events are NOT included by default within the recording, * recording. Key events are NOT included by default within the recording,
@ -1042,6 +1050,11 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user,
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv, guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_RECORDING_EXCLUDE_MOUSE, 0); IDX_RECORDING_EXCLUDE_MOUSE, 0);
/* Parse touch exclusion flag */
settings->recording_exclude_touch =
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_RECORDING_EXCLUDE_TOUCH, 0);
/* Parse key event inclusion flag */ /* Parse key event inclusion flag */
settings->recording_include_keys = settings->recording_include_keys =
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv, guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,

View File

@ -505,6 +505,14 @@ typedef struct guac_rdp_settings {
*/ */
int recording_exclude_mouse; int recording_exclude_mouse;
/**
* Non-zero if changes to touch state should NOT be included in the session
* recording, zero otherwise. Touch state is included by default, as it may
* be necessary for touch interactions to be rendered in any resulting
* video.
*/
int recording_exclude_touch;
/** /**
* Non-zero if keys pressed and released should be included in the session * Non-zero if keys pressed and released should be included in the session
* recording, zero otherwise. Key events are NOT included by default within * recording, zero otherwise. Key events are NOT included by default within

View File

@ -234,6 +234,7 @@ void* ssh_client_thread(void* data) {
settings->create_recording_path, settings->create_recording_path,
!settings->recording_exclude_output, !settings->recording_exclude_output,
!settings->recording_exclude_mouse, !settings->recording_exclude_mouse,
0, /* Touch events not supported */
settings->recording_include_keys); settings->recording_include_keys);
} }

View File

@ -581,6 +581,7 @@ void* guac_telnet_client_thread(void* data) {
settings->create_recording_path, settings->create_recording_path,
!settings->recording_exclude_output, !settings->recording_exclude_output,
!settings->recording_exclude_mouse, !settings->recording_exclude_mouse,
0, /* Touch events not supported */
settings->recording_include_keys); settings->recording_include_keys);
} }

View File

@ -396,6 +396,7 @@ void* guac_vnc_client_thread(void* data) {
settings->create_recording_path, settings->create_recording_path,
!settings->recording_exclude_output, !settings->recording_exclude_output,
!settings->recording_exclude_mouse, !settings->recording_exclude_mouse,
0, /* Touch events not supported */
settings->recording_include_keys); settings->recording_include_keys);
} }