[WIP]: Spice protocol support.

This commit is contained in:
Virtually Nick 2021-01-01 10:05:20 -05:00
parent 5dbf4820ab
commit c75a1fe008
61 changed files with 7363 additions and 0 deletions

View File

@ -37,6 +37,7 @@ DIST_SUBDIRS = \
src/pulse \
src/protocols/kubernetes \
src/protocols/rdp \
src/protocols/spice \
src/protocols/ssh \
src/protocols/telnet \
src/protocols/vnc
@ -65,6 +66,10 @@ if ENABLE_RDP
SUBDIRS += src/protocols/rdp
endif
if ENABLE_SPICE
SUBDIRS += src/protocols/spice
endif
if ENABLE_SSH
SUBDIRS += src/protocols/ssh
endif

View File

@ -634,6 +634,27 @@ then
fi
#
# spice-glib
#
have_spice_glib=disabled
AC_ARG_WITH([spice],
[AS_HELP_STRING([--with-spice],
[support SPICE @<:@default=check@:>@])],
[],
[with_spice=check])
if test "x$with_spice" != "xno"
then
have_spice_glib=yes
PKG_CHECK_MODULES([GLIB2], [glib-2.0],, [have_spice_glib=no])
PKG_CHECK_MODULES([SPICE], [spice-client-glib-2.0],, [have_spice_glib=no])
fi
AM_CONDITIONAL([ENABLE_SPICE], [test "x${have_spice_glib}" = "xyes"])
AC_SUBST(SPICE_LIBS)
#
# FreeRDP 2 (libfreerdp2, libfreerdp-client2, and libwinpr2)
@ -1188,6 +1209,7 @@ AC_CONFIG_FILES([Makefile
src/protocols/kubernetes/tests/Makefile
src/protocols/rdp/Makefile
src/protocols/rdp/tests/Makefile
src/protocols/spice/Makefile
src/protocols/ssh/Makefile
src/protocols/telnet/Makefile
src/protocols/vnc/Makefile])
@ -1199,6 +1221,7 @@ AC_OUTPUT
AM_COND_IF([ENABLE_KUBERNETES], [build_kubernetes=yes], [build_kubernetes=no])
AM_COND_IF([ENABLE_RDP], [build_rdp=yes], [build_rdp=no])
AM_COND_IF([ENABLE_SPICE], [build_spice=yes], [build_spice=no])
AM_COND_IF([ENABLE_SSH], [build_ssh=yes], [build_ssh=no])
AM_COND_IF([ENABLE_TELNET], [build_telnet=yes], [build_telnet=no])
AM_COND_IF([ENABLE_VNC], [build_vnc=yes], [build_vnc=no])
@ -1260,6 +1283,7 @@ $PACKAGE_NAME version $PACKAGE_VERSION
Kubernetes .... ${build_kubernetes}
RDP ........... ${build_rdp}
SPICE ......... ${build_spice}
SSH ........... ${build_ssh}
Telnet ........ ${build_telnet}
VNC ........... ${build_vnc}

View File

@ -0,0 +1,133 @@
#
# 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.
#
# NOTE: Parts of this file (Makefile.am) are automatically transcluded verbatim
# into Makefile.in. Though the build system (GNU Autotools) automatically adds
# its own license boilerplate to the generated Makefile.in, that boilerplate
# does not apply to the transcluded portions of Makefile.am which are licensed
# to you by the ASF under the Apache License, Version 2.0, as described above.
#
AUTOMAKE_OPTIONS = foreign
ACLOCAL_AMFLAGS = -I m4
lib_LTLIBRARIES = libguac-client-spice.la
nodist_libguac_client_spice_la_SOURCES = \
_generated_keymaps.c
libguac_client_spice_la_SOURCES = \
argv.c \
auth.c \
channels/audio.c \
channels/clipboard.c \
channels/cursor.c \
channels/display.c \
channels/file.c \
client.c \
decompose.c \
input.c \
keyboard.c \
keymap.c \
log.c \
settings.c \
spice.c \
user.c
noinst_HEADERS = \
argv.h \
auth.h \
channels/audio.h \
channels/clipboard.h \
channels/cursor.h \
channels/display.h \
channels/file.h \
client.h \
decompose.h \
input.h \
keyboard.h \
keymap.h \
log.h \
settings.h \
spice.h \
user.h
libguac_client_spice_la_CFLAGS = \
-Werror -Wall -pedantic -Iinclude \
@COMMON_INCLUDE@ \
@COMMON_SSH_INCLUDE@ \
@LIBGUAC_INCLUDE@ \
@GLIB2_CFLAGS@ \
@SPICE_CFLAGS@
libguac_client_spice_la_LDFLAGS = \
-version-info 0:0:0 \
@CAIRO_LIBS@ \
@GLIB2_LIBS@ \
@SPICE_LIBS@
libguac_client_spice_la_LIBADD = \
@COMMON_LTLIB@ \
@LIBGUAC_LTLIB@
# Optional SFTP support
if ENABLE_COMMON_SSH
libguac_client_spice_la_SOURCES += sftp.c
noinst_HEADERS += sftp.h
libguac_client_spice_la_LIBADD += @COMMON_SSH_LTLIB@
endif
#
# Autogenerated keymaps and channel wrapper functions
#
CLEANFILES = \
_generated_keymaps.c
BUILT_SOURCES = \
_generated_keymaps.c
spice_keymaps = \
$(srcdir)/keymaps/base.keymap \
$(srcdir)/keymaps/failsafe.keymap \
$(srcdir)/keymaps/de_de_qwertz.keymap \
$(srcdir)/keymaps/de_ch_qwertz.keymap \
$(srcdir)/keymaps/en_gb_qwerty.keymap \
$(srcdir)/keymaps/en_us_qwerty.keymap \
$(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 \
$(srcdir)/keymaps/it_it_qwerty.keymap \
$(srcdir)/keymaps/ja_jp_qwerty.keymap \
$(srcdir)/keymaps/no_no_qwerty.keymap \
$(srcdir)/keymaps/pl_pl_qwerty.keymap \
$(srcdir)/keymaps/pt_br_qwerty.keymap \
$(srcdir)/keymaps/sv_se_qwerty.keymap \
$(srcdir)/keymaps/da_dk_qwerty.keymap \
$(srcdir)/keymaps/tr_tr_qwerty.keymap
_generated_keymaps.c: $(spice_keymaps) $(srcdir)/keymaps/generate.pl
$(AM_V_GEN) $(srcdir)/keymaps/generate.pl $(spice_keymaps)
EXTRA_DIST = \
$(spice_keymaps) \
keymaps/generate.pl

View File

@ -0,0 +1,53 @@
/*
* 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 "argv.h"
#include "spice.h"
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <guacamole/user.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
int guac_spice_argv_callback(guac_user* user, const char* mimetype,
const char* name, const char* value, void* data) {
guac_client* client = user->client;
guac_spice_client* spice_client = (guac_spice_client*) client->data;
guac_spice_settings* settings = spice_client->settings;
/* Update username */
if (strcmp(name, GUAC_SPICE_ARGV_USERNAME) == 0) {
free(settings->username);
settings->username = strdup(value);
}
/* Update password */
else if (strcmp(name, GUAC_SPICE_ARGV_PASSWORD) == 0) {
free(settings->password);
settings->password = strdup(value);
}
return 0;
}

View File

@ -0,0 +1,47 @@
/*
* 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_SPICE_ARGV_H
#define GUAC_SPICE_ARGV_H
#include "config.h"
#include <guacamole/argv.h>
#include <guacamole/user.h>
/**
* Handles a received argument value from a Guacamole "argv" instruction,
* updating the given connection parameter.
*/
guac_argv_callback guac_spice_argv_callback;
/**
* The name of the parameter Guacamole will use to specify/update the username
* for the SPICE connection.
*/
#define GUAC_SPICE_ARGV_USERNAME "username"
/**
* The name of the parameter Guacamole will use to specify/update the password
* for the SPICE connection.
*/
#define GUAC_SPICE_ARGV_PASSWORD "password"
#endif /* GUAC_SPICE_ARGV_H */

View File

@ -0,0 +1,71 @@
/*
* 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 "argv.h"
#include "auth.h"
#include "spice.h"
#include <guacamole/argv.h>
#include <guacamole/client.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <guacamole/string.h>
#include <glib-unix.h>
#include <pthread.h>
#include <string.h>
gboolean guac_spice_get_credentials(guac_client* client) {
guac_spice_client* spice_client = ((guac_spice_client*) client->data);
guac_spice_settings* settings = spice_client->settings;
char* params[3] = {NULL};
int i = 0;
/* Check if username is not provided. */
if (settings->username == NULL) {
guac_argv_register(GUAC_SPICE_ARGV_USERNAME, guac_spice_argv_callback, NULL, 0);
params[i] = GUAC_SPICE_ARGV_USERNAME;
i++;
}
/* Check if password is not provided. */
if (settings->password == NULL) {
guac_argv_register(GUAC_SPICE_ARGV_PASSWORD, guac_spice_argv_callback, NULL, 0);
params[i] = GUAC_SPICE_ARGV_PASSWORD;
i++;
}
params[i] = NULL;
/* If we have empty parameters, request them and await response. */
if (i > 0) {
guac_client_owner_send_required(client, (const char**) params);
guac_argv_await((const char**) params);
return true;
}
guac_client_log(client, GUAC_LOG_DEBUG,
"Unable to retrieve any credentials from the user.");
return false;
}

View File

@ -0,0 +1,46 @@
/*
* 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_SPICE_AUTH_H
#define GUAC_SPICE_AUTH_H
#include "config.h"
#include <guacamole/client.h>
#include <glib-unix.h>
/**
* Handler invoked when an authentication error is received from the SPICE
* server, which retrieves the credentials from the Guacamole Client accessing
* the connection, if those credentials have not been explicitly set in the
* configuration. Returns TRUE if credentials are successfully retrieved, or
* FALSE otherwise.
*
* @param client
* The guac_client that is attempting to connect to the SPICE server and
* that will be asked for the credentials.
*
* @return
* TRUE if the credentials are retrieved from the users; otherwise FALSE.
*/
gboolean guac_spice_get_credentials(guac_client* client);
#endif /* GUAC_SPICE_AUTH_H */

View File

@ -0,0 +1,68 @@
/*
* 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 "audio.h"
#include <guacamole/client.h>
void guac_spice_client_audio_playback_data_handler(
SpicePlaybackChannel* channel, gpointer data, gint size,
guac_client* client) {
guac_client_log(client, GUAC_LOG_DEBUG, "Calling audio playback data handler.");
}
void guac_spice_client_audio_playback_delay_handler(
SpicePlaybackChannel* channel, guac_client* client) {
guac_client_log(client, GUAC_LOG_DEBUG, "Calling audio playback delay handler.");
}
void guac_spice_client_audio_playback_start_handler(
SpicePlaybackChannel* channel, gint format, gint channels, gint rate,
guac_client* client) {
guac_client_log(client, GUAC_LOG_DEBUG, "Calling audio playback start handler.");
}
void guac_spice_client_audio_playback_stop_handler(
SpicePlaybackChannel* channel, guac_client* client) {
guac_client_log(client, GUAC_LOG_DEBUG, "Calling audio playback stop handler.");
}
void guac_spice_client_audio_record_start_handler(SpiceRecordChannel* channel,
gint format, gint channels, gint rate, guac_client* client) {
guac_client_log(client, GUAC_LOG_DEBUG, "Calling audio record start handler.");
}
void guac_spice_client_audio_record_stop_handler(SpiceRecordChannel* channel,
guac_client* client) {
guac_client_log(client, GUAC_LOG_DEBUG, "Calling audio record stop handler.");
}

View File

@ -0,0 +1,133 @@
/*
* 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_SPICE_AUDIO_H
#define GUAC_SPICE_AUDIO_H
#include "config.h"
#include <guacamole/client.h>
#include <spice-client-glib-2.0/spice-client.h>
/**
* A callback function invoked with the SPICE client receives audio playback
* data from the SPICE server.
*
* @param channel
* The SpicePlaybackChannel on which the data was sent.
*
* @param data
* The audio data.
*
* @param size
* The number of bytes of data sent.
*
* @param client
* The guac_client associated with this event.
*/
void guac_spice_client_audio_playback_data_handler(
SpicePlaybackChannel* channel, gpointer data, gint size,
guac_client* client);
/**
* A callback function invoked when the SPICE server requests the audio playback
* delay value from the client.
*
* @param channel
* The SpicePlaybackChannel channel on which this event occurred.
*
* @param client
* The guac_client associated with this event.
*/
void guac_spice_client_audio_playback_delay_handler(
SpicePlaybackChannel* channel, guac_client* client);
/**
* A callback function invoked by the client when the SPICE server sends a
* signal indicating that it is starting an audio transmission.
*
* @param channel
* The SpicePlaybackChannel on which this event occurred.
*
* @param format
* The SPICE_AUDIO_FMT value of the format of the audio data.
*
* @param channels
* The number of channels of audio data present.
*
* @param rate
* The audio sampling rate at which the data will be sent.
*
* @param client
* The guac_client associated with this event.
*/
void guac_spice_client_audio_playback_start_handler(
SpicePlaybackChannel* channel, gint format, gint channels, gint rate,
guac_client* client);
/**
* The callback function invoked by the client when the SPICE server sends
* an event indicating that audio playback will cease.
*
* @param channel
* The SpicePlaybackChannel on which the recording is being halted.
*
* @param client
* The guac_client associated with this event.
*/
void guac_spice_client_audio_playback_stop_handler(
SpicePlaybackChannel* channel, guac_client* client);
/**
* The callback function invoked by the client when the SPICE server requests
* that the client begin recording audio data to send to the server.
*
* @param channel
* The SpiceRecordChannel on which the record event is being requested.
*
* @param format
* The SPICE_AUDIO_FMT value representing the required recording format.
*
* @param channels
* The number of audio channels that should be recorded.
*
* @param rate
* The rate at which the recording should take place.
*
* @param client
* The guac_client associated with this event.
*/
void guac_spice_client_audio_record_start_handler(SpiceRecordChannel* channel,
gint format, gint channels, gint rate, guac_client* client);
/**
* The callback function invoked by the client when the SPICE server sends
* an event requesting that recording be stopped.
*
* @param channel
* The channel associated with the event.
*
* @param client
* The guac_client associated with the event.
*/
void guac_spice_client_audio_record_stop_handler(SpiceRecordChannel* channel,
guac_client* client);
#endif /* GUAC_SPICE_AUDIO_H */

View File

@ -0,0 +1,145 @@
/*
* 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 "client.h"
#include "clipboard.h"
#include "common/clipboard.h"
#include "common/iconv.h"
#include "spice.h"
#include "spice-constants.h"
#include "user.h"
#include <guacamole/client.h>
#include <guacamole/stream.h>
#include <guacamole/user.h>
#include <spice-1/spice/vd_agent.h>
int guac_spice_set_clipboard_encoding(guac_client* client,
const char* name) {
guac_client_log(client, GUAC_LOG_DEBUG, "Setting clipboard encoding.");
return 0;
}
int guac_spice_clipboard_handler(guac_user* user, guac_stream* stream,
char* mimetype) {
guac_client_log(user->client, GUAC_LOG_DEBUG, "Calling SPICE clipboard handler.");
guac_spice_client* spice_client = (guac_spice_client*) user->client->data;
guac_common_clipboard_reset(spice_client->clipboard, mimetype);
/* Set handlers for clipboard stream */
stream->blob_handler = guac_spice_clipboard_blob_handler;
stream->end_handler = guac_spice_clipboard_end_handler;
return 0;
}
int guac_spice_clipboard_blob_handler(guac_user* user, guac_stream* stream,
void* data, int length) {
guac_client_log(user->client, GUAC_LOG_DEBUG, "Calling SPICE clipboard BLOB handler.");
/* Append new data */
guac_spice_client* spice_client = (guac_spice_client*) user->client->data;
guac_common_clipboard_append(spice_client->clipboard, (char*) data, length);
return 0;
}
int guac_spice_clipboard_end_handler(guac_user* user, guac_stream* stream) {
guac_client_log(user->client, GUAC_LOG_DEBUG, "Calling SPICE clipboard end handler.");
guac_spice_client* spice_client = (guac_spice_client*) user->client->data;
const char* input = spice_client->clipboard->buffer;
/* Send via VNC only if finished connecting */
if (spice_client->main_channel != NULL)
spice_main_channel_clipboard_selection_notify(spice_client->main_channel,
VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD,
VD_AGENT_CLIPBOARD_UTF8_TEXT,
(const unsigned char*) input,
spice_client->clipboard->length);
return 0;
}
void guac_spice_clipboard_selection_handler(SpiceMainChannel channel,
guint selection, guint type, gpointer data, guint size,
guac_client* client) {
guac_client_log(client, GUAC_LOG_DEBUG, "Notifying client of clipboard data"
" available from the guest.");
guac_spice_client* spice_client = (guac_spice_client*) client->data;
switch (type) {
case VD_AGENT_CLIPBOARD_UTF8_TEXT:
guac_common_clipboard_append(spice_client->clipboard, (char *) data, size);
break;
default:
guac_client_log(client, GUAC_LOG_WARNING, "Guacamole currently does"
" not support clipboard data other than plain text.");
}
}
void guac_spice_clipboard_selection_grab_handler(SpiceMainChannel channel,
guint selection, gpointer types, guint ntypes, guint extra, guac_client* client) {
guac_client_log(client, GUAC_LOG_DEBUG, "Notifying client of clipboard grab"
" in the guest.");
guac_client_log(client, GUAC_LOG_DEBUG, "Arg: channel: 0x%08x", channel);
guac_client_log(client, GUAC_LOG_DEBUG, "Arg: selection: 0x%08x", selection);
guac_client_log(client, GUAC_LOG_DEBUG, "Arg: types: 0x%08x", types);
guac_client_log(client, GUAC_LOG_DEBUG, "Arg: ntypes: 0x%08x", ntypes);
guac_client_log(client, GUAC_LOG_DEBUG, "Arg: extra: 0x%08x", extra);
}
void guac_spice_clipboard_selection_release_handler(SpiceMainChannel channel,
guint selection, guint extra, guac_client* client) {
guac_client_log(client, GUAC_LOG_DEBUG, "Notifying client of clipboard"
" release in the guest.");
}
void guac_spice_clipboard_selection_request_handler(SpiceMainChannel channel,
guint selection, guint types, guac_client* client) {
guac_client_log(client, GUAC_LOG_DEBUG, "Requesting clipboard data from"
" the client.");
guac_spice_client* spice_client = (guac_spice_client*) client->data;
const char* input = spice_client->clipboard->buffer;
spice_main_channel_clipboard_selection_notify(spice_client->main_channel,
VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD,
VD_AGENT_CLIPBOARD_UTF8_TEXT,
(const unsigned char*) input,
spice_client->clipboard->length);
}

View File

@ -0,0 +1,150 @@
/*
* 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_SPICE_CLIPBOARD_H
#define GUAC_SPICE_CLIPBOARD_H
#include "config.h"
#include <guacamole/client.h>
#include <guacamole/user.h>
#include <spice-client-glib-2.0/spice-client.h>
/**
* Sets the encoding of clipboard data exchanged with the SPICE server to the
* encoding having the given name. If the name is NULL, or is invalid, the
* standard ISO8859-1 encoding will be used.
*
* @param client
* The client to set the clipboard encoding of.
*
* @param name
* The name of the encoding to use for all clipboard data. Valid values
* are: "ISO8859-1", "UTF-8", "UTF-16", "CP1252", or NULL.
*
* @return
* Zero if the chosen encoding is standard for SPICE, or non-zero if the
* SPICE standard is being violated.
*/
int guac_spice_set_clipboard_encoding(guac_client* client,
const char* name);
/**
* Handler for inbound clipboard data from Guacamole users.
*/
guac_user_clipboard_handler guac_spice_clipboard_handler;
/**
* Handler for stream data related to clipboard.
*/
guac_user_blob_handler guac_spice_clipboard_blob_handler;
/**
* Handler for end-of-stream related to clipboard.
*/
guac_user_end_handler guac_spice_clipboard_end_handler;
/**
* A handler that will be registered with the SPICE client to handle clipboard
* data sent from the SPICE server to the client.
*
* @param channel
* The main SPICE channel on which this event was fired.
*
* @param selection
* The clipboard on which the selection occurred.
*
* @param type
* The type of the data that is on the clipboard.
*
* @param data
* A pointer to the location containing the data that is on the clipboard.
*
* @param size
* The amount of data in bytes.
*
* @param client
* The guac_client associated with this event handler, passed when the
* handler was registered.
*/
void guac_spice_clipboard_selection_handler(SpiceMainChannel channel,
guint selection, guint type, gpointer data, guint size,
guac_client* client);
/**
* A handler that will be registered with the SPICE client to handle clipboard
* events where the guest (vdagent) within the SPICE server notifies the client
* that data is available on the clipboard.
*
* @param channel
* The main SpiceChannel on which this event is fired.
*
* @param selection
* The SPICE clipboard from which the event is fired.
*
* @param types
* The type of data being sent by the agent.
*
* @param ntypes
* The number of data types represented.
*
* @param client
* The guac_client that was passed in when the callback was registered.
*/
void guac_spice_clipboard_selection_grab_handler(SpiceMainChannel channel,
guint selection, gpointer types, guint ntypes, guint other, guac_client* client);
/**
* A handler that will be called by the SPICE client when the SPICE server
* is done with the clipboard and releases control of it.
*
* @param chennl
* The main SPICE channel on which this event is fired.
*
* @param selection
* The SPICE server clipboard releasing control.
*
* @param client
* The guac_client that was registered with the callback.
*/
void guac_spice_clipboard_selection_release_handler(SpiceMainChannel channel,
guint selection, guint other, guac_client* client);
/**
* A handler that will be called by the SPICE client when the SPICE server
* would like to check and receive the contents of the client's clipboard.
*
* @param channel
* The main SPICE channel on which this event is fired.
*
* @param selection
* The SPICE server clipboard that is requesting data.
*
* @param types
* The type of data to be sent to the SPICE server.
*
* @param client
* The guac_client object that was registered with the callback.
*/
void guac_spice_clipboard_selection_request_handler(SpiceMainChannel channel,
guint selection, guint types, guac_client* client);
#endif /* GUAC_SPICE_CLIPBOARD_H */

View File

@ -0,0 +1,80 @@
/*
* 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 "client.h"
#include "common/cursor.h"
#include "common/display.h"
#include "common/surface.h"
#include "spice.h"
#include "spice-constants.h"
#include <cairo/cairo.h>
#include <guacamole/client.h>
#include <guacamole/layer.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <spice-client-glib-2.0/spice-client.h>
/* Define cairo_format_stride_for_width() if missing */
#ifndef HAVE_CAIRO_FORMAT_STRIDE_FOR_WIDTH
#define cairo_format_stride_for_width(format, width) (width*4)
#endif
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <syslog.h>
void guac_spice_cursor_hide(SpiceChannel* channel, guac_client* client) {
guac_client_log(client, GUAC_LOG_TRACE, "Cursor hide signal received.");
guac_spice_client* spice_client = (guac_spice_client*) client->data;
guac_common_cursor_set_blank(spice_client->display->cursor);
}
void guac_spice_cursor_move(SpiceChannel* channel, int x, int y,
guac_client* client) {
guac_client_log(client, GUAC_LOG_TRACE, "Cursor move signal received.");
guac_spice_client* spice_client = (guac_spice_client*) client->data;
guac_common_cursor_update(spice_client->display->cursor, client->__owner, x, y, spice_client->display->cursor->button_mask);
}
void guac_spice_cursor_reset(SpiceChannel* channel, guac_client* client) {
guac_client_log(client, GUAC_LOG_DEBUG, "Cursor reset signal received, not yet implemented");
}
void guac_spice_cursor_set(SpiceChannel* channel, int width, int height,
int x, int y, gpointer* rgba, guac_client* client) {
guac_client_log(client, GUAC_LOG_TRACE, "Cursor set signal received.");
guac_spice_client* spice_client = (guac_spice_client*) client->data;
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
/* Update stored cursor information */
guac_common_cursor_set_argb(spice_client->display->cursor, x, y,
(const unsigned char*) rgba, width, height, stride);
}

View File

@ -0,0 +1,101 @@
/*
* 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_SPICE_CURSOR_H
#define GUAC_SPICE_CURSOR_H
#include "config.h"
#include <spice-client-glib-2.0/spice-client.h>
/**
* The callback function that is executed when the cursor hide signal is received
* from the SPICE server.
*
* @param channel
* The channel which received the cursor hide event.
*
* @param client
* The guac_client associated with this session.
*/
void guac_spice_cursor_hide(SpiceCursorChannel* channel, guac_client* client);
/**
* The callback function that is executed when the cursor move signal is
* received from the server.
*
* @param channel
* The channel that received the cursor move event.
*
* @param x
* The x position of the cursor.
*
* @param y
* The y position of the cursor.
*
* @param client
* The guac_client associated with this SPICE session.
*/
void guac_spice_cursor_move(SpiceCursorChannel* channel, int x, int y,
guac_client* client);
/**
* The callback function that is executed in response to the cursor reset
* signal, resetting the cursor to the default context.
*
* @param channel
* The channel that received the cursor reset signal.
*
* @param client
* The guac_client associated with this SPICE session.
*/
void guac_spice_cursor_reset(SpiceCursorChannel* channel, guac_client* client);
/**
* The callback function that is executed in response to receiving the cursor
* set signal from the SPICE server, which sets the width, height, and image
* of the cursor, and the x and y coordinates of the cursor hotspot.
*
* @param channel
* The channel that received the cursor set signal.
*
* @param width
* The width of the cursor image.
*
* @param height
* The height of the cursor image.
*
* @param x
* The x coordinate of the cursor hotspot.
*
* @param y
* The y coordinate of the cursor hotspot.
*
* @param rgba
* A pointer to the memory region containing the image data for the cursor,
* or NULL if the default cursor image should be used.
*
* @param client
* The guac_client associated with this SPICE session.
*/
void guac_spice_cursor_set(SpiceCursorChannel* channel, int width, int height,
int x, int y, gpointer* rgba, guac_client* client);
#endif /* GUAC_SPICE_CURSOR_H */

