mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-11-12 14:57:20 +00:00
740 lines
21 KiB
C
740 lines
21 KiB
C
/**
|
|
* Looking Glass
|
|
* Copyright © 2017-2026 The Looking Glass Authors
|
|
* https://looking-glass.io
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
* Software Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "network.h"
|
|
#include "connection.h"
|
|
#include "control.h"
|
|
#include "cursor.h"
|
|
#include "input.h"
|
|
#include "known_hosts.h"
|
|
#include "recovery.h"
|
|
|
|
#include "app.h"
|
|
#include "interface/renderer.h"
|
|
|
|
#include "common/debug.h"
|
|
#include "common/event.h"
|
|
#include "common/locking.h"
|
|
#include <LGProtocol/NetworkProtocol.h>
|
|
#include <LGProtocol/NetworkServices.h>
|
|
#include "common/option.h"
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define NETWORK_ADDRESS_MAX 255U
|
|
#define NETWORK_ENDPOINT_TEXT_SIZE (NETWORK_ADDRESS_MAX + 9U)
|
|
#define NETWORK_MAX_PAYLOAD \
|
|
(LG_NET_CURSOR_SHAPE_HEADER_WIRE_SIZE + LG_NET_CURSOR_MAX_SHAPE_LENGTH)
|
|
#define NETWORK_MAX_DATAGRAM 1136U
|
|
|
|
static const LGNetCapability NETWORK_CAPABILITIES[] =
|
|
{
|
|
{
|
|
.service = LG_NET_SERVICE_RECOVERY,
|
|
.versionMin = LG_NET_RECOVERY_VERSION_MIN,
|
|
.versionMax = LG_NET_RECOVERY_VERSION_CURRENT,
|
|
.flags = LG_NET_CAPABILITY_RELIABLE,
|
|
.features = 0,
|
|
.maxPayloadLength = NETWORK_MAX_PAYLOAD,
|
|
.maxDatagramPayload = 0,
|
|
.maxBidirectionalStreams = 0,
|
|
.maxUnidirectionalStreams = 0,
|
|
},
|
|
{
|
|
.service = LG_NET_SERVICE_CURSOR,
|
|
.versionMin = LG_NET_CURSOR_VERSION_MIN,
|
|
.versionMax = LG_NET_CURSOR_VERSION_CURRENT,
|
|
.flags = LG_NET_CAPABILITY_RELIABLE |
|
|
LG_NET_CAPABILITY_DATAGRAM,
|
|
.features = 0,
|
|
.maxPayloadLength = NETWORK_MAX_PAYLOAD,
|
|
.maxDatagramPayload = NETWORK_MAX_DATAGRAM,
|
|
.maxBidirectionalStreams = 0,
|
|
.maxUnidirectionalStreams = 0,
|
|
},
|
|
{
|
|
.service = LG_NET_SERVICE_INPUT,
|
|
.versionMin = LG_NET_INPUT_VERSION_MIN,
|
|
.versionMax = LG_NET_INPUT_VERSION_CURRENT,
|
|
.flags = LG_NET_CAPABILITY_RELIABLE |
|
|
LG_NET_CAPABILITY_DATAGRAM,
|
|
.features = 0,
|
|
.maxPayloadLength = NETWORK_MAX_PAYLOAD,
|
|
.maxDatagramPayload = NETWORK_MAX_DATAGRAM,
|
|
.maxBidirectionalStreams = 0,
|
|
.maxUnidirectionalStreams = 0,
|
|
},
|
|
{
|
|
.service = LG_NET_SERVICE_CONTROL,
|
|
.versionMin = LG_NET_CONTROL_VERSION_MIN,
|
|
.versionMax = LG_NET_CONTROL_VERSION_CURRENT,
|
|
.flags = LG_NET_CAPABILITY_RELIABLE,
|
|
.features = 0,
|
|
.maxPayloadLength = NETWORK_MAX_PAYLOAD,
|
|
.maxDatagramPayload = 0,
|
|
.maxBidirectionalStreams = 0,
|
|
.maxUnidirectionalStreams = 0,
|
|
},
|
|
};
|
|
|
|
struct NetworkTrustPrompt
|
|
{
|
|
MsgBoxHandle message;
|
|
NetworkTrustDecisionFn decision;
|
|
void * decisionOpaque;
|
|
};
|
|
|
|
struct LG_Transport
|
|
{
|
|
NetworkConnection * connection;
|
|
NetworkControl * control;
|
|
NetworkCursor * cursor;
|
|
NetworkInput * input;
|
|
NetworkRecovery * recovery;
|
|
NetworkKnownHosts knownHosts;
|
|
LGEvent * frameEvent;
|
|
LGEvent * promptIdle;
|
|
|
|
LG_Lock callbackLock;
|
|
LG_Lock stateLock;
|
|
LG_Lock promptLock;
|
|
|
|
LG_VideoStatus videoStatus;
|
|
LG_VideoStatusFn videoStatusFn;
|
|
void * videoStatusOpaque;
|
|
|
|
bool rendererAttached;
|
|
bool promptCallbackActive;
|
|
struct NetworkTrustPrompt prompt;
|
|
};
|
|
|
|
static bool networkAddressValidate(struct Option * option,
|
|
const char ** error)
|
|
{
|
|
const char * address = option->value.x_string;
|
|
if (address && *address && strlen(address) <= NETWORK_ADDRESS_MAX)
|
|
{
|
|
for (const unsigned char * c = (const unsigned char *)address; *c; ++c)
|
|
if (*c < 0x20U || *c == 0x7fU)
|
|
goto invalid;
|
|
return true;
|
|
}
|
|
|
|
invalid:
|
|
*error = "The network address must be 1-255 characters without controls";
|
|
return false;
|
|
}
|
|
|
|
static bool networkPortValidate(struct Option * option, const char ** error)
|
|
{
|
|
if (option->value.x_int > 0 && option->value.x_int <= UINT16_MAX)
|
|
return true;
|
|
|
|
*error = "The network port must be between 1 and 65535";
|
|
return false;
|
|
}
|
|
|
|
static void networkSetup(void)
|
|
{
|
|
static struct Option options[] =
|
|
{
|
|
{
|
|
.module = "network",
|
|
.name = "address",
|
|
.description = "Network server address",
|
|
.type = OPTION_TYPE_STRING,
|
|
.value.x_string = "127.0.0.1",
|
|
.validator = networkAddressValidate,
|
|
},
|
|
{
|
|
.module = "network",
|
|
.name = "port",
|
|
.description = "Network server UDP port",
|
|
.type = OPTION_TYPE_INT,
|
|
.value.x_int = LG_NET_DEFAULT_PORT,
|
|
.validator = networkPortValidate,
|
|
},
|
|
{
|
|
.module = "network",
|
|
.name = "password",
|
|
.description = "Network password (empty uses no authentication)",
|
|
.type = OPTION_TYPE_STRING,
|
|
.sensitive = true,
|
|
.value.x_string = NULL,
|
|
},
|
|
{
|
|
.module = "network",
|
|
.name = "forgetCertificate",
|
|
.description = "Forget the saved certificate for this server",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = false,
|
|
},
|
|
{0}
|
|
};
|
|
|
|
option_register(options);
|
|
}
|
|
|
|
static void networkTrustDecision(bool trusted, void * opaque)
|
|
{
|
|
LG_Transport * this = opaque;
|
|
NetworkTrustDecisionFn decision;
|
|
void * decisionOpaque;
|
|
|
|
LG_LOCK(this->promptLock);
|
|
decision = this->prompt.decision;
|
|
decisionOpaque = this->prompt.decisionOpaque;
|
|
this->prompt.message = NULL;
|
|
this->prompt.decision = NULL;
|
|
this->prompt.decisionOpaque = NULL;
|
|
if (decision)
|
|
{
|
|
this->promptCallbackActive = true;
|
|
lgResetEvent(this->promptIdle);
|
|
}
|
|
LG_UNLOCK(this->promptLock);
|
|
|
|
if (decision)
|
|
decision(decisionOpaque, trusted);
|
|
|
|
LG_LOCK(this->promptLock);
|
|
this->promptCallbackActive = false;
|
|
lgSignalEvent(this->promptIdle);
|
|
LG_UNLOCK(this->promptLock);
|
|
}
|
|
|
|
static bool networkTrustPrompt(void * opaque, const char * endpoint,
|
|
const char * fingerprint, NetworkTrustDecisionFn decision,
|
|
void * decisionOpaque)
|
|
{
|
|
LG_Transport * this = opaque;
|
|
|
|
LG_LOCK(this->promptLock);
|
|
if (this->prompt.message || this->prompt.decision ||
|
|
this->promptCallbackActive)
|
|
{
|
|
LG_UNLOCK(this->promptLock);
|
|
return false;
|
|
}
|
|
|
|
this->prompt.decision = decision;
|
|
this->prompt.decisionOpaque = decisionOpaque;
|
|
this->prompt.message = app_confirmMsgBox(
|
|
"Trust network server?", networkTrustDecision, this,
|
|
"The identity of %s has not been seen before.\n\n"
|
|
"SHA-256 fingerprint:\n%s\n\n"
|
|
"Trust and remember this server?", endpoint, fingerprint);
|
|
const bool queued = this->prompt.message != NULL;
|
|
if (!queued)
|
|
{
|
|
this->prompt.decision = NULL;
|
|
this->prompt.decisionOpaque = NULL;
|
|
}
|
|
LG_UNLOCK(this->promptLock);
|
|
return queued;
|
|
}
|
|
|
|
static void networkCancelTrustPrompt(void * opaque)
|
|
{
|
|
LG_Transport * this = opaque;
|
|
MsgBoxHandle message;
|
|
|
|
LG_LOCK(this->promptLock);
|
|
message = this->prompt.message;
|
|
this->prompt.message = NULL;
|
|
this->prompt.decision = NULL;
|
|
this->prompt.decisionOpaque = NULL;
|
|
LG_UNLOCK(this->promptLock);
|
|
|
|
if (message)
|
|
app_msgBoxClose(message);
|
|
lgWaitEvent(this->promptIdle, TIMEOUT_INFINITE);
|
|
LG_LOCK(this->promptLock);
|
|
LG_UNLOCK(this->promptLock);
|
|
}
|
|
|
|
static void networkNotifyVideoStatus(LG_Transport * this)
|
|
{
|
|
LG_VideoStatusFn callback;
|
|
void * opaque;
|
|
LG_VideoStatus status;
|
|
|
|
LG_LOCK(this->callbackLock);
|
|
LG_LOCK(this->stateLock);
|
|
callback = this->videoStatusFn;
|
|
opaque = this->videoStatusOpaque;
|
|
status = this->videoStatus;
|
|
LG_UNLOCK(this->stateLock);
|
|
|
|
if (callback)
|
|
callback(opaque, &status);
|
|
LG_UNLOCK(this->callbackLock);
|
|
}
|
|
|
|
static uint64_t networkNextEpoch(uint64_t epoch)
|
|
{
|
|
++epoch;
|
|
return epoch ? epoch : 1U;
|
|
}
|
|
|
|
static void networkSetUnavailable(LG_VideoComponentStatus * status,
|
|
LG_TransportStatus reason)
|
|
{
|
|
if (status->available)
|
|
status->epoch = networkNextEpoch(status->epoch);
|
|
status->available = false;
|
|
status->reason = reason;
|
|
}
|
|
|
|
static void networkSetAvailable(LG_VideoComponentStatus * status)
|
|
{
|
|
if (!status->available)
|
|
status->epoch = networkNextEpoch(status->epoch);
|
|
status->available = true;
|
|
status->reason = LG_TRANSPORT_OK;
|
|
}
|
|
|
|
static void networkConnectionState(void * opaque, bool connected,
|
|
LG_TransportStatus reason)
|
|
{
|
|
LG_Transport * this = opaque;
|
|
const LG_TransportStatus status = connected ?
|
|
LG_TRANSPORT_UNAVAILABLE : reason;
|
|
|
|
networkInputConnectionState(this->input, connected);
|
|
networkControlConnectionState(this->control, connected);
|
|
networkRecoveryConnectionState(this->recovery, connected);
|
|
const bool cursorAvailable =
|
|
networkCursorConnectionState(this->cursor, connected);
|
|
|
|
LG_LOCK(this->stateLock);
|
|
networkSetUnavailable(&this->videoStatus.frame, status);
|
|
if (cursorAvailable)
|
|
networkSetAvailable(&this->videoStatus.pointer);
|
|
else
|
|
networkSetUnavailable(&this->videoStatus.pointer, status);
|
|
LG_UNLOCK(this->stateLock);
|
|
|
|
lgSignalEvent(this->frameEvent);
|
|
networkNotifyVideoStatus(this);
|
|
}
|
|
|
|
static void networkPacket(void * opaque, const LGNetPacketView * packet,
|
|
bool datagram)
|
|
{
|
|
LG_Transport * this = opaque;
|
|
bool valid;
|
|
switch (packet->envelope.service)
|
|
{
|
|
case LG_NET_SERVICE_RECOVERY:
|
|
valid = networkRecoveryPacket(this->recovery, packet, datagram);
|
|
break;
|
|
case LG_NET_SERVICE_CURSOR:
|
|
valid = networkCursorPacket(this->cursor, packet, datagram);
|
|
break;
|
|
case LG_NET_SERVICE_INPUT:
|
|
valid = networkInputPacket(this->input, packet, datagram);
|
|
break;
|
|
case LG_NET_SERVICE_CONTROL:
|
|
valid = networkControlPacket(this->control, packet, datagram);
|
|
break;
|
|
default:
|
|
valid = false;
|
|
break;
|
|
}
|
|
|
|
if (!valid)
|
|
{
|
|
DEBUG_ERROR("Invalid network service %u message %u",
|
|
(unsigned)packet->envelope.service,
|
|
(unsigned)packet->envelope.messageType);
|
|
networkConnectionCancel(this->connection);
|
|
}
|
|
}
|
|
|
|
static bool networkCreate(LG_Transport ** result)
|
|
{
|
|
if (!result)
|
|
return false;
|
|
|
|
LG_Transport * this = calloc(1, sizeof(*this));
|
|
if (!this)
|
|
return false;
|
|
|
|
LG_LOCK_INIT(this->callbackLock);
|
|
LG_LOCK_INIT(this->stateLock);
|
|
LG_LOCK_INIT(this->promptLock);
|
|
|
|
this->videoStatus.frame.epoch = 1;
|
|
this->videoStatus.frame.reason = LG_TRANSPORT_DISCONNECTED;
|
|
this->videoStatus.pointer.epoch = 1;
|
|
this->videoStatus.pointer.reason = LG_TRANSPORT_DISCONNECTED;
|
|
if (!networkKnownHostsInit(&this->knownHosts))
|
|
goto fail;
|
|
|
|
this->frameEvent = lgCreateEvent(true, 0);
|
|
this->promptIdle = lgCreateEvent(false, 0);
|
|
if (!this->frameEvent || !this->promptIdle ||
|
|
!lgSignalEvent(this->promptIdle))
|
|
goto fail;
|
|
|
|
const char * address = option_get_string("network", "address");
|
|
const uint16_t port = (uint16_t)option_get_int("network", "port");
|
|
char endpoint[NETWORK_ENDPOINT_TEXT_SIZE];
|
|
if (!address)
|
|
goto fail;
|
|
snprintf(endpoint, sizeof(endpoint), "[%s]:%u", address, port);
|
|
if (option_get_bool("network", "forgetCertificate") &&
|
|
!networkKnownHostsForget(&this->knownHosts, endpoint))
|
|
{
|
|
DEBUG_ERROR("Failed to forget the network server certificate");
|
|
goto fail;
|
|
}
|
|
|
|
const NetworkConnectionParams params =
|
|
{
|
|
.address = address,
|
|
.port = port,
|
|
.password = option_get_string("network", "password"),
|
|
.knownHosts = &this->knownHosts,
|
|
.trustPrompt = networkTrustPrompt,
|
|
.trustCancel = networkCancelTrustPrompt,
|
|
.packet = networkPacket,
|
|
.state = networkConnectionState,
|
|
.capabilities = NETWORK_CAPABILITIES,
|
|
.capabilityCount = sizeof(NETWORK_CAPABILITIES) /
|
|
sizeof(NETWORK_CAPABILITIES[0]),
|
|
.opaque = this,
|
|
};
|
|
if (!networkConnectionCreate(&this->connection, ¶ms))
|
|
goto fail;
|
|
if (!networkControlCreate(&this->control, this->connection) ||
|
|
!networkCursorCreate(&this->cursor, this->connection) ||
|
|
!networkInputCreate(&this->input, this->connection) ||
|
|
!networkRecoveryCreate(&this->recovery, this->connection))
|
|
goto fail;
|
|
|
|
*result = this;
|
|
return true;
|
|
|
|
fail:
|
|
networkRecoveryDestroy(&this->recovery);
|
|
networkInputDestroy(&this->input);
|
|
networkCursorDestroy(&this->cursor);
|
|
networkControlDestroy(&this->control);
|
|
networkConnectionDestroy(&this->connection);
|
|
if (this->frameEvent)
|
|
lgFreeEvent(this->frameEvent);
|
|
if (this->promptIdle)
|
|
lgFreeEvent(this->promptIdle);
|
|
networkKnownHostsFree(&this->knownHosts);
|
|
LG_LOCK_FREE(this->promptLock);
|
|
LG_LOCK_FREE(this->stateLock);
|
|
LG_LOCK_FREE(this->callbackLock);
|
|
free(this);
|
|
return false;
|
|
}
|
|
|
|
static void networkDisconnect(LG_Transport * this);
|
|
|
|
static void networkDestroy(LG_Transport ** transport)
|
|
{
|
|
if (!transport || !*transport)
|
|
return;
|
|
|
|
LG_Transport * this = *transport;
|
|
networkDisconnect(this);
|
|
|
|
LG_LOCK(this->callbackLock);
|
|
LG_LOCK(this->stateLock);
|
|
this->videoStatusFn = NULL;
|
|
this->videoStatusOpaque = NULL;
|
|
LG_UNLOCK(this->stateLock);
|
|
LG_UNLOCK(this->callbackLock);
|
|
|
|
networkRecoveryDestroy(&this->recovery);
|
|
networkInputDestroy(&this->input);
|
|
networkCursorDestroy(&this->cursor);
|
|
networkControlDestroy(&this->control);
|
|
networkConnectionDestroy(&this->connection);
|
|
lgFreeEvent(this->frameEvent);
|
|
lgFreeEvent(this->promptIdle);
|
|
networkKnownHostsFree(&this->knownHosts);
|
|
LG_LOCK_FREE(this->promptLock);
|
|
LG_LOCK_FREE(this->stateLock);
|
|
LG_LOCK_FREE(this->callbackLock);
|
|
free(this);
|
|
*transport = NULL;
|
|
}
|
|
|
|
static LG_TransportStatus networkConnectCancellable(LG_Transport * this,
|
|
LG_TransportSession * session, LG_TransportCancelledFn cancelled,
|
|
void * opaque)
|
|
{
|
|
return networkConnectionConnect(
|
|
this->connection, session, cancelled, opaque);
|
|
}
|
|
|
|
static LG_TransportStatus networkConnect(LG_Transport * this,
|
|
LG_TransportSession * session)
|
|
{
|
|
return networkConnectCancellable(this, session, NULL, NULL);
|
|
}
|
|
|
|
static void networkDisconnect(LG_Transport * this)
|
|
{
|
|
networkInputConnectionState(this->input, false);
|
|
networkControlConnectionState(this->control, false);
|
|
networkRecoveryConnectionState(this->recovery, false);
|
|
networkCursorConnectionState(this->cursor, false);
|
|
networkConnectionDisconnect(this->connection);
|
|
lgSignalEvent(this->frameEvent);
|
|
}
|
|
|
|
static bool networkSessionValid(LG_Transport * this)
|
|
{
|
|
return networkConnectionValid(this->connection);
|
|
}
|
|
|
|
static void networkSetStatusListener(LG_Transport * this,
|
|
LG_VideoStatusFn callback, void * opaque)
|
|
{
|
|
LG_VideoStatus status;
|
|
|
|
LG_LOCK(this->callbackLock);
|
|
LG_LOCK(this->stateLock);
|
|
this->videoStatusFn = callback;
|
|
this->videoStatusOpaque = opaque;
|
|
status = this->videoStatus;
|
|
LG_UNLOCK(this->stateLock);
|
|
|
|
if (callback)
|
|
callback(opaque, &status);
|
|
LG_UNLOCK(this->callbackLock);
|
|
}
|
|
|
|
static bool networkSupportsDMA(LG_Transport * this)
|
|
{
|
|
(void)this;
|
|
return false;
|
|
}
|
|
|
|
static bool networkAttachRenderer(LG_Transport * this,
|
|
const LG_RendererInterop * interop)
|
|
{
|
|
const size_t requiredSize = offsetof(LG_RendererInterop,
|
|
egl.sharedTextureImport) + sizeof(interop->egl.sharedTextureImport);
|
|
if (!interop ||
|
|
interop->version < LG_RENDERER_INTEROP_EGL_TEXTURE_VERSION ||
|
|
interop->size < requiredSize ||
|
|
interop->type != LG_RENDERER_INTEROP_EGL ||
|
|
!interop->egl.display || !interop->egl.config ||
|
|
!interop->egl.shareContext || !interop->egl.sharedTextureImport)
|
|
{
|
|
DEBUG_ERROR("The network transport requires EGL shared textures");
|
|
return false;
|
|
}
|
|
|
|
this->rendererAttached = true;
|
|
return true;
|
|
}
|
|
|
|
static void networkDetachRenderer(LG_Transport * this)
|
|
{
|
|
this->rendererAttached = false;
|
|
}
|
|
|
|
static LG_TransportStatus networkNextFrame(LG_Transport * this, bool useDMA,
|
|
LG_TransportFrame * frame)
|
|
{
|
|
(void)useDMA;
|
|
(void)frame;
|
|
|
|
if (!this->rendererAttached)
|
|
return LG_TRANSPORT_ERROR;
|
|
if (!networkSessionValid(this))
|
|
return LG_TRANSPORT_DISCONNECTED;
|
|
|
|
lgWaitEvent(this->frameEvent, 100);
|
|
return networkSessionValid(this) ?
|
|
LG_TRANSPORT_UNAVAILABLE : LG_TRANSPORT_DISCONNECTED;
|
|
}
|
|
|
|
static void networkGetFrameTiming(LG_Transport * this,
|
|
const LG_TransportFrame * frame, LG_TransportFrameTiming * timing)
|
|
{
|
|
(void)this;
|
|
(void)frame;
|
|
memset(timing, 0, sizeof(*timing));
|
|
}
|
|
|
|
static void networkReleaseFrame(LG_Transport * this,
|
|
LG_TransportFrame * frame)
|
|
{
|
|
(void)this;
|
|
(void)frame;
|
|
}
|
|
|
|
static void networkCancelFrameWait(LG_Transport * this)
|
|
{
|
|
lgSignalEvent(this->frameEvent);
|
|
}
|
|
|
|
static void networkStopFrame(LG_Transport * this)
|
|
{
|
|
lgSignalEvent(this->frameEvent);
|
|
}
|
|
|
|
static LG_TransportStatus networkNextPointer(LG_Transport * this,
|
|
LG_TransportPointer * pointer)
|
|
{
|
|
const LG_TransportStatus status =
|
|
networkCursorNext(this->cursor, pointer);
|
|
if (status != LG_TRANSPORT_OK)
|
|
return status;
|
|
|
|
LG_LOCK(this->stateLock);
|
|
pointer->epoch = this->videoStatus.pointer.epoch;
|
|
LG_UNLOCK(this->stateLock);
|
|
return LG_TRANSPORT_OK;
|
|
}
|
|
|
|
static void networkReleasePointer(LG_Transport * this,
|
|
LG_TransportPointer * pointer)
|
|
{
|
|
networkCursorRelease(this->cursor, pointer);
|
|
}
|
|
|
|
static void networkCancelPointerWait(LG_Transport * this)
|
|
{
|
|
networkCursorCancel(this->cursor);
|
|
}
|
|
|
|
static void networkStopPointer(LG_Transport * this)
|
|
{
|
|
networkCursorStop(this->cursor);
|
|
}
|
|
|
|
static const LG_FrameOps networkFrameOps =
|
|
{
|
|
.setStatusListener = networkSetStatusListener,
|
|
.supportsDMA = networkSupportsDMA,
|
|
.attachRenderer = networkAttachRenderer,
|
|
.detachRenderer = networkDetachRenderer,
|
|
.nextFrame = networkNextFrame,
|
|
.getFrameTiming = networkGetFrameTiming,
|
|
.releaseFrame = networkReleaseFrame,
|
|
.cancelFrameWait = networkCancelFrameWait,
|
|
.stopFrame = networkStopFrame,
|
|
.nextPointer = networkNextPointer,
|
|
.releasePointer = networkReleasePointer,
|
|
.cancelPointerWait = networkCancelPointerWait,
|
|
.stopPointer = networkStopPointer,
|
|
};
|
|
|
|
static const LG_VideoOps networkVideoOps =
|
|
{
|
|
.name = "network",
|
|
.type = LG_VIDEO_TYPE_FRAME,
|
|
.frame = &networkFrameOps,
|
|
};
|
|
|
|
static const LG_VideoOps * networkGetVideoOps(LG_Transport * this)
|
|
{
|
|
(void)this;
|
|
return &networkVideoOps;
|
|
}
|
|
|
|
static const LG_InputOps * networkGetInputOps(LG_Transport * this,
|
|
void ** opaque)
|
|
{
|
|
*opaque = NULL;
|
|
if (!networkConnectionServiceAvailable(this->connection,
|
|
LG_NET_SERVICE_INPUT, LG_NET_CAPABILITY_RELIABLE))
|
|
return NULL;
|
|
*opaque = this->input;
|
|
return networkInputGetOps();
|
|
}
|
|
|
|
static const LG_AudioOps * networkGetAudioOps(LG_Transport * this,
|
|
void ** opaque)
|
|
{
|
|
(void)this;
|
|
*opaque = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
static const LG_ClipboardOps * networkGetClipboardOps(LG_Transport * this,
|
|
void ** opaque)
|
|
{
|
|
(void)this;
|
|
*opaque = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
static LG_TransportStatus networkSendControl(LG_Transport * this,
|
|
const LG_TransportControl * control, LG_TransportControlToken * token)
|
|
{
|
|
return networkControlSend(this->control, control, token);
|
|
}
|
|
|
|
static LG_TransportStatus networkControlStatusOp(LG_Transport * this,
|
|
LG_TransportControlToken token)
|
|
{
|
|
return networkControlStatus(this->control, token);
|
|
}
|
|
|
|
static LG_TransportStatus networkGetRecoveryInfo(LG_Transport * this,
|
|
LG_RecoveryInfo * info)
|
|
{
|
|
return networkRecoveryGetInfo(this->recovery, info);
|
|
}
|
|
|
|
static LG_TransportStatus networkRequestRecovery(LG_Transport * this,
|
|
LG_RecoveryRequest request, uint64_t * instance, uint32_t * serial)
|
|
{
|
|
return networkRecoveryRequest(
|
|
this->recovery, request, instance, serial);
|
|
}
|
|
|
|
const LG_TransportOps LGT_Network =
|
|
{
|
|
.name = "network",
|
|
.setup = networkSetup,
|
|
.create = networkCreate,
|
|
.destroy = networkDestroy,
|
|
.connect = networkConnect,
|
|
.connectCancellable = networkConnectCancellable,
|
|
.disconnect = networkDisconnect,
|
|
.sessionValid = networkSessionValid,
|
|
.getVideoOps = networkGetVideoOps,
|
|
.getInputOps = networkGetInputOps,
|
|
.getAudioOps = networkGetAudioOps,
|
|
.getClipboardOps = networkGetClipboardOps,
|
|
.getRecoveryInfo = networkGetRecoveryInfo,
|
|
.requestRecovery = networkRequestRecovery,
|
|
.sendControl = networkSendControl,
|
|
.controlStatus = networkControlStatusOp,
|
|
};
|