Compare commits

...

64 Commits

Author SHA1 Message Date
Virtually Nick
47b9360d46
GUACAMOLE-1714: Merge update guacenc for const parameters/values introduced in FFmpeg 5.0. 2023-02-02 20:36:32 -05:00
Mike Jumper
98c2a6adcb
GUACAMOLE-377: Merge correction ensuring users receive a proper frame boundary when joining. 2023-01-24 14:25:09 -08:00
Alex Leitner
3b0a9bac75 GUACAMOLE-377: Send a sync instruction to users when synchronizing surfaces. 2023-01-23 20:55:01 +00:00
Mike Jumper
f6893ed319 Merge 1.5.0 changes back to master. 2023-01-10 21:54:05 -08:00
Virtually Nick
4afc1d85ce Merge 1.5.0 changes back to master. 2023-01-04 15:53:11 -05:00
James Muehlner
add7ce361b Merge 1.5.0 changes back to master. 2022-11-29 03:46:57 +00:00
Mike Jumper
55941823ec Merge 1.5.0 changes back to master. 2022-11-25 23:24:08 -08:00
Dan Fandrich
5cf408ebbb GUACAMOLE-1714: Adapt to const parameters of ffmpeg 5.0. 2022-11-07 12:16:35 -08:00
Mike Jumper
3ca6bb0a61
GUACAMOLE-1708: Merge correction to missing Czech keyboard character mapping. 2022-11-06 09:05:52 -08:00
Max
457a169c49 GUACAMOLE-1708: Added Czech keyboard keymap fix missing char 2022-11-06 12:17:21 +01:00
Mike Jumper
bad381cebe
GUACAMOLE-1708: Merge RDP support for Czech keyboard layout. 2022-11-05 08:56:56 -07:00
Max
6171da6d0b GUACAMOLE-1708: Added Czech keyboard keymap for RDP 2022-11-01 21:56:23 +01:00
Mike Jumper
067f2a91a0
GUACAMOLE-1682: Merge automatic newline normalization of terminal clipboard. 2022-10-18 12:41:48 -07:00
Alex Leitner
bc52485570 GUACAMOLE-1682: Normalize conflicting newline encodings in clipboards between Linux and Windows systems for ssh sessions. 2022-10-18 19:38:56 +00:00
Mike Jumper
b20afa275a
GUACAMOLE-1669: Merge fix for RSA key upgrade failure if FIPS mode is enabled. 2022-09-13 14:45:55 -07:00
James Muehlner
b096e47f57 GUACAMOLE-1669: Include ext-info-c in preferred KEX algorithms to ensure RSA key upgrades can happen. 2022-09-13 21:39:38 +00:00
Mike Jumper
4d211e0c9e
GUACAMOLE-1674: Merge changes removing NLA from negotiation if FIPS is enabled. 2022-09-08 09:44:47 -07:00
James Muehlner
dffbeac57a GUACAMOLE-1674: Warn about NLA mode if FIPS mode is enabled, or disable if possible. 2022-08-30 23:23:56 +00:00
Mike Jumper
0361adc01f
GUACAMOLE-1669: Merge FIPS support for SSH connections. 2022-08-24 15:29:46 -07:00
James Muehlner
1971a9dad2 GUACAMOLE-1669: Prefer FIPS compliant ciphers and algorithms when FIPS mode is enabled. 2022-08-24 22:23:46 +00:00
Virtually Nick
5dbf4820ab Merge 1.5.0 changes back to master. 2022-08-19 15:48:51 -04:00
James Muehlner
15f6e9f678 Merge 1.5.0 changes back to master. 2022-08-16 18:48:31 +00:00
Virtually Nick
6ab82446bb
GUACAMOLE-1652: Merge only call SSL init functions when the library version requires it. 2022-07-30 07:36:37 -04:00
James Muehlner
9c93337d97 GUACAMOLE-1652: Migrate OpenSSL initialization to modern methods for OpenSSL >= 1.1.0. 2022-07-30 02:24:31 +00:00
James Muehlner
cdee93ae25 GUACAMOLE-1652: Only call SSL init functions when the library version requires it. 2022-07-30 02:22:36 +00:00
Mike Jumper
eee3ac092c
GUACAMOLE-1622: Merge correction to terminal resize regression. 2022-07-13 16:20:19 -07:00
Alex Leitner
5bb56ed5ba GUACAMOLE-1622: Restructured code to resolve scrollbar resizing bug where the scrollbar would clip off the side of the terminal. This fix also resolves the issue where the text would blur at certain intervals of resizing the window. 2022-07-13 21:57:47 +00:00
Mike Jumper
0aae5eeadb
GUACAMOLE-1636: Merge corrections to typos within RDP comments/documentation. 2022-07-13 13:55:05 -07:00
Jimmy
6d994db9d2 GUACAMOLE-1636: Fix a typo mistake invokved. 2022-07-13 23:47:13 +03:00
Jimmy
cba5484be0 GUACAMOLE-1636: Fix a typo mistake recieved. 2022-07-13 23:41:42 +03:00
Jimmy
4048dd4900 GUACAMOLE-1636: Fix a typo mistake assicated. 2022-07-13 23:32:12 +03:00
Jimmy
98556fbe2e GUACAMOLE-1636: Fix a typo mistake coordinare. 2022-07-13 23:24:06 +03:00
Jimmy
f438a36612 GUACAMOLE-1636: Fix a typo mistake synchonize. 2022-07-13 23:17:50 +03:00
Jimmy
e8d966aec6 GUACAMOLE-1636: Fix a typo mistake Versoin. 2022-07-13 23:10:36 +03:00
Jimmy
523532a52d GUACAMOLE-1636: Fix a typo mistake offscren. 2022-07-13 23:02:37 +03:00
Mike Jumper
51c640fdbd
GUACAMOLE-1436: Merge addition of missing FreeRDP winpr headers. 2022-07-05 12:09:00 -07:00
Virtually Nick
4cf1bfae0e
GUACAMOLE-377: Merge update unit tests for new prototype of guac_protocol_send_sync(). 2022-07-05 14:30:57 -04:00
Michael Jumper
9642afc468 GUACAMOLE-377: Update unit tests for new prototype of guac_protocol_send_sync().
The new guac_protocol_send_sync() requires an additional parameter: the
number of logical frames associated with the sync.
2022-07-05 10:58:38 -07:00
James Muehlner
ffb6c809be
GUACAMOLE-1622: Merge addition of margins to ssh sessions. 2022-06-22 09:32:00 -07:00
Alex Leitner
64ea9c4d1f GUACAMOLE-1622: Clarified comments to describe if param is a pointer. 2022-06-21 16:16:52 +00:00
Alex Leitner
a5834fd319 GUACAMOLE-1622: Separated logic into single responsibility functions. 2022-06-16 17:09:41 +00:00
Alex Leitner
1e9cd9137b GUACAMOLE-1622: Added margins to ssh sessions. 2022-06-15 16:59:20 +00:00
James Muehlner
d4cd9b3e3a
GUACAMOLE-377: Merge support for RemoteFX. 2022-06-09 17:41:23 -07:00
Michael Jumper
31f1b2c7c4 GUACAMOLE-377: Rename single-letter "e" event arguments variable to "args" for readability. 2022-06-09 09:02:11 -07:00
Michael Jumper
ce27936ed5 GUACAMOLE-377: Add frame boundaries around cursor set operations if otherwise absent. 2022-06-09 09:02:11 -07:00
Michael Jumper
b7f05b9e4f GUACAMOLE-377: Ensure backing surface of underlying FreeRDP GDI implementation is resized when desktop is resized. 2022-06-09 09:02:11 -07:00
Michael Jumper
d5761ad625 GUACAMOLE-377: Warn about required color depth only if actually overridden. 2022-06-09 09:02:11 -07:00
Michael Jumper
b26f9d64d6 GUACAMOLE-377: Clarify usage of EndPaint to detect frames. 2022-06-09 09:02:11 -07:00
Michael Jumper
da80163e24 GUACAMOLE-377: Enable graphics pipeline extension by default. 2022-06-09 09:02:11 -07:00
Michael Jumper
28396ae345 GUACAMOLE-377: Expect explicit RDP frame boundaries only after at least one frame boundary has been received. 2022-05-18 15:43:54 -07:00
Michael Jumper
a0e9f6ed9b GUACAMOLE-377: Leverage client timestamp tracking for RDP frame duration. 2022-05-18 15:43:54 -07:00
Michael Jumper
bde8cdee46 GUACAMOLE-377: Add general RDP support for frame markers. 2022-05-18 15:43:54 -07:00
Michael Jumper
669e02b4dc GUACAMOLE-377: Leverage RDPGFX to report remote frame statistics to the client. 2022-05-12 13:50:20 -07:00
Michael Jumper
52c8683bcf GUACAMOLE-377: Add protocol-level support for reporting remote frame statistics. 2022-05-12 13:50:20 -07:00
Michael Jumper
c19eab9691 GUACAMOLE-377: Revise processing lag calculations to consider cumulative processing lag. 2022-05-12 13:50:20 -07:00
Michael Jumper
dd85c54961 GUACAMOLE-377: Add handling for EndPaint required by software GDI implementation of RDPGFX. 2022-05-12 13:50:20 -07:00
Michael Jumper
c795bf9e4a GUACAMOLE-377: Control RemoteFX / GFX support with "enable-gfx" parameter. 2022-05-12 13:50:20 -07:00
Michael Jumper
c469300941 GUACAMOLE-377: Support for RDPGFX. 2022-05-12 13:50:20 -07:00
James Muehlner
81300052e0
GUACAMOLE-1595: Merge mouse mask initialization fix. 2022-05-02 17:24:58 -07:00
Michael Jumper
df4e5c6fdf GUACAMOLE-1595: Ensure all mouse buttons are initially released when terminal starts. 2022-05-03 00:20:08 +00:00
Virtually Nick
a175a3d902
GUACAMOLE-1312: Merge add Canadian French RDP keymap 2022-03-23 09:58:58 -04:00
Kaven Rousseau
9cbd768210 GUACAMOLE-1312: Added fr_ca keymap 2022-03-17 16:13:42 -04:00
Virtually Nick
c716a07abc Merge 1.5.0 changes back to master. 2022-03-17 15:24:48 -04:00
Virtually Nick
bce1d2a434 GUACAMOLE-1436: Add winpr file.h dependencies as required. 2021-12-27 09:42:57 -05:00
44 changed files with 1092 additions and 168 deletions