View File

@ -0,0 +1,170 @@
/*
* 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 "client.h"
#include "common/display.h"
#include "common/iconv.h"
#include "common/surface.h"
#include "spice.h"
#include <cairo/cairo.h>
#include <guacamole/client.h>
#include <guacamole/layer.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <spice-client-glib-2.0/spice-client.h>
/* Define cairo_format_stride_for_width() if missing */
#ifndef HAVE_CAIRO_FORMAT_STRIDE_FOR_WIDTH
#define cairo_format_stride_for_width(format, width) (width*4)
#endif
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <syslog.h>
void guac_spice_client_display_update(SpiceChannel* channel, int x,
int y, int w, int h, guac_client* client) {
guac_client_log(client, GUAC_LOG_DEBUG, "Received request to update Spice display: %d, %d, %d, %d", x, y, w, h);
guac_spice_client* spice_client = (guac_spice_client*) client->data;
/* Retrieve the primary display buffer */
SpiceDisplayPrimary primary;
if (spice_display_channel_get_primary(channel, 0, &primary)) {
/* Create cairo surface from decoded buffer */
cairo_surface_t* surface = cairo_image_surface_create_for_data(
primary.data,
CAIRO_FORMAT_RGB24,
primary.width,
primary.height,
primary.stride);
/* A region smaller than the entire display should be updated. */
if ((x > 0 || y > 0 ) && (w < primary.width || h < primary.height)) {
cairo_surface_t* updateArea = cairo_surface_create_similar_image(surface, CAIRO_FORMAT_RGB24, w, h);
cairo_t* updateContext = cairo_create(updateArea);
cairo_set_operator(updateContext, CAIRO_OPERATOR_SOURCE);
cairo_set_source_surface(updateContext, surface, 0 - x, 0 - y);
cairo_rectangle(updateContext, 0, 0, w, h);
cairo_fill(updateContext);
guac_common_surface_draw(spice_client->display->default_surface, x, y, updateArea);
cairo_surface_destroy(updateArea);
}
/* The entire display should be updated. */
else {
guac_common_surface_draw(spice_client->display->default_surface,
0, 0, surface);
}
/* Free surfaces */
cairo_surface_destroy(surface);
}
/* Flush surface and mark end of frame. */
guac_common_surface_flush(spice_client->display->default_surface);
guac_client_end_frame(client);
guac_socket_flush(client->socket);
}
void guac_spice_client_display_gl_draw(SpiceChannel* channel, int x,
int y, int w, int h, guac_client* client) {
guac_client_log(client, GUAC_LOG_DEBUG, "Received gl draw request.");
guac_spice_client* spice_client = (guac_spice_client*) client->data;
/* Copy specified rectangle within default layer */
guac_common_surface_copy(spice_client->display->default_surface,
x, y, w, h,
spice_client->display->default_surface, x, y);
}
void guac_spice_client_display_mark(SpiceChannel* channel, gint mark,
guac_client* client) {
guac_client_log(client, GUAC_LOG_DEBUG, "Received signal to mark display.");
int channelId;
g_object_get(channel, "channel-id", &channelId, NULL);
guac_client_log(client, GUAC_LOG_DEBUG, "Channel %i marked as available.", channelId);
}
void guac_spice_client_display_primary_create(SpiceChannel* channel,
gint format, gint width, gint height, gint stride, gint shmid,
gpointer imgdata, guac_client* client) {
guac_client_log(client, GUAC_LOG_DEBUG, "Received request to create primary display.");
guac_spice_client* spice_client = (guac_spice_client*) client->data;
spice_client->display = guac_common_display_alloc(client,
width, height);
guac_client_log(client, GUAC_LOG_TRACE, "Creating Cairo image surface.");
cairo_surface_t* surface = cairo_image_surface_create_for_data(imgdata, CAIRO_FORMAT_RGB24,
width, height, stride);
/* Draw directly to default layer */
guac_client_log(client, GUAC_LOG_TRACE, "Drawing to the default surface.");
guac_common_surface_draw(spice_client->display->default_surface,
0, 0, surface);
guac_client_log(client, GUAC_LOG_TRACE, "Flushing the default surface.");
guac_common_surface_flush(spice_client->display->default_surface);
guac_client_end_frame(client);
guac_socket_flush(client->socket);
}
void guac_spice_client_display_primary_destroy(SpiceChannel* channel,
guac_client* client) {
guac_client_log(client, GUAC_LOG_DEBUG, "Received request to destroy the primary display.");
guac_spice_client* spice_client = (guac_spice_client*) client->data;
guac_common_display_free(spice_client->display);
}
void* guac_spice_client_streaming_handler(SpiceChannel* channel,
gboolean streaming_mode, guac_client* client) {
guac_client_log(client, GUAC_LOG_DEBUG, "Received call to streaming handler.");
guac_spice_client* spice_client = (guac_spice_client*) client->data;
return spice_client->display;
}

View File

@ -0,0 +1,182 @@
/*
* 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_SPICE_DISPLAY_H
#define GUAC_SPICE_DISPLAY_H
#include "config.h"
#include <spice-client-glib-2.0/spice-client.h>
/**
* Callback invoked by the SPICE library when it receives a new binary image
* data from the SPICE server. The image itself will be stored in the designated
* sub-rectangle of client->framebuffer.
*
* @param channel
* The SpiceDisplayChannel that received the update event.
*
* @param x
* The X coordinate of the upper-left corner of the destination rectangle
* in which the image should be drawn, in pixels.
*
* @param y
* The Y coordinate of the upper-left corner of the destination rectangle
* in which the image should be drawn, in pixels.
*
* @param w
* The width of the image, in pixels.
*
* @param h
* The height of the image, in pixels.
*
* @param guac_client
* The guac_client associated with the event.
*/
void guac_spice_client_display_update(SpiceDisplayChannel* channel, int x,
int y, int w, int h, guac_client* client);
/**
* The callback function invoked when the RED_DISPLAY_MARK command is received
* from the SPICE server and the display should be exposed.
*
* @param channel
* The SpiceDisplayChannel on which the event was received.
*
* @param mark
* Non-zero when the display mark has been received.
*
* @param client
* The guac_client associated with this channel and event.
*/
void guac_spice_client_display_mark(SpiceDisplayChannel* channel, gint mark,
guac_client* client);
/**
* The callback function invoked when primary display buffer data is sent from
* the SPICE server to the client.
*
* @param channel
* The SpiceDisplayChannel on which this event was received.
*
* @param format
* The SPICE format of the received data.
*
* @param width
* The total width of the display.
*
* @param height
* The total height of the display.
*
* @param stride
* The buffer width padding.
*
* @param shmid
* The identifier of the shared memory segment associated with the data, or
* -1 if shared memory is not in use.
*
* @param imgdata
* A pointer to the buffer containing the surface data.
*
* @param client
* The guac_client associated with this channel/event.
*/
void guac_spice_client_display_primary_create(SpiceDisplayChannel* channel,
gint format, gint width, gint height, gint stride, gint shmid,
gpointer imgdata, guac_client* client);
/**
* The callback function invoked by the client when the primary surface is
* destroyed and should no longer be accessed.
*
* @param channel
* The SpiceDisplayChannel on which the primary surface destroy event was
* received.
*
* @param client
* The guac_client associated with this channel/event.
*/
void guac_spice_client_display_primary_destroy(SpiceDisplayChannel* channel,
guac_client* client);
/**
* Callback function that is invoked when a channel specifies that a display
* is marked as active and should be exposed.
*
* @param channel
* The channel on which the mark was received.
*
* @param marked
* TRUE if the display should be marked; otherwise false.
*
* @param client
* The guac_client associated with the channel/event.
*/
void guac_spice_client_display_mark(SpiceDisplayChannel* channel,
gboolean marked, guac_client* client);
/**
* Callback invoked by the SPICE client when it receives a CopyRect message.
* CopyRect specified a rectangle of source data within the display and a
* set of X/Y coordinates to which that rectangle should be copied.
*
* @param channel
* The SpiceDisplayChannel that received the gl_draw event.
*
* @param x
* The X coordinate of the upper-left corner of the source rectangle
* from which the image data should be copied, in pixels.
*
* @param y
* The Y coordinate of the upper-left corner of the source rectangle
* from which the image data should be copied, in pixels.
*
* @param w
* The width of the source and destination rectangles, in pixels.
*
* @param h
* The height of the source and destination rectangles, in pixels.
*
* @param guac_client
* The guac_client associated with this gl_draw event.
*/
void guac_spice_client_display_gl_draw(SpiceDisplayChannel* channel, int x,
int y, int w, int h, guac_client* client);
/**
* The callback function invoked by the client when it receives a request to
* change streaming mode.
*
* @param channel
* The SpiceDisplayChannel that received the streaming mode change request.
*
* @param streaming_mode
* TRUE if the display channel should be in streaming mode; otherwise FALSE.
*
* @param guac_client
* The guac_client associated with this event.
*
* @return
* A pointer to the display.
*/
void* guac_spice_client_streaming_handler(SpiceDisplayChannel* channel,
gboolean streaming_mode, guac_client* client);
#endif /* GUAC_SPICE_DISPLAY_H */

View File

@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "config.h"
#include "file.h"
#include <guacamole/client.h>
void guac_spice_client_file_transfer_handler(SpiceMainChannel* main_channel,
SpiceFileTransferTask* task, guac_client* client) {
guac_client_log(client, GUAC_LOG_DEBUG, "File transfer handler.");
}
#include "file.h"

View File

@ -0,0 +1,47 @@
/*
* 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_SPICE_FILE_H
#define GUAC_SPICE_FILE_H
#include "config.h"
#include <guacamole/client.h>
#include <spice-client-glib-2.0/spice-client.h>
/**
* A handler that is called when the SPICE client receives notification of
* a new file transfer task.
*
* @param main_channel
* The main channel associated with the SPICE session.
*
* @param task
* The file transfer task that triggered this function call.
*
* @param client
* The guac_client object associated with this session.
*/
void guac_spice_client_file_transfer_handler(SpiceMainChannel* main_channel,
SpiceFileTransferTask* task, guac_client* client);
#endif /* GUAC_SPICE_FILE_H */

View File

@ -0,0 +1,375 @@
/*
* 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 "auth.h"
#include "client.h"
#include "channels/audio.h"
#include "channels/clipboard.h"
#include "channels/cursor.h"
#include "channels/display.h"
#include "channels/file.h"
#include "input.h"
#include "keyboard.h"
#include "user.h"
#include "spice.h"
#include "spice-constants.h"
#ifdef ENABLE_COMMON_SSH
#include "common-ssh/sftp.h"
#include "common-ssh/ssh.h"
#include "common-ssh/user.h"
#endif
#include <guacamole/client.h>
#include <guacamole/recording.h>
#include <glib/gmain.h>
#include <pthread.h>
#include <spice-client-glib-2.0/spice-client.h>
#include <stdlib.h>
#include <string.h>
/**
* Handle events for the main SPICE channel, taking the appropriate action
* for known events, and logging warnings for unknowns and non-fatal events.
*
* @param channel
* The channel for which to handle events.
*
* @param event
* The event that is being handled.
*
* @param client
* The guacamole_client associated with this event.
*/
static void guac_spice_client_main_channel_handler(SpiceChannel *channel,
SpiceChannelEvent event, guac_client* client) {
guac_spice_client* spice_client = (guac_spice_client*) client->data;
guac_client_log(client, GUAC_LOG_DEBUG, "Received new main channel event: %u", event);
/* Handle the various possible SPICE events. */
switch (event) {
/* Channel has been closed, so we abort the connection. */
case SPICE_CHANNEL_CLOSED:
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
"Disconnected from SPICE server.");
break;
/* Channel has been opened - log it and do nothing else. */
case SPICE_CHANNEL_OPENED:
guac_client_log(client, GUAC_LOG_DEBUG, "Channel opened.");
break;
/* Error authenticating, log a warning and prompt user. */
case SPICE_CHANNEL_ERROR_AUTH:
guac_client_log(client, GUAC_LOG_WARNING,
"Channel authentication failed.");
/* Trigger a credential prompt. */
if (guac_spice_get_credentials(client)
&& spice_session_connect(spice_client->spice_session))
guac_client_log(client, GUAC_LOG_DEBUG, "Session connection started.");
else
guac_client_abort(client,
GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
"Failed to get credentials to connect to server.");
break;
/* TLS error, abort the connection with a warning. */
case SPICE_CHANNEL_ERROR_TLS:
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
"TLS failure connecting to SPICE server.");
break;
/* I/O error, abort the connection with a warning. */
case SPICE_CHANNEL_ERROR_IO:
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
"IO error communicating with SPICE server.");
break;
/* SPICE link error, abort the connection with a warning. */
case SPICE_CHANNEL_ERROR_LINK:
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
"Link error communicating with SPICE server.");
break;
/* Connect error, abort the connection with a warning. */
case SPICE_CHANNEL_ERROR_CONNECT:
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
"Connection error communicating with SPICe server.");
break;
/* Some other unknown event - log it and move on. */
default:
guac_client_log(client, GUAC_LOG_WARNING,
"Unknown event received on channel.");
}
}
int guac_client_init(guac_client* client) {
/* Set client args */
client->args = GUAC_SPICE_CLIENT_ARGS;
guac_client_log(client, GUAC_LOG_DEBUG, "Initializing SPICE client.");
/* Alloc client data */
guac_spice_client* spice_client = calloc(1, sizeof(guac_spice_client));
client->data = spice_client;
guac_client_log(client, GUAC_LOG_DEBUG, "Initializing clipboard.");
/* Init clipboard */
spice_client->clipboard =
guac_common_clipboard_alloc(GUAC_SPICE_CLIPBOARD_MAX_LENGTH);
guac_client_log(client, GUAC_LOG_DEBUG, "Setting up user handlers.");
/* Set handlers */
client->join_handler = guac_spice_user_join_handler;
client->leave_handler = guac_spice_user_leave_handler;
client->free_handler = guac_spice_client_free_handler;
guac_client_log(client, GUAC_LOG_DEBUG, "Setting up mutex locks.");
/* Recursive attribute for locks */
pthread_mutexattr_init(&(spice_client->attributes));
pthread_mutexattr_settype(&(spice_client->attributes),
PTHREAD_MUTEX_RECURSIVE);
/* Init required locks */
pthread_rwlock_init(&(spice_client->lock), NULL);
pthread_mutex_init(&(spice_client->message_lock), &(spice_client->attributes));
return 0;
}
int guac_spice_client_free_handler(guac_client* client) {
guac_spice_client* spice_client = (guac_spice_client*) client->data;
guac_spice_settings* settings = spice_client->settings;
/* Clean up SPICE client*/
SpiceSession* spice_session = spice_client->spice_session;
if (spice_session != NULL) {
/* Wait for client thread to finish */
pthread_join(spice_client->client_thread, NULL);
/* Disconnect the session, destroying data */
spice_session_disconnect(spice_session);
/* Free up the glib main loop. */
g_main_loop_unref(spice_client->spice_mainloop);
}
#ifdef ENABLE_COMMON_SSH
/* Free SFTP filesystem, if loaded */
if (spice_client->sftp_filesystem)
guac_common_ssh_destroy_sftp_filesystem(spice_client->sftp_filesystem);
/* Free SFTP session */
if (spice_client->sftp_session)
guac_common_ssh_destroy_session(spice_client->sftp_session);
/* Free SFTP user */
if (spice_client->sftp_user)
guac_common_ssh_destroy_user(spice_client->sftp_user);
guac_common_ssh_uninit();
#endif
/* Clean up recording, if in progress */
if (spice_client->recording != NULL)
guac_recording_free(spice_client->recording);
/* Free clipboard */
if (spice_client->clipboard != NULL)
guac_common_clipboard_free(spice_client->clipboard);
/* Free display */
if (spice_client->display != NULL)
guac_common_display_free(spice_client->display);
/* Free SPICE keyboard state */
guac_spice_keyboard_free(spice_client->keyboard);
spice_client->keyboard = NULL;
/* Free parsed settings */
if (settings != NULL)
guac_spice_settings_free(settings);
pthread_rwlock_destroy(&(spice_client->lock));
pthread_mutex_destroy(&(spice_client->message_lock));
/* Free generic data struct */
free(client->data);
return 0;
}
void guac_spice_client_channel_handler(SpiceSession *spice_session,
SpiceChannel *channel, guac_client* client) {
guac_spice_client* spice_client = (guac_spice_client*) client->data;
guac_spice_settings* settings = spice_client->settings;
int id;
/* Get the channel ID. */
g_object_get(channel, SPICE_PROPERTY_CHANNEL_ID, &id, NULL);
guac_client_log(client, GUAC_LOG_DEBUG, "New channel created: %i", id);
/* Check if this is the main channel and register handlers. */
if (SPICE_IS_MAIN_CHANNEL(channel)) {
guac_client_log(client, GUAC_LOG_DEBUG, "Setting up main channel.");
spice_client->main_channel = SPICE_MAIN_CHANNEL(channel);
/* Register the main channel event handler and return. */
g_signal_connect(channel, SPICE_SIGNAL_CHANNEL_EVENT,
G_CALLBACK(guac_spice_client_main_channel_handler), client);
/* Register clipboard handlers. */
g_signal_connect(channel, SPICE_SIGNAL_MAIN_CLIPBOARD_SELECTION,
G_CALLBACK(guac_spice_clipboard_selection_handler), client);
g_signal_connect(channel, SPICE_SIGNAL_MAIN_CLIPBOARD_SELECTION_GRAB,
G_CALLBACK(guac_spice_clipboard_selection_grab_handler), client);
g_signal_connect(channel, SPICE_SIGNAL_MAIN_CLIPBOARD_SELECTION_RELEASE,
G_CALLBACK(guac_spice_clipboard_selection_release_handler), client);
g_signal_connect(channel, SPICE_SIGNAL_MAIN_CLIPBOARD_SELECTION_REQUEST,
G_CALLBACK(guac_spice_clipboard_selection_request_handler), client);
/* Update the main display with our size. */
if (client->__owner != NULL)
spice_main_channel_update_display(spice_client->main_channel,
GUAC_SPICE_DEFAULT_DISPLAY_ID,
0,
0,
client->__owner->info.optimal_width,
client->__owner->info.optimal_height, TRUE);
return;
}
/* Check if this is the display channel and register display handlers. */
if (SPICE_IS_DISPLAY_CHANNEL(channel) && id == 0) {
guac_client_log(client, GUAC_LOG_DEBUG, "Setting up display channel.");
int width, height;
SpiceDisplayPrimary primary;
g_object_get(channel, "width", &width, "height", &height, NULL);
spice_client->spice_display = SPICE_DISPLAY_CHANNEL(channel);
spice_client->display = guac_common_display_alloc(client, width, height);
/* Register callbacks fr the various display signals. */
g_signal_connect(channel, SPICE_SIGNAL_DISPLAY_INVALIDATE,
G_CALLBACK(guac_spice_client_display_update), client);
g_signal_connect(channel, SPICE_SIGNAL_DISPLAY_MARK,
G_CALLBACK(guac_spice_client_display_mark), client);
g_signal_connect(channel, SPICE_SIGNAL_DISPLAY_PRIMARY_CREATE,
G_CALLBACK(guac_spice_client_display_primary_create), client);
g_signal_connect(channel, SPICE_SIGNAL_DISPLAY_PRIMARY_DESTROY,
G_CALLBACK(guac_spice_client_display_primary_destroy), client);
g_signal_connect(channel, SPICE_SIGNAL_GL_DRAW,
G_CALLBACK(guac_spice_client_display_gl_draw), client);
g_signal_connect(channel, SPICE_SIGNAL_STREAMING_MODE,
G_CALLBACK(guac_spice_client_streaming_handler), client);
/* Attempt to get the primary display, and set it up. */
if (spice_display_channel_get_primary(channel, 0, &primary)) {
guac_spice_client_display_primary_create(
spice_client->spice_display, primary.format,
primary.width, primary.height, primary.stride,
primary.shmid, primary.data, client);
guac_spice_client_display_mark(spice_client->spice_display,
primary.marked, client);
}
}
/* Check for audio playback channel and set up the channel. */
if (SPICE_IS_PLAYBACK_CHANNEL(channel) && settings->audio_enabled) {
guac_client_log(client, GUAC_LOG_DEBUG, "Setting up audio playback channel.");
spice_client->playback_channel = SPICE_PLAYBACK_CHANNEL(channel);
g_signal_connect(channel, SPICE_SIGNAL_PLAYBACK_DATA,
G_CALLBACK(guac_spice_client_audio_playback_data_handler), client);
g_signal_connect(channel, SPICE_SIGNAL_PLAYBACK_GET_DELAY,
G_CALLBACK(guac_spice_client_audio_playback_delay_handler), client);
g_signal_connect(channel, SPICE_SIGNAL_PLAYBACK_START,
G_CALLBACK(guac_spice_client_audio_playback_start_handler), client);
g_signal_connect(channel, SPICE_SIGNAL_PLAYBACK_STOP,
G_CALLBACK(guac_spice_client_audio_playback_stop_handler), client);
}
/* Check for audio recording channel and set up the channel. */
if (SPICE_IS_RECORD_CHANNEL(channel) && settings->audio_enabled) {
guac_client_log(client, GUAC_LOG_DEBUG, "Setting up audio record channel.");
spice_client->record_channel = SPICE_RECORD_CHANNEL(channel);
g_signal_connect(channel, SPICE_SIGNAL_RECORD_START,
G_CALLBACK(guac_spice_client_audio_record_start_handler), client);
g_signal_connect(channel, SPICE_SIGNAL_RECORD_STOP,
G_CALLBACK(guac_spice_client_audio_record_stop_handler), client);
}
/* Check for cursor channel and set it up. */
if (SPICE_IS_CURSOR_CHANNEL(channel)) {
guac_client_log(client, GUAC_LOG_DEBUG, "Setting up cursor channel.");
spice_client->cursor_channel = SPICE_CURSOR_CHANNEL(channel);
g_signal_connect(channel, SPICE_SIGNAL_CURSOR_HIDE,
G_CALLBACK(guac_spice_cursor_hide), client);
g_signal_connect(channel, SPICE_SIGNAL_CURSOR_MOVE,
G_CALLBACK(guac_spice_cursor_move), client);
g_signal_connect(channel, SPICE_SIGNAL_CURSOR_RESET,
G_CALLBACK(guac_spice_cursor_reset), client);
g_signal_connect(channel, SPICE_SIGNAL_CURSOR_SET,
G_CALLBACK(guac_spice_cursor_set), client);
}
/* Check if this is an inputs channel and set it up. */
if (SPICE_IS_INPUTS_CHANNEL(channel)) {
guac_client_log(client, GUAC_LOG_DEBUG, "Setting up inputs channel.");
spice_client->inputs_channel = SPICE_INPUTS_CHANNEL(channel);
/* Register callback that sets keyboard modifiers */
g_signal_connect(channel, SPICE_SIGNAL_INPUTS_MODIFIERS,
G_CALLBACK(guac_spice_keyboard_set_indicators), client);
}
/* File transfer channel */
if (SPICE_IS_WEBDAV_CHANNEL(channel)) {
guac_client_log(client, GUAC_LOG_DEBUG, "Setting up webdav channel.");
if (settings->file_transfer
&& settings->file_directory != NULL
&& strcmp(settings->file_directory, "") != 0) {
}
}
guac_client_log(client, GUAC_LOG_DEBUG, "Calling spice_channel_connect for channel %d.", id);
if (!spice_channel_connect(channel))
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
"Unable to connect the channel.");
}

View File

@ -0,0 +1,73 @@
/*
* 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_SPICE_CLIENT_H
#define GUAC_SPICE_CLIENT_H
#include "config.h"
#include <guacamole/client.h>
#include <spice-client-glib-2.0/spice-client.h>
/**
* The maximum duration of a frame in milliseconds.
*/
#define GUAC_SPICE_FRAME_DURATION 40
/**
* The amount of time to allow per message read within a frame, in
* milliseconds. If the server is silent for at least this amount of time, the
* frame will be considered finished.
*/
#define GUAC_SPICE_FRAME_TIMEOUT 0
/**
* The amount of time to wait for a new message from the SPICE server when
* beginning a new frame. This value must be kept reasonably small such that
* a slow SPICE server will not prevent external events from being handled (such
* as the stop signal from guac_client_stop()), but large enough that the
* message handling loop does not eat up CPU spinning.
*/
#define GUAC_SPICE_FRAME_START_TIMEOUT 1000000
/**
* The number of milliseconds to wait between connection attempts.
*/
#define GUAC_SPICE_CONNECT_INTERVAL 1000
/**
* The maximum number of bytes to allow within the clipboard.
*/
#define GUAC_SPICE_CLIPBOARD_MAX_LENGTH 262144
/**
* Handler which frees all data associated with the guac_client.
*/
guac_client_free_handler guac_spice_client_free_handler;
/**
* Handler for new channel events.
*/
void guac_spice_client_channel_handler(SpiceSession *spice_session,
SpiceChannel *channel, guac_client* client);
#endif /* GUAC_SPICE_CLIENT_H */

