Compare commits
75 Commits
working/no
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
47b9360d46 | ||
|
98c2a6adcb | ||
|
3b0a9bac75 | ||
|
f6893ed319 | ||
|
a5214c971a | ||
|
ccfcef8c0f | ||
|
1a7a57ed19 | ||
|
eac064bde9 | ||
|
4afc1d85ce | ||
|
818b5f79df | ||
|
8ef60bfa9d | ||
|
d90e0e97fe | ||
|
ec7964e8fb | ||
|
add7ce361b | ||
|
7d16f67d6d | ||
|
e3adb97085 | ||
|
55941823ec | ||
|
07acce8a76 | ||
|
5cf408ebbb | ||
|
3ca6bb0a61 | ||
|
457a169c49 | ||
|
bad381cebe | ||
|
6171da6d0b | ||
|
067f2a91a0 | ||
|
bc52485570 | ||
|
b20afa275a | ||
|
b096e47f57 | ||
|
4d211e0c9e | ||
|
dffbeac57a | ||
|
0361adc01f | ||
|
1971a9dad2 | ||
|
5dbf4820ab | ||
|
15f6e9f678 | ||
|
6ab82446bb | ||
|
9c93337d97 | ||
|
cdee93ae25 | ||
|
eee3ac092c | ||
|
5bb56ed5ba | ||
|
0aae5eeadb | ||
|
6d994db9d2 | ||
|
cba5484be0 | ||
|
4048dd4900 | ||
|
98556fbe2e | ||
|
f438a36612 | ||
|
e8d966aec6 | ||
|
523532a52d | ||
|
51c640fdbd | ||
|
4cf1bfae0e | ||
|
9642afc468 | ||
|
ffb6c809be | ||
|
64ea9c4d1f | ||
|
a5834fd319 | ||
|
1e9cd9137b | ||
|
d4cd9b3e3a | ||
|
31f1b2c7c4 | ||
|
ce27936ed5 | ||
|
b7f05b9e4f | ||
|
d5761ad625 | ||
|
b26f9d64d6 | ||
|
da80163e24 | ||
|
28396ae345 | ||
|
a0e9f6ed9b | ||
|
bde8cdee46 | ||
|
669e02b4dc | ||
|
52c8683bcf | ||
|
c19eab9691 | ||
|
dd85c54961 | ||
|
c795bf9e4a | ||
|
c469300941 | ||
|
81300052e0 | ||
|
df4e5c6fdf | ||
|
a175a3d902 | ||
|
9cbd768210 | ||
|
c716a07abc | ||
|
bce1d2a434 |
@ -117,7 +117,7 @@ error() {
|
||||
##
|
||||
usage() {
|
||||
cat >&2 <<END
|
||||
guacctl 1.4.0, Apache Guacamole terminal session control utility.
|
||||
guacctl 1.5.0, Apache Guacamole terminal session control utility.
|
||||
Usage: guacctl [OPTION] [FILE or NAME]...
|
||||
|
||||
-d, --download download each of the files listed.
|
||||
|
@ -18,7 +18,7 @@
|
||||
#
|
||||
|
||||
AC_PREREQ([2.61])
|
||||
AC_INIT([guacamole-server], [1.4.0])
|
||||
AC_INIT([guacamole-server], [1.5.0])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects])
|
||||
AM_SILENT_RULES([yes])
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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 \
|
||||
@ -100,7 +102,7 @@ libguac_la_SOURCES = \
|
||||
pool.c \
|
||||
protocol.c \
|
||||
raw_encoder.c \
|
||||
recording.c \
|
||||
recording.c \
|
||||
socket.c \
|
||||
socket-broadcast.c \
|
||||
socket-fd.c \
|
||||
@ -137,7 +139,7 @@ libguac_la_CFLAGS = \
|
||||
-Werror -Wall -pedantic
|
||||
|
||||
libguac_la_LDFLAGS = \
|
||||
-version-info 20:0:0 \
|
||||
-version-info 21:0:0 \
|
||||
-no-undefined \
|
||||
@CAIRO_LIBS@ \
|
||||
@DL_LIBS@ \
|
||||
|
@ -302,15 +302,15 @@ int guac_client_add_user(guac_client* client, guac_user* user, int argc, char**
|
||||
/* Update owner pointer if user is owner */
|
||||
if (user->owner)
|
||||
client->__owner = user;
|
||||
|
||||
/* Notify owner of user joining connection. */
|
||||
else
|
||||
guac_client_owner_notify_join(client, user);
|
||||
|
||||
}
|
||||
|
||||
pthread_rwlock_unlock(&(client->__users_lock));
|
||||
|
||||
/* Notify owner of user joining connection. */
|
||||
if (retval == 0 && !user->owner)
|
||||
guac_client_owner_notify_join(client, user);
|
||||
|
||||
return retval;
|
||||
|
||||
}
|
||||
@ -335,12 +335,12 @@ void guac_client_remove_user(guac_client* client, guac_user* user) {
|
||||
if (user->owner)
|
||||
client->__owner = NULL;
|
||||
|
||||
/* Update owner of user having left the connection. */
|
||||
else
|
||||
guac_client_owner_notify_leave(client, user);
|
||||
|
||||
pthread_rwlock_unlock(&(client->__users_lock));
|
||||
|
||||
/* Update owner of user having left the connection. */
|
||||
if (!user->owner)
|
||||
guac_client_owner_notify_leave(client, user);
|
||||
|
||||
/* Call handler, if defined */
|
||||
if (user->leave_handler)
|
||||
user->leave_handler(user);
|
||||
@ -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
51
src/libguac/fips.c
Normal 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;
|
||||
|
||||
}
|
@ -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
|
||||
|
33
src/libguac/guacamole/fips.h
Normal file
33
src/libguac/guacamole/fips.h
Normal 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
|
@ -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 */
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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];
|
||||
|
@ -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;
|
||||
|
@ -121,31 +121,39 @@ 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 (processing_lag < 0)
|
||||
processing_lag = 0;
|
||||
|
||||
user->processing_lag = processing_lag;
|
||||
if (frame_processing_lag < 0)
|
||||
frame_processing_lag = 0;
|
||||
|
||||
}
|
||||
|
||||
/* Record baseline duration of frame by excluding lag */
|
||||
user->last_frame_duration = frame_duration - user->processing_lag;
|
||||
/* 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;
|
||||
|
||||
user->processing_lag = 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);
|
||||
|
@ -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 \
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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.
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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.
|
||||
|
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 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
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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));
|
||||
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
*
|
||||
|
79
src/protocols/rdp/keymaps/cs-cz-qwertz.keymap
Normal file
79
src/protocols/rdp/keymaps/cs-cz-qwertz.keymap
Normal 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
|
55
src/protocols/rdp/keymaps/fr_ca_qwerty.keymap
Normal file
55
src/protocols/rdp/keymaps/fr_ca_qwerty.keymap
Normal 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
|
@ -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>
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -77,6 +77,8 @@ libguac_terminal_la_LIBADD = \
|
||||
@LIBGUAC_LTLIB@
|
||||
|
||||
libguac_terminal_la_LDFLAGS = \
|
||||
-version-info 0:0:0 \
|
||||
-no-undefined \
|
||||
@CAIRO_LIBS@ \
|
||||
@MATH_LIBS@ \
|
||||
@PANGO_LIBS@ \
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
@ -852,14 +937,13 @@ void guac_terminal_commit_cursor(guac_terminal* term) {
|
||||
|
||||
}
|
||||
|
||||
int guac_terminal_write(guac_terminal* term, const char* c, int size) {
|
||||
int guac_terminal_write(guac_terminal* term, const char* buffer, int length) {
|
||||
|
||||
guac_terminal_lock(term);
|
||||
while (size > 0) {
|
||||
for (int written = 0; written < length; written++) {
|
||||
|
||||
/* Read and advance to next character */
|
||||
char current = *(c++);
|
||||
size--;
|
||||
char current = *(buffer++);
|
||||
|
||||
/* Write character to typescript, if any */
|
||||
if (term->typescript != NULL)
|
||||
@ -872,7 +956,7 @@ int guac_terminal_write(guac_terminal* term, const char* c, int size) {
|
||||
guac_terminal_unlock(term);
|
||||
|
||||
guac_terminal_notify(term);
|
||||
return 0;
|
||||
return length;
|
||||
|
||||
}
|
||||
|
||||
@ -1299,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);
|
||||
|
||||
@ -1382,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);
|
||||
|
||||
@ -2097,11 +2165,20 @@ 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) {
|
||||
|
||||
/* Remove the user from the terminal cursor */
|
||||
guac_common_cursor_remove_user(terminal->cursor, user);
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -29,6 +29,22 @@
|
||||
#include "terminal.h"
|
||||
#include "typescript.h"
|
||||
|
||||
/**
|
||||
* Handler for characters printed to the terminal. When a character is printed,
|
||||
* the current char handler for the terminal is called and given that
|
||||
* character.
|
||||
*
|
||||
* @param term
|
||||
* The terminal receiving the character.
|
||||
*
|
||||
* @param c
|
||||
* The received character.
|
||||
*
|
||||
* @return
|
||||
* Zero if the character was handled successfully, non-zero otherwise.
|
||||
*/
|
||||
typedef int guac_terminal_char_handler(guac_terminal* term, unsigned char c);
|
||||
|
||||
struct guac_terminal {
|
||||
|
||||
/**
|
||||
|
@ -141,19 +141,35 @@ typedef enum guac_terminal_cursor_type {
|
||||
} guac_terminal_cursor_type;
|
||||
|
||||
/**
|
||||
* Handler for characters printed to the terminal. When a character is printed,
|
||||
* the current char handler for the terminal is called and given that
|
||||
* character.
|
||||
*/
|
||||
typedef int guac_terminal_char_handler(guac_terminal* term, unsigned char c);
|
||||
|
||||
/**
|
||||
* Handler for setting the destination path for file uploads.
|
||||
* Handler that is invoked whenever the necessary terminal codes are sent to
|
||||
* to the given terminal to change the path for future file uploads.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the terminal receiving the upload path
|
||||
* change terminal code.
|
||||
*
|
||||
* @param path
|
||||
* The requested path.
|
||||
*/
|
||||
typedef void guac_terminal_upload_path_handler(guac_client* client, char* path);
|
||||
|
||||
/**
|
||||
* Handler for creating an outbound file download stream for a specified file.
|
||||
* Handler that is invoked whenever the necessary terminal codes are sent to
|
||||
* initiate a download of a given remote file.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the terminal receiving the file download
|
||||
* terminal code.
|
||||
*
|
||||
* @param filename
|
||||
* The name of the requested file. This may be a relative or absolute path,
|
||||
* and it is up to the implementation to define how this value is
|
||||
* interpreted.
|
||||
*
|
||||
* @return
|
||||
* The file stream created for the file download, already configured to
|
||||
* properly handle "ack" responses, etc. from the client, or NULL if the
|
||||
* stream could not be created.
|
||||
*/
|
||||
typedef guac_stream* guac_terminal_file_download_handler(guac_client* client, char* filename);
|
||||
|
||||
@ -276,11 +292,16 @@ guac_terminal_options* guac_terminal_options_create(
|
||||
|
||||
/**
|
||||
* Frees all resources associated with the given terminal.
|
||||
*
|
||||
* @param term
|
||||
* The terminal to free.
|
||||
*/
|
||||
void guac_terminal_free(guac_terminal* term);
|
||||
|
||||
/**
|
||||
* Set the upload path handler for the given terminal.
|
||||
* Sets the upload path handler for the given terminal. The upload path handler
|
||||
* is invoked whenever the terminal codes requesting an upload path change are
|
||||
* sent.
|
||||
*
|
||||
* @param terminal
|
||||
* The terminal to set the upload path handler for.
|
||||
@ -293,12 +314,14 @@ void guac_terminal_set_upload_path_handler(guac_terminal* terminal,
|
||||
guac_terminal_upload_path_handler* upload_path_handler);
|
||||
|
||||
/**
|
||||
* Set the file download handler for the given terminal.
|
||||
* Sets the file download handler for the given terminal. The file download
|
||||
* handler is invoked whenever the terminal codes requesting download of a
|
||||
* given remote file are sent.
|
||||
*
|
||||
* @param terminal
|
||||
* The terminal to set the file download handler for.
|
||||
*
|
||||
* @param upload_path_handler
|
||||
* @param file_download_handler
|
||||
* The handler to be called whenever the necessary terminal codes are sent to
|
||||
* the given terminal to initiate a download of a given remote file.
|
||||
*/
|
||||
@ -308,6 +331,13 @@ void guac_terminal_set_file_download_handler(guac_terminal* terminal,
|
||||
/**
|
||||
* Renders a single frame of terminal data. If data is not yet available,
|
||||
* this function will block until data is written.
|
||||
*
|
||||
* @param terminal
|
||||
* The terminal that should be rendered.
|
||||
*
|
||||
* @return
|
||||
* Zero if the frame was rendered successfully, non-zero if an error
|
||||
* occurred.
|
||||
*/
|
||||
int guac_terminal_render_frame(guac_terminal* terminal);
|
||||
|
||||
@ -316,6 +346,19 @@ int guac_terminal_render_frame(guac_terminal* terminal);
|
||||
* supplied by calls to guac_terminal_send_key(),
|
||||
* guac_terminal_send_mouse(), and guac_terminal_send_stream(). If input is not
|
||||
* yet available, this function will block.
|
||||
*
|
||||
* @param terminal
|
||||
* The terminal to read from.
|
||||
*
|
||||
* @param c
|
||||
* The buffer that should receive the bytes read.
|
||||
*
|
||||
* @param size
|
||||
* The number of bytes available within the buffer.
|
||||
*
|
||||
* @return
|
||||
* The number of bytes read into the buffer, zero if end-of-file is reached
|
||||
* (STDIN has been closed), or a negative value if an error occurs.
|
||||
*/
|
||||
int guac_terminal_read_stdin(guac_terminal* terminal, char* c, int size);
|
||||
|
||||
@ -377,8 +420,23 @@ char* guac_terminal_prompt(guac_terminal* terminal, const char* title,
|
||||
|
||||
/**
|
||||
* Writes the given format string and arguments to this terminal's STDOUT in
|
||||
* the same manner as printf(). This function may block until space is
|
||||
* the same manner as printf(). The entire populated format string will always
|
||||
* be written unless an error occurs. This function may block until space is
|
||||
* freed in the output buffer by guac_terminal_render_frame().
|
||||
*
|
||||
* @param terminal
|
||||
* The terminal to write to.
|
||||
*
|
||||
* @param format
|
||||
* A printf-style format string describing the data to be written to
|
||||
* STDOUT.
|
||||
*
|
||||
* @param ...
|
||||
* Any arguments to use when filling the format string.
|
||||
*
|
||||
* @return
|
||||
* The number of bytes written to STDOUT, or a negative value if an error
|
||||
* occurs preventing the data from being written in its entirety.
|
||||
*/
|
||||
int guac_terminal_printf(guac_terminal* terminal, const char* format, ...);
|
||||
|
||||
@ -486,9 +544,24 @@ int guac_terminal_send_data(guac_terminal* term, const char* data, int length);
|
||||
int guac_terminal_send_string(guac_terminal* term, const char* data);
|
||||
|
||||
/**
|
||||
* Writes the given string of characters to the terminal.
|
||||
* Writes the given buffer to the given terminal's STDOUT. All requested bytes
|
||||
* will be written unless an error occurs. This function may block until space
|
||||
* is freed in the output buffer by guac_terminal_render_frame().
|
||||
*
|
||||
* @param term
|
||||
* The terminal to write to.
|
||||
*
|
||||
* @param buffer
|
||||
* A buffer containing the characters to be written to the terminal.
|
||||
*
|
||||
* @param length
|
||||
* The number of bytes within the given string to write to the terminal.
|
||||
*
|
||||
* @return
|
||||
* The number of bytes written to STDOUT, or a negative value if an error
|
||||
* occurs preventing the data from being written in its entirety.
|
||||
*/
|
||||
int guac_terminal_write(guac_terminal* term, const char* c, int size);
|
||||
int guac_terminal_write(guac_terminal* term, const char* buffer, int length);
|
||||
|
||||
/**
|
||||
* Initializes the handlers of the given guac_stream such that it serves as the
|
||||
@ -532,7 +605,7 @@ int guac_terminal_send_stream(guac_terminal* term, guac_user* user,
|
||||
* STDIN.
|
||||
*
|
||||
* @param ...
|
||||
* Any srguments to use when filling the format string.
|
||||
* Any arguments to use when filling the format string.
|
||||
*
|
||||
* @return
|
||||
* The number of bytes written to STDIN, or a negative value if an error
|
||||
@ -560,7 +633,20 @@ void guac_terminal_dup(guac_terminal* term, guac_user* user,
|
||||
guac_socket* socket);
|
||||
|
||||
/**
|
||||
* Resize the terminal to the given dimensions.
|
||||
* Resize the client display and terminal to the given pixel dimensions.
|
||||
*
|
||||
* @param term
|
||||
* The terminal to resize.
|
||||
*
|
||||
* @param width
|
||||
* The new terminal width, in pixels.
|
||||
*
|
||||
* @param height
|
||||
* The new terminal height, in pixels.
|
||||
*
|
||||
* @return
|
||||
* Zero if the terminal was successfully resized to the given dimensions,
|
||||
* non-zero otherwise.
|
||||
*/
|
||||
int guac_terminal_resize(guac_terminal* term, int width, int height);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user