2021-02-06 14:56:17 -08:00
|
|
|
/*
|
|
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
|
|
* or more contributor license agreements. See the NOTICE file
|
|
|
|
* distributed with this work for additional information
|
|
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
|
|
* to you under the Apache License, Version 2.0 (the
|
|
|
|
* "License"); you may not use this file except in compliance
|
|
|
|
* with the License. You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing,
|
|
|
|
* software distributed under the License is distributed on an
|
|
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
|
|
* KIND, either express or implied. See the License for the
|
|
|
|
* specific language governing permissions and limitations
|
|
|
|
* under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "channels/rdpei.h"
|
|
|
|
#include "common/surface.h"
|
|
|
|
#include "plugins/channels.h"
|
|
|
|
#include "rdp.h"
|
|
|
|
#include "settings.h"
|
|
|
|
|
|
|
|
#include <freerdp/client/rdpei.h>
|
|
|
|
#include <freerdp/freerdp.h>
|
|
|
|
#include <freerdp/event.h>
|
|
|
|
#include <guacamole/client.h>
|
|
|
|
#include <guacamole/timestamp.h>
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2021-04-07 17:13:48 -07:00
|
|
|
guac_rdp_rdpei* guac_rdp_rdpei_alloc(guac_client* client) {
|
2021-02-06 14:56:17 -08:00
|
|
|
|
|
|
|
guac_rdp_rdpei* rdpei = malloc(sizeof(guac_rdp_rdpei));
|
2021-04-07 17:13:48 -07:00
|
|
|
rdpei->client = client;
|
2021-02-06 14:56:17 -08:00
|
|
|
|
|
|
|
/* Not yet connected */
|
|
|
|
rdpei->rdpei = NULL;
|
|
|
|
|
|
|
|
/* No active touches */
|
|
|
|
for (int i = 0; i < GUAC_RDP_RDPEI_MAX_TOUCHES; i++)
|
|
|
|
rdpei->touch[i].active = 0;
|
|
|
|
|
|
|
|
return rdpei;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void guac_rdp_rdpei_free(guac_rdp_rdpei* rdpei) {
|
|
|
|
free(rdpei);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Callback which associates handlers specific to Guacamole with the
|
|
|
|
* RdpeiClientContext instance allocated by FreeRDP to deal with received
|
|
|
|
* RDPEI (multi-touch input) messages.
|
|
|
|
*
|
|
|
|
* This function is called whenever a channel connects via the PubSub event
|
|
|
|
* system within FreeRDP, but only has any effect if the connected channel is
|
|
|
|
* the RDPEI channel. This specific callback is registered with the
|
|
|
|
* PubSub system of the relevant rdpContext when guac_rdp_rdpei_load_plugin() is
|
|
|
|
* called.
|
|
|
|
*
|
|
|
|
* @param context
|
|
|
|
* The rdpContext associated with the active RDP session.
|
|
|
|
*
|
|
|
|
* @param e
|
|
|
|
* Event-specific arguments, mainly the name of the channel, and a
|
|
|
|
* reference to the associated plugin loaded for that channel by FreeRDP.
|
|
|
|
*/
|
|
|
|
static void guac_rdp_rdpei_channel_connected(rdpContext* context,
|
|
|
|
ChannelConnectedEventArgs* e) {
|
|
|
|
|
|
|
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
|
|
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
|
|
|
guac_rdp_rdpei* guac_rdpei = rdp_client->rdpei;
|
|
|
|
|
|
|
|
/* Ignore connection event if it's not for the RDPEI channel */
|
|
|
|
if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) != 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Store reference to the RDPEI plugin once it's connected */
|
|
|
|
RdpeiClientContext* rdpei = (RdpeiClientContext*) e->pInterface;
|
|
|
|
guac_rdpei->rdpei = rdpei;
|
|
|
|
|
|
|
|
/* Declare level of multi-touch support */
|
|
|
|
guac_common_surface_set_multitouch(rdp_client->display->default_surface,
|
|
|
|
GUAC_RDP_RDPEI_MAX_TOUCHES);
|
|
|
|
|
|
|
|
guac_client_log(client, GUAC_LOG_DEBUG, "RDPEI channel will be used for "
|
|
|
|
"multi-touch support.");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-07-28 15:50:13 -07:00
|
|
|
/**
|
|
|
|
* Callback which disassociates Guacamole from the RdpeiClientContext instance
|
|
|
|
* that was originally allocated by FreeRDP and is about to be deallocated.
|
|
|
|
*
|
|
|
|
* This function is called whenever a channel disconnects via the PubSub event
|
|
|
|
* system within FreeRDP, but only has any effect if the disconnected channel
|
|
|
|
* is the RDPEI channel. This specific callback is registered with the PubSub
|
|
|
|
* system of the relevant rdpContext when guac_rdp_rdpei_load_plugin() is
|
|
|
|
* called.
|
|
|
|
*
|
|
|
|
* @param context
|
|
|
|
* The rdpContext associated with the active RDP session.
|
|
|
|
*
|
|
|
|
* @param e
|
|
|
|
* Event-specific arguments, mainly the name of the channel, and a
|
|
|
|
* reference to the associated plugin loaded for that channel by FreeRDP.
|
|
|
|
*/
|
|
|
|
static void guac_rdp_rdpei_channel_disconnected(rdpContext* context,
|
|
|
|
ChannelDisconnectedEventArgs* e) {
|
|
|
|
|
|
|
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
|
|
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
|
|
|
guac_rdp_rdpei* guac_rdpei = rdp_client->rdpei;
|
|
|
|
|
|
|
|
/* Ignore disconnection event if it's not for the RDPEI channel */
|
|
|
|
if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) != 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Channel is no longer connected */
|
|
|
|
guac_rdpei->rdpei = NULL;
|
|
|
|
|
|
|
|
guac_client_log(client, GUAC_LOG_DEBUG, "RDPDI channel disconnected.");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-02-06 14:56:17 -08:00
|
|
|
void guac_rdp_rdpei_load_plugin(rdpContext* context) {
|
|
|
|
|
|
|
|
/* Subscribe to and handle channel connected events */
|
|
|
|
PubSub_SubscribeChannelConnected(context->pubSub,
|
|
|
|
(pChannelConnectedEventHandler) guac_rdp_rdpei_channel_connected);
|
|
|
|
|
2021-07-28 15:50:13 -07:00
|
|
|
/* Subscribe to and handle channel disconnected events */
|
|
|
|
PubSub_SubscribeChannelDisconnected(context->pubSub,
|
|
|
|
(pChannelDisconnectedEventHandler) guac_rdp_rdpei_channel_disconnected);
|
|
|
|
|
2021-02-06 14:56:17 -08:00
|
|
|
/* Add "rdpei" channel */
|
|
|
|
guac_freerdp_dynamic_channel_collection_add(context->settings, "rdpei", NULL);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int guac_rdp_rdpei_touch_update(guac_rdp_rdpei* rdpei, int id, int x, int y,
|
|
|
|
double force) {
|
|
|
|
|
2021-04-07 17:13:48 -07:00
|
|
|
guac_client* client = rdpei->client;
|
|
|
|
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
|
|
|
|
2021-02-06 14:56:17 -08:00
|
|
|
int contact_id; /* Ignored */
|
|
|
|
|
|
|
|
/* Track touches only if channel is connected */
|
|
|
|
RdpeiClientContext* context = rdpei->rdpei;
|
|
|
|
if (context == NULL)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Locate active touch having provided ID */
|
|
|
|
guac_rdp_rdpei_touch* touch = NULL;
|
|
|
|
for (int i = 0; i < GUAC_RDP_RDPEI_MAX_TOUCHES; i++) {
|
|
|
|
if (rdpei->touch[i].active && rdpei->touch[i].id == id) {
|
|
|
|
touch = &rdpei->touch[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If no such touch exists, add it */
|
|
|
|
if (touch == NULL) {
|
|
|
|
for (int i = 0; i < GUAC_RDP_RDPEI_MAX_TOUCHES; i++) {
|
|
|
|
if (!rdpei->touch[i].active) {
|
|
|
|
touch = &rdpei->touch[i];
|
|
|
|
touch->id = id;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the touch couldn't be added, we're already at maximum touch capacity.
|
|
|
|
* Drop the event. */
|
|
|
|
if (touch == NULL)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Signal the end of an established touch if touch force has become zero
|
|
|
|
* (this should be a safe comparison, as zero has an exact representation
|
|
|
|
* in floating point, and the client side will use an exact value to
|
|
|
|
* represent the absence of a touch) */
|
|
|
|
if (force == 0.0) {
|
|
|
|
|
|
|
|
/* Ignore release of touches that we aren't tracking */
|
|
|
|
if (!touch->active)
|
|
|
|
return 1;
|
|
|
|
|
2021-04-07 17:13:48 -07:00
|
|
|
pthread_mutex_lock(&(rdp_client->message_lock));
|
2021-02-06 14:56:17 -08:00
|
|
|
context->TouchEnd(context, id, x, y, &contact_id);
|
2021-04-07 17:13:48 -07:00
|
|
|
pthread_mutex_unlock(&(rdp_client->message_lock));
|
|
|
|
|
2021-02-06 14:56:17 -08:00
|
|
|
touch->active = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Signal the start of a touch if this is the first we've seen it */
|
|
|
|
else if (!touch->active) {
|
2021-04-07 17:13:48 -07:00
|
|
|
|
|
|
|
pthread_mutex_lock(&(rdp_client->message_lock));
|
2021-02-06 14:56:17 -08:00
|
|
|
context->TouchBegin(context, id, x, y, &contact_id);
|
2021-04-07 17:13:48 -07:00
|
|
|
pthread_mutex_unlock(&(rdp_client->message_lock));
|
|
|
|
|
2021-02-06 14:56:17 -08:00
|
|
|
touch->active = 1;
|
2021-04-07 17:13:48 -07:00
|
|
|
|
2021-02-06 14:56:17 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Established touches need only be updated */
|
2021-04-07 17:13:48 -07:00
|
|
|
else {
|
|
|
|
pthread_mutex_lock(&(rdp_client->message_lock));
|
2021-02-06 14:56:17 -08:00
|
|
|
context->TouchUpdate(context, id, x, y, &contact_id);
|
2021-04-07 17:13:48 -07:00
|
|
|
pthread_mutex_unlock(&(rdp_client->message_lock));
|
|
|
|
}
|
2021-02-06 14:56:17 -08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|