View File

@ -0,0 +1,176 @@
/*
* 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 "keyboard.h"
/**
* The X11 keysym for the dead key which types a grave (`).
*/
#define DEAD_GRAVE 0xFE50
/**
* The X11 keysym for the dead key which types an acute (´). Note that this is
* NOT equivalent to an apostrophe or single quote.
*/
#define DEAD_ACUTE 0xFE51
/**
* The X11 keysym for the dead key which types a circumflex/caret (^).
*/
#define DEAD_CIRCUMFLEX 0xFE52
/**
* The X11 keysym for the dead key which types a tilde (~).
*/
#define DEAD_TILDE 0xFE53
/**
* The X11 keysym for the dead key which types a dieresis/umlaut (¨).
*/
#define DEAD_DIERESIS 0xFE57
/**
* The X11 keysym for the dead key which types an abovering (˚). Note that this
* is NOT equivalent to the degree symbol.
*/
#define DEAD_ABOVERING 0xFE58
/**
* The decomposed form of a key that can be typed using two keypresses: a dead
* key followed by a base key. For example, on a keyboard which lacks a single
* dedicated key for doing the same, "ó" would be typed using the dead acute
* key followed by the "o" key. The dead key and base key are pressed and
* released in sequence; they are not held down.
*/
typedef struct guac_spice_decomposed_key {
/**
* The keysym of the dead key which must first be pressed and released to
* begin typing the desired character. The dead key defines the diacritic
* which will be applied to the character typed by the base key.
*/
int dead_keysym;
/**
* The keysym of the base key which must be pressed and released to finish
* typing the desired character. The base key defines the normal form of
* the character (the form which lacks any diacritic) to which the
* diacritic defined by the previously-pressed dead key will be applied.
*/
int base_keysym;
} guac_spice_decomposed_key;
/**
* A lookup table of all known decomposed forms of various keysyms. Keysyms map
* directly to entries within this table. A keysym which has no entry within
* this table does not have a defined decomposed form (or at least does not
* have a decomposed form relevant to SPICE).
*/
guac_spice_decomposed_key guac_spice_decomposed_keys[256] = {
/* ^ */ [0x005E] = { DEAD_CIRCUMFLEX, ' ' },
/* ` */ [0x0060] = { DEAD_GRAVE, ' ' },
/* ~ */ [0x007E] = { DEAD_TILDE, ' ' },
/* ¨ */ [0x00A8] = { DEAD_DIERESIS, ' ' },
/* ´ */ [0x00B4] = { DEAD_ACUTE, ' ' },
/* À */ [0x00C0] = { DEAD_GRAVE, 'A' },
/* Á */ [0x00C1] = { DEAD_ACUTE, 'A' },
/* Â */ [0x00C2] = { DEAD_CIRCUMFLEX, 'A' },
/* Ã */ [0x00C3] = { DEAD_TILDE, 'A' },
/* Ä */ [0x00C4] = { DEAD_DIERESIS, 'A' },
/* Å */ [0x00C5] = { DEAD_ABOVERING, 'A' },
/* È */ [0x00C8] = { DEAD_GRAVE, 'E' },
/* É */ [0x00C9] = { DEAD_ACUTE, 'E' },
/* Ê */ [0x00CA] = { DEAD_CIRCUMFLEX, 'E' },
/* Ë */ [0x00CB] = { DEAD_DIERESIS, 'E' },
/* Ì */ [0x00CC] = { DEAD_GRAVE, 'I' },
/* Í */ [0x00CD] = { DEAD_ACUTE, 'I' },
/* Î */ [0x00CE] = { DEAD_CIRCUMFLEX, 'I' },
/* Ï */ [0x00CF] = { DEAD_DIERESIS, 'I' },
/* Ñ */ [0x00D1] = { DEAD_TILDE, 'N' },
/* Ò */ [0x00D2] = { DEAD_GRAVE, 'O' },
/* Ó */ [0x00D3] = { DEAD_ACUTE, 'O' },
/* Ô */ [0x00D4] = { DEAD_CIRCUMFLEX, 'O' },
/* Õ */ [0x00D5] = { DEAD_TILDE, 'O' },
/* Ö */ [0x00D6] = { DEAD_DIERESIS, 'O' },
/* Ù */ [0x00D9] = { DEAD_GRAVE, 'U' },
/* Ú */ [0x00DA] = { DEAD_ACUTE, 'U' },
/* Û */ [0x00DB] = { DEAD_CIRCUMFLEX, 'U' },
/* Ü */ [0x00DC] = { DEAD_DIERESIS, 'U' },
/* Ý */ [0x00DD] = { DEAD_ACUTE, 'Y' },
/* à */ [0x00E0] = { DEAD_GRAVE, 'a' },
/* á */ [0x00E1] = { DEAD_ACUTE, 'a' },
/* â */ [0x00E2] = { DEAD_CIRCUMFLEX, 'a' },
/* ã */ [0x00E3] = { DEAD_TILDE, 'a' },
/* ä */ [0x00E4] = { DEAD_DIERESIS, 'a' },
/* å */ [0x00E5] = { DEAD_ABOVERING, 'a' },
/* è */ [0x00E8] = { DEAD_GRAVE, 'e' },
/* é */ [0x00E9] = { DEAD_ACUTE, 'e' },
/* ê */ [0x00EA] = { DEAD_CIRCUMFLEX, 'e' },
/* ë */ [0x00EB] = { DEAD_DIERESIS, 'e' },
/* ì */ [0x00EC] = { DEAD_GRAVE, 'i' },
/* í */ [0x00ED] = { DEAD_ACUTE, 'i' },
/* î */ [0x00EE] = { DEAD_CIRCUMFLEX, 'i' },
/* ï */ [0x00EF] = { DEAD_DIERESIS, 'i' },
/* ñ */ [0x00F1] = { DEAD_TILDE, 'n' },
/* ò */ [0x00F2] = { DEAD_GRAVE, 'o' },
/* ó */ [0x00F3] = { DEAD_ACUTE, 'o' },
/* ô */ [0x00F4] = { DEAD_CIRCUMFLEX, 'o' },
/* õ */ [0x00F5] = { DEAD_TILDE, 'o' },
/* ö */ [0x00F6] = { DEAD_DIERESIS, 'o' },
/* ù */ [0x00F9] = { DEAD_GRAVE, 'u' },
/* ú */ [0x00FA] = { DEAD_ACUTE, 'u' },
/* û */ [0x00FB] = { DEAD_CIRCUMFLEX, 'u' },
/* ü */ [0x00FC] = { DEAD_DIERESIS, 'u' },
/* ý */ [0x00FD] = { DEAD_ACUTE, 'y' },
/* ÿ */ [0x00FF] = { DEAD_DIERESIS, 'y' }
};
int guac_spice_decompose_keysym(guac_spice_keyboard* keyboard, int keysym) {
/* Verify keysym is within range of lookup table */
if (keysym < 0x00 || keysym > 0xFF)
return 1;
/* Verify keysym is actually defined within lookup table */
guac_spice_decomposed_key* key = &guac_spice_decomposed_keys[keysym];
if (!key->dead_keysym)
return 1;
/* Cannot type using decomposed keys if those keys are not defined within
* the current layout */
if (!guac_spice_keyboard_is_defined(keyboard, key->dead_keysym)
|| !guac_spice_keyboard_is_defined(keyboard, key->base_keysym))
return 1;
/* Press dead key */
guac_spice_keyboard_update_keysym(keyboard, key->dead_keysym, 1, GUAC_SPICE_KEY_SOURCE_SYNTHETIC);
guac_spice_keyboard_update_keysym(keyboard, key->dead_keysym, 0, GUAC_SPICE_KEY_SOURCE_SYNTHETIC);
/* Press base key */
guac_spice_keyboard_update_keysym(keyboard, key->base_keysym, 1, GUAC_SPICE_KEY_SOURCE_SYNTHETIC);
guac_spice_keyboard_update_keysym(keyboard, key->base_keysym, 0, GUAC_SPICE_KEY_SOURCE_SYNTHETIC);
/* Decomposed key successfully typed */
return 0;
}

View File

@ -0,0 +1,50 @@
/*
* 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_SPICE_DECOMPOSE_H
#define GUAC_SPICE_DECOMPOSE_H
#include "config.h"
#include "keyboard.h"
/**
* Attempts to type the given keysym by decomposing the associated character
* into the dead key and base key pair which would be used to type that
* character on a keyboard which lacks the necessary dedicated key. The key
* events for the dead key and base key are sent only if the keyboard layout of
* the given keyboard defines those keys.
*
* For example, the keysym for "ò" (0x00F2) would decompose into a dead grave
* (`) and the base key "o". May it rest in peace.
*
* @param keyboard
* The guac_spice_keyboard associated with the current SPICE session.
*
* @param keysym
* The keysym being pressed.
*
* @return
* Zero if the keysym was successfully decomposed and sent to the SPICE
* server as a pair of key events (the dead key and base key), non-zero
* otherwise.
*/
int guac_spice_decompose_keysym(guac_spice_keyboard* keyboard, int keysym);
#endif

154
src/protocols/spice/input.c Normal file
View File

@ -0,0 +1,154 @@
/*
* 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 "common/cursor.h"
#include "common/display.h"
#include "spice.h"
#include "spice-constants.h"
#include <guacamole/recording.h>
#include <guacamole/user.h>
#include <spice-client-glib-2.0/spice-client.h>
int guac_spice_user_mouse_handler(guac_user* user, int x, int y, int mask) {
guac_client* client = user->client;
guac_spice_client* spice_client = (guac_spice_client*) client->data;
/* Store the old button mask. */
int curr_button_mask = spice_client->display->cursor->button_mask;
guac_user_log(user, GUAC_LOG_TRACE, "Handling mouse event: %d, %d, 0x%08x", x, y, mask);
/* Update current mouse location/state */
guac_common_cursor_update(spice_client->display->cursor, user, x, y, mask);
/* Report mouse position and button state within recording. */
if (spice_client->recording != NULL)
guac_recording_report_mouse(spice_client->recording, x, y, mask);
/* Send SPICE event only if finished connecting */
if (spice_client->inputs_channel != NULL) {
spice_inputs_channel_position(spice_client->inputs_channel, x, y, GUAC_SPICE_DEFAULT_DISPLAY_ID, mask);
/* Button state has changed, so we need to evaluate which buttons changed. */
if (curr_button_mask != mask) {
/* Left click. */
if (curr_button_mask ^ GUAC_CLIENT_MOUSE_LEFT && mask & GUAC_CLIENT_MOUSE_LEFT) {
guac_user_log(user, GUAC_LOG_TRACE, "Left click!");
spice_inputs_channel_button_press(spice_client->inputs_channel, SPICE_MOUSE_BUTTON_LEFT, mask);
}
/* Left release. */
if (curr_button_mask & GUAC_CLIENT_MOUSE_LEFT && mask ^ GUAC_CLIENT_MOUSE_LEFT) {
guac_user_log(user, GUAC_LOG_TRACE, "Left release!");
spice_inputs_channel_button_release(spice_client->inputs_channel, SPICE_MOUSE_BUTTON_LEFT, mask);
}
/* Middle click. */
if (curr_button_mask ^ GUAC_CLIENT_MOUSE_MIDDLE && mask & GUAC_CLIENT_MOUSE_MIDDLE) {
guac_user_log(user, GUAC_LOG_TRACE, "Middle click!");
spice_inputs_channel_button_press(spice_client->inputs_channel, SPICE_MOUSE_BUTTON_MIDDLE, mask);
}
/* Middle release. */
if (curr_button_mask & GUAC_CLIENT_MOUSE_MIDDLE && mask ^ GUAC_CLIENT_MOUSE_MIDDLE) {
guac_user_log(user, GUAC_LOG_TRACE, "Middle release!");
spice_inputs_channel_button_release(spice_client->inputs_channel, SPICE_MOUSE_BUTTON_MIDDLE, mask);
}
/* Right click. */
if (curr_button_mask ^ GUAC_CLIENT_MOUSE_RIGHT && mask & GUAC_CLIENT_MOUSE_RIGHT) {
guac_user_log(user, GUAC_LOG_TRACE, "Right click!");
spice_inputs_channel_button_press(spice_client->inputs_channel, SPICE_MOUSE_BUTTON_RIGHT, mask);
}
/* Right release. */
if (curr_button_mask & GUAC_CLIENT_MOUSE_RIGHT && mask ^ GUAC_CLIENT_MOUSE_RIGHT) {
guac_user_log(user, GUAC_LOG_TRACE, "Right release!");
spice_inputs_channel_button_release(spice_client->inputs_channel, SPICE_MOUSE_BUTTON_RIGHT, mask);
}
/* Scroll up. */
if (curr_button_mask ^ GUAC_CLIENT_MOUSE_SCROLL_UP && mask & GUAC_CLIENT_MOUSE_SCROLL_UP) {
guac_user_log(user, GUAC_LOG_TRACE, "Scroll up!");
spice_inputs_channel_button_press(spice_client->inputs_channel, SPICE_MOUSE_BUTTON_UP, mask);
}
/* Scroll up stop. */
if (curr_button_mask & GUAC_CLIENT_MOUSE_SCROLL_UP && mask ^ GUAC_CLIENT_MOUSE_SCROLL_UP) {
guac_user_log(user, GUAC_LOG_TRACE, "Scroll up stop!");
spice_inputs_channel_button_release(spice_client->inputs_channel, SPICE_MOUSE_BUTTON_UP, mask);
}
/* Scroll down. */
if (curr_button_mask ^ GUAC_CLIENT_MOUSE_SCROLL_DOWN && mask & GUAC_CLIENT_MOUSE_SCROLL_DOWN) {
guac_user_log(user, GUAC_LOG_TRACE, "Scroll down!");
spice_inputs_channel_button_press(spice_client->inputs_channel, SPICE_MOUSE_BUTTON_DOWN, mask);
}
/* Scroll down stop. */
if (curr_button_mask & GUAC_CLIENT_MOUSE_SCROLL_DOWN && mask ^ GUAC_CLIENT_MOUSE_SCROLL_DOWN) {
guac_user_log(user, GUAC_LOG_TRACE, "Scroll down stop!");
spice_inputs_channel_button_release(spice_client->inputs_channel, SPICE_MOUSE_BUTTON_DOWN, mask);
}
}
}
return 0;
}
int guac_spice_user_key_handler(guac_user* user, int keysym, int pressed) {
guac_spice_client* spice_client = (guac_spice_client*) user->client->data;
int retval = 0;
pthread_rwlock_rdlock(&(spice_client->lock));
guac_user_log(user, GUAC_LOG_TRACE, "Handling keypress: 0x%08x", keysym);
/* Report key state within recording */
if (spice_client->recording != NULL)
guac_recording_report_key(spice_client->recording,
keysym, pressed);
/* Skip if keyboard not yet ready */
if (spice_client->inputs_channel == NULL || spice_client->keyboard == NULL)
goto complete;
/* Update keysym state */
retval = guac_spice_keyboard_update_keysym(spice_client->keyboard,
keysym, pressed, GUAC_SPICE_KEY_SOURCE_CLIENT);
complete:
pthread_rwlock_unlock(&(spice_client->lock));
return retval;
}
void guac_spice_mouse_mode_update(SpiceChannel* channel, guac_client* client) {
guac_client_log(client, GUAC_LOG_DEBUG, "Updating mouse mode, not implemented.");
}

View File

@ -0,0 +1,50 @@
/*
* 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_SPICE_INPUT_H
#define GUAC_SPICE_INPUT_H
#include "config.h"
#include <guacamole/user.h>
/**
* Handler for Guacamole user mouse events.
*/
guac_user_mouse_handler guac_spice_user_mouse_handler;
/**
* Handler for Guacamole user key events.
*/
guac_user_key_handler guac_spice_user_key_handler;
/**
* A callback that is invoked when the SPICE server updates the mouse mode.
*
* @param channel
* The channel on which the update occurred.
*
* @param client
* The guac_client instance associated with this session.
*/
void guac_spice_mouse_mode_update(SpiceChannel* channel, guac_client* client);
#endif /* GUAC_SPICE_INPUT_H */

View File

