Skip to content

Commit

Permalink
GUACAMOLE-1231: Implement basic support for restoring minimized RAIL …
Browse files Browse the repository at this point in the history
…windows.
  • Loading branch information
necouchman committed Jan 1, 2024
1 parent f0ab665 commit 789fda2
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 20 deletions.
91 changes: 71 additions & 20 deletions src/protocols/rdp/channels/rail.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,15 @@
#include <freerdp/event.h>
#include <freerdp/freerdp.h>
#include <freerdp/rail.h>
#include <freerdp/window.h>
#include <guacamole/client.h>
#include <guacamole/mem.h>
#include <winpr/wtypes.h>
#include <winpr/wtsapi.h>

#include <stddef.h>
#include <string.h>

#ifdef FREERDP_RAIL_CALLBACKS_REQUIRE_CONST
/**
* FreeRDP 2.0.0-rc4 and newer requires the final argument for all RAIL
* callbacks to be const.
*/
#define RAIL_CONST const
#else
/**
* FreeRDP 2.0.0-rc3 and older requires the final argument for all RAIL
* callbacks to NOT be const.
*/
#define RAIL_CONST
#endif

/**
* Completes initialization of the RemoteApp session, responding to the server
* handshake, sending client status and system parameters, and executing the
Expand Down Expand Up @@ -81,11 +69,12 @@ static UINT guac_rdp_rail_complete_handshake(RailClientContext* rail) {
/* Build number 7600 (0x1DB0) apparently represents Windows 7 and
* compatibility with RDP 7.0. As of this writing, this is the same
* build number sent for RAIL connections by xfreerdp. */
.buildNumber = 7600
.buildNumber = 9600

};

