diff --git a/limelight-common/Connection.c b/limelight-common/Connection.c index d95521e..8eb3725 100644 --- a/limelight-common/Connection.c +++ b/limelight-common/Connection.c @@ -4,16 +4,18 @@ static int stage = STAGE_NONE; static CONNECTION_LISTENER_CALLBACKS ListenerCallbacks; -static const char* stageNames [] = { +static const char* stageNames[STAGE_MAX] = { "none", "platform initialization", "handshake", "control stream initialization", "video stream initialization", "audio stream initialization", + "input stream initialization", "control stream establishment", "video stream establishment", - "audio stream establishment" + "audio stream establishment", + "input stream establishment" }; const char* LiGetStageName(int stage) { @@ -21,6 +23,12 @@ const char* LiGetStageName(int stage) { } void LiStopConnection(void) { + if (stage == STAGE_INPUT_STREAM_START) { + Limelog("Stopping input stream..."); + stopInputStream(); + stage--; + Limelog("done\n"); + } if (stage == STAGE_AUDIO_STREAM_START) { Limelog("Stopping audio stream..."); stopAudioStream(); @@ -39,6 +47,12 @@ void LiStopConnection(void) { stage--; Limelog("done\n"); } + if (stage == STAGE_INPUT_STREAM_INIT) { + Limelog("Cleaning up input stream..."); + destroyInputStream(); + stage--; + Limelog("done\n"); + } if (stage == STAGE_AUDIO_STREAM_INIT) { Limelog("Cleaning up audio stream..."); destroyAudioStream(); @@ -83,7 +97,7 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN err = initializePlatformSockets(); if (err != 0) { Limelog("failed: %d\n", err); - ListenerCallbacks.stageFailed(STAGE_PLATFORM_INIT); + ListenerCallbacks.stageFailed(STAGE_PLATFORM_INIT, err); goto Cleanup; } stage++; @@ -96,7 +110,7 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN err = performHandshake(host); if (err != 0) { Limelog("failed: %d\n", err); - ListenerCallbacks.stageFailed(STAGE_HANDSHAKE); + ListenerCallbacks.stageFailed(STAGE_HANDSHAKE, err); goto Cleanup; } stage++; @@ -109,7 +123,7 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN err = initializeControlStream(host, streamConfig, &ListenerCallbacks); if (err != 0) { Limelog("failed: %d\n", err); - ListenerCallbacks.stageFailed(STAGE_CONTROL_STREAM_INIT); + ListenerCallbacks.stageFailed(STAGE_CONTROL_STREAM_INIT, err); goto Cleanup; } stage++; @@ -133,12 +147,20 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN ListenerCallbacks.stageComplete(STAGE_AUDIO_STREAM_INIT); Limelog("done\n"); + Limelog("Initializing input stream..."); + ListenerCallbacks.stageStarting(STAGE_INPUT_STREAM_INIT); + initializeInputStream(host, &ListenerCallbacks); + stage++; + LC_ASSERT(stage == STAGE_INPUT_STREAM_INIT); + ListenerCallbacks.stageComplete(STAGE_INPUT_STREAM_INIT); + Limelog("done\n"); + Limelog("Starting control stream..."); ListenerCallbacks.stageStarting(STAGE_CONTROL_STREAM_START); err = startControlStream(); if (err != 0) { Limelog("failed: %d\n", err); - ListenerCallbacks.stageFailed(STAGE_CONTROL_STREAM_START); + ListenerCallbacks.stageFailed(STAGE_CONTROL_STREAM_START, err); goto Cleanup; } stage++; @@ -151,7 +173,7 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN err = startVideoStream(renderContext, drFlags); if (err != 0) { Limelog("Video stream start failed: %d\n", err); - ListenerCallbacks.stageFailed(STAGE_VIDEO_STREAM_START); + ListenerCallbacks.stageFailed(STAGE_VIDEO_STREAM_START, err); goto Cleanup; } stage++; @@ -164,7 +186,7 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN err = startAudioStream(); if (err != 0) { Limelog("Audio stream start failed: %d\n", err); - ListenerCallbacks.stageFailed(STAGE_AUDIO_STREAM_START); + ListenerCallbacks.stageFailed(STAGE_AUDIO_STREAM_START, err); goto Cleanup; } stage++; @@ -172,6 +194,21 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN ListenerCallbacks.stageComplete(STAGE_AUDIO_STREAM_START); Limelog("done\n"); + Limelog("Starting input stream..."); + ListenerCallbacks.stageStarting(STAGE_INPUT_STREAM_START); + err = startInputStream(); + if (err != 0) { + Limelog("Input stream start failed: %d\n", err); + ListenerCallbacks.stageFailed(STAGE_INPUT_STREAM_START, err); + goto Cleanup; + } + stage++; + LC_ASSERT(stage == STAGE_INPUT_STREAM_START); + ListenerCallbacks.stageComplete(STAGE_INPUT_STREAM_START); + Limelog("done\n"); + + ListenerCallbacks.connectionStarted(); + Cleanup: return err; } \ No newline at end of file diff --git a/limelight-common/Input.h b/limelight-common/Input.h new file mode 100644 index 0000000..0fb92ef --- /dev/null +++ b/limelight-common/Input.h @@ -0,0 +1,35 @@ +#pragma once + +#pragma pack(push, 1) + +typedef struct _NV_INPUT_HEADER { + int packetType; +} NV_INPUT_HEADER, PNV_INPUT_HEADER; + +#define PACKET_TYPE_KEYBOARD 0x0A +typedef struct _NV_KEYBOARD_PACKET { + NV_INPUT_HEADER header; + char keyAction; + int zero1; + short keyCode; + char modifiers; + short zero2; +} NV_KEYBOARD_PACKET, *PNV_KEYBOARD_PACKET; + +#define PACKET_TYPE_MOUSE_MOVE 0x08 +#define MOUSE_MOVE_MAGIC 0x06000000 +typedef struct _NV_MOUSE_MOVE_PACKET { + NV_INPUT_HEADER header; + int magic; + short deltaX; + short deltaY; +} NV_MOUSE_MOVE_PACKET, *PNV_MOUSE_MOVE_PACKET; + +#define PACKET_TYPE_MOUSE_BUTTON 0x05 +typedef struct _NV_MOUSE_BUTTON_PACKET { + NV_INPUT_HEADER header; + char action; + int button; +} NV_MOUSE_BUTTON_PACKET, *PNV_MOUSE_BUTTON_PACKET; + +#pragma pack(pop) \ No newline at end of file diff --git a/limelight-common/InputStream.c b/limelight-common/InputStream.c new file mode 100644 index 0000000..2ae7d6f --- /dev/null +++ b/limelight-common/InputStream.c @@ -0,0 +1,168 @@ +#include "Limelight-internal.h" +#include "PlatformSockets.h" +#include "PlatformThreads.h" +#include "LinkedBlockingQueue.h" +#include "Input.h" + +static IP_ADDRESS host; +static SOCKET inputSock = INVALID_SOCKET; +static PCONNECTION_LISTENER_CALLBACKS listenerCallbacks; + +static LINKED_BLOCKING_QUEUE packetQueue; +static PLT_THREAD inputSendThread; + +typedef struct _PACKET_HOLDER { + int packetLength; + union { + NV_KEYBOARD_PACKET keyboard; + NV_MOUSE_MOVE_PACKET mouseMove; + NV_MOUSE_BUTTON_PACKET mouseButton; + } packet; +} PACKET_HOLDER, *PPACKET_HOLDER; + +int initializeInputStream(IP_ADDRESS addr, PCONNECTION_LISTENER_CALLBACKS clCallbacks) { + host = addr; + listenerCallbacks = clCallbacks; + + LbqInitializeLinkedBlockingQueue(&packetQueue, 30); + + return 0; +} + +void destroyInputStream(void) { + PLINKED_BLOCKING_QUEUE_ENTRY entry, nextEntry; + + entry = LbqDestroyLinkedBlockingQueue(&packetQueue); + + while (entry != NULL) { + nextEntry = entry->next; + free(entry->data); + free(entry); + entry = nextEntry; + } +} + +static void inputSendThreadProc(void* context) { + int err; + PPACKET_HOLDER holder; + + while (!PltIsThreadInterrupted(&inputSendThread)) { + err = LbqWaitForQueueElement(&packetQueue, (void**) &holder); + if (err != LBQ_SUCCESS) { + Limelog("Input thread terminating #1\n"); + listenerCallbacks->connectionTerminated(err); + return; + } + + err = send(inputSock, (const char*) &holder->packet, holder->packetLength, 0); + free(holder); + if (err <= 0) { + Limelog("Input thread terminating #2\n"); + listenerCallbacks->connectionTerminated(err); + return; + } + } +} + +int startInputStream(void) { + int err; + + inputSock = connectTcpSocket(host, 35043); + if (inputSock == INVALID_SOCKET) { + return LastSocketError(); + } + + enableNoDelay(inputSock); + + err = PltCreateThread(inputSendThreadProc, NULL, &inputSendThread); + if (err != 0) { + return err; + } + + return err; +} + +int stopInputStream(void) { + PltInterruptThread(&inputSendThread); + + if (inputSock != INVALID_SOCKET) { + closesocket(inputSock); + inputSock = INVALID_SOCKET; + } + + PltJoinThread(&inputSendThread); + PltCloseThread(&inputSendThread); + + return 0; +} + +int LiSendMouseMoveEvent(short deltaX, short deltaY) { + PPACKET_HOLDER holder; + int err; + + holder = malloc(sizeof(*holder)); + if (holder == NULL) { + return -1; + } + + holder->packetLength = sizeof(NV_MOUSE_MOVE_PACKET); + holder->packet.mouseMove.header.packetType = htonl(PACKET_TYPE_MOUSE_MOVE); + holder->packet.mouseMove.magic = htonl(MOUSE_MOVE_MAGIC); + holder->packet.mouseMove.deltaX = htons(deltaX); + holder->packet.mouseMove.deltaY = htons(deltaY); + + err = LbqOfferQueueItem(&packetQueue, holder); + if (err != LBQ_SUCCESS) { + free(holder); + } + + return err; +} + +int LiSendMouseButtonEvent(char action, int button) { + PPACKET_HOLDER holder; + int err; + + holder = malloc(sizeof(*holder)); + if (holder == NULL) { + return -1; + } + + holder->packetLength = sizeof(NV_MOUSE_BUTTON_PACKET); + holder->packet.mouseButton.header.packetType = htonl(PACKET_TYPE_MOUSE_BUTTON); + holder->packet.mouseButton.action = action; + holder->packet.mouseButton.button = htonl(button); + + err = LbqOfferQueueItem(&packetQueue, holder); + if (err != LBQ_SUCCESS) { + free(holder); + } + + return err; +} + +int LiSendKeyboardEvent(short keyCode, char keyAction, char modifiers) { + PPACKET_HOLDER holder; + int err; + + holder = malloc(sizeof(*holder)); + if (holder == NULL) { + return -1; + } + + holder->packetLength = sizeof(NV_KEYBOARD_PACKET); + holder->packet.keyboard.header.packetType = htonl(PACKET_TYPE_KEYBOARD); + holder->packet.keyboard.keyAction = keyAction; + holder->packet.keyboard.zero1 = 0; + holder->packet.keyboard.keyCode = htons(keyCode); + holder->packet.keyboard.modifiers = modifiers; + holder->packet.keyboard.zero2 = 0; + + err = LbqOfferQueueItem(&packetQueue, holder); + if (err != LBQ_SUCCESS) { + free(holder); + } + + return err; +} + diff --git a/limelight-common/Limelight-internal.h b/limelight-common/Limelight-internal.h index 5f3e46b..706aa15 100644 --- a/limelight-common/Limelight-internal.h +++ b/limelight-common/Limelight-internal.h @@ -33,4 +33,9 @@ void stopVideoStream(void); void initializeAudioStream(IP_ADDRESS host, PAUDIO_RENDERER_CALLBACKS arCallbacks, PCONNECTION_LISTENER_CALLBACKS clCallbacks); void destroyAudioStream(void); int startAudioStream(void); -void stopAudioStream(void); \ No newline at end of file +void stopAudioStream(void); + +int initializeInputStream(IP_ADDRESS addr, PCONNECTION_LISTENER_CALLBACKS clCallbacks); +void destroyInputStream(void); +int startInputStream(void); +int stopInputStream(void); diff --git a/limelight-common/Limelight.h b/limelight-common/Limelight.h index 20c9cf6..60b4e3c 100644 --- a/limelight-common/Limelight.h +++ b/limelight-common/Limelight.h @@ -59,13 +59,16 @@ typedef struct _AUDIO_RENDERER_CALLBACKS { #define STAGE_CONTROL_STREAM_INIT 3 #define STAGE_VIDEO_STREAM_INIT 4 #define STAGE_AUDIO_STREAM_INIT 5 -#define STAGE_CONTROL_STREAM_START 6 -#define STAGE_VIDEO_STREAM_START 7 -#define STAGE_AUDIO_STREAM_START 8 +#define STAGE_INPUT_STREAM_INIT 6 +#define STAGE_CONTROL_STREAM_START 7 +#define STAGE_VIDEO_STREAM_START 8 +#define STAGE_AUDIO_STREAM_START 9 +#define STAGE_INPUT_STREAM_START 10 +#define STAGE_MAX 11 typedef void(*ConnListenerStageStarting)(int stage); typedef void(*ConnListenerStageComplete)(int stage); -typedef void(*ConnListenerStageFailed)(int stage); +typedef void(*ConnListenerStageFailed)(int stage, int errorCode); typedef void(*ConnListenerConnectionStarted)(void); typedef void(*ConnListenerConnectionTerminated)(int errorCode); @@ -88,6 +91,22 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN void LiStopConnection(void); const char* LiGetStageName(int stage); +int LiSendMouseMoveEvent(short deltaX, short deltaY); + +#define BUTTON_ACTION_PRESS 0x07 +#define BUTTON_ACTION_RELEASE 0x08 +#define BUTTON_LEFT 0x01 +#define BUTTON_MIDDLE 0x02 +#define BUTTON_RIGHT 0x03 +int LiSendMouseButtonEvent(char action, int button); + +#define KEY_ACTION_DOWN 0x03 +#define KEY_ACTION_UP 0x04 +#define MODIFIER_SHIFT 0x01 +#define MODIFIER_CTRL 0x02 +#define MODIFIER_ALT 0x04 +int LiSendKeyboardEvent(short keyCode, char keyAction, char modifiers); + #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/limelight-common/limelight-common.vcxproj b/limelight-common/limelight-common.vcxproj index 26d8ed1..aff9254 100644 --- a/limelight-common/limelight-common.vcxproj +++ b/limelight-common/limelight-common.vcxproj @@ -126,9 +126,6 @@ true - - - @@ -136,6 +133,7 @@ + @@ -144,6 +142,7 @@ + diff --git a/limelight-common/limelight-common.vcxproj.filters b/limelight-common/limelight-common.vcxproj.filters index 75591bb..dc1a9bc 100644 --- a/limelight-common/limelight-common.vcxproj.filters +++ b/limelight-common/limelight-common.vcxproj.filters @@ -14,9 +14,6 @@ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - Source Files @@ -51,6 +48,9 @@ Source Files + + Source Files + @@ -77,5 +77,8 @@ Source Files + + Source Files + \ No newline at end of file