@ -0,0 +1,652 @@
/*
* 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 "decompose.h"
#include "keyboard.h"
#include "keymap.h"
#include "spice.h"
#include "spice-constants.h"
#include <guacamole/client.h>
#include <stdlib.h>
/**
* Translates the given keysym into the corresponding lock flag, as would be
* required by the SPICE synchronize event. If the given keysym does not
* represent a lock key, zero is returned.
*
* @param keysym
* The keysym to translate into a SPICE lock flag.
*
* @return
* The SPICE lock flag which corresponds to the given keysym, or zero if the
* given keysym does not represent a lock key.
*/
static int guac_spice_keyboard_lock_flag(int keysym) {
/* Translate keysym into corresponding lock flag */
switch (keysym) {
/* Scroll lock */
case GUAC_SPICE_KEYSYM_SCROLL_LOCK:
return SPICE_INPUTS_SCROLL_LOCK;
/* Num lock */
case GUAC_SPICE_KEYSYM_NUM_LOCK:
return SPICE_INPUTS_NUM_LOCK;
/* Caps lock */
case GUAC_SPICE_KEYSYM_CAPS_LOCK:
return SPICE_INPUTS_CAPS_LOCK;
}
/* Not a lock key */
return 0;
}
/**
* Immediately sends an SPICE key event having the given scancode and flags.
*
* @param spice_client
* The SPICE client instance associated with the SPICE session along which the
* key event should be sent.
*
* @param scancode
* The scancode of the key to press or release via the SPICE key event.
*
* @param flags
* Any SPICE-specific flags required for the provided scancode to have the
* intended meaning, such as KBD_FLAGS_EXTENDED. The possible flags and
* their meanings are dictated by SPICE. KBD_FLAGS_DOWN and KBD_FLAGS_UP
* need not be specified here - they will automatically be added depending
* on the value specified for the pressed parameter.
*
* @param pressed
* Non-zero if the key is being pressed, zero if the key is being released.
*/
static void guac_spice_send_key_event(guac_spice_client* spice_client,
int scancode, int flags, int pressed) {
/* Send actual key press or release */
pthread_mutex_lock(&(spice_client->message_lock));
if (pressed)
spice_inputs_channel_key_press(spice_client->inputs_channel, scancode);
else
spice_inputs_channel_key_release(spice_client->inputs_channel, scancode);
pthread_mutex_unlock(&(spice_client->message_lock));
}
/**
* Immediately sends an SPICE synchonize event having the given flags. An SPICE
* 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.
*
* @param spice_client
* The SPICE client instance associated with the SPICE session along which the
* synchronize event should be sent.
*
* @param modifiers
* Bitwise OR of the flags representing the lock keys which should be set,
* if any, as dictated by the SPICE protocol. If no flags are set, then no
* lock keys will be active.
*/
static void guac_spice_send_synchronize_event(guac_spice_client* spice_client,
unsigned int modifiers) {
/* Skip if inputs channel is not connected */
if (spice_client->inputs_channel == NULL)
return;
/* Synchronize lock key states */
pthread_mutex_lock(&(spice_client->message_lock));
spice_inputs_channel_set_key_locks(spice_client->inputs_channel, modifiers);
pthread_mutex_unlock(&(spice_client->message_lock));
}
/**
* Given a keyboard instance and X11 keysym, returns a pointer to the
* keys_by_keysym entry that represents the key having that keysym within the
* keyboard, regardless of whether the key is currently defined. If no such key
* can exist (the keysym cannot be mapped or is out of range), NULL is
* returned.
*
* @param keyboard
* The guac_spice_keyboard associated with the current SPICE session.
*
* @param keysym
* The keysym of the key to lookup within the given keyboard.
*
* @return
* A pointer to the keys_by_keysym entry which represents or can represent
* the key having the given keysym, or NULL if no such keysym can be
* defined within a guac_spice_keyboard structure.
*/
static guac_spice_key** guac_spice_keyboard_map_key(guac_spice_keyboard* keyboard,
int keysym) {
int index;
/* Map keysyms between 0x0000 and 0xFFFF directly */
if (keysym >= 0x0000 && keysym <= 0xFFFF)
index = keysym;
/* Map all Unicode keysyms from U+0000 to U+FFFF */
else if (keysym >= 0x1000000 && keysym <= 0x100FFFF)
index = 0x10000 + (keysym & 0xFFFF);
/* All other keysyms are unmapped */
else
return NULL;
/* Corresponding key mapping (defined or not) has been located */
return &(keyboard->keys_by_keysym[index]);
}
/**
* Returns the number of bits that are set within the given integer (the number
* of 1s in the binary expansion of the given integer).
*
* @param value
* The integer to read.
*
* @return
* The number of bits that are set within the given integer.
*/
static int guac_spice_count_bits(unsigned int value) {
int bits = 0;
while (value) {
bits += value & 1;
value >>= 1;
}
return bits;
}
/**
* Returns an estimated cost for sending the necessary SPICE events to type the
* key described by the given guac_spice_keysym_desc, given the current lock and
* modifier state of the keyboard. A higher cost value indicates that a greater
* number of events are expected to be required.
*
* Lower-cost approaches should be preferred when multiple alternatives exist
* for typing a particular key, as the lower cost implies fewer additional key
* events required to produce the expected behavior. For example, if Caps Lock
* is enabled, typing an uppercase "A" by pressing the "A" key has a lower cost
* than disabling Caps Lock and pressing Shift+A.
*
* @param keyboard
* The guac_spice_keyboard associated with the current SPICE session.
*
* @param def
* The guac_spice_keysym_desc that describes the key being pressed, as well
* as any requirements that must be satisfied for the key to be interpreted
* as expected.
*
* @return
* An arbitrary integer value which indicates the overall estimated
* complexity of typing the given key.
*/
static int guac_spice_keyboard_get_cost(guac_spice_keyboard* keyboard,
const guac_spice_keysym_desc* def) {
unsigned int modifiers = guac_spice_keyboard_get_modifier_flags(keyboard);
/* Each change to any key requires one event, by definition */
int cost = 1;
/* Each change to a lock requires roughly two key events */
unsigned int update_locks = (def->set_modifiers & ~keyboard->modifiers) | (def->clear_modifiers & keyboard->modifiers);
cost += guac_spice_count_bits(update_locks) * 2;
/* Each change to a modifier requires one key event */
unsigned int update_modifiers = (def->clear_modifiers & modifiers) | (def->set_modifiers & ~modifiers);
cost += guac_spice_count_bits(update_modifiers);
return cost;
}
/**
* Returns a pointer to the guac_spice_key structure representing the
* definition(s) and state of the key having the given keysym. If no such key
* is defined within the keyboard layout of the SPICE server, NULL is returned.
*
* @param keyboard
* The guac_spice_keyboard associated with the current SPICE session.
*
* @param keysym
* The keysym of the key to lookup within the given keyboard.
*
* @return
* A pointer to the guac_spice_key structure representing the definition(s)
* and state of the key having the given keysym, or NULL if no such key is
* defined within the keyboard layout of the SPICE server.
*/
static guac_spice_key* guac_spice_keyboard_get_key(guac_spice_keyboard* keyboard,
int keysym) {
/* Verify that the key is actually defined */
guac_spice_key** key_by_keysym = guac_spice_keyboard_map_key(keyboard, keysym);
if (key_by_keysym == NULL)
return NULL;
return *key_by_keysym;
}
/**
* Given a key which may have multiple possible definitions, returns the
* definition that currently has the lowest cost, taking into account the
* current keyboard lock and modifier states.
*
* @param keyboard
* The guac_spice_keyboard associated with the current SPICE session.
*
* @param key
* The key whose lowest-cost possible definition should be retrieved.
*
* @return
* A pointer to the guac_spice_keysym_desc which defines the current
* lowest-cost method of typing the given key.
*/
static const guac_spice_keysym_desc* guac_spice_keyboard_get_definition(guac_spice_keyboard* keyboard,
guac_spice_key* key) {
/* Consistently map the same entry so long as the key is held */
if (key->pressed != NULL)
return key->pressed;
/* Calculate cost of first definition of key (there must always be at least
* one definition) */
const guac_spice_keysym_desc* best_def = key->definitions[0];
int best_cost = guac_spice_keyboard_get_cost(keyboard, best_def);
/* If further definitions exist, choose the definition with the lowest
* overall cost */
for (int i = 1; i < key->num_definitions; i++) {
const guac_spice_keysym_desc* def = key->definitions[i];
int cost = guac_spice_keyboard_get_cost(keyboard, def);
if (cost < best_cost) {
best_def = def;
best_cost = cost;
}
}
return best_def;
}
/**
* Adds the keysym/scancode mapping described by the given guac_spice_keysym_desc
* to the internal mapping of the keyboard. If insufficient space remains for
* additional keysyms, or the given keysym has already reached the maximum
* number of possible definitions, the mapping is ignored and the failure is
* logged.
*
* @param keyboard
* The guac_spice_keyboard associated with the current SPICE session.
*
* @param mapping
* The keysym/scancode mapping that should be added to the given keyboard.
*/
static void guac_spice_keyboard_add_mapping(guac_spice_keyboard* keyboard,
const guac_spice_keysym_desc* mapping) {
/* Locate corresponding keysym-to-key translation entry within keyboard
* structure */
guac_spice_key** key_by_keysym = guac_spice_keyboard_map_key(keyboard, mapping->keysym);
if (key_by_keysym == NULL) {
guac_client_log(keyboard->client, GUAC_LOG_DEBUG, "Ignoring unmappable keysym 0x%X", mapping->keysym);
return;
}
/* If not yet pointing to a key, point keysym-to-key translation entry at
* next available storage */
if (*key_by_keysym == NULL) {
if (keyboard->num_keys == GUAC_SPICE_KEYBOARD_MAX_KEYSYMS) {
guac_client_log(keyboard->client, GUAC_LOG_DEBUG, "Key definition "
"for keysym 0x%X dropped: Keymap exceeds maximum "
"supported number of keysyms",
mapping->keysym);
return;
}
*key_by_keysym = &keyboard->keys[keyboard->num_keys++];
}
guac_spice_key* key = *key_by_keysym;
/* Add new definition only if sufficient space remains */
if (key->num_definitions == GUAC_SPICE_KEY_MAX_DEFINITIONS) {
guac_client_log(keyboard->client, GUAC_LOG_DEBUG, "Key definition "
"for keysym 0x%X dropped: Maximum number of possible "
"definitions has been reached for this keysym",
mapping->keysym);
return;
}
/* Store new possible definition of key */
key->definitions[key->num_definitions++] = mapping;
}
/**
* Loads all keysym/scancode mappings declared within the given keymap and its
* parent keymap, if any. These mappings are stored within the given
* guac_spice_keyboard structure for future use in translating keysyms to the
* scancodes required by SPICE key events.
*
* @param keyboard
* The guac_spice_keyboard which should be initialized with the
* keysym/scancode mapping defined in the given keymap.
*
* @param keymap
* The keymap to use to populate the given client's keysym/scancode
* mapping.
*/
static void guac_spice_keyboard_load_keymap(guac_spice_keyboard* keyboard,
const guac_spice_keymap* keymap) {
/* If parent exists, load parent first */
if (keymap->parent != NULL)
guac_spice_keyboard_load_keymap(keyboard, keymap->parent);
/* Log load */
guac_client_log(keyboard->client, GUAC_LOG_INFO,
"Loading keymap \"%s\"", keymap->name);
/* Copy mapping into keymap */
const guac_spice_keysym_desc* mapping = keymap->mapping;
while (mapping->keysym != 0) {
guac_spice_keyboard_add_mapping(keyboard, mapping++);
}
}
guac_spice_keyboard* guac_spice_keyboard_alloc(guac_client* client,
const guac_spice_keymap* keymap) {
guac_spice_keyboard* keyboard = calloc(1, sizeof(guac_spice_keyboard));
keyboard->client = client;
/* Load keymap into keyboard */
guac_spice_keyboard_load_keymap(keyboard, keymap);
return keyboard;
}
void guac_spice_keyboard_free(guac_spice_keyboard* keyboard) {
free(keyboard);
}
int guac_spice_keyboard_is_defined(guac_spice_keyboard* keyboard, int keysym) {
/* Return whether the mapping actually exists */
return guac_spice_keyboard_get_key(keyboard, keysym) != NULL;
}
int guac_spice_keyboard_is_pressed(guac_spice_keyboard* keyboard, int keysym) {
guac_spice_key* key = guac_spice_keyboard_get_key(keyboard, keysym);
return key != NULL && key->pressed != NULL;
}
unsigned int guac_spice_keyboard_get_modifier_flags(guac_spice_keyboard* keyboard) {
unsigned int modifier_flags = 0;
/* Shift */
if (guac_spice_keyboard_is_pressed(keyboard, GUAC_SPICE_KEYSYM_LSHIFT)
|| guac_spice_keyboard_is_pressed(keyboard, GUAC_SPICE_KEYSYM_RSHIFT))
modifier_flags |= GUAC_SPICE_KEYMAP_MODIFIER_SHIFT;
/* Dedicated AltGr key */
if (guac_spice_keyboard_is_pressed(keyboard, GUAC_SPICE_KEYSYM_RALT)
|| guac_spice_keyboard_is_pressed(keyboard, GUAC_SPICE_KEYSYM_ALTGR))
modifier_flags |= GUAC_SPICE_KEYMAP_MODIFIER_ALTGR;
/* AltGr via Ctrl+Alt */
if (guac_spice_keyboard_is_pressed(keyboard, GUAC_SPICE_KEYSYM_LALT)
&& (guac_spice_keyboard_is_pressed(keyboard, GUAC_SPICE_KEYSYM_RCTRL)
|| guac_spice_keyboard_is_pressed(keyboard, GUAC_SPICE_KEYSYM_LCTRL)))
modifier_flags |= GUAC_SPICE_KEYMAP_MODIFIER_ALTGR;
return modifier_flags;
}
/**
* Presses/releases the requested key by sending one or more SPICE key events, as
* defined within the keymap defining that key.
*
* @param keyboard
* The guac_spice_keyboard associated with the current SPICE session.
*
* @param key
* The guac_spice_keysym_desc of the key being pressed or released, as
* retrieved from the relevant keymap.
*
* @param pressed
* Zero if the key is being released, non-zero otherwise.
*
* @return
* Zero if the key was successfully pressed/released, non-zero if the key
* cannot be sent using SPICE key events.
*/
static const guac_spice_keysym_desc* guac_spice_keyboard_send_defined_key(guac_spice_keyboard* keyboard,
guac_spice_key* key, int pressed) {
guac_client* client = keyboard->client;
guac_spice_client* spice_client = (guac_spice_client*) client->data;
const guac_spice_keysym_desc* keysym_desc = guac_spice_keyboard_get_definition(keyboard, key);
if (keysym_desc->scancode == 0)
return NULL;
/* Update state of required locks and modifiers only when key is just
* now being pressed */
if (pressed) {
guac_spice_keyboard_update_locks(keyboard,
keysym_desc->set_locks,
keysym_desc->clear_locks);
guac_spice_keyboard_update_modifiers(keyboard,
keysym_desc->set_modifiers,
keysym_desc->clear_modifiers);
}
/* Fire actual key event for target key */
guac_client_log(client, GUAC_LOG_TRACE, "Firing scancode event: %08x", keysym_desc->scancode);
guac_spice_send_key_event(spice_client, keysym_desc->scancode,
keysym_desc->flags, pressed);
return keysym_desc;
}
void guac_spice_keyboard_update_locks(guac_spice_keyboard* keyboard,
unsigned int set_modifiers, unsigned int clear_modifiers) {
guac_client* client = keyboard->client;
guac_spice_client* spice_client = (guac_spice_client*) client->data;
/* Calculate updated lock flags */
unsigned int modifiers = (keyboard->modifiers | set_modifiers) & ~clear_modifiers;
/* Synchronize remote side only if lock flags have changed */
if (modifiers != keyboard->modifiers) {
guac_spice_send_synchronize_event(spice_client, modifiers);
keyboard->modifiers = modifiers;
}
}
void guac_spice_keyboard_update_modifiers(guac_spice_keyboard* keyboard,
unsigned int set_flags, unsigned int clear_flags) {
unsigned int modifier_flags = guac_spice_keyboard_get_modifier_flags(keyboard);
/* Only clear modifiers that are set */
clear_flags &= modifier_flags;
/* Only set modifiers that are currently cleared */
set_flags &= ~modifier_flags;
/* Press/release Shift as needed */
if (set_flags & GUAC_SPICE_KEYMAP_MODIFIER_SHIFT) {
guac_spice_keyboard_update_keysym(keyboard, GUAC_SPICE_KEYSYM_LSHIFT, 1, GUAC_SPICE_KEY_SOURCE_SYNTHETIC);
}
else if (clear_flags & GUAC_SPICE_KEYMAP_MODIFIER_SHIFT) {
guac_spice_keyboard_update_keysym(keyboard, GUAC_SPICE_KEYSYM_LSHIFT, 0, GUAC_SPICE_KEY_SOURCE_SYNTHETIC);
guac_spice_keyboard_update_keysym(keyboard, GUAC_SPICE_KEYSYM_RSHIFT, 0, GUAC_SPICE_KEY_SOURCE_SYNTHETIC);
}
/* Press/release AltGr as needed */
if (set_flags & GUAC_SPICE_KEYMAP_MODIFIER_ALTGR) {
guac_spice_keyboard_update_keysym(keyboard, GUAC_SPICE_KEYSYM_ALTGR, 1, GUAC_SPICE_KEY_SOURCE_SYNTHETIC);
}
else if (clear_flags & GUAC_SPICE_KEYMAP_MODIFIER_ALTGR) {
guac_spice_keyboard_update_keysym(keyboard, GUAC_SPICE_KEYSYM_ALTGR, 0, GUAC_SPICE_KEY_SOURCE_SYNTHETIC);
guac_spice_keyboard_update_keysym(keyboard, GUAC_SPICE_KEYSYM_LALT, 0, GUAC_SPICE_KEY_SOURCE_SYNTHETIC);
guac_spice_keyboard_update_keysym(keyboard, GUAC_SPICE_KEYSYM_RALT, 0, GUAC_SPICE_KEY_SOURCE_SYNTHETIC);
guac_spice_keyboard_update_keysym(keyboard, GUAC_SPICE_KEYSYM_LCTRL, 0, GUAC_SPICE_KEY_SOURCE_SYNTHETIC);
guac_spice_keyboard_update_keysym(keyboard, GUAC_SPICE_KEYSYM_RCTRL, 0, GUAC_SPICE_KEY_SOURCE_SYNTHETIC);
}
}
int guac_spice_keyboard_update_keysym(guac_spice_keyboard* keyboard,
int keysym, int pressed, guac_spice_key_source source) {
/* Synchronize lock keys states, if this has not yet been done */
if (!keyboard->synchronized) {
guac_client* client = keyboard->client;
guac_spice_client* spice_client = (guac_spice_client*) client->data;
/* Synchronize remote lock key states with local state */
guac_spice_send_synchronize_event(spice_client, keyboard->modifiers);
keyboard->synchronized = 1;
}
guac_spice_key* key = guac_spice_keyboard_get_key(keyboard, keysym);
/* Update tracking of client-side keyboard state but only for keys which
* are tracked server-side, as well (to ensure that the key count remains
* correct, even if a user sends extra unbalanced or excessive press and
* release events) */
if (source == GUAC_SPICE_KEY_SOURCE_CLIENT && key != NULL) {
if (pressed && !key->user_pressed) {
keyboard->user_pressed_keys++;
key->user_pressed = 1;
}
else if (!pressed && key->user_pressed) {
keyboard->user_pressed_keys--;
key->user_pressed = 0;
}
}
/* Send events and update server-side lock state only if server-side key
* state is changing (or if server-side state of this key is untracked) */
if (key == NULL || (pressed && key->pressed == NULL) || (!pressed && key->pressed != NULL)) {
/* Toggle locks on keydown */
if (pressed)
keyboard->modifiers ^= guac_spice_keyboard_lock_flag(keysym);
/* If key is known, update state and attempt to send using normal SPICE key
* events */
const guac_spice_keysym_desc* definition = NULL;
if (key != NULL) {
definition = guac_spice_keyboard_send_defined_key(keyboard, key, pressed);
key->pressed = pressed ? definition : NULL;
}
/* Fall back to dead keys or Unicode events if otherwise undefined inside
* current keymap (note that we only handle "pressed" here, as neither
* Unicode events nor dead keys can have a pressed/released state) */
if (definition == NULL && pressed) {
guac_client_log(keyboard->client, GUAC_LOG_WARNING,
"Undefined key will not be sent: %d", keysym);
}
}
/* Reset SPICE server keyboard state (releasing any automatically
* pressed keys) once all keys have been released on the client
* side */
if (source == GUAC_SPICE_KEY_SOURCE_CLIENT && keyboard->user_pressed_keys == 0)
guac_spice_keyboard_reset(keyboard);
return 0;
}
void guac_spice_keyboard_reset(guac_spice_keyboard* keyboard) {
/* Release all pressed keys */
for (int i = 0; i < keyboard->num_keys; i++) {
guac_spice_key* key = &keyboard->keys[i];
if (key->pressed != NULL)
guac_spice_keyboard_update_keysym(keyboard, key->pressed->keysym, 0,
GUAC_SPICE_KEY_SOURCE_SYNTHETIC);
}
}
void guac_spice_keyboard_set_indicators(SpiceChannel* channel, guac_client* client) {
guac_spice_client* spice_client = (guac_spice_client*) client->data;
pthread_rwlock_rdlock(&(spice_client->lock));
/* Skip if keyboard not yet ready */
guac_spice_keyboard* keyboard = spice_client->keyboard;
if (keyboard == NULL)
goto complete;
unsigned int modifiers;
g_object_get(channel, SPICE_PROPERTY_KEY_MODIFIERS, &modifiers, NULL);
/* Update with received locks */
guac_client_log(client, GUAC_LOG_DEBUG, "Received updated keyboard lock flags from SPICE server: 0x%X", modifiers);
keyboard->modifiers = modifiers;
complete:
pthread_rwlock_unlock(&(spice_client->lock));
}

View File

@ -0,0 +1,323 @@
/*
* 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_SPICE_KEYBOARD_H
#define GUAC_SPICE_KEYBOARD_H
#include "keymap.h"
#include <guacamole/client.h>
#include <spice-client-glib-2.0/spice-client.h>
/**
* The maximum number of distinct keysyms that any particular keyboard may support.
*/
#define GUAC_SPICE_KEYBOARD_MAX_KEYSYMS 1024
/**
* The maximum number of unique modifier variations that any particular keysym
* may define. For example, on a US English keyboard, an uppercase "A" may be
* typed by pressing Shift+A with Caps Lock unset, or by pressing A with Caps
* Lock set (two variations).
*/
#define GUAC_SPICE_KEY_MAX_DEFINITIONS 4
/**
* All possible sources of SPICE key events tracked by guac_spice_keyboard.
*/
typedef enum guac_spice_key_source {
/**
* The key event was received directly from the Guacamole client via a
* "key" instruction.
*/
GUAC_SPICE_KEY_SOURCE_CLIENT = 0,
/**
* The key event is being synthesized internally within the SPICE support.
*/
GUAC_SPICE_KEY_SOURCE_SYNTHETIC = 1
} guac_spice_key_source;
/**
* A representation of a single key within the overall local keyboard,
* including the definition of that key within the SPICE server's keymap and
* whether the key is currently pressed locally.
*/
typedef struct guac_spice_key {
/**
* All definitions of this key within the SPICE server's keymap (keyboard
* layout). Each definition describes which scancode corresponds to this
* key from the perspective of the SPICE server, as well as which other
* scancodes must be pressed/released for this key to have the desired
* meaning.
*/
const guac_spice_keysym_desc* definitions[GUAC_SPICE_KEY_MAX_DEFINITIONS];
/**
* The number of definitions within the definitions array. If this key does
* not exist within the SPICE server's keymap, this will be 0.
*/
int num_definitions;
/**
* The definition of this key that is currently pressed. If this key is not
* currently pressed, this will be NULL.
*/
const guac_spice_keysym_desc* pressed;
/**
* Whether this key is currently pressed by the user, and is included among
* the total tracked by user_pressed_keys within guac_spice_keyboard.
*/
int user_pressed;
} guac_spice_key;
/**
* The current keyboard state of an SPICE session.
*/
typedef struct guac_spice_keyboard {
/**
* The guac_client associated with the SPICE session whose keyboard state is
* being managed by this guac_spice_keyboard.
*/
guac_client* client;
/**
* The local state of all known lock keys, as a bitwise OR of all SPICE lock
* key flags. Legal flags are KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK,
* KBD_SYNC_CAPS_LOCK, and KBD_SYNC_KANA_LOCK.
*/
int modifiers;
/**
* Whether the states of remote lock keys (Caps lock, Num lock, etc.) have
* been synchronized with local lock key states.
*/
int synchronized;
/**
* The number of keys stored within the keys array.
*/
unsigned int num_keys;
/**
* The local state of all keys, as well as the necessary information to
* translate received keysyms into scancodes or sequences of scancodes for
* SPICE. The state of each key is updated based on received Guacamole key
* events, while the information describing the behavior and scancode
* mapping of each key is populated based on an associated keymap.
*
* Keys within this array are in arbitrary order.
*/
guac_spice_key keys[GUAC_SPICE_KEYBOARD_MAX_KEYSYMS];
/**
* Lookup table into the overall keys array, locating the guac_spice_key
* associated with any particular keysym. If a keysym has no corresponding
* guac_spice_key within the keys array, its entry within this lookuptable
* will be NULL.
*
* The index of the key for a given keysym is determined based on a
* simple transformation of the keysym itself. Keysyms between 0x0000 and
* 0xFFFF inclusive are mapped to 0x00000 through 0x0FFFF, while keysyms
* between 0x1000000 and 0x100FFFF inclusive (keysyms which are derived
* from Unicode) are mapped to 0x10000 through 0x1FFFF.
*/
guac_spice_key* keys_by_keysym[0x20000];
/**
* The total number of keys that the user of the connection is currently
* holding down. This value indicates only the client-side keyboard state.
* It DOES NOT indicate the number of keys currently pressed within the SPICE
* server.
*/
int user_pressed_keys;
} guac_spice_keyboard;
/**
* Allocates a new guac_spice_keyboard which manages the keyboard state of the
* SPICE session associated with the given guac_client. Keyboard events will be
* dynamically translated from keysym to SPICE scancode according to the
* provided keymap. The returned guac_spice_keyboard must eventually be freed
* with guac_spice_keyboard_free().
*
* @param client
* The guac_client associated with the SPICE session whose keyboard state is
* to be managed by the newly-allocated guac_spice_keyboard.
*
* @param keymap
* The keymap which should be used to translate keyboard events.
*
* @return
* A newly-allocated guac_spice_keyboard which manages the keyboard state
* for the SPICE session associated given guac_client.
*/
guac_spice_keyboard* guac_spice_keyboard_alloc(guac_client* client,
const guac_spice_keymap* keymap);
/**
* Frees all memory allocated for the given guac_spice_keyboard. The
* guac_spice_keyboard must have been previously allocated via
* guac_spice_keyboard_alloc().
*
* @param keyboard
* The guac_spice_keyboard instance which should be freed.
*/
void guac_spice_keyboard_free(guac_spice_keyboard* keyboard);
/**
* Returns whether the given keysym is defined for the keyboard layout
* associated with the given keyboard.
*
* @param keyboard
* The guac_spice_keyboard instance to check.
*
* @param keysym
* The keysym of the key being checked against the keyboard layout of the
* given keyboard.
*
* @return
* Non-zero if the key is explicitly defined within the keyboard layout of
* the given keyboard, zero otherwise.
*/
int guac_spice_keyboard_is_defined(guac_spice_keyboard* keyboard, int keysym);
/**
* Returns whether the key having the given keysym is currently pressed.
*
* @param keyboard
* The guac_spice_keyboard instance to check.
*
* @param keysym
* The keysym of the key being checked.
*
* @return
* Non-zero if the key is currently pressed, zero otherwise.
*/
int guac_spice_keyboard_is_pressed(guac_spice_keyboard* keyboard, int keysym);
/**
* Returns the local state of all known modifier keys, as a bitwise OR of the
* modifier flags used by the keymaps. Alternative methods of producing the
* effect of certain modifiers, such as holding Ctrl+Alt for AltGr when a
* dedicated AltGr key is unavailable, are taken into account.
*
* @see GUAC_SPICE_KEYMAP_MODIFIER_SHIFT
* @see GUAC_SPICE_KEYMAP_MODIFIER_ALTGR
*
* @param keyboard
* The guac_spice_keyboard associated with the current SPICE session.
*
* @return
* The local state of all known modifier keys.
*/
unsigned int guac_spice_keyboard_get_modifier_flags(guac_spice_keyboard* keyboard);
/**
* Updates the local state of the lock keys (such as Caps lock or Num lock),
* synchronizing the remote state of those keys if it is expected to differ.
*
* @param keyboard
* The guac_spice_keyboard associated with the current SPICE session.
*
* @param set_modifiers
* The lock key flags which should be set. Legal flags are
* KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK, KBD_SYNC_CAPS_LOCK, and
* KBD_SYNC_KANA_LOCK.
*
* @param clear_modifiers
* The lock key flags which should be cleared. Legal flags are
* KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK, KBD_SYNC_CAPS_LOCK, and
* KBD_SYNC_KANA_LOCK.
*/
void guac_spice_keyboard_update_locks(guac_spice_keyboard* keyboard,
unsigned int set_modifiers, unsigned int clear_modifiers);
/**
* Updates the local state of the modifier keys (such as Shift or AltGr),
* synchronizing the remote state of those keys if it is expected to differ.
* Valid modifier flags are defined by keymap.h.
*
* @see GUAC_SPICE_KEYMAP_MODIFIER_SHIFT
* @see GUAC_SPICE_KEYMAP_MODIFIER_ALTGR
*
* @param keyboard
* The guac_spice_keyboard associated with the current SPICE session.
*
* @param set_modifiers
* The modifier key flags which should be set.
*
* @param clear_modifiers
* The modifier key flags which should be cleared.
*/
void guac_spice_keyboard_update_modifiers(guac_spice_keyboard* keyboard,
unsigned int set_modifiers, unsigned int clear_modifiers);
/**
* Updates the local state of the given keysym, sending the key events required
* to replicate that state remotely (on the SPICE server). The key events sent
* will depend on the current keymap.
*
* @param keyboard
* The guac_spice_keyboard associated with the current SPICE session.
*
* @param keysym
* The keysym being pressed or released.
*
* @param pressed
* Zero if the keysym is being released, non-zero otherwise.
*
* @param source
* The source of the key event represented by this call to
* guac_spice_keyboard_update_keysym().
*
* @return
* Zero if the keys were successfully sent, non-zero otherwise.
*/
int guac_spice_keyboard_update_keysym(guac_spice_keyboard* keyboard,
int keysym, int pressed, guac_spice_key_source source);
/**
* Releases all currently pressed keys, sending key release events to the SPICE
* server as necessary. Lock states (Caps Lock, etc.) are not affected.
*
* @param keyboard
* The guac_spice_keyboard associated with the current SPICE session.
*/
void guac_spice_keyboard_reset(guac_spice_keyboard* keyboard);
/**
* Callback which is invoked when the SPICE server reports changes to keyboard
* lock status using a Server Set Keyboard Indicators PDU.
*
* @param channel
* The spiceContext associated with the current SPICE session.
*
* @param client
* The guac_client object associated with the callback.
*/
void guac_spice_keyboard_set_indicators(SpiceChannel* channel, guac_client* client);
#endif

View File

@ -0,0 +1,41 @@
/*
* 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 "keymap.h"
#include <string.h>
const guac_spice_keymap* guac_spice_keymap_find(const char* name) {
/* For each keymap */
const guac_spice_keymap** current = GUAC_SPICE_KEYMAPS;
while (*current != NULL) {
/* If name matches, done */
if (strcmp((*current)->name, name) == 0)
return *current;
current++;
}
/* Failure */
return NULL;
}

View File

@ -0,0 +1,202 @@
/*
* 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_SPICE_KEYMAP_H
#define GUAC_SPICE_KEYMAP_H
/**
* The X11 keysym for Num Lock.
*/
#define GUAC_SPICE_KEYSYM_NUM_LOCK 0xFF7F
/**
* The X11 keysym for Scroll Lock.
*/
#define GUAC_SPICE_KEYSYM_SCROLL_LOCK 0xFF14
/**
* The X11 keysym for Caps Lock.
*/
#define GUAC_SPICE_KEYSYM_CAPS_LOCK 0xFFE5
/**
* The X11 keysym for Kana Lock.
*/
#define GUAC_SPICE_KEYSYM_KANA_LOCK 0xFF2D
/**
* The X11 keysym for Left Shift.
*/
#define GUAC_SPICE_KEYSYM_LSHIFT 0xFFE1
/**
* The X11 keysym for Right Shift.
*/
#define GUAC_SPICE_KEYSYM_RSHIFT 0xFFE2
/**
* The X11 keysym for Left Ctrl.
*/
#define GUAC_SPICE_KEYSYM_LCTRL 0xFFE3
/**
* The X11 keysym for Right Ctrl.
*/
#define GUAC_SPICE_KEYSYM_RCTRL 0xFFE4
/**
* The X11 keysym for Left Alt.
*/
#define GUAC_SPICE_KEYSYM_LALT 0xFFE9
/**
* The X11 keysym for Right Alt.
*/
#define GUAC_SPICE_KEYSYM_RALT 0xFFEA
/**
* The X11 keysym for AltGr.
*/
#define GUAC_SPICE_KEYSYM_ALTGR 0xFE03
/**
* Bitwise flag value representing the Shift modifier.
*/
#define GUAC_SPICE_KEYMAP_MODIFIER_SHIFT 1
/**
* Bitwise flag value representing the AltGr modifier.
*/
#define GUAC_SPICE_KEYMAP_MODIFIER_ALTGR 2
/**
* Represents a keysym-to-scancode mapping for SPICE, with extra information
* about the state of prerequisite keysyms.
*/
typedef struct guac_spice_keysym_desc {
/**
* The keysym being mapped.
*/
int keysym;
/**
* The scancode this keysym maps to.
*/
int scancode;
/**
* Required SPICE-specific flags that must be sent along with the scancode.
*/
int flags;
/**
* Bitwise-OR of the flags of any modifiers that must be active for the
* associated scancode to be interpreted as this keysym.
*
* If the associated keysym is pressed, and any of these modifiers are not
* currently active, Guacamole's SPICE support must send additional events
* to activate these modifiers prior to sending the scancode for this
* keysym.
*
* @see GUAC_SPICE_KEYMAP_MODIFIER_SHIFT
* @see GUAC_SPICE_KEYMAP_MODIFIER_ALTGR
*/
const unsigned int set_modifiers;
/**
* Bitwise-OR of the flags of any modifiers that must NOT be active for the
* associated scancode to be interpreted as this keysym.
*
* If the associated keysym is pressed, and any of these modifiers are
* currently active, Guacamole's SPICE support must send additional events
* to deactivate these modifiers prior to sending the scancode for this
* keysym.
*
* @see GUAC_SPICE_KEYMAP_MODIFIER_SHIFT
* @see GUAC_SPICE_KEYMAP_MODIFIER_ALTGR
*/
const unsigned int clear_modifiers;
/**
* Bitwise OR of the flags of all lock keys (ie: Caps lock, Num lock, etc.)
* which must be active for this keysym to be properly typed. Legal flags
* are KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK, KBD_SYNC_CAPS_LOCK, and
* KBD_SYNC_KANA_LOCK.
*/
const unsigned int set_locks;
/**
* Bitwise OR of the flags of all lock keys (ie: Caps lock, Num lock, etc.)
* which must be inactive for this keysym to be properly typed. Legal flags
* are KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK, KBD_SYNC_CAPS_LOCK, and
* KBD_SYNC_KANA_LOCK.
*/
const unsigned int clear_locks;
} guac_spice_keysym_desc;
/**
* Hierarchical keysym mapping
*/
typedef struct guac_spice_keymap guac_spice_keymap;
struct guac_spice_keymap {
/**
* The parent mapping this map will inherit its initial mapping from.
* Any other mapping information will add to or override the mapping
* inherited from the parent.
*/
const guac_spice_keymap* parent;
/**
* Descriptive name of this keymap
*/
const char* name;
/**
* Null-terminated array of scancode mappings.
*/
const guac_spice_keysym_desc* mapping;
};
/**
* The name of the default keymap, which MUST exist.
*/
#define GUAC_SPICE_DEFAULT_KEYMAP "en-us-qwerty"
/**
* NULL-terminated array of all keymaps.
*/
extern const guac_spice_keymap* GUAC_SPICE_KEYMAPS[];
/**
* Return the keymap having the given name, if any, or NULL otherwise.
*
* @param name
* The name of the keymap to find.
*
* @return
* The keymap having the given name, or NULL if no such keymap exists.
*/
const guac_spice_keymap* guac_spice_keymap_find(const char* name);
#endif

