GUACAMOLE-377: Merge support for RemoteFX.
This commit is contained in:
commit
d4cd9b3e3a
@ -413,15 +413,19 @@ void* guac_client_for_user(guac_client* client, guac_user* user,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int guac_client_end_frame(guac_client* client) {
|
int guac_client_end_frame(guac_client* client) {
|
||||||
|
return guac_client_end_multiple_frames(client, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int guac_client_end_multiple_frames(guac_client* client, int frames) {
|
||||||
|
|
||||||
/* Update and send timestamp */
|
/* Update and send timestamp */
|
||||||
client->last_sent_timestamp = guac_timestamp_current();
|
client->last_sent_timestamp = guac_timestamp_current();
|
||||||
|
|
||||||
/* Log received timestamp and calculated lag (at TRACE level only) */
|
/* Log received timestamp and calculated lag (at TRACE level only) */
|
||||||
guac_client_log(client, GUAC_LOG_TRACE, "Server completed "
|
guac_client_log(client, GUAC_LOG_TRACE, "Server completed "
|
||||||
"frame %" PRIu64 "ms.", client->last_sent_timestamp);
|
"frame %" PRIu64 "ms (%i logical frames)", client->last_sent_timestamp, frames);
|
||||||
|
|
||||||
return guac_protocol_send_sync(client->socket, client->last_sent_timestamp);
|
return guac_protocol_send_sync(client->socket, client->last_sent_timestamp, frames);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,18 +509,47 @@ void* guac_client_for_user(guac_client* client, guac_user* user,
|
|||||||
guac_user_callback* callback, void* data);
|
guac_user_callback* callback, void* data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the end of the current frame by sending a "sync" instruction to
|
* Marks the end of the current frame by sending a "sync" instruction to all
|
||||||
* all connected users. This instruction will contain the current timestamp.
|
* connected users, where the number of input frames that were considered in
|
||||||
* The last_sent_timestamp member of guac_client will be updated accordingly.
|
* creating this frame is either unknown or inapplicable. This instruction will
|
||||||
|
* contain the current timestamp. The last_sent_timestamp member of guac_client
|
||||||
|
* will be updated accordingly.
|
||||||
*
|
*
|
||||||
* If an error occurs sending the instruction, a non-zero value is
|
* If an error occurs sending the instruction, a non-zero value is
|
||||||
* returned, and guac_error is set appropriately.
|
* returned, and guac_error is set appropriately.
|
||||||
*
|
*
|
||||||
* @param client The guac_client which has finished a frame.
|
* @param client
|
||||||
* @return Zero on success, non-zero on error.
|
* The guac_client which has finished a frame.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* Zero on success, non-zero on error.
|
||||||
*/
|
*/
|
||||||
int guac_client_end_frame(guac_client* client);
|
int guac_client_end_frame(guac_client* client);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the end of the current frame by sending a "sync" instruction to all
|
||||||
|
* connected users, where that frame may combine or otherwise represent the
|
||||||
|
* changes of an arbitrary number of input frames. This instruction will
|
||||||
|
* contain the current timestamp, as well as the number of frames that were
|
||||||
|
* considered in creating that frame. The last_sent_timestamp member of
|
||||||
|
* guac_client will be updated accordingly.
|
||||||
|
*
|
||||||
|
* If an error occurs sending the instruction, a non-zero value is
|
||||||
|
* returned, and guac_error is set appropriately.
|
||||||
|
*
|
||||||
|
* @param client
|
||||||
|
* The guac_client which has finished a frame.
|
||||||
|
*
|
||||||
|
* @param frames
|
||||||
|
* The number of distinct frames that were considered or combined when
|
||||||
|
* generating the current frame, or zero if the boundaries of relevant
|
||||||
|
* frames are unknown.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* Zero on success, non-zero on error.
|
||||||
|
*/
|
||||||
|
int guac_client_end_multiple_frames(guac_client* client, int frames);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the given guac_client using the initialization routine provided
|
* Initializes the given guac_client using the initialization routine provided
|
||||||
* by the plugin corresponding to the named protocol. This will automatically
|
* by the plugin corresponding to the named protocol. This will automatically
|
||||||
|
@ -363,11 +363,22 @@ int guac_protocol_send_select(guac_socket* socket, const char* protocol);
|
|||||||
* If an error occurs sending the instruction, a non-zero value is
|
* If an error occurs sending the instruction, a non-zero value is
|
||||||
* returned, and guac_error is set appropriately.
|
* returned, and guac_error is set appropriately.
|
||||||
*
|
*
|
||||||
* @param socket The guac_socket connection to use.
|
* @param socket
|
||||||
* @param timestamp The current timestamp (in milliseconds).
|
* The guac_socket connection to use.
|
||||||
* @return Zero on success, non-zero on error.
|
*
|
||||||
|
* @param timestamp
|
||||||
|
* The current timestamp (in milliseconds).
|
||||||
|
*
|
||||||
|
* @param frames
|
||||||
|
* The number of distinct frames that were considered or combined when
|
||||||
|
* generating the frame terminated by this instruction, or zero if the
|
||||||
|
* boundaries of relevant frames are unknown.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* Zero on success, non-zero on error.
|
||||||
*/
|
*/
|
||||||
int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp);
|
int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp,
|
||||||
|
int frames);
|
||||||
|
|
||||||
/* OBJECT INSTRUCTIONS */
|
/* OBJECT INSTRUCTIONS */
|
||||||
|
|
||||||
|
@ -1181,7 +1181,8 @@ int guac_protocol_send_start(guac_socket* socket, const guac_layer* layer,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp) {
|
int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp,
|
||||||
|
int frames) {
|
||||||
|
|
||||||
int ret_val;
|
int ret_val;
|
||||||
|
|
||||||
@ -1189,6 +1190,8 @@ int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp) {
|
|||||||
ret_val =
|
ret_val =
|
||||||
guac_socket_write_string(socket, "4.sync,")
|
guac_socket_write_string(socket, "4.sync,")
|
||||||
|| __guac_socket_write_length_int(socket, timestamp)
|
|| __guac_socket_write_length_int(socket, timestamp)
|
||||||
|
|| guac_socket_write_string(socket, ",")
|
||||||
|
|| __guac_socket_write_length_int(socket, frames)
|
||||||
|| guac_socket_write_string(socket, ";");
|
|| guac_socket_write_string(socket, ";");
|
||||||
|
|
||||||
guac_socket_instruction_end(socket);
|
guac_socket_instruction_end(socket);
|
||||||
|
@ -120,31 +120,39 @@ int __guac_handle_sync(guac_user* user, int argc, char** argv) {
|
|||||||
/* Calculate length of frame, including network and processing lag */
|
/* Calculate length of frame, including network and processing lag */
|
||||||
frame_duration = current - timestamp;
|
frame_duration = current - timestamp;
|
||||||
|
|
||||||
/* Update lag statistics if at least one frame has been rendered */
|
/* Calculate processing lag portion of length of frame */
|
||||||
|
int frame_processing_lag = 0;
|
||||||
if (user->last_frame_duration != 0) {
|
if (user->last_frame_duration != 0) {
|
||||||
|
|
||||||
/* Calculate lag using the previous frame as a baseline */
|
/* Calculate lag using the previous frame as a baseline */
|
||||||
int processing_lag = frame_duration - user->last_frame_duration;
|
frame_processing_lag = frame_duration - user->last_frame_duration;
|
||||||
|
|
||||||
/* Adjust back to zero if cumulative error leads to a negative
|
/* Adjust back to zero if cumulative error leads to a negative
|
||||||
* value */
|
* value */
|
||||||
if (processing_lag < 0)
|
if (frame_processing_lag < 0)
|
||||||
processing_lag = 0;
|
frame_processing_lag = 0;
|
||||||
|
|
||||||
user->processing_lag = processing_lag;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Record baseline duration of frame by excluding lag */
|
/* Record baseline duration of frame by excluding lag (this is the
|
||||||
user->last_frame_duration = frame_duration - user->processing_lag;
|
* network round-trip time) */
|
||||||
|
int estimated_rtt = frame_duration - frame_processing_lag;
|
||||||
|
user->last_frame_duration = estimated_rtt;
|
||||||
|
|
||||||
|
/* Calculate cumulative accumulated processing lag relative to server timeline */
|
||||||
|
int processing_lag = current - user->last_received_timestamp - estimated_rtt;
|
||||||
|
if (processing_lag < 0)
|
||||||
|
processing_lag = 0;
|
||||||
|
|
||||||
|
user->processing_lag = processing_lag;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Log received timestamp and calculated lag (at TRACE level only) */
|
/* Log received timestamp and calculated lag (at TRACE level only) */
|
||||||
guac_user_log(user, GUAC_LOG_TRACE,
|
guac_user_log(user, GUAC_LOG_TRACE,
|
||||||
"User confirmation of frame %" PRIu64 "ms received "
|
"User confirmation of frame %" PRIu64 "ms received "
|
||||||
"at %" PRIu64 "ms (processing_lag=%ims)",
|
"at %" PRIu64 "ms (processing_lag=%ims, estimated_rtt=%ims)",
|
||||||
timestamp, current, user->processing_lag);
|
timestamp, current, user->processing_lag, user->last_frame_duration);
|
||||||
|
|
||||||
if (user->sync_handler)
|
if (user->sync_handler)
|
||||||
return user->sync_handler(user, timestamp);
|
return user->sync_handler(user, timestamp);
|
||||||
|
@ -57,6 +57,7 @@ libguac_client_rdp_la_SOURCES = \
|
|||||||
channels/rdpdr/rdpdr-printer.c \
|
channels/rdpdr/rdpdr-printer.c \
|
||||||
channels/rdpdr/rdpdr.c \
|
channels/rdpdr/rdpdr.c \
|
||||||
channels/rdpei.c \
|
channels/rdpei.c \
|
||||||
|
channels/rdpgfx.c \
|
||||||
channels/rdpsnd/rdpsnd-messages.c \
|
channels/rdpsnd/rdpsnd-messages.c \
|
||||||
channels/rdpsnd/rdpsnd.c \
|
channels/rdpsnd/rdpsnd.c \
|
||||||
client.c \
|
client.c \
|
||||||
@ -103,6 +104,7 @@ noinst_HEADERS = \
|
|||||||
channels/rdpdr/rdpdr-printer.h \
|
channels/rdpdr/rdpdr-printer.h \
|
||||||
channels/rdpdr/rdpdr.h \
|
channels/rdpdr/rdpdr.h \
|
||||||
channels/rdpei.h \
|
channels/rdpei.h \
|
||||||
|
channels/rdpgfx.h \
|
||||||
channels/rdpsnd/rdpsnd-messages.h \
|
channels/rdpsnd/rdpsnd-messages.h \
|
||||||
channels/rdpsnd/rdpsnd.h \
|
channels/rdpsnd/rdpsnd.h \
|
||||||
client.h \
|
client.h \
|
||||||
|
@ -509,12 +509,12 @@ static UINT guac_rdp_cliprdr_format_data_response(CliprdrClientContext* cliprdr,
|
|||||||
* @param context
|
* @param context
|
||||||
* The rdpContext associated with the active RDP session.
|
* The rdpContext associated with the active RDP session.
|
||||||
*
|
*
|
||||||
* @param e
|
* @param args
|
||||||
* Event-specific arguments, mainly the name of the channel, and a
|
* Event-specific arguments, mainly the name of the channel, and a
|
||||||
* reference to the associated plugin loaded for that channel by FreeRDP.
|
* reference to the associated plugin loaded for that channel by FreeRDP.
|
||||||
*/
|
*/
|
||||||
static void guac_rdp_cliprdr_channel_connected(rdpContext* context,
|
static void guac_rdp_cliprdr_channel_connected(rdpContext* context,
|
||||||
ChannelConnectedEventArgs* e) {
|
ChannelConnectedEventArgs* args) {
|
||||||
|
|
||||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
@ -526,12 +526,12 @@ static void guac_rdp_cliprdr_channel_connected(rdpContext* context,
|
|||||||
assert(clipboard != NULL);
|
assert(clipboard != NULL);
|
||||||
|
|
||||||
/* Ignore connection event if it's not for the CLIPRDR channel */
|
/* Ignore connection event if it's not for the CLIPRDR channel */
|
||||||
if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) != 0)
|
if (strcmp(args->name, CLIPRDR_SVC_CHANNEL_NAME) != 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* The structure pointed to by pInterface is guaranteed to be a
|
/* The structure pointed to by pInterface is guaranteed to be a
|
||||||
* CliprdrClientContext if the channel is CLIPRDR */
|
* CliprdrClientContext if the channel is CLIPRDR */
|
||||||
CliprdrClientContext* cliprdr = (CliprdrClientContext*) e->pInterface;
|
CliprdrClientContext* cliprdr = (CliprdrClientContext*) args->pInterface;
|
||||||
|
|
||||||
/* Associate FreeRDP CLIPRDR context and its Guacamole counterpart with
|
/* Associate FreeRDP CLIPRDR context and its Guacamole counterpart with
|
||||||
* eachother */
|
* eachother */
|
||||||
@ -562,12 +562,12 @@ static void guac_rdp_cliprdr_channel_connected(rdpContext* context,
|
|||||||
* @param context
|
* @param context
|
||||||
* The rdpContext associated with the active RDP session.
|
* The rdpContext associated with the active RDP session.
|
||||||
*
|
*
|
||||||
* @param e
|
* @param args
|
||||||
* Event-specific arguments, mainly the name of the channel, and a
|
* Event-specific arguments, mainly the name of the channel, and a
|
||||||
* reference to the associated plugin loaded for that channel by FreeRDP.
|
* reference to the associated plugin loaded for that channel by FreeRDP.
|
||||||
*/
|
*/
|
||||||
static void guac_rdp_cliprdr_channel_disconnected(rdpContext* context,
|
static void guac_rdp_cliprdr_channel_disconnected(rdpContext* context,
|
||||||
ChannelDisconnectedEventArgs* e) {
|
ChannelDisconnectedEventArgs* args) {
|
||||||
|
|
||||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
@ -579,7 +579,7 @@ static void guac_rdp_cliprdr_channel_disconnected(rdpContext* context,
|
|||||||
assert(clipboard != NULL);
|
assert(clipboard != NULL);
|
||||||
|
|
||||||
/* Ignore disconnection event if it's not for the CLIPRDR channel */
|
/* Ignore disconnection event if it's not for the CLIPRDR channel */
|
||||||
if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) != 0)
|
if (strcmp(args->name, CLIPRDR_SVC_CHANNEL_NAME) != 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Channel is no longer connected */
|
/* Channel is no longer connected */
|
||||||
|
@ -68,19 +68,19 @@ void guac_rdp_disp_free(guac_rdp_disp* disp) {
|
|||||||
* @param context
|
* @param context
|
||||||
* The rdpContext associated with the active RDP session.
|
* The rdpContext associated with the active RDP session.
|
||||||
*
|
*
|
||||||
* @param e
|
* @param args
|
||||||
* Event-specific arguments, mainly the name of the channel, and a
|
* Event-specific arguments, mainly the name of the channel, and a
|
||||||
* reference to the associated plugin loaded for that channel by FreeRDP.
|
* reference to the associated plugin loaded for that channel by FreeRDP.
|
||||||
*/
|
*/
|
||||||
static void guac_rdp_disp_channel_connected(rdpContext* context,
|
static void guac_rdp_disp_channel_connected(rdpContext* context,
|
||||||
ChannelConnectedEventArgs* e) {
|
ChannelConnectedEventArgs* args) {
|
||||||
|
|
||||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
guac_rdp_disp* guac_disp = rdp_client->disp;
|
guac_rdp_disp* guac_disp = rdp_client->disp;
|
||||||
|
|
||||||
/* Ignore connection event if it's not for the Display Update channel */
|
/* Ignore connection event if it's not for the Display Update channel */
|
||||||
if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) != 0)
|
if (strcmp(args->name, DISP_DVC_CHANNEL_NAME) != 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Init module with current display size */
|
/* Init module with current display size */
|
||||||
@ -89,7 +89,7 @@ static void guac_rdp_disp_channel_connected(rdpContext* context,
|
|||||||
guac_rdp_get_height(context->instance));
|
guac_rdp_get_height(context->instance));
|
||||||
|
|
||||||
/* Store reference to the display update plugin once it's connected */
|
/* Store reference to the display update plugin once it's connected */
|
||||||
DispClientContext* disp = (DispClientContext*) e->pInterface;
|
DispClientContext* disp = (DispClientContext*) args->pInterface;
|
||||||
guac_disp->disp = disp;
|
guac_disp->disp = disp;
|
||||||
|
|
||||||
guac_client_log(client, GUAC_LOG_DEBUG, "Display update channel "
|
guac_client_log(client, GUAC_LOG_DEBUG, "Display update channel "
|
||||||
@ -110,19 +110,19 @@ static void guac_rdp_disp_channel_connected(rdpContext* context,
|
|||||||
* @param context
|
* @param context
|
||||||
* The rdpContext associated with the active RDP session.
|
* The rdpContext associated with the active RDP session.
|
||||||
*
|
*
|
||||||
* @param e
|
* @param args
|
||||||
* Event-specific arguments, mainly the name of the channel, and a
|
* Event-specific arguments, mainly the name of the channel, and a
|
||||||
* reference to the associated plugin loaded for that channel by FreeRDP.
|
* reference to the associated plugin loaded for that channel by FreeRDP.
|
||||||
*/
|
*/
|
||||||
static void guac_rdp_disp_channel_disconnected(rdpContext* context,
|
static void guac_rdp_disp_channel_disconnected(rdpContext* context,
|
||||||
ChannelDisconnectedEventArgs* e) {
|
ChannelDisconnectedEventArgs* args) {
|
||||||
|
|
||||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
guac_rdp_disp* guac_disp = rdp_client->disp;
|
guac_rdp_disp* guac_disp = rdp_client->disp;
|
||||||
|
|
||||||
/* Ignore disconnection event if it's not for the Display Update channel */
|
/* Ignore disconnection event if it's not for the Display Update channel */
|
||||||
if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) != 0)
|
if (strcmp(args->name, DISP_DVC_CHANNEL_NAME) != 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Channel is no longer connected */
|
/* Channel is no longer connected */
|
||||||
|
@ -230,22 +230,22 @@ static UINT guac_rdp_rail_handshake_ex(RailClientContext* rail,
|
|||||||
* @param context
|
* @param context
|
||||||
* The rdpContext associated with the active RDP session.
|
* The rdpContext associated with the active RDP session.
|
||||||
*
|
*
|
||||||
* @param e
|
* @param args
|
||||||
* Event-specific arguments, mainly the name of the channel, and a
|
* Event-specific arguments, mainly the name of the channel, and a
|
||||||
* reference to the associated plugin loaded for that channel by FreeRDP.
|
* reference to the associated plugin loaded for that channel by FreeRDP.
|
||||||
*/
|
*/
|
||||||
static void guac_rdp_rail_channel_connected(rdpContext* context,
|
static void guac_rdp_rail_channel_connected(rdpContext* context,
|
||||||
ChannelConnectedEventArgs* e) {
|
ChannelConnectedEventArgs* args) {
|
||||||
|
|
||||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
|
|
||||||
/* Ignore connection event if it's not for the RAIL channel */
|
/* Ignore connection event if it's not for the RAIL channel */
|
||||||
if (strcmp(e->name, RAIL_SVC_CHANNEL_NAME) != 0)
|
if (strcmp(args->name, RAIL_SVC_CHANNEL_NAME) != 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* The structure pointed to by pInterface is guaranteed to be a
|
/* The structure pointed to by pInterface is guaranteed to be a
|
||||||
* RailClientContext if the channel is RAIL */
|
* RailClientContext if the channel is RAIL */
|
||||||
RailClientContext* rail = (RailClientContext*) e->pInterface;
|
RailClientContext* rail = (RailClientContext*) args->pInterface;
|
||||||
|
|
||||||
/* Init FreeRDP RAIL context, ensuring the guac_client can be accessed from
|
/* Init FreeRDP RAIL context, ensuring the guac_client can be accessed from
|
||||||
* within any RAIL-specific callbacks */
|
* within any RAIL-specific callbacks */
|
||||||
|
@ -66,23 +66,23 @@ void guac_rdp_rdpei_free(guac_rdp_rdpei* rdpei) {
|
|||||||
* @param context
|
* @param context
|
||||||
* The rdpContext associated with the active RDP session.
|
* The rdpContext associated with the active RDP session.
|
||||||
*
|
*
|
||||||
* @param e
|
* @param args
|
||||||
* Event-specific arguments, mainly the name of the channel, and a
|
* Event-specific arguments, mainly the name of the channel, and a
|
||||||
* reference to the associated plugin loaded for that channel by FreeRDP.
|
* reference to the associated plugin loaded for that channel by FreeRDP.
|
||||||
*/
|
*/
|
||||||
static void guac_rdp_rdpei_channel_connected(rdpContext* context,
|
static void guac_rdp_rdpei_channel_connected(rdpContext* context,
|
||||||
ChannelConnectedEventArgs* e) {
|
ChannelConnectedEventArgs* args) {
|
||||||
|
|
||||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
guac_rdp_rdpei* guac_rdpei = rdp_client->rdpei;
|
guac_rdp_rdpei* guac_rdpei = rdp_client->rdpei;
|
||||||
|
|
||||||
/* Ignore connection event if it's not for the RDPEI channel */
|
/* Ignore connection event if it's not for the RDPEI channel */
|
||||||
if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) != 0)
|
if (strcmp(args->name, RDPEI_DVC_CHANNEL_NAME) != 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Store reference to the RDPEI plugin once it's connected */
|
/* Store reference to the RDPEI plugin once it's connected */
|
||||||
RdpeiClientContext* rdpei = (RdpeiClientContext*) e->pInterface;
|
RdpeiClientContext* rdpei = (RdpeiClientContext*) args->pInterface;
|
||||||
guac_rdpei->rdpei = rdpei;
|
guac_rdpei->rdpei = rdpei;
|
||||||
|
|
||||||
/* Declare level of multi-touch support */
|
/* Declare level of multi-touch support */
|
||||||
@ -107,19 +107,19 @@ static void guac_rdp_rdpei_channel_connected(rdpContext* context,
|
|||||||
* @param context
|
* @param context
|
||||||
* The rdpContext associated with the active RDP session.
|
* The rdpContext associated with the active RDP session.
|
||||||
*
|
*
|
||||||
* @param e
|
* @param args
|
||||||
* Event-specific arguments, mainly the name of the channel, and a
|
* Event-specific arguments, mainly the name of the channel, and a
|
||||||
* reference to the associated plugin loaded for that channel by FreeRDP.
|
* reference to the associated plugin loaded for that channel by FreeRDP.
|
||||||
*/
|
*/
|
||||||
static void guac_rdp_rdpei_channel_disconnected(rdpContext* context,
|
static void guac_rdp_rdpei_channel_disconnected(rdpContext* context,
|
||||||
ChannelDisconnectedEventArgs* e) {
|
ChannelDisconnectedEventArgs* args) {
|
||||||
|
|
||||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
guac_rdp_rdpei* guac_rdpei = rdp_client->rdpei;
|
guac_rdp_rdpei* guac_rdpei = rdp_client->rdpei;
|
||||||
|
|
||||||
/* Ignore disconnection event if it's not for the RDPEI channel */
|
/* Ignore disconnection event if it's not for the RDPEI channel */
|
||||||
if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) != 0)
|
if (strcmp(args->name, RDPEI_DVC_CHANNEL_NAME) != 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Channel is no longer connected */
|
/* Channel is no longer connected */
|
||||||
|
122
src/protocols/rdp/channels/rdpgfx.c
Normal file
122
src/protocols/rdp/channels/rdpgfx.c
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 "channels/rdpgfx.h"
|
||||||
|
#include "plugins/channels.h"
|
||||||
|
#include "rdp.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
#include <freerdp/client/rdpgfx.h>
|
||||||
|
#include <freerdp/freerdp.h>
|
||||||
|
#include <freerdp/gdi/gfx.h>
|
||||||
|
#include <freerdp/event.h>
|
||||||
|
#include <guacamole/client.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback which associates handlers specific to Guacamole with the
|
||||||
|
* RdpgfxClientContext instance allocated by FreeRDP to deal with received
|
||||||
|
* RDPGFX (Graphics Pipeline) messages.
|
||||||
|
*
|
||||||
|
* This function is called whenever a channel connects via the PubSub event
|
||||||
|
* system within FreeRDP, but only has any effect if the connected channel is
|
||||||
|
* the RDPGFX channel. This specific callback is registered with the
|
||||||
|
* PubSub system of the relevant rdpContext when guac_rdp_rdpgfx_load_plugin() is
|
||||||
|
* called.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* The rdpContext associated with the active RDP session.
|
||||||
|
*
|
||||||
|
* @param args
|
||||||
|
* Event-specific arguments, mainly the name of the channel, and a
|
||||||
|
* reference to the associated plugin loaded for that channel by FreeRDP.
|
||||||
|
*/
|
||||||
|
static void guac_rdp_rdpgfx_channel_connected(rdpContext* context,
|
||||||
|
ChannelConnectedEventArgs* args) {
|
||||||
|
|
||||||
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
|
|
||||||
|
/* Ignore connection event if it's not for the RDPGFX channel */
|
||||||
|
if (strcmp(args->name, RDPGFX_DVC_CHANNEL_NAME) != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Init GDI-backed support for the Graphics Pipeline */
|
||||||
|
RdpgfxClientContext* rdpgfx = (RdpgfxClientContext*) args->pInterface;
|
||||||
|
rdpGdi* gdi = context->gdi;
|
||||||
|
|
||||||
|
if (!gdi_graphics_pipeline_init(gdi, rdpgfx))
|
||||||
|
guac_client_log(client, GUAC_LOG_WARNING, "Rendering backend for RDPGFX "
|
||||||
|
"channel could not be loaded. Graphics may not render at all!");
|
||||||
|
else
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG, "RDPGFX channel will be used for "
|
||||||
|
"the RDP Graphics Pipeline Extension.");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback which handles any RDPGFX cleanup specific to Guacamole.
|
||||||
|
*
|
||||||
|
* This function is called whenever a channel disconnects via the PubSub event
|
||||||
|
* system within FreeRDP, but only has any effect if the disconnected channel
|
||||||
|
* is the RDPGFX channel. This specific callback is registered with the PubSub
|
||||||
|
* system of the relevant rdpContext when guac_rdp_rdpgfx_load_plugin() is
|
||||||
|
* called.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* The rdpContext associated with the active RDP session.
|
||||||
|
*
|
||||||
|
* @param args
|
||||||
|
* Event-specific arguments, mainly the name of the channel, and a
|
||||||
|
* reference to the associated plugin loaded for that channel by FreeRDP.
|
||||||
|
*/
|
||||||
|
static void guac_rdp_rdpgfx_channel_disconnected(rdpContext* context,
|
||||||
|
ChannelDisconnectedEventArgs* args) {
|
||||||
|
|
||||||
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
|
|
||||||
|
/* Ignore disconnection event if it's not for the RDPGFX channel */
|
||||||
|
if (strcmp(args->name, RDPGFX_DVC_CHANNEL_NAME) != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Un-init GDI-backed support for the Graphics Pipeline */
|
||||||
|
RdpgfxClientContext* rdpgfx = (RdpgfxClientContext*) args->pInterface;
|
||||||
|
rdpGdi* gdi = context->gdi;
|
||||||
|
gdi_graphics_pipeline_uninit(gdi, rdpgfx);
|
||||||
|
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG, "RDPGFX channel support unloaded.");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void guac_rdp_rdpgfx_load_plugin(rdpContext* context) {
|
||||||
|
|
||||||
|
/* Subscribe to and handle channel connected events */
|
||||||
|
PubSub_SubscribeChannelConnected(context->pubSub,
|
||||||
|
(pChannelConnectedEventHandler) guac_rdp_rdpgfx_channel_connected);
|
||||||
|
|
||||||
|
/* Subscribe to and handle channel disconnected events */
|
||||||
|
PubSub_SubscribeChannelDisconnected(context->pubSub,
|
||||||
|
(pChannelDisconnectedEventHandler) guac_rdp_rdpgfx_channel_disconnected);
|
||||||
|
|
||||||
|
/* Add "rdpgfx" channel */
|
||||||
|
guac_freerdp_dynamic_channel_collection_add(context->settings, "rdpgfx", NULL);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
49
src/protocols/rdp/channels/rdpgfx.h
Normal file
49
src/protocols/rdp/channels/rdpgfx.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 GUAC_RDP_CHANNELS_RDPGFX_H
|
||||||
|
#define GUAC_RDP_CHANNELS_RDPGFX_H
|
||||||
|
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
#include <freerdp/client/rdpgfx.h>
|
||||||
|
#include <freerdp/freerdp.h>
|
||||||
|
#include <guacamole/client.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds FreeRDP's "rdpgfx" plugin to the list of dynamic virtual channel plugins
|
||||||
|
* to be loaded by FreeRDP's "drdynvc" plugin. The context of the plugin will
|
||||||
|
* automatically be assicated with the guac_rdp_rdpgfx instance pointed to by the
|
||||||
|
* current guac_rdp_client. The plugin will only be loaded once the "drdynvc"
|
||||||
|
* plugin is loaded. The "rdpgfx" plugin ultimately adds support for the RDP
|
||||||
|
* Graphics Pipeline Extension.
|
||||||
|
*
|
||||||
|
* If failures occur, messages noting the specifics of those failures will be
|
||||||
|
* logged.
|
||||||
|
*
|
||||||
|
* This MUST be called within the PreConnect callback of the freerdp instance
|
||||||
|
* for Graphics Pipeline support to be loaded.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* The rdpContext associated with the active RDP session.
|
||||||
|
*/
|
||||||
|
void guac_rdp_rdpgfx_load_plugin(rdpContext* context);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
#include <cairo/cairo.h>
|
#include <cairo/cairo.h>
|
||||||
#include <freerdp/freerdp.h>
|
#include <freerdp/freerdp.h>
|
||||||
|
#include <freerdp/gdi/gdi.h>
|
||||||
#include <freerdp/graphics.h>
|
#include <freerdp/graphics.h>
|
||||||
#include <freerdp/primary.h>
|
#include <freerdp/primary.h>
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
@ -371,11 +372,112 @@ BOOL guac_rdp_gdi_set_bounds(rdpContext* context, const rdpBounds* bounds) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL guac_rdp_gdi_end_paint(rdpContext* context) {
|
void guac_rdp_gdi_mark_frame(rdpContext* context, int starting) {
|
||||||
/* IGNORE */
|
|
||||||
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
|
|
||||||
|
/* The server supports defining explicit frames */
|
||||||
|
rdp_client->frames_supported = 1;
|
||||||
|
|
||||||
|
/* A new frame is beginning */
|
||||||
|
if (starting) {
|
||||||
|
rdp_client->in_frame = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The current frame has ended */
|
||||||
|
guac_timestamp frame_end = guac_timestamp_current();
|
||||||
|
int time_elapsed = frame_end - client->last_sent_timestamp;
|
||||||
|
rdp_client->in_frame = 0;
|
||||||
|
|
||||||
|
/* A new frame has been received from the RDP server and processed */
|
||||||
|
rdp_client->frames_received++;
|
||||||
|
|
||||||
|
/* Flush a new frame if the client is ready for it */
|
||||||
|
if (time_elapsed >= guac_client_get_processing_lag(client)) {
|
||||||
|
guac_common_display_flush(rdp_client->display);
|
||||||
|
guac_client_end_multiple_frames(client, rdp_client->frames_received);
|
||||||
|
guac_socket_flush(client->socket);
|
||||||
|
rdp_client->frames_received = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL guac_rdp_gdi_frame_marker(rdpContext* context, const FRAME_MARKER_ORDER* frame_marker) {
|
||||||
|
guac_rdp_gdi_mark_frame(context, frame_marker->action == FRAME_START);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOL guac_rdp_gdi_surface_frame_marker(rdpContext* context, const SURFACE_FRAME_MARKER* surface_frame_marker) {
|
||||||
|
|
||||||
|
guac_rdp_gdi_mark_frame(context, surface_frame_marker->frameAction == SURFACECMD_FRAMEACTION_END);
|
||||||
|
|
||||||
|
if (context->settings->FrameAcknowledge > 0)
|
||||||
|
IFCALL(context->update->SurfaceFrameAcknowledge, context,
|
||||||
|
surface_frame_marker->frameId);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL guac_rdp_gdi_begin_paint(rdpContext* context) {
|
||||||
|
|
||||||
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
|
|
||||||
|
/* Leverage BeginPaint handler to detect start of frame for RDPGFX channel */
|
||||||
|
if (rdp_client->settings->enable_gfx && rdp_client->frames_supported)
|
||||||
|
guac_rdp_gdi_mark_frame(context, 1);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL guac_rdp_gdi_end_paint(rdpContext* context) {
|
||||||
|
|
||||||
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
|
rdpGdi* gdi = context->gdi;
|
||||||
|
|
||||||
|
/* Ignore EndPaint handler unless needed to detect end of frame for RDPGFX
|
||||||
|
* channel */
|
||||||
|
if (!rdp_client->settings->enable_gfx)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
/* Ignore paint if GDI output is suppressed */
|
||||||
|
if (gdi->suppressOutput)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
/* Ignore paint if nothing has been done (empty rect) */
|
||||||
|
if (gdi->primary->hdc->hwnd->invalid->null)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
INT32 x = gdi->primary->hdc->hwnd->invalid->x;
|
||||||
|
INT32 y = gdi->primary->hdc->hwnd->invalid->y;
|
||||||
|
UINT32 w = gdi->primary->hdc->hwnd->invalid->w;
|
||||||
|
UINT32 h = gdi->primary->hdc->hwnd->invalid->h;
|
||||||
|
|
||||||
|
/* Create surface from image data */
|
||||||
|
cairo_surface_t* surface = cairo_image_surface_create_for_data(
|
||||||
|
gdi->primary_buffer + 4*x + y*gdi->stride,
|
||||||
|
CAIRO_FORMAT_RGB24, w, h, gdi->stride);
|
||||||
|
|
||||||
|
/* Send surface to buffer */
|
||||||
|
guac_common_surface_draw(rdp_client->display->default_surface, x, y, surface);
|
||||||
|
|
||||||
|
/* Free surface */
|
||||||
|
cairo_surface_destroy(surface);
|
||||||
|
|
||||||
|
/* Next frame */
|
||||||
|
if (gdi->inGfxFrame) {
|
||||||
|
guac_rdp_gdi_mark_frame(context, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
BOOL guac_rdp_gdi_desktop_resize(rdpContext* context) {
|
BOOL guac_rdp_gdi_desktop_resize(rdpContext* context) {
|
||||||
|
|
||||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
@ -391,7 +493,8 @@ BOOL guac_rdp_gdi_desktop_resize(rdpContext* context) {
|
|||||||
guac_rdp_get_width(context->instance),
|
guac_rdp_get_width(context->instance),
|
||||||
guac_rdp_get_height(context->instance));
|
guac_rdp_get_height(context->instance));
|
||||||
|
|
||||||
return TRUE;
|
return gdi_resize(context->gdi, guac_rdp_get_width(context->instance),
|
||||||
|
guac_rdp_get_height(context->instance));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,8 +156,68 @@ BOOL guac_rdp_gdi_opaquerect(rdpContext* context,
|
|||||||
BOOL guac_rdp_gdi_set_bounds(rdpContext* context, const rdpBounds* bounds);
|
BOOL guac_rdp_gdi_set_bounds(rdpContext* context, const rdpBounds* bounds);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler called when a paint operation is complete. We don't actually
|
* Notifies the internal GDI implementation that a frame is either starting or
|
||||||
* use this, but FreeRDP requires it. Calling this function has no effect.
|
* ending. If the frame is ending and the connected client is ready to receive
|
||||||
|
* a new frame, a new frame will be flushed to the client.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* The rdpContext associated with the current RDP session.
|
||||||
|
*
|
||||||
|
* @param starting
|
||||||
|
* Non-zero if the frame in question is starting, zero if the frame is
|
||||||
|
* ending.
|
||||||
|
*/
|
||||||
|
void guac_rdp_gdi_mark_frame(rdpContext* context, int starting);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler called when a frame boundary is received from the RDP server in the
|
||||||
|
* form of a frame marker command. Each frame boundary may be the beginning or
|
||||||
|
* the end of a frame.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* The rdpContext associated with the current RDP session.
|
||||||
|
*
|
||||||
|
* @param frame_marker
|
||||||
|
* The received frame marker.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* TRUE if successful, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
BOOL guac_rdp_gdi_frame_marker(rdpContext* context, const FRAME_MARKER_ORDER* frame_marker);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler called when a frame boundary is received from the RDP server in the
|
||||||
|
* form of a surface frame marker. Each frame boundary may be the beginning or
|
||||||
|
* the end of a frame.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* The rdpContext associated with the current RDP session.
|
||||||
|
*
|
||||||
|
* @param surface_frame_marker
|
||||||
|
* The received frame marker.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* TRUE if successful, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
BOOL guac_rdp_gdi_surface_frame_marker(rdpContext* context, const SURFACE_FRAME_MARKER* surface_frame_marker);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler called when a paint operation is beginning. This function is
|
||||||
|
* expected to be called by the FreeRDP GDI implementation of RemoteFX when a
|
||||||
|
* new frame has started.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* The rdpContext associated with the current RDP session.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* TRUE if successful, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
BOOL guac_rdp_gdi_begin_paint(rdpContext* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler called when a paint operation is complete. This function is
|
||||||
|
* expected to be called by the FreeRDP GDI implementation of RemoteFX when a
|
||||||
|
* new frame has been completed.
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* The rdpContext associated with the current RDP session.
|
* The rdpContext associated with the current RDP session.
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "common/cursor.h"
|
#include "common/cursor.h"
|
||||||
#include "common/display.h"
|
#include "common/display.h"
|
||||||
#include "common/surface.h"
|
#include "common/surface.h"
|
||||||
|
#include "gdi.h"
|
||||||
#include "pointer.h"
|
#include "pointer.h"
|
||||||
#include "rdp.h"
|
#include "rdp.h"
|
||||||
|
|
||||||
@ -78,11 +79,22 @@ BOOL guac_rdp_pointer_set(rdpContext* context, const rdpPointer* pointer) {
|
|||||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
|
|
||||||
|
/* Add explicit frame boundaries around cursor set operation if not already
|
||||||
|
* in a frame (the RDP protocol does not send nor expect frame boundaries
|
||||||
|
* for cursor changes, but Guacamole does expect this) */
|
||||||
|
int in_frame = rdp_client->in_frame;
|
||||||
|
|
||||||
|
if (rdp_client->frames_supported && !in_frame)
|
||||||
|
guac_rdp_gdi_mark_frame(context, 1);
|
||||||
|
|
||||||
/* Set cursor */
|
/* Set cursor */
|
||||||
guac_common_cursor_set_surface(rdp_client->display->cursor,
|
guac_common_cursor_set_surface(rdp_client->display->cursor,
|
||||||
pointer->xPos, pointer->yPos,
|
pointer->xPos, pointer->yPos,
|
||||||
((guac_rdp_pointer*) pointer)->layer->surface);
|
((guac_rdp_pointer*) pointer)->layer->surface);
|
||||||
|
|
||||||
|
if (rdp_client->frames_supported && !in_frame)
|
||||||
|
guac_rdp_gdi_mark_frame(context, 0);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -106,9 +118,20 @@ BOOL guac_rdp_pointer_set_null(rdpContext* context) {
|
|||||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
|
|
||||||
|
/* Add explicit frame boundaries around cursor set operation if not already
|
||||||
|
* in a frame (the RDP protocol does not send nor expect frame boundaries
|
||||||
|
* for cursor changes, but Guacamole does expect this) */
|
||||||
|
int in_frame = rdp_client->in_frame;
|
||||||
|
|
||||||
|
if (rdp_client->frames_supported && !in_frame)
|
||||||
|
guac_rdp_gdi_mark_frame(context, 1);
|
||||||
|
|
||||||
/* Set cursor to empty/blank graphic */
|
/* Set cursor to empty/blank graphic */
|
||||||
guac_common_cursor_set_blank(rdp_client->display->cursor);
|
guac_common_cursor_set_blank(rdp_client->display->cursor);
|
||||||
|
|
||||||
|
if (rdp_client->frames_supported && !in_frame)
|
||||||
|
guac_rdp_gdi_mark_frame(context, 0);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -118,9 +141,20 @@ BOOL guac_rdp_pointer_set_default(rdpContext* context) {
|
|||||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||||
|
|
||||||
|
/* Add explicit frame boundaries around cursor set operation if not already
|
||||||
|
* in a frame (the RDP protocol does not send nor expect frame boundaries
|
||||||
|
* for cursor changes, but Guacamole does expect this) */
|
||||||
|
int in_frame = rdp_client->in_frame;
|
||||||
|
|
||||||
|
if (rdp_client->frames_supported && !in_frame)
|
||||||
|
guac_rdp_gdi_mark_frame(context, 1);
|
||||||
|
|
||||||
/* Set cursor to embedded pointer */
|
/* Set cursor to embedded pointer */
|
||||||
guac_common_cursor_set_pointer(rdp_client->display->cursor);
|
guac_common_cursor_set_pointer(rdp_client->display->cursor);
|
||||||
|
|
||||||
|
if (rdp_client->frames_supported && !in_frame)
|
||||||
|
guac_rdp_gdi_mark_frame(context, 0);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include "channels/rail.h"
|
#include "channels/rail.h"
|
||||||
#include "channels/rdpdr/rdpdr.h"
|
#include "channels/rdpdr/rdpdr.h"
|
||||||
#include "channels/rdpei.h"
|
#include "channels/rdpei.h"
|
||||||
|
#include "channels/rdpgfx.h"
|
||||||
#include "channels/rdpsnd/rdpsnd.h"
|
#include "channels/rdpsnd/rdpsnd.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "color.h"
|
#include "color.h"
|
||||||
@ -137,15 +138,6 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Load plugin providing Dynamic Virtual Channel support, if required */
|
|
||||||
if (instance->settings->SupportDynamicChannels &&
|
|
||||||
guac_freerdp_channels_load_plugin(context, "drdynvc",
|
|
||||||
instance->settings)) {
|
|
||||||
guac_client_log(client, GUAC_LOG_WARNING,
|
|
||||||
"Failed to load drdynvc plugin. Display update and audio "
|
|
||||||
"input support will be disabled.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Init FreeRDP internal GDI implementation */
|
/* Init FreeRDP internal GDI implementation */
|
||||||
if (!gdi_init(instance, guac_rdp_get_native_pixel_format(FALSE)))
|
if (!gdi_init(instance, guac_rdp_get_native_pixel_format(FALSE)))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -187,9 +179,13 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
|
|||||||
|
|
||||||
/* Set up GDI */
|
/* Set up GDI */
|
||||||
instance->update->DesktopResize = guac_rdp_gdi_desktop_resize;
|
instance->update->DesktopResize = guac_rdp_gdi_desktop_resize;
|
||||||
|
instance->update->BeginPaint = guac_rdp_gdi_begin_paint;
|
||||||
instance->update->EndPaint = guac_rdp_gdi_end_paint;
|
instance->update->EndPaint = guac_rdp_gdi_end_paint;
|
||||||
instance->update->SetBounds = guac_rdp_gdi_set_bounds;
|
instance->update->SetBounds = guac_rdp_gdi_set_bounds;
|
||||||
|
|
||||||
|
instance->update->SurfaceFrameMarker = guac_rdp_gdi_surface_frame_marker;
|
||||||
|
instance->update->altsec->FrameMarker = guac_rdp_gdi_frame_marker;
|
||||||
|
|
||||||
rdpPrimaryUpdate* primary = instance->update->primary;
|
rdpPrimaryUpdate* primary = instance->update->primary;
|
||||||
primary->DstBlt = guac_rdp_gdi_dstblt;
|
primary->DstBlt = guac_rdp_gdi_dstblt;
|
||||||
primary->PatBlt = guac_rdp_gdi_patblt;
|
primary->PatBlt = guac_rdp_gdi_patblt;
|
||||||
@ -204,6 +200,19 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
|
|||||||
offscreen_cache_register_callbacks(instance->update);
|
offscreen_cache_register_callbacks(instance->update);
|
||||||
palette_cache_register_callbacks(instance->update);
|
palette_cache_register_callbacks(instance->update);
|
||||||
|
|
||||||
|
/* Load "rdpgfx" plugin for Graphics Pipeline Extension */
|
||||||
|
if (settings->enable_gfx)
|
||||||
|
guac_rdp_rdpgfx_load_plugin(context);
|
||||||
|
|
||||||
|
/* Load plugin providing Dynamic Virtual Channel support, if required */
|
||||||
|
if (instance->settings->SupportDynamicChannels &&
|
||||||
|
guac_freerdp_channels_load_plugin(context, "drdynvc",
|
||||||
|
instance->settings)) {
|
||||||
|
guac_client_log(client, GUAC_LOG_WARNING,
|
||||||
|
"Failed to load drdynvc plugin. Display update and audio "
|
||||||
|
"input support will be disabled.");
|
||||||
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -544,7 +553,6 @@ static int guac_rdp_handle_connection(guac_client* client) {
|
|||||||
if (wait_result > 0) {
|
if (wait_result > 0) {
|
||||||
|
|
||||||
int processing_lag = guac_client_get_processing_lag(client);
|
int processing_lag = guac_client_get_processing_lag(client);
|
||||||
guac_timestamp frame_start = guac_timestamp_current();
|
|
||||||
|
|
||||||
/* Read server messages until frame is built */
|
/* Read server messages until frame is built */
|
||||||
do {
|
do {
|
||||||
@ -564,7 +572,15 @@ static int guac_rdp_handle_connection(guac_client* client) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Continue handling inbound data if we are in the middle of an RDP frame */
|
||||||
|
if (rdp_client->in_frame) {
|
||||||
|
wait_result = rdp_guac_client_wait_for_messages(client, GUAC_RDP_FRAME_START_TIMEOUT);
|
||||||
|
if (wait_result >= 0)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* Calculate time remaining in frame */
|
/* Calculate time remaining in frame */
|
||||||
|
guac_timestamp frame_start = client->last_sent_timestamp;
|
||||||
frame_end = guac_timestamp_current();
|
frame_end = guac_timestamp_current();
|
||||||
frame_remaining = frame_start + GUAC_RDP_FRAME_DURATION
|
frame_remaining = frame_start + GUAC_RDP_FRAME_DURATION
|
||||||
- frame_end;
|
- frame_end;
|
||||||
@ -587,12 +603,6 @@ static int guac_rdp_handle_connection(guac_client* client) {
|
|||||||
|
|
||||||
} while (wait_result > 0);
|
} while (wait_result > 0);
|
||||||
|
|
||||||
/* Record end of frame, excluding server-side rendering time (we
|
|
||||||
* assume server-side rendering time will be consistent between any
|
|
||||||
* two subsequent frames, and that this time should thus be
|
|
||||||
* excluded from the required wait period of the next frame). */
|
|
||||||
last_frame_end = frame_start;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Test whether the RDP server is closing the connection */
|
/* Test whether the RDP server is closing the connection */
|
||||||
@ -607,11 +617,13 @@ static int guac_rdp_handle_connection(guac_client* client) {
|
|||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_UNAVAILABLE,
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_UNAVAILABLE,
|
||||||
"Connection closed.");
|
"Connection closed.");
|
||||||
|
|
||||||
/* Flush frame only if successful */
|
/* Flush frame only if successful and an RDP frame is not known to be
|
||||||
else {
|
* in progress */
|
||||||
|
else if (!rdp_client->frames_supported || rdp_client->frames_received) {
|
||||||
guac_common_display_flush(rdp_client->display);
|
guac_common_display_flush(rdp_client->display);
|
||||||
guac_client_end_frame(client);
|
guac_client_end_multiple_frames(client, rdp_client->frames_received);
|
||||||
guac_socket_flush(client->socket);
|
guac_socket_flush(client->socket);
|
||||||
|
rdp_client->frames_received = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -91,6 +91,24 @@ typedef struct guac_rdp_client {
|
|||||||
*/
|
*/
|
||||||
guac_common_surface* current_surface;
|
guac_common_surface* current_surface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the RDP server supports defining explicit frame boundaries.
|
||||||
|
*/
|
||||||
|
int frames_supported;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the RDP server has reported that a new frame is in progress, and
|
||||||
|
* we are now receiving updates relevant to that frame.
|
||||||
|
*/
|
||||||
|
int in_frame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of distinct frames received from the RDP server since last
|
||||||
|
* flush, if the RDP server supports reporting frame boundaries. If the RDP
|
||||||
|
* server does not support tracking frames, this will be zero.
|
||||||
|
*/
|
||||||
|
int frames_received;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current state of the keyboard with respect to the RDP session.
|
* The current state of the keyboard with respect to the RDP session.
|
||||||
*/
|
*/
|
||||||
|
@ -80,6 +80,7 @@ const char* GUAC_RDP_CLIENT_ARGS[] = {
|
|||||||
"disable-bitmap-caching",
|
"disable-bitmap-caching",
|
||||||
"disable-offscreen-caching",
|
"disable-offscreen-caching",
|
||||||
"disable-glyph-caching",
|
"disable-glyph-caching",
|
||||||
|
"disable-gfx",
|
||||||
"preconnection-id",
|
"preconnection-id",
|
||||||
"preconnection-blob",
|
"preconnection-blob",
|
||||||
"timezone",
|
"timezone",
|
||||||
@ -371,6 +372,13 @@ enum RDP_ARGS_IDX {
|
|||||||
*/
|
*/
|
||||||
IDX_DISABLE_GLYPH_CACHING,
|
IDX_DISABLE_GLYPH_CACHING,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "true" if the RDP Graphics Pipeline Extension should not be used, and
|
||||||
|
* traditional RDP graphics should be used instead, "false" or blank if the
|
||||||
|
* Graphics Pipeline Extension should be used if available.
|
||||||
|
*/
|
||||||
|
IDX_DISABLE_GFX,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The preconnection ID to send within the preconnection PDU when
|
* The preconnection ID to send within the preconnection PDU when
|
||||||
* initiating an RDP connection, if any.
|
* initiating an RDP connection, if any.
|
||||||
@ -908,11 +916,6 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user,
|
|||||||
GUAC_RDP_CLIENT_ARGS[IDX_DISABLE_GLYPH_CACHING]);
|
GUAC_RDP_CLIENT_ARGS[IDX_DISABLE_GLYPH_CACHING]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Session color depth */
|
|
||||||
settings->color_depth =
|
|
||||||
guac_user_parse_args_int(user, GUAC_RDP_CLIENT_ARGS, argv,
|
|
||||||
IDX_COLOR_DEPTH, RDP_DEFAULT_DEPTH);
|
|
||||||
|
|
||||||
/* Preconnection ID */
|
/* Preconnection ID */
|
||||||
settings->preconnection_id = -1;
|
settings->preconnection_id = -1;
|
||||||
if (argv[IDX_PRECONNECTION_ID][0] != '\0') {
|
if (argv[IDX_PRECONNECTION_ID][0] != '\0') {
|
||||||
@ -1129,6 +1132,16 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user,
|
|||||||
settings->resize_method = GUAC_RESIZE_NONE;
|
settings->resize_method = GUAC_RESIZE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* RDP Graphics Pipeline enable/disable */
|
||||||
|
settings->enable_gfx =
|
||||||
|
!guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||||
|
IDX_DISABLE_GFX, 0);
|
||||||
|
|
||||||
|
/* Session color depth */
|
||||||
|
settings->color_depth =
|
||||||
|
guac_user_parse_args_int(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||||
|
IDX_COLOR_DEPTH, settings->enable_gfx ? RDP_GFX_REQUIRED_DEPTH : RDP_DEFAULT_DEPTH);
|
||||||
|
|
||||||
/* Multi-touch input enable/disable */
|
/* Multi-touch input enable/disable */
|
||||||
settings->enable_touch =
|
settings->enable_touch =
|
||||||
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
|
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||||
@ -1397,6 +1410,29 @@ void guac_rdp_push_settings(guac_client* client,
|
|||||||
/* Explicitly set flag value */
|
/* Explicitly set flag value */
|
||||||
rdp_settings->PerformanceFlags = guac_rdp_get_performance_flags(guac_settings);
|
rdp_settings->PerformanceFlags = guac_rdp_get_performance_flags(guac_settings);
|
||||||
|
|
||||||
|
/* Always request frame markers */
|
||||||
|
rdp_settings->FrameMarkerCommandEnabled = TRUE;
|
||||||
|
rdp_settings->SurfaceFrameMarkerEnabled = TRUE;
|
||||||
|
|
||||||
|
/* Enable RemoteFX / Graphics Pipeline */
|
||||||
|
if (guac_settings->enable_gfx) {
|
||||||
|
|
||||||
|
rdp_settings->SupportGraphicsPipeline = TRUE;
|
||||||
|
rdp_settings->RemoteFxCodec = TRUE;
|
||||||
|
|
||||||
|
if (rdp_settings->ColorDepth != RDP_GFX_REQUIRED_DEPTH) {
|
||||||
|
guac_client_log(client, GUAC_LOG_WARNING, "Ignoring requested "
|
||||||
|
"color depth of %i bpp, as the RDP Graphics Pipeline "
|
||||||
|
"requires %i bpp.", rdp_settings->ColorDepth, RDP_GFX_REQUIRED_DEPTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Required for RemoteFX / Graphics Pipeline */
|
||||||
|
rdp_settings->FastPathOutput = TRUE;
|
||||||
|
rdp_settings->ColorDepth = RDP_GFX_REQUIRED_DEPTH;
|
||||||
|
rdp_settings->SoftwareGdi = TRUE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/* Set individual flags - some FreeRDP versions overwrite the above */
|
/* Set individual flags - some FreeRDP versions overwrite the above */
|
||||||
rdp_settings->AllowFontSmoothing = guac_settings->font_smoothing_enabled;
|
rdp_settings->AllowFontSmoothing = guac_settings->font_smoothing_enabled;
|
||||||
rdp_settings->DisableWallpaper = !guac_settings->wallpaper_enabled;
|
rdp_settings->DisableWallpaper = !guac_settings->wallpaper_enabled;
|
||||||
|
@ -58,6 +58,11 @@
|
|||||||
*/
|
*/
|
||||||
#define RDP_DEFAULT_DEPTH 16
|
#define RDP_DEFAULT_DEPTH 16
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color depth required by the RDPGFX channel, in bits.
|
||||||
|
*/
|
||||||
|
#define RDP_GFX_REQUIRED_DEPTH 32
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The filename to use for the screen recording, if not specified.
|
* The filename to use for the screen recording, if not specified.
|
||||||
*/
|
*/
|
||||||
@ -552,6 +557,11 @@ typedef struct guac_rdp_settings {
|
|||||||
*/
|
*/
|
||||||
int enable_audio_input;
|
int enable_audio_input;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the RDP Graphics Pipeline Extension is enabled.
|
||||||
|
*/
|
||||||
|
int enable_gfx;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether multi-touch support is enabled.
|
* Whether multi-touch support is enabled.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user