From 5647f7a90d5ef8590e5623c3bba7caf32db55df7 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Tue, 28 Apr 2015 21:03:25 +0200 Subject: [PATCH] Keyboard and mouse support --- CMakeLists.txt | 9 ++- README.md | 2 +- cmake/FindOpus.cmake | 62 +++++++++++++++++++ src/input.c | 144 +++++++++++++++++++++++++++++++++++++++++++ src/input.h | 21 +++++++ src/keyboard.h | 142 ++++++++++++++++++++++++++++++++++++++++++ src/main.c | 7 +++ 7 files changed, 384 insertions(+), 3 deletions(-) create mode 100644 cmake/FindOpus.cmake create mode 100644 src/input.c create mode 100644 src/input.h create mode 100644 src/keyboard.h diff --git a/CMakeLists.txt b/CMakeLists.txt index be6ddfb..580a7d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,8 @@ project(moonlight-embedded) cmake_minimum_required(VERSION 3.2) +SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") + aux_source_directory(./common/limelight-common SRC_LIST) aux_source_directory(./common/limelight-common/OpenAES SRC_LIST) aux_source_directory(./src SRC_LIST) @@ -15,5 +17,8 @@ find_package(CURL) find_package(OpenSSL) find_package(EXPAT) -include_directories(./moonlight-common-c) -target_link_libraries (moonlight PUBLIC ${CMAKE_THREAD_LIBS_INIT} ${CURL_LIBRARIES} ${OPENSSL_LIBRARIES} ${EXPAT_LIBRARIES}) +find_package(PkgConfig REQUIRED) +pkg_check_modules(EVDEV libevdev) + +include_directories(./moonlight-common-c ${EVDEV_INCLUDE_DIRS}) +target_link_libraries (moonlight PUBLIC ${CMAKE_THREAD_LIBS_INIT} ${CURL_LIBRARIES} ${OPENSSL_LIBRARIES} ${EXPAT_LIBRARIES} ${EVDEV_LIBRARIES}) diff --git a/README.md b/README.md index c07b70c..078e2a4 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ implementation. * Play games! ##Usage - Usage: moonlight host game + Usage: moonlight host game input ##Compile diff --git a/cmake/FindOpus.cmake b/cmake/FindOpus.cmake new file mode 100644 index 0000000..fae095f --- /dev/null +++ b/cmake/FindOpus.cmake @@ -0,0 +1,62 @@ +# - Find opus library +# Find the native Opus headers and libraries. Opus depends on Ogg and will +# provide Ogg headers/libraries as well. +# +# OPUS_INCLUDE_DIRS - where to find opus/opus.h, ogg/ogg.h, etc +# OPUS_LIBRARIES - List of libraries when using libopus +# OPUS_FOUND - True if opus is found. + + +#============================================================================= +#Copyright 2000-2009 Kitware, Inc., Insight Software Consortium +#Copyright 2014, Mateusz "maxmati" Nowotynski +#All rights reserved. +# +#Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +#* Redistributions of source code must retain the above copyright notice, +#this list of conditions and the following disclaimer. +# +#* Redistributions in binary form must reproduce the above copyright notice, +#this list of conditions and the following disclaimer in the documentation +#and/or other materials provided with the distribution. +# +#* Neither the names of Kitware, Inc., the Insight Software Consortium, nor +#the names of their contributors may be used to endorse or promote products +#derived from this software without specific prior written permission. +# +#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +#AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +#IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +#ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +#LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +#CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +#SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +#INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +#CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +#ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +#POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + + +# Look for the opusfile header file. +find_path( OPUS_INCLUDE_DIR + NAMES opus/opus.h + DOC "Opus include directory" ) +mark_as_advanced( OPUS_INCLUDE_DIR ) + +# Look for the opus library. +find_library( OPUS_LIBRARY + NAMES opus + DOC "Path to Opus library" ) +mark_as_advanced( OPUS_LIBRARY ) + + +# handle the QUIETLY and REQUIRED arguments and set OPUSFILE_FOUND to TRUE if +# all listed variables are TRUE +include( ${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake ) +FIND_PACKAGE_HANDLE_STANDARD_ARGS( Opus DEFAULT_MSG OPUS_LIBRARY OPUS_INCLUDE_DIR ) + +set( OPUS_LIBRARIES ${OPUS_LIBRARY}) +set( OPUS_INCLUDE_DIRS ${OPUS_INCLUDE_DIR} ${OPUS_INCLUDE_DIR}/opus ) \ No newline at end of file diff --git a/src/input.c b/src/input.c new file mode 100644 index 0000000..6ea5963 --- /dev/null +++ b/src/input.c @@ -0,0 +1,144 @@ +/* + * This file is part of Moonlight Embedded. + * + * Copyright (C) 2015 Iwan Timmer + * + * Moonlight 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 3 of the License, or + * (at your option) any later version. + * + * Moonlight 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 Moonlight; if not, see . + */ + +#include "keyboard.h" + +#include "libevdev/libevdev.h" +#include "limelight-common/Limelight.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +struct input_device { + struct libevdev *dev; + int fd; + __s32 mouseDeltaX, mouseDeltaY, mouseScroll; +}; + +static struct pollfd* fds = NULL; +static struct input_device* devices = NULL; +static int numDevices = 0; + +void input_create(char* device) { + int dev = numDevices; + numDevices++; + + if (fds == NULL) { + fds = malloc(sizeof(struct pollfd)); + devices = malloc(sizeof(struct input_device)); + } else { + fds = realloc(fds, sizeof(struct pollfd)*numDevices); + devices = realloc(devices, sizeof(struct input_device)*numDevices); + } + + if (fds == NULL || devices == NULL) { + fprintf(stderr, "Not enough memory\n"); + exit(EXIT_FAILURE); + } + + devices[dev].fd = open(device, O_RDONLY|O_NONBLOCK); + if (devices[dev].fd <= 0) { + fprintf(stderr, "Failed to open device\n"); + exit(EXIT_FAILURE); + } + + devices[dev].dev = libevdev_new(); + libevdev_set_fd(devices[dev].dev, devices[dev].fd); + fds[dev].fd = devices[dev].fd; + fds[dev].events = POLLIN; +} + +static void handle_event(struct input_event *ev, struct input_device *dev) { + switch (ev->type) { + case EV_SYN: + if (dev->mouseDeltaX != 0 || dev->mouseDeltaY != 0) { + LiSendMouseMoveEvent(dev->mouseDeltaX, dev->mouseDeltaY); + dev->mouseDeltaX = 0; + dev->mouseDeltaY = 0; + } + if (dev->mouseScroll != 0) { + LiSendScrollEvent(dev->mouseScroll); + dev->mouseScroll = 0; + } + break; + case EV_KEY: + if (ev->code < sizeof(keyCodes)/sizeof(keyCodes[0])) { + short code = 0x80 << 8 | keyCodes[ev->code]; + LiSendKeyboardEvent(code, ev->value?KEY_ACTION_DOWN:KEY_ACTION_UP, 0); + } else { + int mouseCode = 0; + switch (ev->code) { + case BTN_LEFT: + mouseCode = BUTTON_LEFT; + break; + case BTN_MIDDLE: + mouseCode = BUTTON_MIDDLE; + break; + case BTN_RIGHT: + mouseCode = BUTTON_RIGHT; + break; + } + + if (mouseCode > 0) { + LiSendMouseButtonEvent(ev->value?BUTTON_ACTION_PRESS:BUTTON_ACTION_RELEASE, mouseCode); + } + } + break; + case EV_REL: + switch (ev->code) { + case REL_X: + dev->mouseDeltaX = ev->value; + break; + case REL_Y: + dev->mouseDeltaY = ev->value; + break; + case REL_Z: + dev->mouseScroll = ev->value; + break; + } + break; + } +} + +void input_loop() { + while (poll(fds, numDevices, -1)) { + for (int i=0;i 0) { + int rc; + struct input_event ev; + while ((rc = libevdev_next_event(devices[i].dev, LIBEVDEV_READ_FLAG_NORMAL, &ev)) >= 0) { + if (rc == LIBEVDEV_READ_STATUS_SYNC) + fprintf(stderr, "Error: cannot keep up\n"); + else if (rc == LIBEVDEV_READ_STATUS_SUCCESS); + handle_event(&ev, &devices[i]); + } + if (rc != -EAGAIN && rc < 0) { + fprintf(stderr, "Error: %s\n", strerror(-rc)); + exit(EXIT_FAILURE); + } + } + } + } +} diff --git a/src/input.h b/src/input.h new file mode 100644 index 0000000..6209803 --- /dev/null +++ b/src/input.h @@ -0,0 +1,21 @@ +/* + * This file is part of Moonlight Embedded. + * + * Copyright (C) 2015 Iwan Timmer + * + * Moonlight 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 3 of the License, or + * (at your option) any later version. + * + * Moonlight 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 Moonlight; if not, see . + */ + +void input_create(char* device); +void input_loop(); diff --git a/src/keyboard.h b/src/keyboard.h new file mode 100644 index 0000000..dfd9e0c --- /dev/null +++ b/src/keyboard.h @@ -0,0 +1,142 @@ +/* + * This file is part of Moonlight Embedded. + * + * Copyright (C) 2015 Iwan Timmer + * + * Moonlight 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 3 of the License, or + * (at your option) any later version. + * + * Moonlight 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 Moonlight; if not, see . + */ + +static const short keyCodes[] = { + 0, //VK_RESERVED + 0x1B, //VK_ESCAPE + 0x31, //VK_1 + 0x32, //VK_2 + 0x33, //VK_3 + 0x34, //VK_4 + 0x35, //VK_5 + 0x36, //VK_6 + 0x37, //VK_7 + 0x38, //VK_8 + 0x39, //VK_9 + 0x30, //VK_0 + 0x2D, //VK_MINUS + 0x3D, //VK_EQUALS + 0x08, //VK_BACK_SPACE + 0x09, //VK_TAB + 0x51, //VK_Q + 0x57, //VK_W + 0x45, //VK_E + 0x52, //VK_R + 0x54, //VK_T + 0x59, //VK_Y + 0x55, //VK_U + 0x49, //VK_I + 0x4F, //VK_O + 0x50, //VK_P + 0xA1, //VK_BRACELEFT + 0xA2, //VK_BRACERIGHT + 0x0A, //VK_ENTER + 0x11, //VK_CONTROL Left control + 0x41, //VK_A + 0x53, //VK_S + 0x44, //VK_D + 0x46, //VK_F + 0x47, //VK_G + 0x48, //VK_H + 0x4A, //VK_J + 0x4B, //VK_K + 0x4C, //VK_L + 0x3B, //VK_SEMICOLON + 0, //VK_APOSTROPHE + 0, //VK_GRAVE + 0x10, //VK_SHIFT Left shift + 0x5C, //VK_BACK_SLASH + 0x5A, //VK_Z + 0x58, //VK_X + 0x43, //VK_C + 0x56, //VK_V + 0x42, //VK_B + 0x4E, //VK_N + 0x4D, //VK_M + 0x2C, //VK_COMMA + 0, //VK_DOT + 0x2F, //VK_SLASH + 0x10, //VK_SHIFT Right shift + 0, //VK_KPASTERISK + 0x11, //VK_ALT Left alt + 0x32, //VK_SPACE + 0x14, //VK_CAPS_LOCK + 0x70, //VK_F1 + 0x71, //VK_F2 + 0x72, //VK_F3 + 0x73, //VK_F4 + 0x74, //VK_F5 + 0x75, //VK_F6 + 0x76, //VK_F7 + 0x77, //VK_F8 + 0x78, //VK_F9 + 0x79, //VK_F10 + 0x90, //VK_NUM_LOCK + 0x91, //VK_SCROLL_LOCK + 0x67, //VK_NUMPAD7 + 0x68, //VK_NUMPAD8 + 0x69, //VK_NUMPAD9 + 0, //VK_NUMPAD_MINUS + 0x64, //VK_NUMPAD4 + 0x65, //VK_NUMPAD5 + 0x66, //VK_NUMPAD6 + 0, //VK_NUMPADPLUS + 0x61, //VK_NUMPAD1 + 0x62, //VK_NUMPAD2 + 0x63, //VK_NUMPAD3 + 0x60, //VK_NUMPAD0 + 0, //KeyEvent.VK_NUMPADDOT + 0, + 0, //KeyEvent.VK_ZENKAKUHANKAKU + 0, //KeyEvent.VK_102ND + 0x7A, //VK_F11 + 0x7B, //VK_F12 + 0, //KeyEvent.VK_RO + 0xF1, //VK_KATAKANA + 0xF2, //VK_HIRAGANA + 0, //VK_HENKAN + 0, //VK_KATAKANAHIRAGANA + 0, //VK_MUHENKAN + 0, //VK_KPJPCOMMA + 0, //VK_KPENTER + 0x11, //VK_CONTROL Right ctrl + 0, //VK_KPSLASH + 0, //VK_SYSRQ + 0x11, //VK_ALT Right alt + 0, //KeyEvent.VK_LINEFEED + 0x24, //VK_HOME + 0x26, //VK_UP + 0x22, //VK_PAGE_UP + 0x25, //VK_LEFT + 0x27, //VK_RIGHT + 0x23, //VK_END + 0x28, //VK_DOWN + 0x22, //VK_PAGE_DOWN + 0x9B, //VK_INSERT + 0x7F, //VK_DELETE + 0, //VK_MACRO + 0, //VK_MUTE + 0, //VK_VOLUMEDOWN + 0, //VK_VOLUMEUP + 0, //VK_POWER SC System Power Down + 0, //VK_KPEQUAL + 0, //VK_KPPLUSMINUS + 0x13, //VK_PAUSE + 0, //VK_SCALE AL Compiz Scale (Expose) +}; diff --git a/src/main.c b/src/main.c index e140040..66370bc 100644 --- a/src/main.c +++ b/src/main.c @@ -21,6 +21,7 @@ #include "connection.h" #include "video.h" #include "audio.h" +#include "input.h" #include "limelight-common/Limelight.h" @@ -55,7 +56,13 @@ int main(int argc, char* argv[]) { client_start_app(config, argv[1], appId); + input_create(argv[3]); + struct in_addr addr; inet_aton(address, &addr); LiStartConnection(addr.s_addr, config, &connection_callbacks, &decoder_callbacks, &audio_callbacks, &platform_callbacks, NULL, 0, 4); + + input_loop(); + + LiStopConnection(); }