View File

@ -0,0 +1,93 @@
#
# 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.
#
name "base"
# Typeable characters
map 0x39 ~ " " # Space
map 0x0F ~ 0xff09 # Tab
# Control characters
map 0x0E ~ 0xff08 # BackSpace
map 0x1C ~ 0xff0d # Return
map 0x01 ~ 0xff1b # Escape
map +ext 0x52 ~ 0xff63 # Insert
map +ext 0x53 ~ 0xffff # Delete
map +ext 0x47 ~ 0xff50 # Home
map +ext 0x4F ~ 0xff57 # End
map +ext 0x4B ~ 0xff51 # Left
map +ext 0x48 ~ 0xff52 # Up
map +ext 0x4D ~ 0xff53 # Right
map +ext 0x50 ~ 0xff54 # Down
map +ext 0x49 ~ 0xff55 # Page_Up
map +ext 0x51 ~ 0xff56 # Page_Down
map +ext 0x37 ~ 0xff61 # Print Screen
# Locks
map 0x45 ~ 0xff7f # Num_Lock
map 0x46 ~ 0xff14 # Scroll_Lock
map 0x3A ~ 0xffe5 # Caps_Lock
# Keypad numerals
map -shift +num 0x52 ~ 0xffb0 # KP_0
map -shift +num 0x4F ~ 0xffb1 # KP_1
map -shift +num 0x50 ~ 0xffb2 # KP_2
map -shift +num 0x51 ~ 0xffb3 # KP_3
map -shift +num 0x4B ~ 0xffb4 # KP_4
map -shift +num 0x4C ~ 0xffb5 # KP_5
map -shift +num 0x4D ~ 0xffb6 # KP_6
map -shift +num 0x47 ~ 0xffb7 # KP_7
map -shift +num 0x48 ~ 0xffb8 # KP_8
map -shift +num 0x49 ~ 0xffb9 # KP_9
# Keypad operators
map 0x37 ~ 0xffaa # KP_multiply
map 0x4e ~ 0xffab # KP_add
map 0x4a ~ 0xffad # KP_subtract
map 0x53 ~ 0xffae # KP_decimal
map +ext 0x35 ~ 0xffaf # KP_divide
# F keys
map 0x3B ~ 0xffbe # F1
map 0x3C ~ 0xffbf # F2
map 0x3D ~ 0xffc0 # F3
map 0x3E ~ 0xffc1 # F4
map 0x3F ~ 0xffc2 # F5
map 0x40 ~ 0xffc3 # F6
map 0x41 ~ 0xffc4 # F7
map 0x42 ~ 0xffc5 # F8
map 0x43 ~ 0xffc6 # F9
map 0x44 ~ 0xffc7 # F10
map 0x57 ~ 0xffc8 # F11
map 0x58 ~ 0xffc9 # F12
# Modifiers
map 0x2A ~ 0xffe1 # Shift_L
map 0x36 ~ 0xffe2 # Shift_R
map 0x1D ~ 0xffe3 # Control_L
map +ext 0x1D ~ 0xffe4 # Control_R
map 0x38 ~ 0xffe9 # Alt_L
map +ext 0x38 ~ 0xffea # Alt_R
map +ext 0x38 ~ 0xfe03 # AltGr
map +ext 0x5B ~ 0xffe7 # Meta_L
map +ext 0x5C ~ 0xffe8 # Meta_R
map +ext 0x5B ~ 0xffeb # Super_L
map +ext 0x5C ~ 0xffec # Super_R
map +ext 0x5D ~ 0xff67 # Menu

View File

@ -0,0 +1,73 @@
#
# 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 "da-dk-qwerty"
#
# Basic keys
#
map -caps -altgr -shift 0x29 0x02..0x0C ~ "§1234567890+"
map -caps -altgr -shift 0x10..0x1A ~ "qwertyuiopå"
map -caps -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjklæø'"
map -caps -altgr -shift 0x56 0x2C..0x35 ~ "<zxcvbnm,.-"
map -caps -altgr +shift 0x29 0x02..0x0C ~ "½!"#¤%&/()=?"
map -caps -altgr +shift 0x10..0x1A ~ "QWERTYUIOPÅ"
map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKLÆØ*"
map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">ZXCVBNM;:_"
map +caps -altgr -shift 0x29 0x02..0x0C ~ "§1234567890+"
map +caps -altgr -shift 0x10..0x1A ~ "QWERTYUIOPÅ"
map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKLÆØ'"
map +caps -altgr -shift 0x56 0x2C..0x35 ~ "<ZXCVBNM,.-"
map +caps -altgr +shift 0x29 0x02..0x0C ~ "½!"#¤%&/()=?"
map +caps -altgr +shift 0x10..0x1A ~ "qwertyuiopå"
map +caps -altgr +shift 0x1E..0x28 0x2B ~ "asdfghjklæø*"
map +caps -altgr +shift 0x56 0x2C..0x35 ~ ">zxcvbnm;:_"
#
# Keys requiring AltGr
#
map +altgr -shift 0x03 ~ "@"
map +altgr -shift 0x04 ~ "£"
map +altgr -shift 0x05 ~ "$"
map +altgr -shift 0x08 ~ "{"
map +altgr -shift 0x09 ~ "["
map +altgr -shift 0x0A ~ "]"
map +altgr -shift 0x0B ~ "}"
map +altgr -shift 0x56 ~ "\"
map +altgr -shift 0x12 ~ "€"
map +altgr -shift 0x0D ~ "|"
map +altgr -shift 0x32 ~ "µ"
#
# Dead keys
#
map -altgr -shift 0x0D ~ 0xFE51 # Dead acute
map -altgr +shift 0x0D ~ 0xFE50 # Dead grave
map -altgr -shift 0x1B ~ 0xFE57 # Dead umlaut
map -altgr +shift 0x1B ~ 0xFE52 # Dead circumflex
map +altgr -shift 0x1B ~ 0xFE53 # Dead tilde

View File

@ -0,0 +1,67 @@
#
# 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 "de-ch-qwertz"
#
# Basic keys
#
map -caps -altgr -shift 0x29 0x02..0x0C ~ "§1234567890'"
map -caps -altgr -shift 0x10..0x1A ~ "qwertzuiopü"
map -caps -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjklöä$"
map -caps -altgr -shift 0x56 0x2C..0x35 ~ "<yxcvbnm,.-"
map -caps -altgr +shift 0x29 0x02..0x0C ~ "°+"*ç%&/()=?"
map -caps -altgr +shift 0x10..0x1B ~ "QWERTZUIOPè!"
map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKLéà£"
map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">YXCVBNM;:_"
map +caps -altgr -shift 0x29 0x02..0x0C ~ "§1234567890'"
map +caps -altgr -shift 0x10..0x1A ~ "QWERTZUIOPÜ"
map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKLÖÄ$"
map +caps -altgr -shift 0x56 0x2C..0x35 ~ "<YXCVBNM,.-"
map +caps -altgr +shift 0x29 0x02..0x0C ~ "°+"*ç%&/()=?"
map +caps -altgr +shift 0x10..0x1B ~ "qwertzuiopÈ!"
map +caps -altgr +shift 0x1E..0x28 0x2B ~ "asdfghjklÉÀ£"
map +caps -altgr +shift 0x56 0x2C..0x35 ~ ">yxcvbnm;:_"
#
# Keys requiring AltGr
#
map +altgr -shift 0x02..0x04 ~ "¦@#"
map +altgr -shift 0x07..0x09 ~ "¬|¢"
map +altgr -shift 0x1A..0x1B ~ "[]"
map +altgr -shift 0x28 ~ "{"
map +altgr -shift 0x2B ~ "}"
map +altgr -shift 0x56 ~ "\"
map +altgr -shift 0x12 ~ "€"
#
# Dead keys
#
map +altgr -shift 0x0C ~ 0xFE51 # Dead acute
map -altgr -shift 0x0D ~ 0xFE52 # Dead circumflex
map -altgr +shift 0x0D ~ 0xFE50 # Dead grave
map +altgr -shift 0x0D ~ 0xFE53 # Dead tilde
map -altgr -shift 0x1B ~ 0xFE57 # Dead umlaut

View File

@ -0,0 +1,74 @@
#
# 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 "de-de-qwertz"
#
# Basic keys
#
map -caps -altgr -shift 0x02..0x0C ~ "1234567890ß"
map -caps -altgr -shift 0x10..0x1B ~ "qwertzuiopü+"
map -caps -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjklöä#"
map -caps -altgr -shift 0x56 0x2C..0x35 ~ "<yxcvbnm,.-"
map -caps -altgr +shift 0x29 0x02..0x0C ~ "°!"§$%&/()=?"
map -caps -altgr +shift 0x10..0x1B ~ "QWERTZUIOPÜ*"
map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKLÖÄ'"
map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">YXCVBNM;:_"
map +caps -altgr -shift 0x02..0x0C ~ "!"§$%&/()=?"
map +caps -altgr -shift 0x10..0x1B ~ "QWERTZUIOPÜ*"
map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKLÖÄ'"
map +caps -altgr -shift 0x56 0x2C..0x35 ~ "<YXCVBNM;:-"
map +caps -altgr +shift 0x29 0x02..0x0C ~ "°1234567890ß"
map +caps -altgr +shift 0x10..0x1B ~ "qwertzuiopü+"
map +caps -altgr +shift 0x1E..0x28 0x2B ~ "asdfghjklöä#"
map +caps -altgr +shift 0x56 0x2C..0x35 ~ ">yxcvbnm,._"
#
# Keys requiring AltGr
#
map +altgr -shift 0x03 ~ "²"
map +altgr -shift 0x04 ~ "³"
map +altgr -shift 0x08 ~ "{"
map +altgr -shift 0x09 ~ "["
map +altgr -shift 0x0A ~ "]"
map +altgr -shift 0x0B ~ "}"
map +altgr -shift 0x0C ~ "\"
map +altgr -shift 0x10 ~ "@"
map +altgr -shift 0x12 ~ "€"
map +altgr -shift 0x1B ~ "~"
map +altgr -shift 0x56 ~ "|"
map +altgr -shift 0x32 ~ "µ"
#
# Dead keys
#
map -altgr +shift 0x0D ~ 0xFE50 # Dead grave
map -altgr -shift 0x0D ~ 0xFE51 # Dead acute
map -altgr -shift 0x29 ~ 0xFE52 # Dead circumflex
map +altgr -shift 0x0C ~ 0xFE53 # Dead tilde

View File

@ -0,0 +1,79 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
parent "base"
name "en-gb-qwerty"
#
# Basic keys
#
map -caps -altgr -shift 0x29 0x02..0x0D ~ "`1234567890-="
map -caps -altgr -shift 0x10..0x1B ~ "qwertyuiop[]"
map -caps -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjkl;'#"
map -caps -altgr -shift 0x56 0x2C..0x35 ~ "\zxcvbnm,./"
map -caps -altgr +shift 0x29 0x02..0x0D ~ "¬!"£$%^&*()_+"
map -caps -altgr +shift 0x10..0x1B ~ "QWERTYUIOP{}"
map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKL:@~"
map -caps -altgr +shift 0x56 0x2C..0x35 ~ "|ZXCVBNM<>?"
map +caps -altgr -shift 0x29 0x02..0x0D ~ "`1234567890-="
map +caps -altgr -shift 0x10..0x1B ~ "QWERTYUIOP[]"
map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKL;'#"
map +caps -altgr -shift 0x56 0x2C..0x35 ~ "\ZXCVBNM,./"
map +caps -altgr +shift 0x29 0x02..0x0D ~ "¬!"£$%^&*()_+"
map +caps -altgr +shift 0x10..0x1B ~ "qwertyuiop{}"
map +caps -altgr +shift 0x1E..0x28 0x2B ~ "asdfghjkl:@~"
map +caps -altgr +shift 0x56 0x2C..0x35 ~ "|zxcvbnm<>?"
#
# Keys requiring AltGr (some of which are affected by Caps Lock)
#
map +altgr -shift 0x29 ~ "¦"
map +altgr -shift 0x05 ~ "€"
map -caps +altgr -shift 0x12 ~ "é"
map -caps +altgr +shift 0x12 ~ "É"
map -caps +altgr -shift 0x16 ~ "ú"
map -caps +altgr +shift 0x16 ~ "Ú"
map -caps +altgr -shift 0x17 ~ "í"
map -caps +altgr +shift 0x17 ~ "Í"
map -caps +altgr -shift 0x18 ~ "ó"
map -caps +altgr +shift 0x18 ~ "Ó"
map -caps +altgr -shift 0x1E ~ "á"
map -caps +altgr +shift 0x1E ~ "Á"
map -caps +altgr -shift 0x2E ~ "ç"
map -caps +altgr +shift 0x2E ~ "Ç"
map +caps +altgr +shift 0x12 ~ "é"
map +caps +altgr -shift 0x12 ~ "É"
map +caps +altgr +shift 0x16 ~ "ú"
map +caps +altgr -shift 0x16 ~ "Ú"
map +caps +altgr +shift 0x17 ~ "í"
map +caps +altgr -shift 0x17 ~ "Í"
map +caps +altgr +shift 0x18 ~ "ó"
map +caps +altgr -shift 0x18 ~ "Ó"
map +caps +altgr +shift 0x1E ~ "á"
map +caps +altgr -shift 0x1E ~ "Á"
map +caps +altgr +shift 0x2E ~ "ç"
map +caps +altgr -shift 0x2E ~ "Ç"

View File

@ -0,0 +1,42 @@
#
# 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 "en-us-qwerty"
map -caps -shift 0x29 0x02..0x0D ~ "`1234567890-="
map -caps -shift 0x10..0x1B 0x2B ~ "qwertyuiop[]\"
map -caps -shift 0x1E..0x28 ~ "asdfghjkl;'"
map -caps -shift 0x2C..0x35 ~ "zxcvbnm,./"
map -caps +shift 0x29 0x02..0x0D ~ "~!@#$%^&*()_+"
map -caps +shift 0x10..0x1B 0x2B ~ "QWERTYUIOP{}|"
map -caps +shift 0x1E..0x28 ~ "ASDFGHJKL:""
map -caps +shift 0x2C..0x35 ~ "ZXCVBNM<>?"
map +caps -shift 0x29 0x02..0x0D ~ "`1234567890-="
map +caps -shift 0x10..0x1B 0x2B ~ "QWERTYUIOP[]\"
map +caps -shift 0x1E..0x28 ~ "ASDFGHJKL;'"
map +caps -shift 0x2C..0x35 ~ "ZXCVBNM,./"
map +caps +shift 0x29 0x02..0x0D ~ "~!@#$%^&*()_+"
map +caps +shift 0x10..0x1B 0x2B ~ "qwertyuiop{}|"
map +caps +shift 0x1E..0x28 ~ "asdfghjkl:""
map +caps +shift 0x2C..0x35 ~ "zxcvbnm<>?"

View File

@ -0,0 +1,63 @@
#
# 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 "es-es-qwerty"
#
# Basic keys
#
map -caps -altgr -shift 0x29 0x02..0x0D ~ "º1234567890'¡"
map -caps -altgr -shift 0x10..0x19 0x1B ~ "qwertyuiop+"
map -caps -altgr -shift 0x1E..0x27 0x2B ~ "asdfghjklñç"
map -caps -altgr -shift 0x56 0x2C..0x35 ~ "<zxcvbnm,.-"
map -caps -altgr +shift 0x29 0x02..0x0D ~ "ª!"·$%&/()=?¿"
map -caps -altgr +shift 0x10..0x19 0x1B ~ "QWERTYUIOP*"
map -caps -altgr +shift 0x1E..0x27 0x2B ~ "ASDFGHJKLÑÇ"
map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">ZXCVBNM;:_"
map +caps -altgr -shift 0x29 0x02..0x0D ~ "º1234567890'¡"
map +caps -altgr -shift 0x10..0x19 0x1B ~ "QWERTYUIOP+"
map +caps -altgr -shift 0x1E..0x27 0x2B ~ "ASDFGHJKLÑÇ"
map +caps -altgr -shift 0x56 0x2C..0x35 ~ "<ZXCVBNM,.-"
map +caps -altgr +shift 0x29 0x02..0x0D ~ "ª!"·$%&/()=?¿"
map +caps -altgr +shift 0x10..0x19 0x1B ~ "qwertyuiop*"
map +caps -altgr +shift 0x1E..0x27 0x2B ~ "asdfghjklñç"
map +caps -altgr +shift 0x56 0x2C..0x35 ~ ">zxcvbnm;:_"
#
# Keys requiring AltGr
#
map +altgr -shift 0x29 0x02..0x04 0x07 ~ "\|@#¬"
map +altgr -shift 0x12 0x1A 0x1B ~ "€[]"
map +altgr -shift 0x28 0x2B ~ "{}"
#
# Dead keys
#
map -altgr -shift 0x1A ~ 0xFE50 # Dead grave
map -altgr -shift 0x28 ~ 0xFE51 # Dead acute
map -altgr +shift 0x1A ~ 0xFE52 # Dead circumflex
map +altgr -shift 0x05 ~ 0xFE53 # Dead tilde
map -altgr +shift 0x28 ~ 0xFE57 # Dead diaeresis (umlaut)

View File

@ -0,0 +1,63 @@
#
# 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 "es-latam-qwerty"
#
# Basic keys
#
map -caps -altgr -shift 0x29 0x02..0x0D ~ "|1234567890'¿"
map -caps -altgr -shift 0x10..0x19 0x1B ~ "qwertyuiop+"
map -caps -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjklñ{}"
map -caps -altgr -shift 0x56 0x2C..0x35 ~ "<zxcvbnm,.-"
map -caps -altgr +shift 0x29 0x02..0x0D ~ "º!"#$%&/()=?¡"
map -caps -altgr +shift 0x10..0x19 0x1B ~ "QWERTYUIOP*"
map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKLÑ[]"
map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">ZXCVBNM;:_"
map +caps -altgr -shift 0x29 0x02..0x0D ~ "|1234567890'¿"
map +caps -altgr -shift 0x10..0x19 0x1B ~ "QWERTYUIOP+"
map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKLÑ{}"
map +caps -altgr -shift 0x56 0x2C..0x35 ~ "<ZXCVBNM,.-"
map +caps -altgr +shift 0x29 0x02..0x0D ~ "º!"#$%&/()=?¡"
map +caps -altgr +shift 0x10..0x19 0x1B ~ "qwertyuiop*"
map +caps -altgr +shift 0x1E..0x28 0x2B ~ "asdfghjklñ[]"
map +caps -altgr +shift 0x56 0x2C..0x35 ~ ">zxcvbnm;:_"
#
# Keys requiring AltGr
#
map +altgr -shift 0x29 0x0C ~ "¬\"
map +altgr -shift 0x10 ~ "€"
map +altgr -shift 0x28 ~ "^"
#
# Dead keys
#
map +altgr -shift 0x2B ~ 0xFE50 # Dead grave
map -altgr -shift 0x1A ~ 0xFE51 # Dead acute
map -altgr +shift 0x1A ~ 0xFE57 # Dead diaeresis (umlaut)
map +altgr -shift 0x1B ~ 0xFE53 # Dead tilde

View File

@ -0,0 +1,22 @@
#
# 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 "failsafe"

View File

@ -0,0 +1,76 @@
#
# 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-be-azerty"
#
# Basic keys
#
map -caps -altgr -shift 0x29 0x02..0x0D ~ "²&é"'(§è!çà)-"
map -caps -altgr -shift 0x10..0x19 0x1B ~ "azertyuiop$"
map -caps -altgr -shift 0x1E..0x28 0x2B ~ "qsdfghjklmùµ"
map -caps -altgr -shift 0x56 0x2C..0x35 ~ "<wxcvbn,;:="
map -caps -altgr +shift 0x29 0x02..0x0D ~ "³1234567890°_"
map -caps -altgr +shift 0x10..0x19 0x1B ~ "AZERTYUIOP£"
map -caps -altgr +shift 0x1E..0x28 0x2B ~ "QSDFGHJKLM%£"
map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">WXCVBN?./+"
map +caps -altgr -shift 0x29 0x02..0x0D ~ "²1234567890°_"
map +caps -altgr -shift 0x10..0x19 0x1B ~ "AZERTYUIOP£"
map +caps -altgr -shift 0x1E..0x28 0x2B ~ "QSDFGHJKLM%£"
map +caps -altgr -shift 0x56 0x2C..0x35 ~ "<WXCVBN?./+"
map +caps -altgr +shift 0x29 0x02..0x0D ~ "³&é"'(§è!çà)-"
map +caps -altgr +shift 0x10..0x19 0x1B ~ "azertyuiop$"
map +caps -altgr +shift 0x1E..0x28 0x2B ~ "qsdfghjklmùµ"
map +caps -altgr +shift 0x56 0x2C..0x35 ~ ">wxcvbn,;:="
#
# Keys requiring AltGr (unaffected by Caps Lock, but Shift must not be pressed)
#
map +altgr -shift 0x02..0x04 ~ "|@#"
map +altgr -shift 0x0A..0x0B ~ "{}"
map +altgr -shift 0x1A..0x1B ~ "[]"
map +altgr -shift 0x12 ~ "€"
map +altgr -shift 0x56 ~ "\"
map +altgr -shift 0x07 ~ "^"
#
# Dead keys requiring AltGr (unaffected by Caps Lock or Shift)
#
map +altgr 0x35 ~ 0xFE53 # Dead tilde
map +altgr 0x28 ~ 0xFE51 # Dead acute
map +altgr 0x2B ~ 0xFE50 # Dead grave
#
# Dead keys (affected by Caps Lock and Shift)
#
map -caps -altgr -shift 0x1A ~ 0xFE52 # Dead circumflex
map -caps -altgr +shift 0x1A ~ 0xFE57 # Dead umlaut
map +caps -altgr -shift 0x1A ~ 0xFE57 # Dead umlaut
map +caps -altgr +shift 0x1A ~ 0xFE52 # Dead circumflex

View File

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

View File

@ -0,0 +1,68 @@
#
# 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-ch-qwertz"
#
# Basic keys
#
map -caps -altgr -shift 0x29 0x02..0x0C ~ "§1234567890'"
map -caps -altgr -shift 0x10..0x1A ~ "qwertzuiopè"
map -caps -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjkléà$"
map -caps -altgr -shift 0x56 0x2C..0x35 ~ "<yxcvbnm,.-"
map -caps -altgr +shift 0x29 0x02..0x0C ~ "°+"*ç%&/()=?"
map -caps -altgr +shift 0x10..0x1B ~ "QWERTZUIOPü!"
map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKLöä£"
map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">YXCVBNM;:_"
map +caps -altgr -shift 0x29 0x02..0x0C ~ "§1234567890'"
map +caps -altgr -shift 0x10..0x1A ~ "QWERTZUIOPè"
map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKLéà$"
map +caps -altgr -shift 0x56 0x2C..0x35 ~ "<YXCVBNM,.-"
map +caps -altgr +shift 0x29 0x02..0x0C ~ "°+"*ç%&/()=?"
map +caps -altgr +shift 0x10..0x1B ~ "qwertzuiopü!"
map +caps -altgr +shift 0x1E..0x28 0x2B ~ "asdfghjklöä£"
map +caps -altgr +shift 0x56 0x2C..0x35 ~ ">yxcvbnm;:_"
#
# Keys requiring AltGr
#
map +altgr -shift 0x02..0x04 ~ "¦@#"
map +altgr -shift 0x07..0x09 ~ "¬|¢"
map +altgr -shift 0x1A 0x1B ~ "[]"
map +altgr -shift 0x28 0x2B ~ "{}"
map +altgr -shift 0x56 ~ "\"
map +altgr -shift 0x12 ~ "€"
#
# Dead keys
#
map -altgr -shift 0x1B ~ 0xFE57 # Dead umlaut
map +altgr -shift 0x0C ~ 0xFE51 # Dead acute
map -altgr -shift 0x0D ~ 0xFE52 # Dead circumflex
map -altgr +shift 0x0D ~ 0xFE50 # Dead grave
map +altgr -shift 0x0D ~ 0xFE53 # Dead tilde