View File

@ -22,6 +22,7 @@
#include "common-ssh/user.h"
#include <guacamole/client.h>
#include <guacamole/fips.h>
#include <libssh2.h>
#ifdef LIBSSH2_USES_GCRYPT
@ -46,6 +47,20 @@
GCRY_THREAD_OPTION_PTHREAD_IMPL;
#endif
/**
* A list of all key exchange algorithms that are both FIPS-compliant, and
* OpenSSL-supported. Note that "ext-info-c" is also included. While not a key
* exchange algorithm per se, it must be in the list to ensure that the server
* will send a SSH_MSG_EXT_INFO response, which is required to perform RSA key
* upgrades.
*/
#define FIPS_COMPLIANT_KEX_ALGORITHMS "diffie-hellman-group-exchange-sha256,ext-info-c"
/**
* A list of ciphers that are both FIPS-compliant, and OpenSSL-supported.
*/
#define FIPS_COMPLIANT_CIPHERS "aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,aes192-cbc,aes256-cbc"
#ifdef OPENSSL_REQUIRES_THREADING_CALLBACKS
/**
* Array of mutexes, used by OpenSSL.
@ -165,9 +180,11 @@ int guac_common_ssh_init(guac_client* client) {
CRYPTO_set_locking_callback(guac_common_ssh_openssl_locking_callback);
#endif
/* Init OpenSSL */
#if OPENSSL_VERSION_NUMBER < 0x10100000L
/* Init OpenSSL - only required for OpenSSL Versions < 1.1.0 */
SSL_library_init();
ERR_load_crypto_strings();
#endif
/* Init libssh2 */
libssh2_init(0);
@ -484,6 +501,17 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
return NULL;
}
/*
* If FIPS mode is enabled, prefer only FIPS-compatible algorithms and
* ciphers that are also supported by libssh2. For more info, see:
* https://csrc.nist.gov/CSRC/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp2906.pdf
*/
if (guac_fips_enabled()) {
libssh2_session_method_pref(session, LIBSSH2_METHOD_KEX, FIPS_COMPLIANT_KEX_ALGORITHMS);
libssh2_session_method_pref(session, LIBSSH2_METHOD_CRYPT_CS, FIPS_COMPLIANT_CIPHERS);
libssh2_session_method_pref(session, LIBSSH2_METHOD_CRYPT_SC, FIPS_COMPLIANT_CIPHERS);
}
/* Perform handshake */
if (libssh2_session_handshake(session, fd)) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,

View File