/* Send client handshake response */
guac_client_log(client, GUAC_LOG_DEBUG, "Sending RAIL handshake.");
pthread_mutex_lock(&(rdp_client->message_lock));
status = rail->ClientHandshake(rail, &handshake);
pthread_mutex_unlock(&(rdp_client->message_lock));
Expand All @@ -94,10 +83,13 @@ static UINT guac_rdp_rail_complete_handshake(RailClientContext* rail) {
return status;

RAIL_CLIENT_STATUS_ORDER client_status = {
.flags = 0x00
.flags =
TS_RAIL_CLIENTSTATUS_ALLOWLOCALMOVESIZE
| TS_RAIL_CLIENTSTATUS_APPBAR_REMOTING_SUPPORTED
};

/* Send client status */
guac_client_log(client, GUAC_LOG_DEBUG, "Sending RAIL client status.");
pthread_mutex_lock(&(rdp_client->message_lock));
status = rail->ClientInformation(rail, &client_status);
pthread_mutex_unlock(&(rdp_client->message_lock));
Expand Down Expand Up @@ -135,8 +127,7 @@ static UINT guac_rdp_rail_complete_handshake(RailClientContext* rail) {
},

.params =
SPI_MASK_SET_DRAG_FULL_WINDOWS
| SPI_MASK_SET_HIGH_CONTRAST
SPI_MASK_SET_HIGH_CONTRAST
| SPI_MASK_SET_KEYBOARD_CUES
| SPI_MASK_SET_KEYBOARD_PREF
| SPI_MASK_SET_MOUSE_BUTTON_SWAP
Expand All @@ -145,6 +136,7 @@ static UINT guac_rdp_rail_complete_handshake(RailClientContext* rail) {
};

/* Send client system parameters */
guac_client_log(client, GUAC_LOG_DEBUG, "Sending RAIL client system parameters.");
pthread_mutex_lock(&(rdp_client->message_lock));
status = rail->ClientSystemParam(rail, &sysparam);
pthread_mutex_unlock(&(rdp_client->message_lock));
Expand All @@ -160,6 +152,7 @@ static UINT guac_rdp_rail_complete_handshake(RailClientContext* rail) {
};

/* Execute desired RemoteApp command */
guac_client_log(client, GUAC_LOG_DEBUG, "Executing remote application.");
pthread_mutex_lock(&(rdp_client->message_lock));
status = rail->ClientExecute(rail, &exec);
pthread_mutex_unlock(&(rdp_client->message_lock));
Expand Down Expand Up @@ -189,6 +182,8 @@ static UINT guac_rdp_rail_complete_handshake(RailClientContext* rail) {
*/
static UINT guac_rdp_rail_handshake(RailClientContext* rail,
RAIL_CONST RAIL_HANDSHAKE_ORDER* handshake) {
guac_client* client = (guac_client*) rail->custom;
guac_client_log(client, GUAC_LOG_TRACE, "RAIL handshake callback.");
return guac_rdp_rail_complete_handshake(rail);
}

Expand All @@ -213,9 +208,63 @@ static UINT guac_rdp_rail_handshake(RailClientContext* rail,
*/
static UINT guac_rdp_rail_handshake_ex(RailClientContext* rail,
RAIL_CONST RAIL_HANDSHAKE_EX_ORDER* handshake_ex) {
guac_client* client = (guac_client*) rail->custom;
guac_client_log(client, GUAC_LOG_TRACE, "RAIL handshake ex callback.");
return guac_rdp_rail_complete_handshake(rail);
}

/**
* A callback function that is executed when an update for a RAIL window is
* received from the RDP server.
*
* @param context
* A pointer to the rdpContext structure used by FreeRDP to handle the
* window update.
*
* @param orderInfo
* A pointer to the data structure that contains information about what
* window was updated what updates were performed.
*
* @param windowState
* A pointer to the data structure that contains details of the updates
* to the window, as indicated by flags in the orderInfo field.
*
* @return
* TRUE if the client-side processing of the updates as successful; otherwise
* FALSE.
*/
static BOOL guac_rdp_rail_window_update(rdpContext* context,
RAIL_CONST WINDOW_ORDER_INFO* orderInfo,
RAIL_CONST WINDOW_STATE_ORDER* windowState) {

guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;

guac_client_log(client, GUAC_LOG_TRACE, "RAIL window update callback: %d", orderInfo->fieldFlags);

UINT32 fieldFlags = orderInfo->fieldFlags;

/* If the flag for window visibilty is set, check visibility. */
if (fieldFlags & WINDOW_ORDER_FIELD_SHOW) {
guac_client_log(client, GUAC_LOG_TRACE, "RAIL window visibility change: %d", windowState->showState);

/* State is either hidden or minimized - send restore command. */
if (windowState->showState == GUAC_RDP_RAIL_WINDOW_STATE_HIDDEN
|| windowState->showState == GUAC_RDP_RAIL_WINDOW_STATE_MINIMIZED) {

guac_client_log(client, GUAC_LOG_DEBUG, "RAIL window minimized, sending restore command.");

RAIL_SYSCOMMAND_ORDER syscommand;
syscommand.windowId = orderInfo->windowId;
syscommand.command = SC_RESTORE;
rdp_client->rail_interface->ClientSystemCommand(rdp_client->rail_interface, &syscommand);
}
}

return true;

}

/**
* Callback which associates handlers specific to Guacamole with the
* RailClientContext instance allocated by FreeRDP to deal with received
Expand All @@ -238,6 +287,7 @@ static void guac_rdp_rail_channel_connected(rdpContext* context,
ChannelConnectedEventArgs* args) {

guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;

/* Ignore connection event if it's not for the RAIL channel */
if (strcmp(args->name, RAIL_SVC_CHANNEL_NAME) != 0)
Expand All @@ -246,12 +296,14 @@ static void guac_rdp_rail_channel_connected(rdpContext* context,
/* The structure pointed to by pInterface is guaranteed to be a
* RailClientContext if the channel is RAIL */
RailClientContext* rail = (RailClientContext*) args->pInterface;
rdp_client->rail_interface = rail;

/* Init FreeRDP RAIL context, ensuring the guac_client can be accessed from
* within any RAIL-specific callbacks */
rail->custom = client;
rail->ServerHandshake = guac_rdp_rail_handshake;
rail->ServerHandshakeEx = guac_rdp_rail_handshake_ex;
context->update->window->WindowUpdate = guac_rdp_rail_window_update;

guac_client_log(client, GUAC_LOG_DEBUG, "RAIL (RemoteApp) channel "
"connected.");
Expand Down Expand Up @@ -279,5 +331,4 @@ void guac_rdp_rail_load_plugin(rdpContext* context) {
guac_client_log(client, GUAC_LOG_DEBUG, "Support for RAIL (RemoteApp) "
"registered. Awaiting channel connection.");

}

}
27 changes: 27 additions & 0 deletions src/protocols/rdp/channels/rail.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,34 @@
#ifndef GUAC_RDP_CHANNELS_RAIL_H
#define GUAC_RDP_CHANNELS_RAIL_H

#include "config.h"

#include <freerdp/freerdp.h>
#include <freerdp/window.h>

#ifdef FREERDP_RAIL_CALLBACKS_REQUIRE_CONST
/**
* FreeRDP 2.0.0-rc4 and newer requires the final argument for all RAIL
* callbacks to be const.
*/
#define RAIL_CONST const
#else
/**
* FreeRDP 2.0.0-rc3 and older requires the final argument for all RAIL
* callbacks to NOT be const.
*/
#define RAIL_CONST
#endif

/**
* The RAIL window state that indicates a hidden window.
*/
#define GUAC_RDP_RAIL_WINDOW_STATE_HIDDEN 0x00

/**
* The RAIL window state that indicates a visible but minimized window.
*/
#define GUAC_RDP_RAIL_WINDOW_STATE_MINIMIZED 0x02

/**
* Initializes RemoteApp support for RDP and handling of the RAIL channel. If
Expand Down
1 change: 1 addition & 0 deletions src/protocols/rdp/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "channels/cliprdr.h"
#include "channels/disp.h"
#include "channels/pipe-svc.h"
#include "channels/rail.h"
#include "config.h"
#include "fs.h"
#include "log.h"
Expand Down
7 changes: 7 additions & 0 deletions src/protocols/rdp/rdp.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@

#include <freerdp/codec/color.h>
#include <freerdp/freerdp.h>
#include <freerdp/client/rail.h>
#include <guacamole/audio.h>
#include <guacamole/client.h>
#include <guacamole/recording.h>
Expand Down Expand Up @@ -196,6 +197,12 @@ typedef struct guac_rdp_client {
*/
pthread_mutex_t message_lock;

/**
* A pointer to the RAIL interface provided by the RDP client when rail is
* in use.
*/
RailClientContext* rail_interface;

} guac_rdp_client;

/**
Expand Down

0 comments on commit 789fda2

Please sign in to comment.