View File

@ -0,0 +1,65 @@
#
# 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-fr-azerty"
#
# Basic keys
#
map -caps -altgr -shift 0x29 0x02..0x0D ~ "²&é"'(-è_çà)="
map -caps -altgr -shift 0x10..0x19 0x1B ~ "azertyuiop$"
map -caps -altgr -shift 0x1E..0x28 0x2B ~ "qsdfghjklmù*"
map -caps -altgr -shift 0x56 0x2C..0x35 ~ "<wxcvbn,;:!"
map -caps -altgr +shift 0x02..0x0D ~ "1234567890°+"
map -caps -altgr +shift 0x10..0x19 0x1B ~ "AZERTYUIOP£"
map -caps -altgr +shift 0x1E..0x28 0x2B ~ "QSDFGHJKLM%µ"
map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">WXCVBN?./§"
map +caps -altgr -shift 0x29 0x02..0x0D ~ "²1234567890°+"
map +caps -altgr -shift 0x10..0x19 0x1B ~ "AZERTYUIOP£"
map +caps -altgr -shift 0x1E..0x28 0x2B ~ "QSDFGHJKLM%µ"
map +caps -altgr -shift 0x56 0x2C..0x35 ~ "<WXCVBN?./§"
map +caps -altgr +shift 0x02..0x0D ~ "&é"'(-è_çà)="
map +caps -altgr +shift 0x10..0x19 0x1B ~ "azertyuiop$"
map +caps -altgr +shift 0x1E..0x28 0x2B ~ "qsdfghjklmù*"
map +caps -altgr +shift 0x56 0x2C..0x35 ~ ">wxcvbn,;:!"
#
# Keys requiring AltGr (unaffected by Caps Lock, but Shift must not be pressed)
#
map +altgr -shift 0x03..0x0D ~ "~#{[|`\^@]}"
map +altgr -shift 0x12 ~ "€"
map +altgr -shift 0x1B ~ "¤"
#
# Dead keys (affected by Caps Lock and Shift)
#
map -caps -altgr -shift 0x1A ~ 0xFE52 # Dead circumflex
map -caps -altgr +shift 0x1A ~ 0xFE57 # Dead umlaut
map +caps -altgr -shift 0x1A ~ 0xFE57 # Dead umlaut
map +caps -altgr +shift 0x1A ~ 0xFE52 # Dead circumflex

View File

@ -0,0 +1,253 @@
#!/usr/bin/env perl
#
# 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.
#
#
# generate.pl
#
# Parse .keymap files, producing corresponding .c files that can be included
# into the Spice plugin source.
#
# We need at least Perl 5.8 for Unicode's sake
use 5.008;
sub keymap_symbol {
my $name = shift;
$name =~ s/-/_/g;
return 'guac_spice_keymap_' . $name;
}
#
# _generated_keymaps.c
#
my @keymaps = ();
open OUTPUT, ">", "_generated_keymaps.c";
print OUTPUT
'#include "config.h"' . "\n"
. '#include "keymap.h"' . "\n"
. "\n"
. '#include <stddef.h>' . "\n"
. '#include <spice-client-glib-2.0/spice-client.h>' . "\n"
. "\n";
for my $filename (@ARGV) {
my $content = "";
my $parent = "";
my $layout_name = "";
# Parse file
open INPUT, '<', "$filename";
binmode INPUT, ":encoding(utf8)";
while (<INPUT>) {
chomp;
# Comments
if (m/^\s*#/) {}
# Name
elsif ((my $name) = m/^\s*name\s+"(.*)"\s*(?:#.*)?$/) {
$layout_name = $name;
}
# Parent map
elsif ((my $name) = m/^\s*parent\s+"(.*)"\s*(?:#.*)?$/) {
$parent = keymap_symbol($name);
}
# Map
elsif ((my $range, my $onto) =
m/^\s*map\s+([^~]*)\s+~\s+(".*"|0x[0-9A-Fa-f]+)\s*(?:#.*)?$/) {
my @keysyms = ();
my @scancodes = ();
my $ext_flags = 0;
my $set_shift = 0;
my $set_altgr = 0;
my $set_caps = 0;
my $set_num = 0;
my $clear_shift = 0;
my $clear_altgr = 0;
my $clear_caps = 0;
my $clear_num = 0;
# Parse ranges and options
foreach $_ (split(/\s+/, $range)) {
# Set option/modifier
if ((my $opt) = m/^\+([a-z]+)$/) {
if ($opt eq "shift") { $set_shift = 1; }
elsif ($opt eq "altgr") { $set_altgr = 1; }
elsif ($opt eq "caps") { $set_caps = 1; }
elsif ($opt eq "num") { $set_num = 1; }
elsif ($opt eq "ext") { $ext_flags = 1; }
else {
die "$filename: $.: ERROR: "
. "Invalid set option\n";
}
}
# Clear option/modifier
elsif ((my $opt) = m/^-([a-z]+)$/) {
if ($opt eq "shift") { $clear_shift = 1; }
elsif ($opt eq "altgr") { $clear_altgr = 1; }
elsif ($opt eq "caps") { $clear_caps = 1; }
elsif ($opt eq "num") { $clear_num = 1; }
else {
die "$filename: $.: ERROR: "
. "Invalid clear option\n";
}
}
# Single scancode
elsif ((my $scancode) = m/^(0x[0-9A-Fa-f]+)$/) {
$scancodes[++$#scancodes] = hex($scancode);
}
# Range of scancodes
elsif ((my $start, my $end) =
m/^(0x[0-9A-Fa-f]+)\.\.(0x[0-9A-Fa-f]+)$/) {
for (my $i=hex($start); $i<=hex($end); $i++) {
$scancodes[++$#scancodes] = $i;
}
}
# Invalid token
else {
die "$filename: $.: ERROR: "
. "Invalid token\n";
}
}
# Parse onto
if ($onto =~ m/^0x/) {
$keysyms[0] = hex($onto);
}
else {
foreach my $char (split('',
substr($onto, 1, length($onto)-2))) {
my $codepoint = ord($char);
if ($codepoint >= 0x0100) {
$keysyms[++$#keysyms] = 0x01000000 | $codepoint;
}
else {
$keysyms[++$#keysyms] = $codepoint;
}
}
}
# Check mapping
if ($#keysyms != $#scancodes) {
die "$filename: $.: ERROR: "
. "Keysym and scancode range lengths differ\n";
}
# Write keysym/scancode pairs
for (my $i=0; $i<=$#keysyms; $i++) {
$content .= " {"
. " .keysym = " . $keysyms[$i] . ","
. " .scancode = " . $scancodes[$i];
# Modifiers that must be active
$content .= ", .set_modifiers = 0";
$content .= " | GUAC_SPICE_KEYMAP_MODIFIER_SHIFT" if $set_shift;
$content .= " | GUAC_SPICE_KEYMAP_MODIFIER_ALTGR" if $set_altgr;
# Modifiers that must be inactive
$content .= ", .clear_modifiers = 0";
$content .= " | GUAC_SPICE_KEYMAP_MODIFIER_SHIFT" if $clear_shift;
$content .= " | GUAC_SPICE_KEYMAP_MODIFIER_ALTGR" if $clear_altgr;
# Locks that must be set
$content .= ", .set_locks = 0";
$content .= " | SPICE_INPUTS_NUM_LOCK" if $set_num;
$content .= " | SPICE_INPUTS_CAPS_LOCK" if $set_caps;
# Locks that must NOT be set
$content .= ", .clear_locks = 0";
$content .= " | SPICE_INPUTS_NUM_LOCK" if $clear_num;
$content .= " | SPICE_INPUTS_CAPS_LOCK" if $clear_caps;
$content .= " },\n";
}
}
# Invalid token
elsif (m/\S/) {
die "$filename: $.: ERROR: "
. "Invalid token\n";
}
}
close INPUT;
# Header
my $sym = keymap_symbol($layout_name);
print OUTPUT
"\n"
. '/* Autogenerated from ' . $filename . ' */' . "\n"
. 'static guac_spice_keysym_desc __' . $sym . '[] = {' . "\n"
. $content
. ' {0}' . "\n"
. '};' . "\n";
# Desc header
print OUTPUT "\n"
. 'static const guac_spice_keymap ' . $sym . ' = { ' . "\n";
# Layout name
print OUTPUT " .name = \"$layout_name\",\n";
# Parent layout (if any)
if ($parent) {
print OUTPUT " .parent = &$parent,\n";
}
# Desc footer
print OUTPUT
' .mapping = __' . $sym . "\n"
. '};' . "\n";
$keymaps[++$#keymaps] = $sym;
print STDERR "Added: $layout_name\n";
}
print OUTPUT "\n"
. 'const guac_spice_keymap* GUAC_SPICE_KEYMAPS[] = {' . "\n";
foreach my $keymap (@keymaps) {
print OUTPUT " &$keymap,\n";
}
print OUTPUT
' NULL' . "\n"
. '};' . "\n";
close OUTPUT;

View File

@ -0,0 +1,108 @@
#
# 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 "hu-hu-qwertz"
#
# Basic keys
#
map -caps -altgr -shift 0x29 0x02..0x0D ~ "0123456789öüó"
map -caps -altgr -shift 0x10..0x1B ~ "qwertzuıopőú"
map -caps -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjkléáű"
map -caps -altgr -shift 0x56 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 0x56 0x2C..0x35 ~ "ÍYXCVBNM?:_"
map +caps -altgr -shift 0x29 0x02..0x0D ~ "0123456789ÖÜÓ"
map +caps -altgr -shift 0x10..0x1B ~ "QWERTZUIOPŐÚ"
map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKLÉÁŰ"
map +caps -altgr -shift 0x56 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 0x56 0x2C..0x35 ~ "íyxcvbnm?:_"
#
# Keys requiring AltGr
#
map +altgr -shift 0x02 ~ "~"
map +altgr -shift 0x08 ~ "`"
map +altgr -shift 0x10 ~ "\"
map +altgr -shift 0x11 ~ "|"
map +altgr -shift 0x12 ~ "Ä"
map +altgr -shift 0x16 ~ "€"
map +altgr -shift 0x17 ~ "Í"
map +altgr -shift 0x1A ~ "÷"
map +altgr -shift 0x1B ~ "×"
map +altgr -shift 0x1E ~ "ä"
map +altgr -shift 0x1F ~ "đ"
map +altgr -shift 0x20 ~ "Đ"
map +altgr -shift 0x21 ~ "["
map +altgr -shift 0x22 ~ "]"
map +altgr -shift 0x24 ~ "í"
map +altgr -shift 0x25 ~ "ł"
map +altgr -shift 0x26 ~ "Ł"
map +altgr -shift 0x27 ~ "$"
map +altgr -shift 0x28 ~ "ß"
map +altgr -shift 0x2B ~ "¤"
map +altgr -shift 0x56 ~ "<"
map +altgr -shift 0x2C ~ ">"
map +altgr -shift 0x2D ~ "#"
map +altgr -shift 0x2E ~ "&"
map +altgr -shift 0x2F ~ "@"
map +altgr -shift 0x30 ~ "{"
map +altgr -shift 0x31 ~ "}"
map +altgr -shift 0x32 ~ "<"
map +altgr -shift 0x33 ~ ";"
map +altgr -shift 0x34 ~ ">"
map +altgr -shift 0x35 ~ "*"
#
# Keys requiring AltGr & Shift
#
#
# Dead keys
#
map +altgr -shift 0x03 ~ 0xFE5A # Dead caron
map +altgr -shift 0x04 ~ 0xFE52 # Dead circumflex
map +altgr -shift 0x05 ~ 0xFE55 # Dead breve
map +altgr -shift 0x06 ~ 0xFE58 # Dead abovering
map +altgr -shift 0x07 ~ 0xFE5C # Dead ogonek
map +altgr -shift 0x09 ~ 0xFE56 # Dead abovedot
map +altgr -shift 0x0A ~ 0xFE51 # Dead acute
map +altgr -shift 0x0B ~ 0xFE59 # Dead doubleacute
map +altgr -shift 0x0C ~ 0xFE57 # Dead diaeresis
map +altgr -shift 0x0D ~ 0xFE5B # Dead cedilla
# END

View File

@ -0,0 +1,59 @@
#
# 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 "it-it-qwerty"
#
# Basic keys
#
map -caps -altgr -shift 0x29 0x02..0x0D ~ "\1234567890'ì"
map -caps -altgr -shift 0x10..0x1B ~ "qwertyuiopè+"
map -caps -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjklòàù"
map -caps -altgr -shift 0x56 0x2C..0x35 ~ "<zxcvbnm,.-"
map -caps -altgr +shift 0x29 0x02..0x0D ~ "|!"£$%&/()=?^"
map -caps -altgr +shift 0x10..0x1B ~ "QWERTYUIOPé*"
map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKLç°§"
map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">ZXCVBNM;:_"
map +caps -altgr -shift 0x29 0x02..0x0D ~ "\1234567890'ì"
map +caps -altgr -shift 0x10..0x1B ~ "QWERTYUIOPè+"
map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKLòàù"
map +caps -altgr -shift 0x56 0x2C..0x35 ~ "<ZXCVBNM,.-"
map +caps -altgr +shift 0x29 0x02..0x0D ~ "|!"£$%&/()=?^"
map +caps -altgr +shift 0x10..0x1B ~ "qwertyuiopé*"
map +caps -altgr +shift 0x1E..0x28 0x2B ~ "asdfghjklç°§"
map +caps -altgr +shift 0x56 0x2C..0x35 ~ ">zxcvbnm;:_"
#
# Keys requiring AltGr
#
map +altgr -shift 0x12 ~ "€"
map +altgr -shift 0x1A ~ "["
map +altgr -shift 0x1B ~ "]"
map +altgr -shift 0x27 ~ "@"
map +altgr -shift 0x28 ~ "#"
map +altgr +shift 0x1A ~ "{"
map +altgr +shift 0x1B ~ "}"

View File

@ -0,0 +1,35 @@
#
# 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 "ja-jp-qwerty"
map -shift 0x02..0x0D 0x7D ~ "1234567890-^\"
map -shift 0x10..0x1B ~ "qwertyuiop@["
map -shift 0x1E..0x28 0x2B ~ "asdfghjkl;:]"
map -shift 0x2C..0x35 0x73 ~ "zxcvbnm,./\"
map +shift 0x02..0x0A 0x0C 0x0D 0x7D ~ "!"#$%&'()=~|"
map +shift 0x10..0x1B ~ "QWERTYUIOP`{"
map +shift 0x1E..0x28 0x2B ~ "ASDFGHJKL+*}"
map +shift 0x2C..0x35 0x73 ~ "ZXCVBNM<>?_"
map -shift 0x29 ~ 0xFF28
map -shift 0x29 ~ 0xFF2A
map +shift 0x29 ~ 0xFF29

View File

@ -0,0 +1,75 @@
#
# 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 "no-no-qwerty"
#
# Basic keys
#
map -caps -altgr -shift 0x29 0x02..0x0C ~ "|1234567890+"
map -caps -altgr -shift 0x10..0x1A ~ "qwertyuiopå"
map -caps -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjkløæ'"
map -caps -altgr -shift 0x56 0x2C..0x35 ~ "<zxcvbnm,.-"
map -caps -altgr +shift 0x29 0x02..0x0C ~ "|!"#¤%&/()=?"
map -caps -altgr +shift 0x10..0x1A ~ "QWERTYUIOPÅ"
map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKLØÆ*"
map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">ZXCVBNM;:_"
map +caps -altgr -shift 0x29 0x02..0x0C ~ "|1234567890+"
map +caps -altgr -shift 0x10..0x1A ~ "QWERTYUIOPÅ"
map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKLØÆ'"
map +caps -altgr -shift 0x56 0x2C..0x35 ~ "<ZXCVBNM,.-"
map +caps -altgr +shift 0x29 0x02..0x0C ~ "|!"#¤%&/()=?"
map +caps -altgr +shift 0x10..0x1A ~ "qwertyuiopå"
map +caps -altgr +shift 0x1E..0x28 0x2B ~ "asdfghjkløæ*"
map +caps -altgr +shift 0x56 0x2C..0x35 ~ ">zxcvbnm;:_"
map -altgr -shift 0x0D ~ "\"
#
# Keys requiring AltGr
#
map +altgr -shift 0x03 ~ "@"
map +altgr -shift 0x04 ~ "£"
map +altgr -shift 0x05 ~ "$"
map +altgr -shift 0x08 ~ "{"
map +altgr -shift 0x09 ~ "["
map +altgr -shift 0x0A ~ "]"
map +altgr -shift 0x0B ~ "}"
map +altgr -shift 0x56 ~ "\"
map +altgr -shift 0x12 ~ "€"
map +altgr -shift 0x0D ~ "|"
map +altgr -shift 0x32 ~ "µ"
#
# Dead keys
#
map +altgr -shift 0x0D ~ 0xFE51 # Dead acute
map -altgr +shift 0x0D ~ 0xFE50 # Dead grave
map -altgr -shift 0x1B ~ 0xFE57 # Dead umlaut
map -altgr +shift 0x1B ~ 0xFE52 # Dead circumflex
map +altgr -shift 0x1B ~ 0xFE53 # Dead tilde

View File

@ -0,0 +1,63 @@
#
# 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 "pl-pl-qwerty"
map -caps -shift 0x29 0x02..0x0D ~ "`1234567890-="
map -caps -shift 0x10..0x1B 0x2B ~ "qwertyuiop[]\"
map -caps -shift 0x1E..0x28 ~ "asdfghjkl;'"
map -caps -shift 0x2C..0x35 ~ "zxcvbnm,./"
map -caps +shift 0x29 0x02..0x0D ~ "~!@#$%^&*()_+"
map -caps +shift 0x10..0x1B 0x2B ~ "QWERTYUIOP{}|"
map -caps +shift 0x1E..0x28 ~ "ASDFGHJKL:""
map -caps +shift 0x2C..0x35 ~ "ZXCVBNM<>?"
map +caps -shift 0x29 0x02..0x0D ~ "`1234567890-="
map +caps -shift 0x10..0x1B 0x2B ~ "QWERTYUIOP[]\"
map +caps -shift 0x1E..0x28 ~ "ASDFGHJKL;'"
map +caps -shift 0x2C..0x35 ~ "ZXCVBNM,./"
map +caps +shift 0x29 0x02..0x0D ~ "~!@#$%^&*()_+"
map +caps +shift 0x10..0x1B 0x2B ~ "qwertyuiop{}|"
map +caps +shift 0x1E..0x28 ~ "asdfghjkl:""
map +caps +shift 0x2C..0x35 ~ "zxcvbnm<>?"
#
# Keys requiring AltGr
#
map +altgr -shift 0x16 ~ "€"
map -caps -shift +altgr 0x12 0x18 ~ "ęó"
map -caps -shift +altgr 0x1E 0x1F 0x26 ~ "ąśł"
map -caps -shift +altgr 0x2C 0x2D 0x2E 0x31 ~ "żźćń"
map -caps +shift +altgr 0x12 0x18 ~ "ĘÓ"
map -caps +shift +altgr 0x1E 0x1F 0x26 ~ "ĄŚŁ"
map -caps +shift +altgr 0x2C 0x2D 0x2E 0x31 ~ "ŻŹĆŃ"
map +caps -shift +altgr 0x12 0x18 ~ "ĘÓ"
map +caps -shift +altgr 0x1E 0x1F 0x26 ~ "ĄŚŁ"
map +caps -shift +altgr 0x2C 0x2D 0x2E 0x31 ~ "ŻŹĆŃ"
map +caps +shift +altgr 0x12 0x18 ~ "ęó"
map +caps +shift +altgr 0x1E 0x1F 0x26 ~ "ąśł"
map +caps +shift +altgr 0x2C 0x2D 0x2E 0x31 ~ "żźćń"

View File

@ -0,0 +1,66 @@
#
# 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 "pt-br-qwerty"
#
# Basic keys
#
map -caps -altgr -shift 0x29 0x02..0x0D ~ "'1234567890-="
map -caps -altgr -shift 0x10..0x19 0x1B ~ "qwertyuiop["
map -caps -altgr -shift 0x1E..0x27 0x2B ~ "asdfghjklç]"
map -caps -altgr -shift 0x56 0x2C..0x35 0x73 ~ "\zxcvbnm,.;/"
map -caps -altgr +shift 0x29 0x02..0x06 0x08..0x0D ~ ""!@#$%&*()_+"
map -caps -altgr +shift 0x10..0x19 0x1B ~ "QWERTYUIOP{"
map -caps -altgr +shift 0x1E..0x27 0x2B ~ "ASDFGHJKLÇ}"
map -caps -altgr +shift 0x56 0x2C..0x35 0x73 ~ "|ZXCVBNM<>:?"
map +caps -altgr -shift 0x29 0x02..0x0D ~ "'1234567890-="
map +caps -altgr -shift 0x10..0x19 0x1B ~ "QWERTYUIOP["
map +caps -altgr -shift 0x1E..0x27 0x2B ~ "ASDFGHJKLÇ]"
map +caps -altgr -shift 0x56 0x2C..0x35 0x73 ~ "\ZXCVBNM,.;/"
map +caps -altgr +shift 0x29 0x02..0x06 0x08..0x0D ~ ""!@#$%&*()_+"
map +caps -altgr +shift 0x10..0x19 0x1B ~ "qwertyuiop{"
map +caps -altgr +shift 0x1E..0x27 0x2B ~ "asdfghjklç}"
map +caps -altgr +shift 0x56 0x2C..0x35 0x73 ~ "|zxcvbnm<>:?"
#
# Keys requiring AltGr
#
map +altgr -shift 0x02..0x07 0x0D ~ "¹²³£¢¬§"
map +altgr -shift 0x10..0x11 ~ "/?"
map +altgr -shift 0x12 ~ "°"
map +altgr -shift 0x1B ~ "ª"
map +altgr -shift 0x2B ~ "º"
map +altgr -shift 0x2E ~ "₢"
#
# Dead keys
#
map -altgr +shift 0x07 ~ 0xFE57 # Dead diaeresis (umlaut)
map -altgr +shift 0x1A ~ 0xFE50 # Dead grave
map -altgr -shift 0x1A ~ 0xFE51 # Dead acute
map -altgr +shift 0x28 ~ 0xFE52 # Dead circumflex
map -altgr -shift 0x28 ~ 0xFE53 # Dead tilde

View File

@ -0,0 +1,74 @@
#
# 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 "sv-se-qwerty"
#
# Basic keys
#
map -caps -altgr -shift 0x29 0x02..0x0C ~ "§1234567890+"
map -caps -altgr -shift 0x10..0x1A ~ "qwertyuiopå"
map -caps -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjklöä'"
map -caps -altgr -shift 0x56 0x2C..0x35 ~ "<zxcvbnm,.-"
map -caps -altgr +shift 0x29 0x02..0x0C ~ "½!"#¤%&/()=?"
map -caps -altgr +shift 0x10..0x1A ~ "QWERTYUIOPÅ"
map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKLÖÄ*"
map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">ZXCVBNM;:_"
map +caps -altgr -shift 0x29 0x02..0x0C ~ "§1234567890+"
map +caps -altgr -shift 0x10..0x1A ~ "QWERTYUIOPÅ"
map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKLÖÄ'"
map +caps -altgr -shift 0x56 0x2C..0x35 ~ "<ZXCVBNM,.-"
map +caps -altgr +shift 0x29 0x02..0x0C ~ "½!"#¤%&/()=?"
map +caps -altgr +shift 0x10..0x1A ~ "qwertyuiopå"
map +caps -altgr +shift 0x1E..0x28 0x2B ~ "asdfghjklöä*"
map +caps -altgr +shift 0x56 0x2C..0x35 ~ ">zxcvbnm;:_"
#
# Keys requiring AltGr
#
map +altgr -shift 0x03 ~ "@"
map +altgr -shift 0x04 ~ "£"
map +altgr -shift 0x05 ~ "$"
map +altgr -shift 0x08 ~ "{"
map +altgr -shift 0x09 ~ "["
map +altgr -shift 0x0A ~ "]"
map +altgr -shift 0x0B ~ "}"
map +altgr -shift 0x0C ~ "\"
map +altgr -shift 0x12 ~ "€"
map +altgr -shift 0x56 ~ "|"
map +altgr -shift 0x32 ~ "µ"
#
# Dead keys
#
map -altgr -shift 0x0D ~ 0xFE51 # Dead acute
map -altgr +shift 0x0D ~ 0xFE50 # Dead grave
map -altgr -shift 0x1B ~ 0xFE57 # Dead umlaut
map -altgr +shift 0x1B ~ 0xFE52 # Dead circumflex
map +altgr -shift 0x1B ~ 0xFE53 # Dead tilde

View File

@ -0,0 +1,92 @@
#
# 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 "tr-tr-qwerty"
#
# Basic keys
#
map -caps -altgr -shift 0x29 0x02..0x0D ~ ""1234567890*-"
map -caps -altgr -shift 0x10..0x1B ~ "qwertyuıopğü"
map -caps -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjklşi,"
map -caps -altgr -shift 0x56 0x2C..0x35 ~ "<zxcvbnmöç."
map -caps -altgr +shift 0x29 0x02..0x03 ~ "é!'"
map -caps -altgr +shift 0x05..0x0D ~ "+%&/()=?_"
map -caps -altgr +shift 0x10..0x1B ~ "QWERTYUIOPĞÜ"
map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKLŞİ;"
map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">ZXCVBNMÖÇ:"
map +caps -altgr -shift 0x29 0x02..0x0D ~ ""1234567890*-"
map +caps -altgr -shift 0x10..0x1B ~ "QWERTYUIOPĞÜ"
map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKLŞİ,"
map +caps -altgr -shift 0x56 0x2C..0x35 ~ "<ZXCVBNMÖÇ."
map +caps -altgr +shift 0x29 0x02..0x03 ~ "é!'"
map +caps -altgr +shift 0x05..0x0D ~ "+%&/()=?_"
map +caps -altgr +shift 0x10..0x1B ~ "qwertyuıopğü"
map +caps -altgr +shift 0x1E..0x28 0x2B ~ "asdfghjklşi;"
map +caps -altgr +shift 0x56 0x2C..0x35 ~ ">zxcvbnmöç:"
#
# Keys requiring AltGr
#
map +altgr -shift 0x29 0x02..0x06 ~ "<>£#$½"
map +altgr -shift 0x08..0x0D ~ "{[]}\|"
map +altgr -shift 0x10 ~ "@"
map +altgr -shift 0x12 ~ "€"
map +altgr -shift 0x14 ~ "₺"
map +altgr -shift 0x1F ~ "ß"
map +altgr -shift 0x56 ~ "|"
#
# Keys requiring AltGr and vary by Shift and Caps Lock
#
map -caps +altgr -shift 0x17 ~ "i"
map -caps +altgr -shift 0x1E ~ "æ"
map -caps +altgr +shift 0x17 ~ "İ"
map -caps +altgr +shift 0x1E ~ "Æ"
map +caps +altgr -shift 0x17 ~ "İ"
map +caps +altgr -shift 0x1E ~ "Æ"
map +caps +altgr +shift 0x1E ~ "æ"
map +caps +altgr +shift 0x17 ~ "i"
#
# Dead keys
#
map -altgr +shift 0x04 ~ 0xFE52 # Dead circumflex
map +altgr -shift 0x1A ~ 0xFE57 # Dead diaeresis (umlaut)
map +altgr -shift 0x1B ~ 0xFE53 # Dead tilde
map +altgr -shift 0x27 ~ 0xFE51 # Dead acute
map +altgr -shift 0x2B ~ 0xFE50 # Dead grave
# END

67
src/protocols/spice/log.c Normal file
View File

@ -0,0 +1,67 @@
/*
* 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 "client.h"
#include "common/iconv.h"
#include "common/surface.h"
#include <cairo/cairo.h>
#include <guacamole/client.h>
#include <guacamole/layer.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <spice-client-glib-2.0/spice-client.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
void guac_spice_client_log_info(const char* format, ...) {
char message[2048];
/* Copy log message into buffer */
va_list args;
va_start(args, format);
vsnprintf(message, sizeof(message), format, args);
va_end(args);
/* Log to syslog */
syslog(LOG_INFO, "%s", message);
}
void guac_spice_client_log_error(const char* format, ...) {
char message[2048];
/* Copy log message into buffer */
va_list args;
va_start(args, format);
vsnprintf(message, sizeof(message), format, args);
va_end(args);
/* Log to syslog */
syslog(LOG_ERR, "%s", message);
}

67
src/protocols/spice/log.h Normal file
View File

@ -0,0 +1,67 @@
/*
* 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_SPICE_LOG_H
#define GUAC_SPICE_LOG_H
#include "config.h"
#include "client.h"
#include "common/iconv.h"
#include "common/surface.h"
#include <cairo/cairo.h>
#include <guacamole/client.h>
#include <guacamole/layer.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <spice-client-glib-2.0/spice-client.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
/**
* Callback invoked by SPICE when an informational message needs to be
* logged.
*
* @param format
* A printf-style format string to log.
*
* @param ...
* The values to use when filling the conversion specifiers within the
* format string.
*/
void guac_spice_client_log_info(const char* format, ...);
/**
* Callback invoked by SPICE when an error message needs to be logged.
*
* @param format
* A printf-style format string to log.
*
* @param ...
* The values to use when filling the conversion specifiers within the
* format string.
*/
void guac_spice_client_log_error(const char* format, ...);
#endif /* GUAC_SPICE_LOG_H */

View File

@ -0,0 +1,628 @@
/*
* 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 "argv.h"
#include "client.h"
#include "common/defaults.h"
#include "settings.h"
#include "spice-defaults.h"
#include "spice-constants.h"
#include <guacamole/user.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <spice-client-glib-2.0/spice-client.h>
/* Client plugin arguments */
const char* GUAC_SPICE_CLIENT_ARGS[] = {
"hostname",
"port",
"tls",
"tls-verify",
"ca",
"ca-file",
"pubkey",
"proxy",
"read-only",
"encodings",
GUAC_SPICE_ARGV_USERNAME,
GUAC_SPICE_ARGV_PASSWORD,
"swap-red-blue",
"color-depth",
"cursor",
"autoretry",
"clipboard-encoding",
"enable-audio",
"file-transfer",
"file-directory",
"file-transfer-ro",
"server-layout",
#ifdef ENABLE_COMMON_SSH
"enable-sftp",
"sftp-hostname",
"sftp-host-key",
"sftp-port",
"sftp-username",
"sftp-password",
"sftp-private-key",
"sftp-passphrase",
"sftp-directory",
"sftp-root-directory",
"sftp-server-alive-interval",
"sftp-disable-download",
"sftp-disable-upload",
#endif
"recording-path",
"recording-name",
"recording-exclude-output",
"recording-exclude-mouse",
"recording-include-keys",
"create-recording-path",
"disable-copy",
"disable-paste",
NULL
};
enum SPICE_ARGS_IDX {
/**
* The hostname of the SPICE server (or repeater) to connect to.
*/
IDX_HOSTNAME,
/**
* The port of the SPICE server (or repeater) to connect to.
*/
IDX_PORT,
/**
* Whether or not the connection to the SPICE server should be made via
* TLS.
*/
IDX_TLS,
/**
* The verification mode that should be used to validate TLS connections
* to the SPICE server.
*/
IDX_TLS_VERIFY,
/**
* One or more Base64-encoded certificates that will be used for TLS
* verification.
*/
IDX_CA,
/**
* A path to a file containing one or more certificates that will be used
* when validating TLS connections.
*/
IDX_CA_FILE,
/**
* The public key of the host for TLS verification.
*/
IDX_PUBKEY,
/**
* The proxy server to connect through when connecting to the SPICE server.
*/
IDX_PROXY,
/**
* "true" if this connection should be read-only (user input should be
* dropped), "false" or blank otherwise.
*/
IDX_READ_ONLY,
/**
* Space-separated list of encodings to use within the SPICE session. If not
* specified, this will be:
*
* "zrle ultra copyrect hextile zlib corre rre raw".
*/
IDX_ENCODINGS,
/**
* The username to send to the SPICE server if authentication is requested.
*/
IDX_USERNAME,
/**
* The password to send to the SPICE server if authentication is requested.
*/
IDX_PASSWORD,
/**
* "true" if the red and blue components of each color should be swapped,
* "false" or blank otherwise. This is mainly used for SPICE servers that do
* not properly handle colors.
*/
IDX_SWAP_RED_BLUE,
/**
* The color depth to request, in bits.
*/
IDX_COLOR_DEPTH,
/**
* "remote" if the cursor should be rendered on the server instead of the
* client. All other values will default to local rendering.
*/
IDX_CURSOR,
/**
* The number of connection attempts to make before giving up. By default,
* this will be 0.
*/
IDX_AUTORETRY,
/**
* The encoding to use for clipboard data sent to the SPICE server if we are
* going to be deviating from the standard (which mandates ISO 8829-1).
* Valid values are "ISO8829-1" (the only legal value with respect to the
* SPICE standard), "UTF-8", "UTF-16", and "CP2252".
*/
IDX_CLIPBOARD_ENCODING,
/**
* "true" if audio should be enabled, "false" or blank otherwise.
*/
IDX_ENABLE_AUDIO,
/**
* "true" if file transfer should be enabled, "false" or blank otherwise.
*/
IDX_FILE_TRANSFER,
/**
* The absolute path to the directory that should be shared from the system
* running guacd to the spice server.
*/
IDX_FILE_DIRECTORY,
/**
* Whether or not the shared directory should be read-only to the SPICE
* server.
*/
IDX_FILE_TRANSFER_RO,
/**
* The name of the keymap chosen as the layout of the server. Legal names
* are defined within the *.keymap files in the "keymaps" directory of the
* source for Guacamole's SPICE support.
*/
IDX_SERVER_LAYOUT,
#ifdef ENABLE_COMMON_SSH
/**
* "true" if SFTP should be enabled for the SPICE connection, "false" or
* blank otherwise.
*/
IDX_ENABLE_SFTP,
/**
* The hostname of the SSH server to connect to for SFTP. If blank, the
* hostname of the SPICE server will be used.
*/
IDX_SFTP_HOSTNAME,
/**
* The public SSH host key to identify the SFTP server.
*/
IDX_SFTP_HOST_KEY,
/**
* The port of the SSH server to connect to for SFTP. If blank, the default
* SSH port of "22" will be used.
*/
IDX_SFTP_PORT,
/**
* The username to provide when authenticating with the SSH server for
* SFTP.
*/
IDX_SFTP_USERNAME,
/**
* The password to provide when authenticating with the SSH server for
* SFTP (if not using a private key).
*/
IDX_SFTP_PASSWORD,
/**
* The base64-encoded private key to use when authenticating with the SSH
* server for SFTP (if not using a password).
*/
IDX_SFTP_PRIVATE_KEY,
/**
* The passphrase to use to decrypt the provided base64-encoded private
* key.
*/
IDX_SFTP_PASSPHRASE,
/**
* The default location for file uploads within the SSH server. This will
* apply only to uploads which do not use the filesystem guac_object (where
* the destination directory is otherwise ambiguous).
*/
IDX_SFTP_DIRECTORY,
/**
* The path of the directory within the SSH server to expose as a
* filesystem guac_object. If omitted, "/" will be used by default.
*/
IDX_SFTP_ROOT_DIRECTORY,
/**
* The interval at which SSH keepalive messages are sent to the server for
* SFTP connections. The default is 0 (disabling keepalives), and a value
* of 1 is automatically incremented to 2 by libssh2 to avoid busy loop corner
* cases.
*/
IDX_SFTP_SERVER_ALIVE_INTERVAL,
/**
* If set to "true", file downloads over SFTP will be blocked. If set to
* "false" or not set, file downloads will be allowed.
*/
IDX_SFTP_DISABLE_DOWNLOAD,
/**
* If set to "true", file uploads over SFTP will be blocked. If set to
* "false" or not set, file uploads will be allowed.
*/
IDX_SFTP_DISABLE_UPLOAD,
#endif
/**
* The full absolute path to the directory in which screen recordings
* should be written.
*/
IDX_RECORDING_PATH,
/**
* The name that should be given to screen recordings which are written in
* the given path.
*/
IDX_RECORDING_NAME,
/**
* Whether output which is broadcast to each connected client (graphics,
* streams, etc.) should NOT be included in the session recording. Output
* is included by default, as it is necessary for any recording which must
* later be viewable as video.
*/
IDX_RECORDING_EXCLUDE_OUTPUT,
/**
* Whether changes to mouse state, such as position and buttons pressed or
* released, should NOT be included in the session recording. Mouse state
* is included by default, as it is necessary for the mouse cursor to be
* rendered in any resulting video.
*/
IDX_RECORDING_EXCLUDE_MOUSE,
/**
* Whether keys pressed and released should be included in the session
* recording. Key events are NOT included by default within the recording,
* as doing so has privacy and security implications. Including key events
* may be necessary in certain auditing contexts, but should only be done
* with caution. Key events can easily contain sensitive information, such
* as passwords, credit card numbers, etc.
*/
IDX_RECORDING_INCLUDE_KEYS,
/**
* Whether the specified screen recording path should automatically be
* created if it does not yet exist.
*/
IDX_CREATE_RECORDING_PATH,
/**
* Whether outbound clipboard access should be blocked. If set to "true",
* it will not be possible to copy data from the remote desktop to the
* client using the clipboard. By default, clipboard access is not blocked.
*/
IDX_DISABLE_COPY,
/**
* Whether inbound clipboard access should be blocked. If set to "true", it
* will not be possible to paste data from the client to the remote desktop
* using the clipboard. By default, clipboard access is not blocked.
*/
IDX_DISABLE_PASTE,
SPICE_ARGS_COUNT
};
guac_spice_settings* guac_spice_parse_args(guac_user* user,
int argc, const char** argv) {
/* Validate arg count */
if (argc != SPICE_ARGS_COUNT) {
guac_user_log(user, GUAC_LOG_WARNING, "Incorrect number of connection "
"parameters provided: expected %i, got %i.",
SPICE_ARGS_COUNT, argc);
return NULL;
}
guac_spice_settings* settings = calloc(1, sizeof(guac_spice_settings));
settings->hostname =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_HOSTNAME, SPICE_DEFAULT_HOST);
settings->port =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_PORT, SPICE_DEFAULT_PORT);
settings->tls =
guac_user_parse_args_boolean(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_TLS, false);
char* verify_mode = guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_TLS_VERIFY, NULL);
if (verify_mode != NULL) {
if (strcmp(verify_mode, GUAC_SPICE_PARAMETER_TLS_VERIFY_PUBKEY) == 0)
settings->tls_verify = SPICE_SESSION_VERIFY_PUBKEY;
else if (strcmp(verify_mode, GUAC_SPICE_PARAMETER_TLS_VERIFY_SUBJECT) == 0)
settings->tls_verify = SPICE_SESSION_VERIFY_SUBJECT;
}
else {
settings->tls_verify = SPICE_SESSION_VERIFY_HOSTNAME;
}
free(verify_mode);
settings->ca =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_CA, NULL);
settings->ca_file =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_CA_FILE, NULL);
settings->pubkey =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_PUBKEY, NULL);
settings->proxy =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_PROXY, NULL);
settings->username =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_USERNAME, NULL);
settings->password =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_PASSWORD, NULL);
/* Read-only mode */
settings->read_only =
guac_user_parse_args_boolean(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_READ_ONLY, false);
/* Parse color depth */
settings->color_depth =
guac_user_parse_args_int(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_COLOR_DEPTH, 0);
/* Set encodings if specified */
settings->encodings =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_ENCODINGS,
SPICE_DEFAULT_ENCODINGS);
/* Parse autoretry */
settings->retries =
guac_user_parse_args_int(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_AUTORETRY, 0);
/* Audio enable/disable */
settings->audio_enabled =
guac_user_parse_args_boolean(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_ENABLE_AUDIO, false);
/* File transfer enable/disable */
settings->file_transfer =
guac_user_parse_args_boolean(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_FILE_TRANSFER, false);
/* The directory on the guacd server to share */
settings->file_directory =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_FILE_DIRECTORY, NULL);
/* Whether or not the share should be read-only. */
settings->file_transfer_ro =
guac_user_parse_args_boolean(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_FILE_TRANSFER_RO, false);
/* Pick keymap based on argument */
settings->server_layout = NULL;
if (argv[IDX_SERVER_LAYOUT][0] != '\0')
settings->server_layout =
guac_spice_keymap_find(argv[IDX_SERVER_LAYOUT]);
/* If no keymap requested, use default */
if (settings->server_layout == NULL)
settings->server_layout = guac_spice_keymap_find(GUAC_SPICE_DEFAULT_KEYMAP);
/* Set clipboard encoding if specified */
settings->clipboard_encoding =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_CLIPBOARD_ENCODING, NULL);
#ifdef ENABLE_COMMON_SSH
/* SFTP enable/disable */
settings->enable_sftp =
guac_user_parse_args_boolean(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_ENABLE_SFTP, false);
/* Hostname for SFTP connection */
settings->sftp_hostname =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_SFTP_HOSTNAME, settings->hostname);
/* The public SSH host key. */
settings->sftp_host_key =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_SFTP_HOST_KEY, NULL);
/* Port for SFTP connection */
settings->sftp_port =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_SFTP_PORT, SPICE_DEFAULT_SFTP_PORT);
/* Username for SSH/SFTP authentication */
settings->sftp_username =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_SFTP_USERNAME, "");
/* Password for SFTP (if not using private key) */
settings->sftp_password =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_SFTP_PASSWORD, "");
/* Private key for SFTP (if not using password) */
settings->sftp_private_key =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_SFTP_PRIVATE_KEY, NULL);
/* Passphrase for decrypting the SFTP private key (if applicable */
settings->sftp_passphrase =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_SFTP_PASSPHRASE, "");
/* Default upload directory */
settings->sftp_directory =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_SFTP_DIRECTORY, NULL);
/* SFTP root directory */
settings->sftp_root_directory =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_SFTP_ROOT_DIRECTORY, SPICE_DEFAULT_SFTP_ROOT);
/* Default keepalive value */
settings->sftp_server_alive_interval =
guac_user_parse_args_int(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_SFTP_SERVER_ALIVE_INTERVAL, 0);
settings->sftp_disable_download =
guac_user_parse_args_boolean(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_SFTP_DISABLE_DOWNLOAD, false);
settings->sftp_disable_upload =
guac_user_parse_args_boolean(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_SFTP_DISABLE_UPLOAD, false);
#endif
/* Read recording path */
settings->recording_path =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_RECORDING_PATH, NULL);
/* Read recording name */
settings->recording_name =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_RECORDING_NAME, GUAC_SPICE_DEFAULT_RECORDING_NAME);
/* Parse output exclusion flag */
settings->recording_exclude_output =
guac_user_parse_args_boolean(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_RECORDING_EXCLUDE_OUTPUT, false);
/* Parse mouse exclusion flag */
settings->recording_exclude_mouse =
guac_user_parse_args_boolean(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_RECORDING_EXCLUDE_MOUSE, false);
/* Parse key event inclusion flag */
settings->recording_include_keys =
guac_user_parse_args_boolean(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_RECORDING_INCLUDE_KEYS, false);
/* Parse path creation flag */
settings->create_recording_path =
guac_user_parse_args_boolean(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_CREATE_RECORDING_PATH, false);
/* Parse clipboard copy disable flag */
settings->disable_copy =
guac_user_parse_args_boolean(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_DISABLE_COPY, false);
/* Parse clipboard paste disable flag */
settings->disable_paste =
guac_user_parse_args_boolean(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_DISABLE_PASTE, false);
return settings;
}
void guac_spice_settings_free(guac_spice_settings* settings) {
/* Free settings strings */
free(settings->clipboard_encoding);
free(settings->encodings);
free(settings->hostname);
free(settings->password);
free(settings->recording_name);
free(settings->recording_path);
free(settings->username);
#ifdef ENABLE_SPICE_REPEATER
/* Free SPICE repeater settings */
free(settings->dest_host);
#endif
#ifdef ENABLE_COMMON_SSH
/* Free SFTP settings */
free(settings->sftp_directory);
free(settings->sftp_root_directory);
free(settings->sftp_host_key);
free(settings->sftp_hostname);
free(settings->sftp_passphrase);
free(settings->sftp_password);
free(settings->sftp_port);
free(settings->sftp_private_key);
free(settings->sftp_username);
#endif
/* Free settings structure */
free(settings);
}