@ -166,6 +166,8 @@ void guac_common_display_free(guac_common_display* display) {
void guac_common_display_dup(guac_common_display* display, guac_user* user,
guac_socket* socket) {
guac_client* client = user->client;
pthread_mutex_lock(&display->_lock);
/* Sunchronize shared cursor */
@ -178,6 +180,9 @@ void guac_common_display_dup(guac_common_display* display, guac_user* user,
guac_common_display_dup_layers(display->layers, user, socket);
guac_common_display_dup_layers(display->buffers, user, socket);
/* Sends a sync instruction to mark the boundary of the first frame */
guac_protocol_send_sync(socket, client->last_sent_timestamp, 1);
pthread_mutex_unlock(&display->_lock);
}
@ -384,4 +389,3 @@ void guac_common_display_free_buffer(guac_common_display* display,
pthread_mutex_unlock(&display->_lock);
}

View File

@ -381,10 +381,15 @@ int main(int argc, char* argv[]) {
CRYPTO_set_locking_callback(guacd_openssl_locking_callback);
#endif
/* Init SSL */
#if OPENSSL_VERSION_NUMBER < 0x10100000L
/* Init OpenSSL for OpenSSL Versions < 1.1.0 */
SSL_library_init();
SSL_load_error_strings();
ssl_context = SSL_CTX_new(SSLv23_server_method());
#else
/* Set up OpenSSL for OpenSSL Versions >= 1.1.0 */
ssl_context = SSL_CTX_new(TLS_server_method());
#endif
/* Load key */
if (config->key_file != NULL) {

View File

@ -213,7 +213,7 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) {
#endif
}
AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream, AVCodec* codec,
AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream, const AVCodec* codec,
int bitrate, int width, int height, int gop_size, int qmax, int qmin,
int pix_fmt, AVRational time_base) {
@ -249,7 +249,7 @@ AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream, AVCodec* codec,
}
int guacenc_open_avcodec(AVCodecContext *avcodec_context,
AVCodec *codec, AVDictionary **options,
const AVCodec *codec, AVDictionary **options,
AVStream* stream) {
int ret = avcodec_open2(avcodec_context, codec, options);

View File

@ -128,7 +128,7 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame);
* The pointer to the configured AVCodecContext.
*
*/
AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream, AVCodec* codec,
AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream, const AVCodec* codec,
int bitrate, int width, int height, int gop_size, int qmax, int qmin,
int pix_fmt, AVRational time_base);
@ -158,7 +158,7 @@ AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream, AVCodec* codec,
* Zero on success, a negative value on error.
*/
int guacenc_open_avcodec(AVCodecContext *avcodec_context,
AVCodec *codec, AVDictionary **options,
const AVCodec *codec, AVDictionary **options,
AVStream* stream);
#endif

View File

@ -47,7 +47,7 @@
guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
int width, int height, int bitrate) {
AVOutputFormat *container_format;
const AVOutputFormat *container_format;
AVFormatContext *container_format_context;
AVStream *video_stream;
int ret;
@ -63,7 +63,7 @@ guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
container_format = container_format_context->oformat;
/* Pull codec based on name */
AVCodec* codec = avcodec_find_encoder_by_name(codec_name);
const AVCodec* codec = avcodec_find_encoder_by_name(codec_name);
if (codec == NULL) {
guacenc_log(GUAC_LOG_ERROR, "Failed to locate codec \"%s\".",
codec_name);

View File

@ -44,6 +44,7 @@ libguacinc_HEADERS = \
guacamole/client-types.h \
guacamole/error.h \
guacamole/error-types.h \
guacamole/fips.h \
guacamole/hash.h \
guacamole/layer.h \
guacamole/layer-types.h \
@ -93,6 +94,7 @@ libguac_la_SOURCES = \
encode-jpeg.c \
encode-png.c \
error.c \
fips.c \
hash.c \
id.c \
palette.c \

View File

@ -421,15 +421,19 @@ void* guac_client_for_user(guac_client* client, guac_user* user,
}
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 */
client->last_sent_timestamp = guac_timestamp_current();
/* Log received timestamp and calculated lag (at TRACE level only) */
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);
}

51
src/libguac/fips.c Normal file
View File

@ -0,0 +1,51 @@
/*
* 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 "config.h"
#include "guacamole/fips.h"
/* If OpenSSL is available, include header for version numbers */
#ifdef ENABLE_SSL
#include <openssl/opensslv.h>
/* OpenSSL versions prior to 0.9.7e did not have FIPS support */
#if !defined(OPENSSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x00090705f)
#define GUAC_FIPS_ENABLED 0
/* OpenSSL 3+ uses EVP_default_properties_is_fips_enabled() */
#elif defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
#include <openssl/evp.h>
#define GUAC_FIPS_ENABLED EVP_default_properties_is_fips_enabled(NULL)
/* For OpenSSL versions between 0.9.7e and 3.0, use FIPS_mode() */
#else
#include <openssl/crypto.h>
#define GUAC_FIPS_ENABLED FIPS_mode()
#endif
/* FIPS support does not exist if OpenSSL is not available. */
#else
#define GUAC_FIPS_ENABLED 0
#endif
int guac_fips_enabled() {
return GUAC_FIPS_ENABLED;
}

View File

@ -509,18 +509,47 @@ void* guac_client_for_user(guac_client* client, guac_user* user,
guac_user_callback* callback, void* data);
/**
* Marks the end of the current frame by sending a "sync" instruction to
* all connected users. This instruction will contain the current timestamp.
* The last_sent_timestamp member of guac_client will be updated accordingly.
* Marks the end of the current frame by sending a "sync" instruction to all
* connected users, where the number of input frames that were considered in
* 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
* returned, and guac_error is set appropriately.
*
* @param client The guac_client which has finished a frame.
* @return Zero on success, non-zero on error.
* @param client
* The guac_client which has finished a frame.
*
* @return
* Zero on success, non-zero on error.
*/
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
* by the plugin corresponding to the named protocol. This will automatically

View File

@ -0,0 +1,33 @@
/*
* 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_FIPS_H
#define GUAC_FIPS_H
/**
* Returns a non-zero value if FIPS mode is enabled, or zero if FIPS mode
* is not enabled.
*
* @return
* A non-zero value if FIPS mode is enabled, or zero if FIPS mode is
* not enabled.
*/
int guac_fips_enabled();
#endif

View File

@ -384,11 +384,22 @@ int guac_protocol_send_select(guac_socket* socket, const char* protocol);
* 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 timestamp The current timestamp (in milliseconds).
* @return Zero on success, non-zero on error.
* @param socket
* The guac_socket connection to use.
*
* @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 */

View File

