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