View File

@ -0,0 +1,323 @@
/*
* 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_SPICE_SETTINGS_H
#define GUAC_SPICE_SETTINGS_H
#include "config.h"
#include "keymap.h"
#include <spice-client-glib-2.0/spice-client.h>
#include <stdbool.h>
/**
* The filename to use for the screen recording, if not specified.
*/
#define GUAC_SPICE_DEFAULT_RECORDING_NAME "recording"
/**
* SPICE-specific client data.
*/
typedef struct guac_spice_settings {
/**
* The hostname of the SPICE server (or repeater) to connect to.
*/
char* hostname;
/**
* The port of the SPICE server (or repeater) to connect to.
*/
char* port;
/**
* Whether or not TLS should be used to connect to the SPICE server.
*/
bool tls;
/**
* The type of TLS validation that should be done for encrypted connections
* to SPICE servers.
*/
SpiceSessionVerify tls_verify;
/**
* One or more Base64-encoded certificates to use to validate TLS
* connections to the SPICE server.
*/
char* ca;
/**
* A path to a file containing one more certificates that will be used to
* validate TLS connections.
*/
char* ca_file;
/**
* The public key of the SPICE server for TLS verification.
*/
char* pubkey;
/**
* SPICE supports connecting to remote servers via a proxy server. You can
* specify the proxy server to use in this property.
*/
char* proxy;
/**
* The username given in the arguments.
*/
char* username;
/**
* The password given in the arguments.
*/
char* password;
/**
* Space-separated list of encodings to use within the SPICE session.
*/
char* encodings;
/**
* The color depth to request, in bits.
*/
int color_depth;
/**
* Whether this connection is read-only, and user input should be dropped.
*/
bool read_only;
/**
* Whether audio is enabled.
*/
bool audio_enabled;
/**
* If file transfer capability should be enabled.
*/
bool file_transfer;
/**
* The directory on the server where guacd is running that should be
* shared.
*/
char* file_directory;
/**
* If file transfer capability should be limited to read-only.
*/
bool file_transfer_ro;
/**
* The keymap chosen as the layout of the server.
*/
const guac_spice_keymap* server_layout;
/**
* The number of connection attempts to make before giving up.
*/
int retries;
/**
* The encoding to use for clipboard data sent to the SPICE server, or NULL
* to use the encoding required by the SPICE standard.
*/
char* clipboard_encoding;
/**
* Whether outbound clipboard access should be blocked. If set, it will not
* be possible to copy data from the remote desktop to the client using the
* clipboard.
*/
bool disable_copy;
/**
* Whether inbound clipboard access should be blocked. If set, it will not
* be possible to paste data from the client to the remote desktop using
* the clipboard.
*/
bool disable_paste;
#ifdef ENABLE_COMMON_SSH
/**
* Whether SFTP should be enabled for the SPICE connection.
*/
bool enable_sftp;
/**
* The hostname of the SSH server to connect to for SFTP.
*/
char* sftp_hostname;
/**
* The public SSH host key.
*/
char* sftp_host_key;
/**
* The port of the SSH server to connect to for SFTP.
*/
char* sftp_port;
/**
* The username to provide when authenticating with the SSH server for
* SFTP.
*/
char* sftp_username;
/**
* The password to provide when authenticating with the SSH server for
* SFTP (if not using a private key).
*/
char* sftp_password;
/**
* The base64-encoded private key to use when authenticating with the SSH
* server for SFTP (if not using a password).
*/
char* sftp_private_key;
/**
* The passphrase to use to decrypt the provided base64-encoded private
* key.
*/
char* sftp_passphrase;
/**
* The default location for file uploads within the SSH server. This will
* apply only to uploads which do not use the filesystem guac_object (where
* the destination directory is otherwise ambiguous).
*/
char* sftp_directory;
/**
* The path of the directory within the SSH server to expose as a
* filesystem guac_object.
*/
char* sftp_root_directory;
/**
* The interval at which SSH keepalive messages are sent to the server for
* SFTP connections. The default is 0 (disabling keepalives), and a value
* of 1 is automatically increased to 2 by libssh2 to avoid busy loop corner
* cases.
*/
int sftp_server_alive_interval;
/**
* Whether file downloads over SFTP should be blocked. If set to "true",
* the local client will not be able to download files from the SFTP server.
* If set to "false" or not set, file downloads will be allowed.
*/
bool sftp_disable_download;
/**
* Whether file uploads over SFTP should be blocked. If set to "true", the
* local client will not be able to upload files to the SFTP server. If set
* to "false" or not set, file uploads will be allowed.
*/
bool sftp_disable_upload;
#endif
/**
* The path in which the screen recording should be saved, if enabled. If
* no screen recording should be saved, this will be NULL.
*/
char* recording_path;
/**
* The filename to use for the screen recording, if enabled.
*/
char* recording_name;
/**
* Whether the screen recording path should be automatically created if it
* does not already exist.
*/
bool create_recording_path;
/**
* Whether output which is broadcast to each connected client (graphics,
* streams, etc.) should NOT be included in the session recording. Output
* is included by default, as it is necessary for any recording which must
* later be viewable as video.
*/
bool recording_exclude_output;
/**
* Whether changes to mouse state, such as position and buttons pressed or
* released, should NOT be included in the session recording. Mouse state
* is included by default, as it is necessary for the mouse cursor to be
* rendered in any resulting video.
*/
bool recording_exclude_mouse;
/**
* Whether keys pressed and released should be included in the session
* recording. Key events are NOT included by default within the recording,
* as doing so has privacy and security implications. Including key events
* may be necessary in certain auditing contexts, but should only be done
* with caution. Key events can easily contain sensitive information, such
* as passwords, credit card numbers, etc.
*/
bool recording_include_keys;
} guac_spice_settings;
/**
* Parses all given args, storing them in a newly-allocated settings object. If
* the args fail to parse, NULL is returned.
*
* @param user
* The user who submitted the given arguments while joining the
* connection.
*
* @param argc
* The number of arguments within the argv array.
*
* @param argv
* The values of all arguments provided by the user.
*
* @return
* A newly-allocated settings object which must be freed with
* guac_spice_settings_free() when no longer needed. If the arguments fail
* to parse, NULL is returned.
*/
guac_spice_settings* guac_spice_parse_args(guac_user* user,
int argc, const char** argv);
/**
* Frees the given guac_spice_settings object, having been previously allocated
* via guac_spice_parse_args().
*
* @param settings
* The settings object to free.
*/
void guac_spice_settings_free(guac_spice_settings* settings);
/**
* NULL-terminated array of accepted client args.
*/
extern const char* GUAC_SPICE_CLIENT_ARGS[];
#endif /* SPICE_SETTINGS_H */