@ -1199,7 +1199,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;
@ -1207,6 +1208,8 @@ int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp) {
ret_val =
guac_socket_write_string(socket, "4.sync,")
|| __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_instruction_end(socket);

View File

@ -54,7 +54,7 @@ static void write_instructions(int fd) {
/* Write instructions */
guac_protocol_send_name(socket, "a" UTF8_4 "b" UTF8_4 "c");
guac_protocol_send_sync(socket, 12345);
guac_protocol_send_sync(socket, 12345, 1);
guac_socket_flush(socket);
/* Close and free socket */
@ -76,7 +76,7 @@ static void read_expected_instructions(int fd) {
char expected[] =
"4.name,11.a" UTF8_4 "b" UTF8_4 "c;"
"4.sync,5.12345;";
"4.sync,5.12345,1.1;";
int numread;
char buffer[1024];

View File

@ -65,7 +65,7 @@ static void write_instructions(int fd) {
/* Write instructions */
guac_protocol_send_name(nested_socket, "a" UTF8_4 "b" UTF8_4 "c");
guac_protocol_send_sync(nested_socket, 12345);
guac_protocol_send_sync(nested_socket, 12345, 1);
/* Close and free sockets */
guac_socket_free(nested_socket);
@ -86,9 +86,9 @@ static void write_instructions(int fd) {
static void read_expected_instructions(int fd) {
char expected[] =
"4.nest,3.123,37."
"4.nest,3.123,41."
"4.name,11.a" UTF8_4 "b" UTF8_4 "c;"
"4.sync,5.12345;"
"4.sync,5.12345,1.1;"
";";
int numread;

View File

@ -121,14 +121,27 @@ int __guac_handle_sync(guac_user* user, int argc, char** argv) {
/* Calculate length of frame, including network and processing lag */
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) {
/* 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
* value */
if (frame_processing_lag < 0)
frame_processing_lag = 0;
}
/* Record baseline duration of frame by excluding lag (this is the
* 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;
@ -136,16 +149,11 @@ int __guac_handle_sync(guac_user* user, int argc, char** argv) {
}
/* Record baseline duration of frame by excluding lag */
user->last_frame_duration = frame_duration - user->processing_lag;
}
/* Log received timestamp and calculated lag (at TRACE level only) */
guac_user_log(user, GUAC_LOG_TRACE,
"User confirmation of frame %" PRIu64 "ms received "
"at %" PRIu64 "ms (processing_lag=%ims)",
timestamp, current, user->processing_lag);
"at %" PRIu64 "ms (processing_lag=%ims, estimated_rtt=%ims)",
timestamp, current, user->processing_lag, user->last_frame_duration);
if (user->sync_handler)
return user->sync_handler(user, timestamp);

View File

@ -57,6 +57,7 @@ libguac_client_rdp_la_SOURCES = \
channels/rdpdr/rdpdr-printer.c \
channels/rdpdr/rdpdr.c \
channels/rdpei.c \
channels/rdpgfx.c \
channels/rdpsnd/rdpsnd-messages.c \
channels/rdpsnd/rdpsnd.c \
client.c \
@ -103,6 +104,7 @@ noinst_HEADERS = \
channels/rdpdr/rdpdr-printer.h \
channels/rdpdr/rdpdr.h \
channels/rdpei.h \
channels/rdpgfx.h \
channels/rdpsnd/rdpsnd-messages.h \
channels/rdpsnd/rdpsnd.h \
client.h \
@ -228,6 +230,7 @@ BUILT_SOURCES = \
rdp_keymaps = \
$(srcdir)/keymaps/base.keymap \
$(srcdir)/keymaps/failsafe.keymap \
$(srcdir)/keymaps/cs-cz-qwertz.keymap \
$(srcdir)/keymaps/de_de_qwertz.keymap \
$(srcdir)/keymaps/de_ch_qwertz.keymap \
$(srcdir)/keymaps/en_gb_qwerty.keymap \
@ -235,6 +238,7 @@ rdp_keymaps = \
$(srcdir)/keymaps/es_es_qwerty.keymap \
$(srcdir)/keymaps/es_latam_qwerty.keymap \
$(srcdir)/keymaps/fr_be_azerty.keymap \
$(srcdir)/keymaps/fr_ca_qwerty.keymap \
$(srcdir)/keymaps/fr_ch_qwertz.keymap \
$(srcdir)/keymaps/fr_fr_azerty.keymap \
$(srcdir)/keymaps/hu_hu_qwertz.keymap \

View File

@ -149,7 +149,7 @@ BOOL guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap, BOOL pri
else {
/* Make sure that the recieved bitmap is not NULL before processing */
/* Make sure that the received bitmap is not NULL before processing */
if (bitmap == NULL) {
guac_client_log(client, GUAC_LOG_INFO, "NULL bitmap found in bitmap_setsurface instruction.");
return TRUE;

View File

@ -509,12 +509,12 @@ static UINT guac_rdp_cliprdr_format_data_response(CliprdrClientContext* cliprdr,
* @param context
* The rdpContext associated with the active RDP session.
*
* @param e
* @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_cliprdr_channel_connected(rdpContext* context,
ChannelConnectedEventArgs* e) {
ChannelConnectedEventArgs* args) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
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);
/* 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;
/* The structure pointed to by pInterface is guaranteed to be a
* 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
* eachother */
@ -562,12 +562,12 @@ static void guac_rdp_cliprdr_channel_connected(rdpContext* context,
* @param context
* The rdpContext associated with the active RDP session.
*
* @param e
* @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_cliprdr_channel_disconnected(rdpContext* context,
ChannelDisconnectedEventArgs* e) {
ChannelDisconnectedEventArgs* args) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
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);
/* 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;
/* Channel is no longer connected */

View File

@ -115,7 +115,7 @@ struct guac_rdp_common_svc {
guac_rdp_common_svc_receive_handler* _receive_handler;
/**
* Handler which is invokved when the SVC has been disconnected and is
* Handler which is involved when the SVC has been disconnected and is
* about to be freed.
*/
guac_rdp_common_svc_terminate_handler* _terminate_handler;

View File

@ -68,19 +68,19 @@ void guac_rdp_disp_free(guac_rdp_disp* disp) {
* @param context
* The rdpContext associated with the active RDP session.
*
* @param e
* @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_disp_channel_connected(rdpContext* context,
ChannelConnectedEventArgs* e) {
ChannelConnectedEventArgs* args) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_rdp_disp* guac_disp = rdp_client->disp;
/* 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;
/* 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));
/* 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_client_log(client, GUAC_LOG_DEBUG, "Display update channel "
@ -110,19 +110,19 @@ static void guac_rdp_disp_channel_connected(rdpContext* context,
* @param context
* The rdpContext associated with the active RDP session.
*
* @param e
* @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_disp_channel_disconnected(rdpContext* context,
ChannelDisconnectedEventArgs* e) {
ChannelDisconnectedEventArgs* args) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_rdp_disp* guac_disp = rdp_client->disp;
/* 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;
/* Channel is no longer connected */

View File

@ -109,7 +109,7 @@ void guac_rdp_disp_free(guac_rdp_disp* disp);
/**
* Adds FreeRDP's "disp" 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_disp instance pointed to by the
* automatically be associated with the guac_rdp_disp instance pointed to by the
* current guac_rdp_client. The plugin will only be loaded once the "drdynvc"
* plugin is loaded. The "disp" plugin ultimately adds support for the Display
* Update channel.

View File

@ -230,22 +230,22 @@ static UINT guac_rdp_rail_handshake_ex(RailClientContext* rail,
* @param context
* The rdpContext associated with the active RDP session.
*
* @param e
* @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_rail_channel_connected(rdpContext* context,
ChannelConnectedEventArgs* e) {
ChannelConnectedEventArgs* args) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
/* 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;
/* The structure pointed to by pInterface is guaranteed to be a
* 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
* within any RAIL-specific callbacks */

View File

@ -66,23 +66,23 @@ void guac_rdp_rdpei_free(guac_rdp_rdpei* rdpei) {
* @param context
* The rdpContext associated with the active RDP session.
*
* @param e
* @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_rdpei_channel_connected(rdpContext* context,
ChannelConnectedEventArgs* e) {
ChannelConnectedEventArgs* args) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_rdp_rdpei* guac_rdpei = rdp_client->rdpei;
/* 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;
/* Store reference to the RDPEI plugin once it's connected */
RdpeiClientContext* rdpei = (RdpeiClientContext*) e->pInterface;
RdpeiClientContext* rdpei = (RdpeiClientContext*) args->pInterface;
guac_rdpei->rdpei = rdpei;
/* Declare level of multi-touch support */
@ -107,19 +107,19 @@ static void guac_rdp_rdpei_channel_connected(rdpContext* context,
* @param context
* The rdpContext associated with the active RDP session.
*
* @param e
* @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_rdpei_channel_disconnected(rdpContext* context,
ChannelDisconnectedEventArgs* e) {
ChannelDisconnectedEventArgs* args) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_rdp_rdpei* guac_rdpei = rdp_client->rdpei;
/* 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;
/* Channel is no longer connected */

View File

@ -110,7 +110,7 @@ void guac_rdp_rdpei_free(guac_rdp_rdpei* rdpei);
/**
* Adds FreeRDP's "rdpei" 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_rdpei instance pointed to by the
* automatically be associated with the guac_rdp_rdpei instance pointed to by the
* current guac_rdp_client. The plugin will only be loaded once the "drdynvc"
* plugin is loaded. The "rdpei" plugin ultimately adds support for multi-touch
* input via the RDPEI channel.

View 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);
}

View 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 associated 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

View File

@ -30,6 +30,7 @@
#include <guacamole/stream.h>
#include <guacamole/string.h>
#include <guacamole/user.h>
#include <winpr/file.h>
#include <winpr/nt.h>
#include <winpr/shell.h>

View File

@ -26,6 +26,7 @@
#include <cairo/cairo.h>
#include <freerdp/freerdp.h>
#include <freerdp/gdi/gdi.h>
#include <freerdp/graphics.h>
#include <freerdp/primary.h>
#include <guacamole/client.h>
@ -247,7 +248,7 @@ BOOL guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
int x_src = memblt->nXSrc;
int y_src = memblt->nYSrc;
/* Make sure that the recieved bitmap is not NULL before processing */
/* Make sure that the received bitmap is not NULL before processing */
if (bitmap == NULL) {
guac_client_log(client, GUAC_LOG_INFO, "NULL bitmap found in memblt instruction.");
return TRUE;
@ -371,11 +372,112 @@ BOOL guac_rdp_gdi_set_bounds(rdpContext* context, const rdpBounds* bounds) {
}
BOOL guac_rdp_gdi_end_paint(rdpContext* context) {
/* IGNORE */
void guac_rdp_gdi_mark_frame(rdpContext* context, int starting) {
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;
}
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) {
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_height(context->instance));
return TRUE;
return gdi_resize(context->gdi, guac_rdp_get_width(context->instance),
guac_rdp_get_height(context->instance));
}

View File

@ -156,8 +156,68 @@ BOOL guac_rdp_gdi_opaquerect(rdpContext* context,
BOOL guac_rdp_gdi_set_bounds(rdpContext* context, const rdpBounds* bounds);
/**
* Handler called when a paint operation is complete. We don't actually
* use this, but FreeRDP requires it. Calling this function has no effect.
* Notifies the internal GDI implementation that a frame is either starting or
* 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
* The rdpContext associated with the current RDP session.

View File

@ -96,11 +96,11 @@ BOOL guac_rdp_glyph_new(rdpContext* context, const rdpGlyph* glyph);
* The height of the glyph being drawn.
*
* @param sx
* The X coordinare of the upper-left corner of the glyph within the source
* The X coordinate of the upper-left corner of the glyph within the source
* cache surface containing the glyph.
*
* @param sy
* The Y coordinare of the upper-left corner of the glyph within the source
* The Y coordinate of the upper-left corner of the glyph within the source
* cache surface containing the glyph.
*
* @param redundant

View File

@ -140,7 +140,7 @@ static void guac_rdp_send_unicode_event(guac_rdp_client* rdp_client,
}
/**
* Immediately sends an RDP synchonize event having the given flags. An RDP
* Immediately sends an RDP synchronize event having the given flags. An RDP
* synchronize event sets the state of remote lock keys absolutely, where a
* lock key will be active only if its corresponding flag is set in the event.
*

View File

@ -0,0 +1,79 @@
#
# 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.
#
parent "base"
name "cs-cz-qwertz"
freerdp "KBD_CZECH"
#
# Basic keys
#
map -caps -altgr -shift 0x29 0x02..0x0D ~ ";+ěščřžýáíé=´"
map -caps -altgr -shift 0x10..0x1B ~ "qwertzuıopú)"
map -caps -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjklů§¨"
map -caps -altgr -shift 0x2C..0x35 ~ "yxcvbnm,.-"
map -caps -altgr +shift 0x29 0x02..0x0D ~ "°1234567890%ˇ"
map -caps -altgr +shift 0x10..0x1B ~ "QWERTZUIOP/("
map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKL"!'"
map -caps -altgr +shift 0x2C..0x35 ~ "YXCVBNM?:_"
map +caps -altgr -shift 0x29 0x02..0x0D ~ ";+ĚŠČŘŽÝÁÍÉ=´"
map +caps -altgr -shift 0x10..0x1B ~ "QWERTZUIOPÚ)"
map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKL٧¨"
map +caps -altgr -shift 0x2C..0x35 ~ "YXCVBNM,.-"
map +caps -altgr +shift 0x29 0x02..0x0D ~ "°1234567890%ˇ"
map +caps -altgr +shift 0x10..0x1B ~ "qwertzuiop/("
map +caps -altgr +shift 0x1E..0x28 0x2B ~ "asdfghjkl"!'"
map +caps -altgr +shift 0x2C..0x35 ~ "yxcvbnm?:_"
#
# Keys requiring AltGr
#
map +altgr -shift 0x02 ~ "~"
map +altgr -shift 0x10 ~ "\"
map +altgr -shift 0x11 ~ "|"
map +altgr -shift 0x12 ~ "€"
map +altgr -shift 0x1A ~ "÷"
map +altgr -shift 0x1B ~ "×"
map +altgr -shift 0x1F ~ "đ"
map +altgr -shift 0x20 ~ "Đ"
map +altgr -shift 0x21 ~ "["
map +altgr -shift 0x22 ~ "]"
map +altgr -shift 0x25 ~ "ł"
map +altgr -shift 0x26 ~ "Ł"
map +altgr -shift 0x27 ~ "$"
map +altgr -shift 0x28 ~ "ß"
map +altgr -shift 0x2B ~ "¤"
map +altgr -shift 0x2D ~ "#"
map +altgr -shift 0x2E ~ "&"
map +altgr -shift 0x2F ~ "@"
map +altgr -shift 0x30 ~ "{"
map +altgr -shift 0x31 ~ "}"
map +altgr -shift 0x33 ~ "<"
map +altgr -shift 0x34 ~ ">"
map +altgr -shift 0x35 ~ "*"
# END

View File

@ -0,0 +1,55 @@
#
# 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.
#
parent "base"
name "fr-ca-qwerty"
freerdp "KBD_CANADIAN_FRENCH"
#
# Basic keys
#
map -altgr -shift 0x29 0x02..0x0D ~ "#1234567890-="
map -altgr -shift 0x10..0x1B 0x2B ~ "qwertyuiop^¸<"
map -altgr -shift 0x1E..0x28 ~ "asdfghjkl;`"
map -altgr -shift 0x2C..0x35 ~ "zxcvbnm,.é"
map -altgr +shift 0x29 0x02..0x0D ~ "|!"/$%?&*()_+"
map -altgr +shift 0x10..0x1B 0x2B ~ "QWERTYUIOP^¨>"
map -altgr +shift 0x1E..0x28 ~ "ASDFGHJKL:`"
map -altgr +shift 0x2C..0x35 ~ "ZXCVBNM'.É"
#
# Keys requiring AltGr
#
map +altgr -shift 0x29 0x02..0x0D ~ "\±@£¢¤¬¦²³¼½¾"
map +altgr -shift 0x18..0x1B 0x2B ~ "§¶[]}"
map +altgr -shift 0x27..0x28 ~ "~{"
map +altgr -shift 0x32..0x33 0x35 ~ "µ¯´"
#
# Combined accents
#
map -altgr -shift 0x1A ~ 0x0302 # COMBINING CIRCUMFLEX ACCENT
map -altgr -shift 0x1B ~ 0x0327 # COMBINING CEDILLA
map -altgr +shift 0x1B ~ 0x0308 # COMBINING DIAERESIS
map -altgr -shift 0x28 ~ 0x0300 # COMBINING GRAVE ACCENT
map +altgr -shift 0x35 ~ 0x0301 # COMBINING ACUTE ACCENT

View File

@ -25,6 +25,7 @@
#include <guacamole/socket.h>
#include <guacamole/stream.h>
#include <guacamole/user.h>
#include <winpr/file.h>
#include <winpr/nt.h>
#include <winpr/shell.h>

View File

@ -251,7 +251,7 @@ void guac_rdp_ai_process_version(guac_client* client,
/* Verify we have at least 4 bytes available (UINT32) */
if (Stream_GetRemainingLength(stream) < 4) {
guac_client_log(client, GUAC_LOG_WARNING, "Audio input Versoin PDU "
guac_client_log(client, GUAC_LOG_WARNING, "Audio input Version PDU "
"does not contain the expected number of bytes. Audio input "
"redirection may not work as expected.");
return;

View File

@ -21,6 +21,7 @@
#include "common/cursor.h"
#include "common/display.h"
#include "common/surface.h"
#include "gdi.h"
#include "pointer.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_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 */
guac_common_cursor_set_surface(rdp_client->display->cursor,
pointer->xPos, pointer->yPos,
((guac_rdp_pointer*) pointer)->layer->surface);
if (rdp_client->frames_supported && !in_frame)
guac_rdp_gdi_mark_frame(context, 0);
return TRUE;
}
@ -106,9 +118,20 @@ BOOL guac_rdp_pointer_set_null(rdpContext* context) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
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 */
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;
}
@ -118,9 +141,20 @@ BOOL guac_rdp_pointer_set_default(rdpContext* context) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
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 */
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;
}

View File

@ -28,6 +28,7 @@
#include "channels/rail.h"
#include "channels/rdpdr/rdpdr.h"
#include "channels/rdpei.h"
#include "channels/rdpgfx.h"
#include "channels/rdpsnd/rdpsnd.h"
#include "client.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 */
if (!gdi_init(instance, guac_rdp_get_native_pixel_format(FALSE)))
return FALSE;
@ -187,9 +179,13 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
/* Set up GDI */
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->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;
primary->DstBlt = guac_rdp_gdi_dstblt;
primary->PatBlt = guac_rdp_gdi_patblt;
@ -204,6 +200,19 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
offscreen_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;
}
@ -544,7 +553,6 @@ static int guac_rdp_handle_connection(guac_client* client) {
if (wait_result > 0) {
int processing_lag = guac_client_get_processing_lag(client);
guac_timestamp frame_start = guac_timestamp_current();
/* Read server messages until frame is built */
do {
@ -564,7 +572,15 @@ static int guac_rdp_handle_connection(guac_client* client) {
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 */
guac_timestamp frame_start = client->last_sent_timestamp;
frame_end = guac_timestamp_current();
frame_remaining = frame_start + GUAC_RDP_FRAME_DURATION
- frame_end;
@ -587,12 +603,6 @@ static int guac_rdp_handle_connection(guac_client* client) {
} 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 */
@ -607,11 +617,13 @@ static int guac_rdp_handle_connection(guac_client* client) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_UNAVAILABLE,
"Connection closed.");
/* Flush frame only if successful */
else {
/* Flush frame only if successful and an RDP frame is not known to be
* in progress */
else if (!rdp_client->frames_supported || rdp_client->frames_received) {
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);
rdp_client->frames_received = 0;
}
}

View File

@ -91,6 +91,24 @@ typedef struct guac_rdp_client {
*/
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.
*/

View File

@ -28,6 +28,7 @@
#include <freerdp/settings.h>
#include <freerdp/freerdp.h>
#include <guacamole/client.h>
#include <guacamole/fips.h>
#include <guacamole/string.h>
#include <guacamole/user.h>
#include <guacamole/wol-constants.h>
@ -39,6 +40,16 @@
#include <stdlib.h>
#include <string.h>
/**
* A warning to log when NLA mode is selected while FIPS mode is active on the
* guacd server.
*/
const char fips_nla_mode_warning[] = (
"NLA security mode was selected, but is known to be currently incompatible "
"with FIPS mode (see FreeRDP/FreeRDP#3412). Security negotiation with the "
"RDP server may fail unless TLS security mode is selected instead."
);
/* Client plugin arguments */
const char* GUAC_RDP_CLIENT_ARGS[] = {
"hostname",
@ -80,6 +91,7 @@ const char* GUAC_RDP_CLIENT_ARGS[] = {
"disable-bitmap-caching",
"disable-offscreen-caching",
"disable-glyph-caching",
"disable-gfx",
"preconnection-id",
"preconnection-blob",
"timezone",
@ -360,7 +372,7 @@ enum RDP_ARGS_IDX {
IDX_DISABLE_BITMAP_CACHING,
/**
* "true" if the offscreen caching should be disabled, false if offscren
* "true" if the offscreen caching should be disabled, false if offscreen
* caching should remain enabled.
*/
IDX_DISABLE_OFFSCREEN_CACHING,
@ -371,6 +383,13 @@ enum RDP_ARGS_IDX {
*/
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
* initiating an RDP connection, if any.
@ -698,12 +717,27 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user,
if (strcmp(argv[IDX_SECURITY], "nla") == 0) {
guac_user_log(user, GUAC_LOG_INFO, "Security mode: NLA");
settings->security_mode = GUAC_SECURITY_NLA;
/*
* NLA is known not to work with FIPS; allow the mode selection but
* warn that it will not work.
*/
if (guac_fips_enabled())
guac_user_log(user, GUAC_LOG_WARNING, fips_nla_mode_warning);
}
/* Extended NLA security */
else if (strcmp(argv[IDX_SECURITY], "nla-ext") == 0) {
guac_user_log(user, GUAC_LOG_INFO, "Security mode: Extended NLA");
settings->security_mode = GUAC_SECURITY_EXTENDED_NLA;
/*
* NLA is known not to work with FIPS; allow the mode selection but
* warn that it will not work.
*/
if (guac_fips_enabled())
guac_user_log(user, GUAC_LOG_WARNING, fips_nla_mode_warning);
}
/* TLS security */
@ -908,11 +942,6 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user,
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 */
settings->preconnection_id = -1;
if (argv[IDX_PRECONNECTION_ID][0] != '\0') {
@ -1129,6 +1158,16 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user,
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 */
settings->enable_touch =
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
@ -1397,6 +1436,29 @@ void guac_rdp_push_settings(guac_client* client,
/* Explicitly set flag value */
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 */
rdp_settings->AllowFontSmoothing = guac_settings->font_smoothing_enabled;
rdp_settings->DisableWallpaper = !guac_settings->wallpaper_enabled;
@ -1493,7 +1555,21 @@ void guac_rdp_push_settings(guac_client* client,
case GUAC_SECURITY_ANY:
rdp_settings->RdpSecurity = TRUE;
rdp_settings->TlsSecurity = TRUE;
rdp_settings->NlaSecurity = guac_settings->username && guac_settings->password;
/* Explicitly disable NLA if FIPS mode is enabled - it won't work */
if (guac_fips_enabled()) {
guac_client_log(client, GUAC_LOG_INFO,
"FIPS mode is enabled. Excluding NLA security mode from security negotiation "
"(see: https://github.com/FreeRDP/FreeRDP/issues/3412).");
rdp_settings->NlaSecurity = FALSE;
}
/* NLA mode is allowed if FIPS is not enabled */
else
rdp_settings->NlaSecurity = TRUE;
rdp_settings->ExtSecurity = FALSE;
break;

View File

@ -58,6 +58,11 @@
*/
#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.
*/
@ -552,6 +557,11 @@ typedef struct guac_rdp_settings {
*/
int enable_audio_input;
/**
* Whether the RDP Graphics Pipeline Extension is enabled.
*/
int enable_gfx;
/**
* Whether multi-touch support is enabled.
*/

View File

@ -202,6 +202,19 @@ int __guac_terminal_set(guac_terminal_display* display, int row, int col, int co
}
/**
* Calculate the size of margins around the terminal based on DPI.
*
* @param dpi
* The resolution of the display in DPI.
*
* @return
* Calculated size of margin in pixels.
*/
static int get_margin_by_dpi(int dpi) {
return dpi * GUAC_TERMINAL_MARGINS / GUAC_TERMINAL_MM_PER_INCH;
}
guac_terminal_display* guac_terminal_display_alloc(guac_client* client,
const char* font_name, int font_size, int dpi,
guac_terminal_color* foreground, guac_terminal_color* background,
@ -229,6 +242,13 @@ guac_terminal_display* guac_terminal_display_alloc(guac_client* client,
guac_protocol_send_move(client->socket, display->select_layer,
display->display_layer, 0, 0, 0);
/* Calculate margin size by DPI */
display->margin = get_margin_by_dpi(dpi);
/* Offset the Default Layer to make margins even on all sides */
guac_protocol_send_move(client->socket, display->display_layer,
GUAC_DEFAULT_LAYER, display->margin, display->margin, 0);
display->default_foreground = display->glyph_foreground = *foreground;
display->default_background = display->glyph_background = *background;
display->default_palette = palette;
@ -447,6 +467,10 @@ void guac_terminal_display_set_columns(guac_terminal_display* display, int row,
void guac_terminal_display_resize(guac_terminal_display* display, int width, int height) {
/* Resize display only if dimensions have changed */
if (width == display->width && height == display->height)
return;
guac_terminal_operation* current;
int x, y;
@ -828,6 +852,10 @@ void guac_terminal_display_dup(guac_terminal_display* display, guac_user* user,
guac_protocol_send_move(socket, display->select_layer,
display->display_layer, 0, 0, 0);
/* Offset the Default Layer to make margins even on all sides */
guac_protocol_send_move(socket, display->display_layer,
GUAC_DEFAULT_LAYER, display->margin, display->margin, 0);
/* Send select layer size */
guac_protocol_send_size(socket, display->select_layer,
display->char_width * display->width,
@ -1020,7 +1048,7 @@ int guac_terminal_display_set_font(guac_terminal_display* display,
if (new_width != display->width || new_height != display->height)
guac_terminal_display_resize(display, new_width, new_height);
return 0;
}

View File

@ -20,6 +20,7 @@
#include "common/clipboard.h"
#include "common/cursor.h"
#include "common/iconv.h"
#include "terminal/buffer.h"
#include "terminal/color-scheme.h"
#include "terminal/common.h"
@ -335,6 +336,97 @@ guac_terminal_options* guac_terminal_options_create(
return options;
}
/**
* Calculate the available height and width in characters for text display in
* the terminal and store the results in the pointer arguments.
*
* @param terminal
* The terminal provides character width and height for calculations.
*
* @param height
* The outer height of the terminal, in pixels.
*
* @param width
* The outer width of the terminal, in pixels.
*
* @param rows
* Pointer to the calculated height of the terminal for text display,
* in characters.
*
* @param columns
* Pointer to the calculated width of the terminal for text display,
* in characters.
*/
static void calculate_rows_and_columns(guac_terminal* term,
int height, int width, int *rows, int *columns) {
int margin = term->display->margin;
int char_width = term->display->char_width;
int char_height = term->display->char_height;
/* Calculate available display area */
int available_width = width - GUAC_TERMINAL_SCROLLBAR_WIDTH - 2 * margin;
if (available_width < 0)
available_width = 0;
int available_height = height - 2 * margin;
if (available_height < 0)
available_height = 0;
/* Calculate dimensions */
*rows = available_height / char_height;
*columns = available_width / char_width;
/* Keep height within predefined maximum */
if (*rows > GUAC_TERMINAL_MAX_ROWS)
*rows = GUAC_TERMINAL_MAX_ROWS;
/* Keep width within predefined maximum */
if (*columns > GUAC_TERMINAL_MAX_COLUMNS)
*columns = GUAC_TERMINAL_MAX_COLUMNS;
}
/**
* Calculate the available height and width in pixels of the terminal for text
* display in the terminal and store the results in the pointer arguments.
*
* @param terminal
* The terminal provides character width and height for calculations.
*
* @param rows
* The available height of the terminal for text display, in characters.
*
* @param columns
* The available width of the terminal for text display, in characters.
*
* @param height
* Pointer to the calculated available height of the terminal for text
* display, in pixels.
*
* @param width
* Pointer to the calculated available width of the terminal for text
* display, in pixels.
*/
static void calculate_height_and_width(guac_terminal* term,
int rows, int columns, int *height, int *width) {
int margin = term->display->margin;
int char_width = term->display->char_width;
int char_height = term->display->char_height;
/* Recalculate height if max rows reached */
if (rows == GUAC_TERMINAL_MAX_ROWS) {
int available_height = GUAC_TERMINAL_MAX_ROWS * char_height;
*height = available_height + 2 * margin;
}
/* Recalculate width if max columns reached */
if (columns == GUAC_TERMINAL_MAX_COLUMNS) {
int available_width = GUAC_TERMINAL_MAX_COLUMNS * char_width;
*width = available_width + GUAC_TERMINAL_SCROLLBAR_WIDTH + 2 * margin;
}
}
guac_terminal* guac_terminal_create(guac_client* client,
guac_terminal_options* options) {
@ -363,11 +455,6 @@ guac_terminal* guac_terminal_create(guac_client* client,
&default_char.attributes.background,
default_palette);
/* Calculate available display area */
int available_width = width - GUAC_TERMINAL_SCROLLBAR_WIDTH;
if (available_width < 0)
available_width = 0;
guac_terminal* term = malloc(sizeof(guac_terminal));
term->started = false;
term->client = client;
@ -379,10 +466,6 @@ guac_terminal* guac_terminal_create(guac_client* client,
term->font_name = strdup(options->font_name);
term->font_size = options->font_size;
/* Set size of available screen area */
term->outer_width = width;
term->outer_height = height;
/* Init modified flag and conditional */
term->modified = 0;
pthread_cond_init(&(term->modified_cond), NULL);
@ -425,29 +508,27 @@ guac_terminal* guac_terminal_create(guac_client* client,
term->clipboard = guac_common_clipboard_alloc();
term->disable_copy = options->disable_copy;
/* Calculate character size */
int rows = height / term->display->char_height;
int columns = available_width / term->display->char_width;
/* Calculate available text display area by character size */
int rows, columns;
calculate_rows_and_columns(term, height, width, &rows, &columns);
/* Keep height within predefined maximum */
if (rows > GUAC_TERMINAL_MAX_ROWS) {
rows = GUAC_TERMINAL_MAX_ROWS;
height = rows * term->display->char_height;
}
/* Calculate available display area in pixels */
int adjusted_height = height;
int adjusted_width = width;
calculate_height_and_width(term, rows, columns,
&adjusted_height, &adjusted_width);
/* Keep width within predefined maximum */
if (columns > GUAC_TERMINAL_MAX_COLUMNS) {
columns = GUAC_TERMINAL_MAX_COLUMNS;
available_width = columns * term->display->char_width;
width = available_width + GUAC_TERMINAL_SCROLLBAR_WIDTH;
}
/* Set size of available screen area */
term->outer_height = height;
term->outer_width = width;
/* Set rows and columns size */
term->term_height = rows;
term->term_width = columns;
/* Set pixel size */
term->width = width;
term->height = height;
term->term_width = columns;
term->term_height = rows;
term->height = adjusted_height;
term->width = adjusted_width;
/* Open STDIN pipe */
if (pipe(term->stdin_pipe_fd)) {
@ -476,7 +557,7 @@ guac_terminal* guac_terminal_create(guac_client* client,
/* Allocate scrollbar */
term->scrollbar = guac_terminal_scrollbar_alloc(term->client, GUAC_DEFAULT_LAYER,
width, height, term->term_height);
term->outer_width, term->outer_height, term->term_height);
/* Associate scrollbar with this terminal */
term->scrollbar->data = term;
@ -485,6 +566,10 @@ guac_terminal* guac_terminal_create(guac_client* client,
/* Init terminal */
guac_terminal_reset(term);
/* All mouse buttons are released */
term->mouse_mask = 0;
/* All keyboard modifiers are released */
term->mod_alt =
term->mod_ctrl =
term->mod_shift = 0;
@ -1298,7 +1383,7 @@ static void __guac_terminal_resize(guac_terminal* term, int width, int height) {
guac_terminal_display_flush(term->display);
guac_terminal_display_resize(term->display, width, height);
/* Reraw any characters on right if widening */
/* Redraw any characters on right if widening */
if (width > term->term_width)
__guac_terminal_redraw_rect(term, 0, term->term_width-1, height-1, width-1);
@ -1381,55 +1466,39 @@ int guac_terminal_resize(guac_terminal* terminal, int width, int height) {
/* Acquire exclusive access to terminal */
guac_terminal_lock(terminal);
/* Calculate available text display area by character size */
int rows, columns;
calculate_rows_and_columns(terminal, height, width, &rows, &columns);
/* Calculate available display area in pixels */
int adjusted_height = height;
int adjusted_width = width;
calculate_height_and_width(terminal, rows, columns,
&adjusted_height, &adjusted_width);
/* Set size of available screen area */
terminal->outer_width = width;
terminal->outer_height = height;
terminal->outer_width = width;
/* Calculate available display area */
int available_width = width - GUAC_TERMINAL_SCROLLBAR_WIDTH;
if (available_width < 0)
available_width = 0;
/* Calculate dimensions */
int rows = height / display->char_height;
int columns = available_width / display->char_width;
/* Keep height within predefined maximum */
if (rows > GUAC_TERMINAL_MAX_ROWS) {
rows = GUAC_TERMINAL_MAX_ROWS;
height = rows * display->char_height;
}
/* Keep width within predefined maximum */
if (columns > GUAC_TERMINAL_MAX_COLUMNS) {
columns = GUAC_TERMINAL_MAX_COLUMNS;
available_width = columns * display->char_width;
width = available_width + GUAC_TERMINAL_SCROLLBAR_WIDTH;
}
/* Set pixel sizes */
terminal->width = width;
terminal->height = height;
/* Set pixel size */
terminal->height = adjusted_height;
terminal->width = adjusted_width;
/* Resize default layer to given pixel dimensions */
guac_terminal_repaint_default_layer(terminal, client->socket);
/* Resize terminal if row/column dimensions have changed */
if (columns != terminal->term_width || rows != terminal->term_height) {
guac_client_log(client, GUAC_LOG_DEBUG,
"Resizing terminal to %ix%i", rows, columns);
/* Resize terminal */
/* Resize terminal and set the columns and rows on the terminal struct */
__guac_terminal_resize(terminal, columns, rows);
/* Reset scroll region */
terminal->scroll_end = rows - 1;
}
/* Notify scrollbar of resize */
guac_terminal_scrollbar_parent_resized(terminal->scrollbar, width, height, rows);
guac_terminal_scrollbar_parent_resized(terminal->scrollbar,
terminal->outer_width, terminal->outer_height, terminal->term_height);
guac_terminal_scrollbar_set_bounds(terminal->scrollbar,
-guac_terminal_get_available_scroll(terminal), 0);
@ -2096,7 +2165,16 @@ void guac_terminal_clipboard_reset(guac_terminal* terminal,
void guac_terminal_clipboard_append(guac_terminal* terminal,
const char* data, int length) {
guac_common_clipboard_append(terminal->clipboard, data, length);
/* Allocate and clear space for the converted data */
char output_data[GUAC_COMMON_CLIPBOARD_MAX_LENGTH];
char* output = output_data;
/* Convert clipboard contents */
guac_iconv(GUAC_READ_UTF8_NORMALIZED, &data, length,
GUAC_WRITE_UTF8, &output, GUAC_COMMON_CLIPBOARD_MAX_LENGTH);
guac_common_clipboard_append(terminal->clipboard, output_data, output - output_data);
}
void guac_terminal_remove_user(guac_terminal* terminal, guac_user* user) {

View File

@ -44,6 +44,17 @@
*/
#define GUAC_TERMINAL_MAX_CHAR_WIDTH 2
/**
* The size of margins between the console text and the border in mm.
*/
#define GUAC_TERMINAL_MARGINS 2
/**
* 1 inch is 25.4 millimeters, and we can therefore use the following
* to create a mm to px formula: (mm × dpi) ÷ 25.4 = px.
*/
#define GUAC_TERMINAL_MM_PER_INCH 25.4
/**
* All available terminal operations which affect character cells.
*/
@ -123,6 +134,11 @@ typedef struct guac_terminal_display {
*/
int height;
/**
* The size of margins between the console text and the border in pixels.
*/
int margin;
/**
* The description of the font to use for rendering.
*/