cscg24-guacamole

CSCG 2024 Challenge 'Guacamole Mashup'
git clone https://git.sinitax.com/sinitax/cscg24-guacamole
Log | Files | Refs | sfeed.txt

rdpei.c (7461B)


      1/*
      2 * Licensed to the Apache Software Foundation (ASF) under one
      3 * or more contributor license agreements.  See the NOTICE file
      4 * distributed with this work for additional information
      5 * regarding copyright ownership.  The ASF licenses this file
      6 * to you under the Apache License, Version 2.0 (the
      7 * "License"); you may not use this file except in compliance
      8 * with the License.  You may obtain a copy of the License at
      9 *
     10 *   http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing,
     13 * software distributed under the License is distributed on an
     14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     15 * KIND, either express or implied.  See the License for the
     16 * specific language governing permissions and limitations
     17 * under the License.
     18 */
     19
     20#include "channels/rdpei.h"
     21#include "common/surface.h"
     22#include "plugins/channels.h"
     23#include "rdp.h"
     24#include "settings.h"
     25
     26#include <freerdp/client/rdpei.h>
     27#include <freerdp/freerdp.h>
     28#include <freerdp/event.h>
     29#include <guacamole/client.h>
     30#include <guacamole/mem.h>
     31#include <guacamole/timestamp.h>
     32
     33#include <stdlib.h>
     34#include <string.h>
     35
     36guac_rdp_rdpei* guac_rdp_rdpei_alloc(guac_client* client) {
     37
     38    guac_rdp_rdpei* rdpei = guac_mem_alloc(sizeof(guac_rdp_rdpei));
     39    rdpei->client = client;
     40
     41    /* Not yet connected */
     42    rdpei->rdpei = NULL;
     43
     44    /* No active touches */
     45    for (int i = 0; i < GUAC_RDP_RDPEI_MAX_TOUCHES; i++)
     46        rdpei->touch[i].active = 0;
     47
     48    return rdpei;
     49
     50}
     51
     52void guac_rdp_rdpei_free(guac_rdp_rdpei* rdpei) {
     53    guac_mem_free(rdpei);
     54}
     55
     56/**
     57 * Callback which associates handlers specific to Guacamole with the
     58 * RdpeiClientContext instance allocated by FreeRDP to deal with received
     59 * RDPEI (multi-touch input) messages.
     60 *
     61 * This function is called whenever a channel connects via the PubSub event
     62 * system within FreeRDP, but only has any effect if the connected channel is
     63 * the RDPEI channel. This specific callback is registered with the
     64 * PubSub system of the relevant rdpContext when guac_rdp_rdpei_load_plugin() is
     65 * called.
     66 *
     67 * @param context
     68 *     The rdpContext associated with the active RDP session.
     69 *
     70 * @param e
     71 *     Event-specific arguments, mainly the name of the channel, and a
     72 *     reference to the associated plugin loaded for that channel by FreeRDP.
     73 */
     74static void guac_rdp_rdpei_channel_connected(rdpContext* context,
     75        ChannelConnectedEventArgs* e) {
     76
     77    guac_client* client = ((rdp_freerdp_context*) context)->client;
     78    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
     79    guac_rdp_rdpei* guac_rdpei = rdp_client->rdpei;
     80
     81    /* Ignore connection event if it's not for the RDPEI channel */
     82    if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) != 0)
     83        return;
     84
     85    /* Store reference to the RDPEI plugin once it's connected */
     86    RdpeiClientContext* rdpei = (RdpeiClientContext*) e->pInterface;
     87    guac_rdpei->rdpei = rdpei;
     88
     89    /* Declare level of multi-touch support */
     90    guac_common_surface_set_multitouch(rdp_client->display->default_surface,
     91            GUAC_RDP_RDPEI_MAX_TOUCHES);
     92
     93    guac_client_log(client, GUAC_LOG_DEBUG, "RDPEI channel will be used for "
     94            "multi-touch support.");
     95
     96}
     97
     98/**
     99 * Callback which disassociates Guacamole from the RdpeiClientContext instance
    100 * that was originally allocated by FreeRDP and is about to be deallocated.
    101 *
    102 * This function is called whenever a channel disconnects via the PubSub event
    103 * system within FreeRDP, but only has any effect if the disconnected channel
    104 * is the RDPEI channel. This specific callback is registered with the PubSub
    105 * system of the relevant rdpContext when guac_rdp_rdpei_load_plugin() is
    106 * called.
    107 *
    108 * @param context
    109 *     The rdpContext associated with the active RDP session.
    110 *
    111 * @param e
    112 *     Event-specific arguments, mainly the name of the channel, and a
    113 *     reference to the associated plugin loaded for that channel by FreeRDP.
    114 */
    115static void guac_rdp_rdpei_channel_disconnected(rdpContext* context,
    116        ChannelDisconnectedEventArgs* e) {
    117
    118    guac_client* client = ((rdp_freerdp_context*) context)->client;
    119    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
    120    guac_rdp_rdpei* guac_rdpei = rdp_client->rdpei;
    121
    122    /* Ignore disconnection event if it's not for the RDPEI channel */
    123    if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) != 0)
    124        return;
    125
    126    /* Channel is no longer connected */
    127    guac_rdpei->rdpei = NULL;
    128
    129    guac_client_log(client, GUAC_LOG_DEBUG, "RDPDI channel disconnected.");
    130
    131}
    132
    133void guac_rdp_rdpei_load_plugin(rdpContext* context) {
    134
    135    /* Subscribe to and handle channel connected events */
    136    PubSub_SubscribeChannelConnected(context->pubSub,
    137        (pChannelConnectedEventHandler) guac_rdp_rdpei_channel_connected);
    138
    139    /* Subscribe to and handle channel disconnected events */
    140    PubSub_SubscribeChannelDisconnected(context->pubSub,
    141            (pChannelDisconnectedEventHandler) guac_rdp_rdpei_channel_disconnected);
    142
    143    /* Add "rdpei" channel */
    144    guac_freerdp_dynamic_channel_collection_add(context->settings, "rdpei", NULL);
    145
    146}
    147
    148int guac_rdp_rdpei_touch_update(guac_rdp_rdpei* rdpei, int id, int x, int y,
    149        double force) {
    150
    151    guac_client* client = rdpei->client;
    152    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
    153
    154    int contact_id; /* Ignored */
    155
    156    /* Track touches only if channel is connected */
    157    RdpeiClientContext* context = rdpei->rdpei;
    158    if (context == NULL)
    159        return 1;
    160
    161    /* Locate active touch having provided ID */
    162    guac_rdp_rdpei_touch* touch = NULL;
    163    for (int i = 0; i < GUAC_RDP_RDPEI_MAX_TOUCHES; i++) {
    164        if (rdpei->touch[i].active && rdpei->touch[i].id == id) {
    165            touch = &rdpei->touch[i];
    166            break;
    167        }
    168    }
    169
    170    /* If no such touch exists, add it */
    171    if (touch == NULL) {
    172        for (int i = 0; i < GUAC_RDP_RDPEI_MAX_TOUCHES; i++) {
    173            if (!rdpei->touch[i].active) {
    174                touch = &rdpei->touch[i];
    175                touch->id = id;
    176                break;
    177            }
    178        }
    179    }
    180
    181    /* If the touch couldn't be added, we're already at maximum touch capacity.
    182     * Drop the event. */
    183    if (touch == NULL)
    184        return 1;
    185
    186    /* Signal the end of an established touch if touch force has become zero
    187     * (this should be a safe comparison, as zero has an exact representation
    188     * in floating point, and the client side will use an exact value to
    189     * represent the absence of a touch) */
    190    if (force == 0.0) {
    191
    192        /* Ignore release of touches that we aren't tracking */
    193        if (!touch->active)
    194            return 1;
    195
    196        pthread_mutex_lock(&(rdp_client->message_lock));
    197        context->TouchEnd(context, id, x, y, &contact_id);
    198        pthread_mutex_unlock(&(rdp_client->message_lock));
    199
    200        touch->active = 0;
    201
    202    }
    203
    204    /* Signal the start of a touch if this is the first we've seen it */
    205    else if (!touch->active) {
    206
    207        pthread_mutex_lock(&(rdp_client->message_lock));
    208        context->TouchBegin(context, id, x, y, &contact_id);
    209        pthread_mutex_unlock(&(rdp_client->message_lock));
    210
    211        touch->active = 1;
    212
    213    }
    214
    215    /* Established touches need only be updated */
    216    else {
    217        pthread_mutex_lock(&(rdp_client->message_lock));
    218        context->TouchUpdate(context, id, x, y, &contact_id);
    219        pthread_mutex_unlock(&(rdp_client->message_lock));
    220    }
    221
    222    return 0;
    223
    224}
    225