View File

@ -0,0 +1,42 @@
/*
* 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 "common-ssh/sftp.h"
#include "sftp.h"
#include "spice.h"
#include <guacamole/client.h>
#include <guacamole/stream.h>
#include <guacamole/user.h>
int guac_spice_sftp_file_handler(guac_user* user, guac_stream* stream,
char* mimetype, char* filename) {
guac_client* client = user->client;
guac_spice_client* spice_client = (guac_spice_client*) client->data;
guac_common_ssh_sftp_filesystem* filesystem = spice_client->sftp_filesystem;
/* Handle file upload */
return guac_common_ssh_sftp_handle_file_stream(filesystem, user, stream,
mimetype, filename);
}

View File

@ -0,0 +1,34 @@
/*
* 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_SPICE_SFTP_H
#define GUAC_SPICE_SFTP_H
#include "config.h"
#include <guacamole/user.h>
/**
* Handles an incoming stream from a Guacamole "file" instruction, saving the
* contents of that stream to the file having the given name.
*/
guac_user_file_handler guac_spice_sftp_file_handler;
#endif /* SPICE_SFTP_H */

View File

@ -0,0 +1,360 @@
/*
* 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 SPICE_CONSTANTS_H
#define SPICE_CONSTANTS_H
/**
* The key used to store and retrieve Guacamole-related data from within the
* SPICE client structure.
*/
#define GUAC_SPICE_CLIENT_KEY "GUAC_SPICE"
/**
* The default identifier of the pimary/main display.
*/
#define GUAC_SPICE_DEFAULT_DISPLAY_ID 0
/**
* The TLS verification value from Guacamole Client that indicates that hostname
* verification should be done.
*/
#define GUAC_SPICE_PARAMETER_TLS_VERIFY_HOSTNAME "hostname"
/**
* The TLS verification value from Guacamole Client that indicates that public
* key verification should be performed.
*/
#define GUAC_SPICE_PARAMETER_TLS_VERIFY_PUBKEY "pubkey"
/**
* The TLS verification value from Guacamole Client that indicates that subject
* verification should be performed.
*/
#define GUAC_SPICE_PARAMETER_TLS_VERIFY_SUBJECT "subject"
/**
* The property within a SPICE client channel that indicates if the SPICE
* agent is connected.
*/
#define SPICE_PROPERTY_AGENT_CONNECTED "agent-connected"
/**
* The SPICE client property that defines CA certificates used to validate
* the TLS connection to the SPICE server.
*/
#define SPICE_PROPERTY_CA "ca"
/**
* The SPICE client property that defines a path on the server running guacd
* to the file containing the certificate authority certificates to use to
* validate the TLS connection to the SPICE server.
*/
#define SPICE_PROPERTY_CA_FILE "ca-file"
/**
* The property that the SPICE client uses to set the image cache size. If
* undefined a default of 0 will be used.
*/
#define SPICE_PROPERTY_CACHE_SIZE "cache-size"
/**
* The SPICE client channel property that stores the identifier of the channel.
*/
#define SPICE_PROPERTY_CHANNEL_ID "channel-id"
/**
* SPICE library property that determines whether or not the sockets are provided
* by the client.
*/
#define SPICE_PROPERTY_CLIENT_SOCKETS "client-sockets"
/**
* The property that tells the SPICE client the color depth to use when
* allocating new displays.
*/
#define SPICE_PROPERTY_COLOR_DEPTH "color-depth"
/**
* The property that tells the SPICE client to enable audio playback and
* recording. The SPICE client default is TRUE.
*/
#define SPICE_PROPERTY_ENABLE_AUDIO "enable-audio"
/**
* Property that enables or disables USB redirection.
*/
#define SPICE_PROPERTY_ENABLE_USBREDIR "enable-usbredir"
/**
* The property that contains the hostname, IP address, or URL of the SPICE
* server that the client should attempt to connect to.
*/
#define SPICE_PROPERTY_HOST "host"
/**
* A read-only property exposed by the SPICE client library indicating the
* current state of key modifiers - such as lock keys - on the server.
*/
#define SPICE_PROPERTY_KEY_MODIFIERS "key-modifiers"
/**
* The property that indicates the minimum latency for audio playback.
*/
#define SPICE_PROPERTY_MIN_LATENCY "min-latency"
/**
* The property used to toggle the playback and/or record
* mute status on the SPICE server.
*/
#define SPICE_PROPERTY_MUTE "mute"
/**
* The property used to get or set the number of audio playback and/or recording
* channels that will be available between the SPICE server and client.
*/
#define SPICE_PROPERTY_NUM_CHANNELS "nchannels"
/**
* The property used to tell the SPICE client the password to send on to the
* SPICE server for authentication.
*/
#define SPICE_PROPERTY_PASSWORD "password"
/**
* The property used to set the unencrypted communication port for communicating
* with the SPICE server.
*/
#define SPICE_PROPERTY_PORT "port"
/**
* The property that the SPICE client uses to set the proxy server that is used
* to connect to the SPICE server.
*/
#define SPICE_PROPERTY_PROXY "proxy"
/**
* The property used by the SPICE client to tell the server that the session
* should be read-only.
*/
#define SPICE_PROPERTY_READ_ONLY "read-only"
/**
* The property that the SPICE client uses to determine a local (to guacd)
* directory that will be shared with the SPICE server.
*/
#define SPICE_PROPERTY_SHARED_DIR "shared-dir"
/**
* The property that tells the SPICE client that the shared directory should be
* read-only to the SPICE server and should not allow writes.
*/
#define SPICE_PROPERTY_SHARED_DIR_RO "share-dir-ro"
/**
* The property within the SPICE client that is used to set the port used for
* secure, TLS-based communication with the SPICE server.
*/
#define SPICE_PROPERTY_TLS_PORT "tls-port"
/**
* The property that is used to set the username that the SPICE client will use
* to authenticate with the server.
*/
#define SPICE_PROPERTY_USERNAME "username"
/**
* The property that tells the SPICE client whether or not to verify the
* certificate presented by the SPICE server in TLS communications.
*/
#define SPICE_PROPERTY_VERIFY "verify"
/**
* The property used to get or set the playback and/or recording volume of audio
* on the SPICE server to the remote client.
*/
#define SPICE_PROPERTY_VOLUME "volume"
/**
* The signal sent by the SPICE client when a new channel is created.
*/
#define SPICE_SIGNAL_CHANNEL_NEW "channel-new"
/**
* The signal sent by the SPICE client when a channel is destroyed.
*/
#define SPICE_SIGNAL_CHANNEL_DESTROY "channel-destroy"
/**
* The signal sent by the SPICE client when an event occurs on a channel.
*/
#define SPICE_SIGNAL_CHANNEL_EVENT "channel-event"
/**
* A signal that indicates that the cursor should be hidden from the display
* area.
*/
#define SPICE_SIGNAL_CURSOR_HIDE "cursor-hide"
/**
* A signal that indicates a change in position of the cursor in the display
* area.
*/
#define SPICE_SIGNAL_CURSOR_MOVE "cursor-move"
/**
* A signal that indicates the cursor should be reset to its default context.
*/
#define SPICE_SIGNAL_CURSOR_RESET "cursor-reset"
/**
* A signal sent to modify cursor aspect and position within the display area.
*/
#define SPICE_SIGNAL_CURSOR_SET "cursor-set"
/**
* The signal sent by the SPICE client when the client is disconnected from
* the server.
*/
#define SPICE_SIGNAL_DISCONNECTED "disconnected"
/**
* The signal sent to indicate that a region of the display should be updated.
*/
#define SPICE_SIGNAL_DISPLAY_INVALIDATE "display-invalidate"
/**
* The signal that indicates that a display is ready to be exposed to the client.
*/
#define SPICE_SIGNAL_DISPLAY_MARK "display-mark"
/**
* The signal indicating when the primary display data/buffer is ready.
*/
#define SPICE_SIGNAL_DISPLAY_PRIMARY_CREATE "display-primary-create"
/**
* The signal indicating when the primary display surface should be freed and
* not made available anymore.
*/
#define SPICE_SIGNAL_DISPLAY_PRIMARY_DESTROY "display-primary-destroy"
/**
* The signal indicating that a rectangular region of he display is updated
* and should be redrawn.
*/
#define SPICE_SIGNAL_GL_DRAW "gl-draw"
/**
* The signal sent to indicate that the keyboard modifiers - such as lock keys -
* have changed and should be updated.
*/
#define SPICE_SIGNAL_INPUTS_MODIFIERS "inputs-modifiers"
/**
* The signal sent by the SPICE client when the connected status or capabilities
* of a channel change.
*/
#define SPICE_SIGNAL_MAIN_AGENT_UPDATE "main-agent-update"
/**
* Signal fired by the SPICE client when clipboard selection data is available.
*/
#define SPICE_SIGNAL_MAIN_CLIPBOARD_SELECTION "main-clipboard-selection"
/**
* A signal fired by the SPICE client when clipboard selection data is available
* from the guest, and of what type.
*/
#define SPICE_SIGNAL_MAIN_CLIPBOARD_SELECTION_GRAB "main-clipboard-selection-grab"
/**
* A signal fired by the SPICE client when clipboard selection data is no longer
* available from the guest.
*/
#define SPICE_SIGNAL_MAIN_CLIPBOARD_SELECTION_RELEASE "main-clipboard-selection-release"
/**
* A signal used to request clipboard data from the client.
*/
#define SPICE_SIGNAL_MAIN_CLIPBOARD_SELECTION_REQUEST "main-clipboard-selection-request"
/**
* A signal used to indicate that the mouse mode has changed.
*/
#define SPICE_SIGNAL_MAIN_MOUSE_UPDATE "main-mouse-update"
/**
* A signal sent by the SPICE client when the server has indicated that live
* migration has started.
*/
#define SPICE_SIGNAL_MIGRATION_STARTED "migration-started"
/**
* The signal sent by the SPICE client when a MM time discontinuity is
* detected.
*/
#define SPICE_SIGNAL_MM_TIME_RESET "mm-time-reset"
/**
* The signal fired by the SPICE client when a new file transfer task has been
* initiated.
*/
#define SPICE_SIGNAL_NEW_FILE_TRANSFER "new-file-transfer"
/**
* The signal fired when data is available to be played on the client, which
* contains a pointer to the data to be played.
*/
#define SPICE_SIGNAL_PLAYBACK_DATA "playback-data"
/**
* A signal sent when the server is requesting the current audio playback delay.
*/
#define SPICE_SIGNAL_PLAYBACK_GET_DELAY "playback-get-delay"
/**
* A signal sent when the server is notifying the client that audio playback
* should begin, which also contains characteristics of that audio data.
*/
#define SPICE_SIGNAL_PLAYBACK_START "plaback-start"
/**
* A signal sent when audio playback should cease.
*/
#define SPICE_SIGNAL_PLAYBACK_STOP "playback-stop"
/**
* A signal indicating that the SPICE server would like to capture audio data
* from the client, along with the required format of that data.
*/
#define SPICE_SIGNAL_RECORD_START "record-start"
/**
* A signal indicating that audio capture should cease.
*/
#define SPICE_SIGNAL_RECORD_STOP "record-stop"
/**
* The signal indicating that the SPICE server has gone to streaming mode.
*/
#define SPICE_SIGNAL_STREAMING_MODE "streaming-mode"
#endif /* SPICE_CONSTANTS_H */

View File

@ -0,0 +1,49 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef SPICE_DEFAULTS_H
#define SPICE_DEFAULTS_H
/**
* The default hostname to connect to if none is specified.
*/
#define SPICE_DEFAULT_HOST "localhost"
/**
* The default SPICE port number to connect to if none is specified.
*/
#define SPICE_DEFAULT_PORT "5900"
/**
* The default encodings to use for the SPICE clipboard.
*/
#define SPICE_DEFAULT_ENCODINGS "zrle ultra copyrect hextile zlib corre rre raw"
/**
* The default SFTP port to connect to if SFTP is enabled.
*/
#define SPICE_DEFAULT_SFTP_PORT "22"
/**
* The default root directory to limit SFTP access to.
*/
#define SPICE_DEFAULT_SFTP_ROOT "/"
#endif /* SPICE_DEFAULTS_H */

184
src/protocols/spice/spice.c Normal file
View File

@ -0,0 +1,184 @@
/*
* 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 "auth.h"
#include "client.h"
#include "common/clipboard.h"
#include "common/cursor.h"
#include "common/display.h"
#include "channels/audio.h"
#include "channels/clipboard.h"
#include "channels/cursor.h"
#include "channels/display.h"
#include "channels/file.h"
#include "log.h"
#include "settings.h"
#include "spice.h"
#include "spice-constants.h"
#ifdef ENABLE_COMMON_SSH
#include "common-ssh/sftp.h"
#include "common-ssh/ssh.h"
#include "sftp.h"
#endif
#include <glib/gmain.h>
#include <guacamole/client.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <guacamole/timestamp.h>
#include <spice-client-glib-2.0/spice-client.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
SpiceSession* guac_spice_get_session(guac_client* client) {
guac_client_log(client, GUAC_LOG_DEBUG, "Initializing new SPICE session.");
/* Set up the SPICE session and Guacamole client. */
guac_spice_client* spice_client = (guac_spice_client*) client->data;
guac_spice_settings* spice_settings = spice_client->settings;
/* Create a new SPICE client. */
SpiceSession* spice_session = spice_session_new();
guac_client_log(client, GUAC_LOG_DEBUG, "Registering new channel callback.");
/* Register a callback for handling new channel events. */
g_signal_connect(spice_session, SPICE_SIGNAL_CHANNEL_NEW,
G_CALLBACK(guac_spice_client_channel_handler), client);
guac_client_log(client, GUAC_LOG_DEBUG, "Setting up connection properties.");
guac_client_log(client, GUAC_LOG_DEBUG, "Setting up host/port.");
/* Set hostname and port */
g_object_set(spice_session, SPICE_PROPERTY_HOST, spice_settings->hostname, NULL);
guac_client_log(client, GUAC_LOG_DEBUG, "Connecting to host %s",
spice_settings->hostname);
if (spice_settings->tls) {
guac_client_log(client, GUAC_LOG_DEBUG, "Using TLS mode on port %s",
spice_settings->port);
g_object_set(spice_session,
SPICE_PROPERTY_TLS_PORT, spice_settings->port,
SPICE_PROPERTY_VERIFY, spice_settings->tls_verify,
NULL);
if (spice_settings->ca != NULL)
g_object_set(spice_session, SPICE_PROPERTY_CA, spice_settings->ca, NULL);
if (spice_settings->ca_file != NULL)
g_object_set(spice_session, SPICE_PROPERTY_CA_FILE, spice_settings->ca_file, NULL);
}
else {
guac_client_log(client, GUAC_LOG_DEBUG, "Using plaintext mode on port %s",
spice_settings->port);
g_object_set(spice_session,
SPICE_PROPERTY_PORT, spice_settings->port, NULL);
}
guac_client_log(client, GUAC_LOG_DEBUG, "Setting up keyboard layout: %s",
spice_settings->server_layout);
/* Load keymap into client */
spice_client->keyboard = guac_spice_keyboard_alloc(client,
spice_settings->server_layout);
guac_client_log(client, GUAC_LOG_DEBUG, "Finished setting properties.");
/* Return the configured session. */
return spice_session;
}
void* guac_spice_client_thread(void* data) {
guac_client* client = (guac_client*) data;
guac_spice_client* spice_client = (guac_spice_client*) client->data;
guac_spice_settings* settings = spice_client->settings;
spice_client->spice_mainloop = g_main_loop_new(NULL, false);
/* Attempt connection */
guac_client_log(client, GUAC_LOG_DEBUG, "Attempting initial connection to SPICE server.");
spice_client->spice_session = guac_spice_get_session(client);
int retries_remaining = settings->retries;
/* If unsuccessful, retry as many times as specified */
while (spice_client->spice_session == NULL && retries_remaining > 0) {
guac_client_log(client, GUAC_LOG_INFO,
"Connect failed. Waiting %ims before retrying...",
GUAC_SPICE_CONNECT_INTERVAL);
/* Wait for given interval then retry */
guac_timestamp_msleep(GUAC_SPICE_CONNECT_INTERVAL);
spice_client->spice_session = guac_spice_get_session(client);
retries_remaining--;
}
/* If the final connect attempt fails, return error */
if (spice_client->spice_session == NULL) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND,
"Unable to connect to SPICE server.");
return NULL;
}
guac_client_log(client, GUAC_LOG_DEBUG, "Configuration completed, flushing socket.");
guac_socket_flush(client->socket);
// guac_timestamp last_frame_end = guac_timestamp_current();
guac_client_log(client, GUAC_LOG_DEBUG, "Connection configuration finished, calling spice_session_connect.");
if(!spice_session_connect(spice_client->spice_session))
return NULL;
guac_client_log(client, GUAC_LOG_DEBUG, "Session connected, entering main loop.");
/* Handle messages from SPICE server while client is running */
while (client->state == GUAC_CLIENT_RUNNING) {
/* Run the main loop. */
g_main_loop_run(spice_client->spice_mainloop);
guac_client_log(client, GUAC_LOG_DEBUG, "Finished main loop.");
/* Wait for an error on the main channel. */
if (spice_client->main_channel != NULL
&& spice_channel_get_error(SPICE_CHANNEL(spice_client->main_channel)) != NULL)
break;
}
guac_client_log(client, GUAC_LOG_DEBUG, "Exited main loop, cleaning up.");
/* Kill client and finish connection */
if (spice_client->spice_session != NULL) {
guac_client_log(client, GUAC_LOG_DEBUG, "Cleaning up SPICE session.");
spice_session_disconnect(spice_client->spice_session);
g_object_unref(spice_client->spice_session);
spice_client->spice_session = NULL;
}
guac_client_stop(client);
guac_client_log(client, GUAC_LOG_INFO, "Internal SPICE client disconnected");
return NULL;
}

199
src/protocols/spice/spice.h Normal file
View File

@ -0,0 +1,199 @@
/*
* 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_SPICE_H
#define GUAC_SPICE_H
#include "config.h"
#include "common/clipboard.h"
#include "common/display.h"
#include "common/iconv.h"
#include "common/surface.h"
#include "keyboard.h"
#include "settings.h"
#include <guacamole/client.h>
#include <guacamole/layer.h>
#include <guacamole/recording.h>
#include <glib.h>
#include <spice-client-glib-2.0/spice-client.h>
#ifdef ENABLE_COMMON_SSH
#include "common-ssh/sftp.h"
#include "common-ssh/ssh.h"
#include "common-ssh/user.h"
#endif
#include <pthread.h>
/**
* SPICE-specific client data.
*/
typedef struct guac_spice_client {
/**
* The SPICE client thread.
*/
pthread_t client_thread;
/**
* The underlying SPICE session.
*/
SpiceSession* spice_session;
/**
* The main SPICE channel.
*/
SpiceMainChannel* main_channel;
/**
* The SPICE audio playback channel.
*/
SpicePlaybackChannel* playback_channel;
/**
* The SPICE audio recording/input channel.
*/
SpiceRecordChannel* record_channel;
/**
* The SPICE channel that handles the cursor display and events.
*/
SpiceCursorChannel* cursor_channel;
/**
* The SPICE channel that handles mouse and keyboard inputs.
*/
SpiceInputsChannel* inputs_channel;
/**
* Client settings, parsed from args.
*/
guac_spice_settings* settings;
/**
* The current display state.
*/
guac_common_display* display;
/**
* The SPICE display channel.
*/
SpiceDisplayChannel* spice_display;
/**
* The current state of the keyboard with respect to the RDP session.
*/
guac_spice_keyboard* keyboard;
/**
* The glib main loop
*/
GMainLoop* spice_mainloop;
/**
* Internal clipboard.
*/
guac_common_clipboard* clipboard;
#ifdef ENABLE_COMMON_SSH
/**
* The user and credentials used to authenticate for SFTP.
*/
guac_common_ssh_user* sftp_user;
/**
* The SSH session used for SFTP.
*/
guac_common_ssh_session* sftp_session;
/**
* An SFTP-based filesystem.
*/
guac_common_ssh_sftp_filesystem* sftp_filesystem;
#endif
/**
* The in-progress session recording, or NULL if no recording is in
* progress.
*/
guac_recording* recording;
/**
* Clipboard encoding-specific reader.
*/
guac_iconv_read* clipboard_reader;
/**
* Clipboard encoding-specific writer.
*/
guac_iconv_write* clipboard_writer;
/**
* Common attributes for locks.
*/
pthread_mutexattr_t attributes;
/**
* Lock which is used to synchronizes access to SPICE data structures
* between user input and client threads. It prevents input handlers
* from running when SPICE data structures are allocated or freed
* by the client thread.
*/
pthread_rwlock_t lock;
/**
* Lock which synchronizes the sending of each SPICE message, ensuring
* attempts to send SPICE messages never overlap.
*/
pthread_mutex_t message_lock;
} guac_spice_client;
/**
* Allocates a new rfbClient instance given the parameters stored within the
* client, returning NULL on failure.
*
* @param client
* The guac_client associated with the settings of the desired SPICE
* connection.
*
* @return
* A new rfbClient instance allocated and connected according to the
* parameters stored within the given client, or NULL if connecting to the
* SPICE server fails.
*/
SpiceSession* guac_spice_get_session(guac_client* client);
/**
* SPICE client thread. This thread initiates the SPICE connection and
* ultimately runs throughout the duration of the client, existing as a single
* instance, shared by all users.
*
* @param data
* The guac_client instance associated with the requested SPICE connection.
*
* @return
* Always NULL.
*/
void* guac_spice_client_thread(void* data);
#endif /* GUAC_SPICE_H */

124
src/protocols/spice/user.c Normal file
View File

@ -0,0 +1,124 @@
/*
* 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 "channels/clipboard.h"
#include "input.h"
#include "common/display.h"
#include "common/dot_cursor.h"
#include "common/pointer_cursor.h"
#include "user.h"
#include "sftp.h"
#include "spice.h"
#include <guacamole/argv.h>
#include <guacamole/audio.h>
#include <guacamole/client.h>
#include <guacamole/socket.h>
#include <guacamole/user.h>
#include <pthread.h>
int guac_spice_user_join_handler(guac_user* user, int argc, char** argv) {
guac_spice_client* spice_client = (guac_spice_client*) user->client->data;
/* Parse provided arguments */
guac_spice_settings* settings = guac_spice_parse_args(user,
argc, (const char**) argv);
/* Fail if settings cannot be parsed */
if (settings == NULL) {
guac_user_log(user, GUAC_LOG_INFO,
"Badly formatted client arguments.");
return 1;
}
/* Store settings at user level */
user->data = settings;
/* Connect via SPICE if owner */
if (user->owner) {
/* Store owner's settings at client level */
spice_client->settings = settings;
/* Start client thread */
if (pthread_create(&spice_client->client_thread, NULL, guac_spice_client_thread, user->client)) {
guac_user_log(user, GUAC_LOG_ERROR, "Unable to start SPICE client thread.");
return 1;
}
}
/* If not owner, synchronize with current state */
else {
/* Synchronize with current display */
guac_common_display_dup(spice_client->display, user, user->socket);
guac_socket_flush(user->socket);
}
/* Only handle events if not read-only */
if (!settings->read_only) {
/* General mouse/keyboard events */
user->mouse_handler = guac_spice_user_mouse_handler;
user->key_handler = guac_spice_user_key_handler;
/* Inbound (client to server) clipboard transfer */
if (!settings->disable_paste)
user->clipboard_handler = guac_spice_clipboard_handler;
/* Updates to connection parameters if we own the connection */
if (user->owner)
user->argv_handler = guac_argv_handler;
#ifdef ENABLE_COMMON_SSH
/* Set generic (non-filesystem) file upload handler */
if (settings->enable_sftp && !settings->sftp_disable_upload)
user->file_handler = guac_spice_sftp_file_handler;
#endif
}
return 0;
}
int guac_spice_user_leave_handler(guac_user* user) {
guac_spice_client* spice_client = (guac_spice_client*) user->client->data;
if (spice_client->display) {
/* Update shared cursor state */
guac_common_cursor_remove_user(spice_client->display->cursor, user);
}
/* Free settings if not owner (owner settings will be freed with client) */
if (!user->owner) {
guac_spice_settings* settings = (guac_spice_settings*) user->data;
guac_spice_settings_free(settings);
}
return 0;
}

View File

@ -0,0 +1,38 @@
/*
* 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_SPICE_USER_H
#define GUAC_SPICE_USER_H
#include "config.h"
#include <guacamole/user.h>
/**
* Handler for joining users.
*/
guac_user_join_handler guac_spice_user_join_handler;
/**
* Handler for leaving users.
*/
guac_user_leave_handler guac_spice_user_leave_handler;
#endif /* SPICE_USER_H */