diff --git a/CMakeLists.txt b/CMakeLists.txt index b2f0ae10..e7247215 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,12 @@ -cmake_minimum_required(VERSION 3.1) -project(moonlight-embedded VERSION 2.6.2 LANGUAGES C) +cmake_minimum_required(VERSION 3.6) +project(moonlight-embedded VERSION 2.7.0 LANGUAGES C) SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") SET(CMAKE_C_STANDARD 99) include(${CMAKE_ROOT}/Modules/GNUInstallDirs.cmake) include(${CMAKE_SOURCE_DIR}/cmake/generate_version_header.cmake) +include(CheckCSourceCompiles) + add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-pointer-sign -Wno-sign-compare -Wno-switch) aux_source_directory(./src SRC_LIST) @@ -98,6 +100,11 @@ if (HAVE_GETAUXVAL) list(APPEND MOONLIGHT_DEFINITIONS HAVE_GETAUXVAL) endif() +check_c_source_compiles("int main(void) { return __builtin_cpu_supports(\"aes\"); }" HAVE_BICS_AES) +if (HAVE_BICS_AES) + list(APPEND MOONLIGHT_DEFINITIONS HAVE_BICS_AES) +endif() + if (CEC_FOUND) list(APPEND MOONLIGHT_DEFINITIONS HAVE_LIBCEC) list(APPEND MOONLIGHT_OPTIONS CEC) @@ -109,7 +116,7 @@ endif() if(AMLOGIC_FOUND) list(APPEND MOONLIGHT_DEFINITIONS HAVE_AML) list(APPEND MOONLIGHT_OPTIONS AML) - add_library(moonlight-aml SHARED ./src/video/aml.c ${ILCLIENT_SRC_LIST}) + add_library(moonlight-aml SHARED ./src/video/aml.c ./src/util.c ${ILCLIENT_SRC_LIST}) target_include_directories(moonlight-aml PRIVATE ${AMLOGIC_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR}) target_link_libraries(moonlight-aml gamestream ${AMLOGIC_LIBRARIES}) set_property(TARGET moonlight-aml PROPERTY COMPILE_DEFINITIONS ${AMLOGIC_DEFINITIONS}) @@ -120,7 +127,7 @@ if(BROADCOM-OMX_FOUND) list(APPEND MOONLIGHT_DEFINITIONS HAVE_PI) list(APPEND MOONLIGHT_OPTIONS PI) aux_source_directory(./third_party/ilclient ILCLIENT_SRC_LIST) - add_library(moonlight-pi SHARED ./src/video/pi.c ./src/audio/omx.c ${ILCLIENT_SRC_LIST}) + add_library(moonlight-pi SHARED ./src/video/pi.c ./src/audio/omx.c ./src/util.c ${ILCLIENT_SRC_LIST}) target_include_directories(moonlight-pi PRIVATE ./third_party/ilclient ${BROADCOM_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR} ${OPUS_INCLUDE_DIRS}) target_link_libraries(moonlight-pi gamestream ${BROADCOM_OMX_LIBRARIES} ${OPUS_LIBRARY}) set_property(TARGET moonlight-pi PROPERTY COMPILE_DEFINITIONS ${BROADCOM_OMX_DEFINITIONS}) @@ -130,7 +137,7 @@ endif() if(MMAL_FOUND) list(APPEND MOONLIGHT_DEFINITIONS HAVE_MMAL) list(APPEND MOONLIGHT_OPTIONS MMAL) - add_library(moonlight-mmal SHARED ./src/video/mmal.c) + add_library(moonlight-mmal SHARED ./src/video/mmal.c ./src/util.c) target_include_directories(moonlight-mmal PRIVATE ${MMAL_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR}) target_link_libraries(moonlight-mmal gamestream ${MMAL_LINK_LIBRARIES}) install(TARGETS moonlight-mmal DESTINATION ${CMAKE_INSTALL_LIBDIR}) @@ -139,7 +146,7 @@ endif() if(FREESCALE_FOUND) list(APPEND MOONLIGHT_DEFINITIONS HAVE_IMX) list(APPEND MOONLIGHT_OPTIONS IMX) - add_library(moonlight-imx SHARED ./src/video/imx.c ./src/video/imx_vpu.c) + add_library(moonlight-imx SHARED ./src/video/imx.c ./src/video/imx_vpu.c ./src/util.c) target_include_directories(moonlight-imx PRIVATE ${FREESCALE_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR}) target_link_libraries(moonlight-imx gamestream ${FREESCALE_LIBRARIES}) install(TARGETS moonlight-imx DESTINATION ${CMAKE_INSTALL_LIBDIR}) @@ -148,7 +155,7 @@ endif() if(ROCKCHIP_FOUND) list(APPEND MOONLIGHT_DEFINITIONS HAVE_ROCKCHIP) list(APPEND MOONLIGHT_OPTIONS ROCKCHIP) - add_library(moonlight-rk SHARED ./src/video/rk.c) + add_library(moonlight-rk SHARED ./src/video/rk.c ./src/util.c) target_include_directories(moonlight-rk PRIVATE ${ROCKCHIP_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR}) target_link_libraries(moonlight-rk gamestream ${ROCKCHIP_LIBRARIES}) set_property(TARGET moonlight-rk PROPERTY COMPILE_DEFINITIONS ${ROCKCHIP_DEFINITIONS}) diff --git a/cmake/FindLibUUID.cmake b/cmake/FindLibUUID.cmake index ab564b77..7c2ad79e 100644 --- a/cmake/FindLibUUID.cmake +++ b/cmake/FindLibUUID.cmake @@ -1,51 +1,77 @@ -# - Try to find LIBUUID -# Find LIBUUID headers, libraries and the answer to all questions. +# CMake - Cross Platform Makefile Generator +# Copyright 2000-2024 Kitware, Inc. and Contributors +# All rights reserved. # -# LIBUUID_FOUND True if libuuid got found -# LIBUUID_INCLUDE_DIRS Location of libuuid headers -# LIBUUID_LIBRARIES List of libraries to use libuuid -# -# Copyright (c) 2008 Bjoern Ricks -# -# Redistribution and use is allowed according to the terms of the New -# BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# Distributed under the OSI-approved BSD 3-Clause License. See +# https://cmake.org/licensing for details. # +#[=======================================================================[.rst: +FindLibUUID +------------ + +Find LibUUID include directory and library. + +Imported Targets +^^^^^^^^^^^^^^^^ + +An :ref:`imported target ` named +``LibUUID::LibUUID`` is provided if LibUUID has been found. + +Result Variables +^^^^^^^^^^^^^^^^ + +This module defines the following variables: + +``LibUUID_FOUND`` + True if LibUUID was found, false otherwise. +``LibUUID_INCLUDE_DIRS`` + Include directories needed to include LibUUID headers. +``LibUUID_LIBRARIES`` + Libraries needed to link to LibUUID. + +Cache Variables +^^^^^^^^^^^^^^^ + +This module uses the following cache variables: + +``LibUUID_LIBRARY`` + The location of the LibUUID library file. +``LibUUID_INCLUDE_DIR`` + The location of the LibUUID include directory containing ``uuid/uuid.h``. + +The cache variables should not be used by project code. +They may be set by end users to point at LibUUID components. +#]=======================================================================] + +#----------------------------------------------------------------------------- +find_library(LibUUID_LIBRARY + NAMES uuid + ) +mark_as_advanced(LibUUID_LIBRARY) + +find_path(LibUUID_INCLUDE_DIR + NAMES uuid/uuid.h + ) +mark_as_advanced(LibUUID_INCLUDE_DIR) + +#----------------------------------------------------------------------------- +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibUUID + FOUND_VAR LibUUID_FOUND + REQUIRED_VARS LibUUID_LIBRARY LibUUID_INCLUDE_DIR + ) +set(LIBUUID_FOUND ${LibUUID_FOUND}) -INCLUDE( FindPkgConfig ) - -IF ( LibUuid_FIND_REQUIRED ) - SET( _pkgconfig_REQUIRED "REQUIRED" ) -ELSE( LibUuid_FIND_REQUIRED ) - SET( _pkgconfig_REQUIRED "" ) -ENDIF ( LibUuid_FIND_REQUIRED ) - -IF ( LIBUUID_MIN_VERSION ) - PKG_SEARCH_MODULE( LIBUUID ${_pkgconfig_REQUIRED} uuid>=${LIBUUID_MIN_VERSION} ) -ELSE ( LIBUUID_MIN_VERSION ) - PKG_SEARCH_MODULE( LIBUUID ${_pkgconfig_REQUIRED} uuid ) -ENDIF ( LIBUUID_MIN_VERSION ) - - -IF( NOT LIBUUID_FOUND AND NOT PKG_CONFIG_FOUND ) - FIND_PATH( LIBUUID_INCLUDE_DIRS uuid/uuid.h ) - FIND_LIBRARY( LIBUUID_LIBRARIES uuid) - - # Report results - IF ( LIBUUID_LIBRARIES AND LIBUUID_INCLUDE_DIRS ) - SET( LIBUUID_FOUND 1 ) - IF ( NOT LIBUUID_FIND_QUIETLY ) - MESSAGE( STATUS "Found libuuid: ${LIBUUID_LIBRARIES}" ) - ENDIF ( NOT LIBUUID_FIND_QUIETLY ) - ELSE ( LIBUUID_LIBRARIES AND LIBUUID_INCLUDE_DIRS ) - IF ( LIBUUID_FIND_REQUIRED ) - MESSAGE( SEND_ERROR "Could NOT find libuuid" ) - ELSE ( LIBUUID_FIND_REQUIRED ) - IF ( NOT LIBUUID_FIND_QUIETLY ) - MESSAGE( STATUS "Could NOT find libuuid" ) - ENDIF ( NOT LIBUUID_FIND_QUIETLY ) - ENDIF ( LIBUUID_FIND_REQUIRED ) - ENDIF ( LIBUUID_LIBRARIES AND LIBUUID_INCLUDE_DIRS ) -ENDIF( NOT LIBUUID_FOUND AND NOT PKG_CONFIG_FOUND ) - -MARK_AS_ADVANCED( LIBUUID_LIBRARIES LIBUUID_INCLUDE_DIRS ) +#----------------------------------------------------------------------------- +# Provide documented result variables and targets. +if(LibUUID_FOUND) + set(LibUUID_INCLUDE_DIRS ${LibUUID_INCLUDE_DIR}) + set(LibUUID_LIBRARIES ${LibUUID_LIBRARY}) + if(NOT TARGET LibUUID::LibUUID) + add_library(LibUUID::LibUUID UNKNOWN IMPORTED) + set_target_properties(LibUUID::LibUUID PROPERTIES + IMPORTED_LOCATION "${LibUUID_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${LibUUID_INCLUDE_DIRS}" + ) + endif() +endif() diff --git a/libgamestream/CMakeLists.txt b/libgamestream/CMakeLists.txt index 72776f1d..3b026ded 100644 --- a/libgamestream/CMakeLists.txt +++ b/libgamestream/CMakeLists.txt @@ -23,9 +23,9 @@ target_link_libraries(gamestream moonlight-common) set_target_properties(gamestream PROPERTIES SOVERSION ${SO_VERSION} VERSION ${PROJECT_VERSION}) set_target_properties(moonlight-common PROPERTIES SOVERSION ${SO_VERSION} VERSION ${PROJECT_VERSION}) -target_include_directories(gamestream PRIVATE ../third_party/moonlight-common-c/src ../third_party/h264bitstream ${AVAHI_INCLUDE_DIRS} ${LIBUUID_INCLUDE_DIRS}) +target_include_directories(gamestream PRIVATE ../third_party/moonlight-common-c/src ../third_party/h264bitstream ${AVAHI_INCLUDE_DIRS} ${LibUUID_INCLUDE_DIRS}) target_include_directories(moonlight-common PRIVATE ../third_party/moonlight-common-c/reedsolomon ../third_party/moonlight-common-c/enet/include) -target_link_libraries(gamestream ${CURL_LIBRARIES} ${OPENSSL_LIBRARIES} ${EXPAT_LIBRARIES} ${AVAHI_LIBRARIES} ${LIBUUID_LIBRARIES}) +target_link_libraries(gamestream ${CURL_LIBRARIES} ${OPENSSL_LIBRARIES} ${EXPAT_LIBRARIES} ${AVAHI_LIBRARIES} ${LibUUID_LIBRARIES}) target_link_libraries(gamestream ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS}) diff --git a/libgamestream/client.c b/libgamestream/client.c index ebcb9bf1..ce1a61b9 100644 --- a/libgamestream/client.c +++ b/libgamestream/client.c @@ -439,6 +439,7 @@ int gs_pair(PSERVER_DATA server, char* pin) { char* pairing_secret = NULL; char* client_pairing_secret = NULL; char* client_pairing_secret_hex = NULL; + PHTTP_DATA data = NULL; if (server->paired) { gs_error = "Already paired"; @@ -454,7 +455,7 @@ int gs_pair(PSERVER_DATA server, char* pin) { uuid_generate_random(uuid); uuid_unparse(uuid, uuid_str); snprintf(url, url_max_len, "http://%s:%u/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&phrase=getservercert&salt=%s&clientcert=%s", server->serverInfo.address, server->httpPort, unique_id, uuid_str, salt_hex, cert_hex); - PHTTP_DATA data = http_create_data(); + data = http_create_data(); if (data == NULL) return GS_OUT_OF_MEMORY; else if ((ret = http_request(url, data)) != GS_OK) diff --git a/src/config.c b/src/config.c index f011571e..64029a9b 100644 --- a/src/config.c +++ b/src/config.c @@ -20,6 +20,7 @@ #include "platform_main.h" #include "config.h" #include "util.h" +#include "cpu.h" #include "input/evdev.h" #include "audio/audio.h" @@ -84,6 +85,8 @@ static struct option long_options[] = { {"port", required_argument, NULL, '6'}, {"hdr", no_argument, NULL, '7'}, {"hwdecode", required_argument, NULL, '8'}, + {"dual_screen", required_argument, NULL, '9'}, + {"motion_controls", required_argument, NULL, 'e'}, {"swapfacebuttons", required_argument, NULL, 'A'}, {"swaptriggersandshoulders", required_argument, NULL, 'B'}, {0, 0, 0, 0}, @@ -162,6 +165,14 @@ void parse_argument(int c, char* value, PCONFIGURATION config) { case 'd': config->stream.height = atoi(value); break; + case 'e': + if ((value != NULL) && (strcmp(value, "true") == 0)) { + config->motion_controls = true; + } + else { + config->motion_controls = false; + } + break; case 'g': config->stream.bitrate = atoi(value); break; @@ -306,6 +317,13 @@ void parse_argument(int c, char* value, PCONFIGURATION config) { config->hwdecode = false; } break; + case '9': + if ((value != NULL) && (strcmp(value, "true") == 0)) { + config->dual_screen = true; + } + else { + config->dual_screen = false; + } case 'A': if ((value != NULL) && (strcmp(value, "true") == 0)) { config->swap_face_buttons = true; @@ -387,6 +405,8 @@ void config_save(char* filename, PCONFIGURATION config) { write_config_bool(fd, "swapfacebuttons", config->swap_face_buttons); write_config_bool(fd, "swaptriggersandshoulders", config->swap_triggers_and_shoulders); write_config_bool(fd, "debug", config->debug_level); + write_config_bool(fd, "dual_screen", config->dual_screen); + write_config_bool(fd, "motion_controls", config->motion_controls); if (strcmp(config->app, "Steam") != 0) write_config_string(fd, "app", config->app); @@ -410,23 +430,15 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) { if (has_fast_aes()) { config->stream.encryptionFlags = ENCFLG_ALL; } + else if (has_slow_aes()) { + // For extremely slow CPUs, opt out of audio encryption + config->stream.encryptionFlags = ENCFLG_NONE; + printf("Disabling encryption on low performance CPU\n"); + } else { config->stream.encryptionFlags = ENCFLG_AUDIO; } -#ifdef __arm__ - char cpuinfo[4096] = {}; - if (read_file("/proc/cpuinfo", cpuinfo, sizeof(cpuinfo) - 1) > 0) { - // If this is a ARMv6 CPU (like the Pi 1), we'll assume it's not - // powerful enough to handle audio encryption. The Pi 1 could - // barely handle Opus decoding alone. - if (strstr(cpuinfo, "ARMv6")) { - config->stream.encryptionFlags = ENCFLG_NONE; - printf("Disabling encryption on low performance CPU\n"); - } - } -#endif - config->debug_level = 0; config->platform = "auto"; config->app = "Steam"; @@ -457,6 +469,8 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) { config->stream.fps = 30; config->stream.encryptionFlags = ENCFLG_NONE; config->hwdecode = true; + config->dual_screen = false; + config->motion_controls = false; config->swap_face_buttons = false; config->swap_triggers_and_shoulders = false; diff --git a/src/config.h b/src/config.h index 3de3be05..b457cce2 100644 --- a/src/config.h +++ b/src/config.h @@ -20,6 +20,7 @@ #include #include +#include "platform_main.h" #define MAX_INPUTS 6 @@ -49,6 +50,8 @@ typedef struct _CONFIGURATION { int pin; unsigned short port; bool hwdecode; + bool dual_screen; + bool motion_controls; bool swap_face_buttons; bool swap_triggers_and_shoulders; } CONFIGURATION, *PCONFIGURATION; diff --git a/src/cpu.c b/src/cpu.c new file mode 100644 index 00000000..fb6d6b56 --- /dev/null +++ b/src/cpu.c @@ -0,0 +1,121 @@ +/* + * This file is part of Moonlight Embedded. + * + * 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 "cpu.h" +#include "util.h" + +#include +#include +#include +#include + +#ifdef HAVE_GETAUXVAL +#include + +#ifndef HWCAP2_AES +#define HWCAP2_AES (1 << 0) +#endif +#endif + +#if defined(__linux__) && defined(__riscv) +#if __has_include() +#include +#else +#include + +#if __has_include() +#include +#include +#else +#define __NR_riscv_hwprobe 258 +struct riscv_hwprobe { + int64_t key; + uint64_t value; +}; +#define RISCV_HWPROBE_KEY_IMA_EXT_0 4 +#endif + +// RISC-V Scalar AES [E]ncryption and [D]ecryption +#ifndef RISCV_HWPROBE_EXT_ZKND +#define RISCV_HWPROBE_EXT_ZKND (1 << 11) +#define RISCV_HWPROBE_EXT_ZKNE (1 << 12) +#endif + +// RISC-V Vector AES +#ifndef RISCV_HWPROBE_EXT_ZVKNED +#define RISCV_HWPROBE_EXT_ZVKNED (1 << 21) +#endif + +static int __riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count, + size_t cpu_count, unsigned long *cpus, + unsigned int flags) +{ + return syscall(__NR_riscv_hwprobe, pairs, pair_count, cpu_count, cpus, flags); +} + +#endif +#endif + +bool has_fast_aes() { +#if defined(HAVE_GETAUXVAL) && (defined(__arm__) || defined(__aarch64__)) + #if defined(__arm__) && defined(HWCAP2_AES) + return !!(getauxval(AT_HWCAP2) & HWCAP2_AES); + #elif defined(__aarch64__) + return !!(getauxval(AT_HWCAP) & HWCAP_AES); + #else + return false; + #endif +#elif defined(HAVE_BICS_AES) + return __builtin_cpu_supports("aes"); +#elif defined(__BUILTIN_CPU_SUPPORTS__) && defined(__powerpc__) + return __builtin_cpu_supports("vcrypto"); +#elif defined(__linux__) && defined(__riscv) + struct riscv_hwprobe pairs[1] = { + { RISCV_HWPROBE_KEY_IMA_EXT_0, 0 }, + }; + + // If this syscall is not implemented, we'll get -ENOSYS + // and the value field will remain zero. + __riscv_hwprobe(pairs, sizeof(pairs) / sizeof(struct riscv_hwprobe), 0, NULL, 0); + + return (pairs[0].value & (RISCV_HWPROBE_EXT_ZKNE | RISCV_HWPROBE_EXT_ZKND)) == + (RISCV_HWPROBE_EXT_ZKNE | RISCV_HWPROBE_EXT_ZKND) || + (pairs[0].value & RISCV_HWPROBE_EXT_ZVKNED); +#elif __SIZEOF_SIZE_T__ == 4 + #warning Unknown 32-bit platform. Assuming AES is slow on this CPU. + return false; +#else + #warning Unknown 64-bit platform. Assuming AES is fast on this CPU. + return true; +#endif +} + +bool has_slow_aes() { +#ifdef __arm__ + char cpuinfo[4096] = {}; + if (read_file("/proc/cpuinfo", cpuinfo, sizeof(cpuinfo) - 1) > 0) { + // If this is a ARMv6 CPU (like the Pi 1), we'll assume it's not + // powerful enough to handle audio encryption. The Pi 1 could + // barely handle Opus decoding alone. + if (strstr(cpuinfo, "ARMv6")) { + return true; + } + } +#endif + + return false; +} \ No newline at end of file diff --git a/src/cpu.h b/src/cpu.h new file mode 100644 index 00000000..8e00ba6d --- /dev/null +++ b/src/cpu.h @@ -0,0 +1,21 @@ +/* + * This file is part of Moonlight Embedded. + * + * 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 + +bool has_fast_aes(void); +bool has_slow_aes(void); diff --git a/src/input/evdev.c b/src/input/evdev.c index 806f0ea9..d259d4f6 100644 --- a/src/input/evdev.c +++ b/src/input/evdev.c @@ -838,8 +838,10 @@ void evdev_create(const char* device, struct mapping* mappings, bool verbose, in libevdev_has_event_code(evdev, EV_ABS, ABS_RZ))) && !libevdev_has_event_type(evdev, EV_KEY); bool is_gamepad = - libevdev_has_event_code(evdev, EV_ABS, ABS_X) && - libevdev_has_event_code(evdev, EV_ABS, ABS_Y) && + ((libevdev_has_event_code(evdev, EV_ABS, ABS_X) && + libevdev_has_event_code(evdev, EV_ABS, ABS_Y)) || + (libevdev_has_event_code(evdev, EV_ABS, ABS_HAT0X) && + libevdev_has_event_code(evdev, EV_ABS, ABS_HAT0Y))) && (libevdev_has_event_code(evdev, EV_KEY, BTN_TRIGGER) || libevdev_has_event_code(evdev, EV_KEY, BTN_A) || libevdev_has_event_code(evdev, EV_KEY, BTN_1) || diff --git a/src/input/mapping.c b/src/input/mapping.c index d5549065..232943ee 100644 --- a/src/input/mapping.c +++ b/src/input/mapping.c @@ -152,6 +152,8 @@ struct mapping* mapping_parse(char* mapping) { map->hat_dpdown = int_value; map->hat_dir_dpdown = direction_value; } + } else if (strcmp("crc", key) == 0) { + /* CRC is not supported */ } else fprintf(stderr, "Can't map (%s)\n", option); } else if (ret == 0 && option[0] != '\n') diff --git a/src/input/n3ds_input.c b/src/input/n3ds_input.c index 81738269..f76945eb 100644 --- a/src/input/n3ds_input.c +++ b/src/input/n3ds_input.c @@ -22,36 +22,45 @@ #include "touchpad_bgr.h" #include <3ds.h> -#include #include +#include #include -#define QUIT_BUTTONS (PLAY_FLAG|BACK_FLAG|LB_FLAG|RB_FLAG) -#define TOUCH_GAMEPAD_BUTTONS (SPECIAL_FLAG|LS_CLK_FLAG|RS_CLK_FLAG) -#define TOUCH_MOUSEPAD_BUTTONS (BUTTON_LEFT|BUTTON_RIGHT) -#define SUPPORTED_BUTTONS (A_FLAG|B_FLAG|X_FLAG|Y_FLAG|\ - RIGHT_FLAG|LEFT_FLAG|UP_FLAG|DOWN_FLAG|RB_FLAG|LB_FLAG|\ - LS_CLK_FLAG|RS_CLK_FLAG|BACK_FLAG|PLAY_FLAG|SPECIAL_FLAG) +#define QUIT_BUTTONS (PLAY_FLAG | BACK_FLAG | LB_FLAG | RB_FLAG) +#define TOUCH_GAMEPAD_BUTTONS (SPECIAL_FLAG | LS_CLK_FLAG | RS_CLK_FLAG) +#define TOUCH_MOUSEPAD_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT) +#define SUPPORTED_BUTTONS \ + (A_FLAG | B_FLAG | X_FLAG | Y_FLAG | RIGHT_FLAG | LEFT_FLAG | UP_FLAG | \ + DOWN_FLAG | RB_FLAG | LB_FLAG | LS_CLK_FLAG | RS_CLK_FLAG | BACK_FLAG | \ + PLAY_FLAG | SPECIAL_FLAG) #define N3DS_ANALOG_MAX 150 #define N3DS_C_STICK_MAX 100 #define N3DS_ANALOG_POS_FACTOR 5 #define N3DS_MOUSEPAD_SENSITIVITY 3 -typedef void(*TouchTypeHandler)(touchPosition touch); +typedef void (*TouchTypeHandler)(touchPosition touch); typedef struct _GAMEPAD_STATE { - unsigned char leftTrigger, rightTrigger; - short leftStickX, leftStickY; - short rightStickX, rightStickY; - int buttons, mouse_buttons; - u16 touchpadX, touchpadY; - bool touchpad_active, key_active; - enum N3dsTouchType ttype; - TouchTypeHandler ttype_handler; + unsigned char leftTrigger, rightTrigger; + short leftStickX, leftStickY; + short rightStickX, rightStickY; + int buttons, mouse_buttons; + u16 touchpadX, touchpadY; + bool touchpad_active, key_active, ds_touch_active; + enum N3dsTouchType ttype; + TouchTypeHandler ttype_handler; + float accel_vector_x, accel_vector_y, accel_vector_z; + float gyro_rate_x, gyro_rate_y, gyro_rate_z; } GAMEPAD_STATE; static GAMEPAD_STATE gamepad_state, previous_state; static const int activeGamepadMask = 1; +static float gyro_coeff = 0; +// Note: This was found experimentally and may need a calibration option in +// settings +static float accel_coeff = 52.0; +bool enable_gyro = false; +bool enable_accel = false; static u32 SWAP_A = KEY_A; static u32 SWAP_B = KEY_B; @@ -64,246 +73,316 @@ static u32 SWAP_ZL = KEY_ZL; static u32 SWAP_ZR = KEY_ZR; static void add_gamepad() { - unsigned short capabilities = 0; - unsigned char type = LI_CTYPE_NINTENDO; - LiSendControllerArrivalEvent(0, activeGamepadMask, type, SUPPORTED_BUTTONS, capabilities); + unsigned short capabilities = LI_CCAP_ACCEL | LI_CCAP_GYRO; + unsigned char type = LI_CTYPE_NINTENDO; + LiSendControllerArrivalEvent(0, activeGamepadMask, type, SUPPORTED_BUTTONS, + capabilities); } static void remove_gamepad() { - LiSendMultiControllerEvent(0, ~activeGamepadMask, 0, 0, 0, 0, 0, 0, 0); + LiSendMultiControllerEvent(0, ~activeGamepadMask, 0, 0, 0, 0, 0, 0, 0); } void n3dsinput_init(bool swap_face_buttons, bool swap_triggers_and_shoulders) { - hidInit(); - add_gamepad(); - gamepad_state.ttype = DEBUG_PRINT; - if (swap_face_buttons) { - SWAP_A = KEY_B; - SWAP_B = KEY_A; - SWAP_X = KEY_Y; - SWAP_Y = KEY_X; - } - if (swap_triggers_and_shoulders) { - SWAP_L = KEY_ZL; - SWAP_R = KEY_ZR; - SWAP_ZL = KEY_L; - SWAP_ZR = KEY_R; - } - gfxSetDoubleBuffering(GFX_BOTTOM, false); + hidInit(); + HIDUSER_GetGyroscopeRawToDpsCoefficient(&gyro_coeff); + add_gamepad(); + gamepad_state.ttype = DISABLED; + if (swap_face_buttons) { + SWAP_A = KEY_B; + SWAP_B = KEY_A; + SWAP_X = KEY_Y; + SWAP_Y = KEY_X; + } + if (swap_triggers_and_shoulders) { + SWAP_L = KEY_ZL; + SWAP_R = KEY_ZR; + SWAP_ZL = KEY_L; + SWAP_ZR = KEY_R; + } + gfxSetDoubleBuffering(GFX_BOTTOM, false); } -void n3dsinput_cleanup() { - remove_gamepad(); -} +void n3dsinput_cleanup() { remove_gamepad(); } static inline int n3ds_to_li_button(u32 key_in, u32 key_n3ds, int key_li) { - return ((key_in & key_n3ds) / key_n3ds) * key_li; + return ((key_in & key_n3ds) / key_n3ds) * key_li; } static inline int n3ds_to_li_buttons(u32 key_n3ds) { - int li_out = 0; - li_out |= n3ds_to_li_button(key_n3ds, SWAP_A, A_FLAG); - li_out |= n3ds_to_li_button(key_n3ds, SWAP_B, B_FLAG); - li_out |= n3ds_to_li_button(key_n3ds, KEY_SELECT, BACK_FLAG); - li_out |= n3ds_to_li_button(key_n3ds, KEY_START, PLAY_FLAG); - li_out |= n3ds_to_li_button(key_n3ds, KEY_DRIGHT, RIGHT_FLAG); - li_out |= n3ds_to_li_button(key_n3ds, KEY_DLEFT, LEFT_FLAG); - li_out |= n3ds_to_li_button(key_n3ds, KEY_DUP, UP_FLAG); - li_out |= n3ds_to_li_button(key_n3ds, KEY_DDOWN, DOWN_FLAG); - li_out |= n3ds_to_li_button(key_n3ds, SWAP_R, RB_FLAG); - li_out |= n3ds_to_li_button(key_n3ds, SWAP_L, LB_FLAG); - li_out |= n3ds_to_li_button(key_n3ds, SWAP_X, X_FLAG); - li_out |= n3ds_to_li_button(key_n3ds, SWAP_Y, Y_FLAG); - return li_out; + int li_out = 0; + li_out |= n3ds_to_li_button(key_n3ds, SWAP_A, A_FLAG); + li_out |= n3ds_to_li_button(key_n3ds, SWAP_B, B_FLAG); + li_out |= n3ds_to_li_button(key_n3ds, KEY_SELECT, BACK_FLAG); + li_out |= n3ds_to_li_button(key_n3ds, KEY_START, PLAY_FLAG); + li_out |= n3ds_to_li_button(key_n3ds, KEY_DRIGHT, RIGHT_FLAG); + li_out |= n3ds_to_li_button(key_n3ds, KEY_DLEFT, LEFT_FLAG); + li_out |= n3ds_to_li_button(key_n3ds, KEY_DUP, UP_FLAG); + li_out |= n3ds_to_li_button(key_n3ds, KEY_DDOWN, DOWN_FLAG); + li_out |= n3ds_to_li_button(key_n3ds, SWAP_R, RB_FLAG); + li_out |= n3ds_to_li_button(key_n3ds, SWAP_L, LB_FLAG); + li_out |= n3ds_to_li_button(key_n3ds, SWAP_X, X_FLAG); + li_out |= n3ds_to_li_button(key_n3ds, SWAP_Y, Y_FLAG); + return li_out; } static inline unsigned char n3ds_to_li_trigger(u32 key_in, u32 key_n3ds) { - return ((key_in & key_n3ds) / key_n3ds) * 255UL; + return ((key_in & key_n3ds) / key_n3ds) * 255UL; } static inline int scale_n3ds_axis(int axis_n3ds, int axis_max) { if (axis_n3ds > axis_max) { return SHRT_MAX; - } - else if (axis_n3ds < -axis_max) { + } else if (axis_n3ds < -axis_max) { return -SHRT_MAX; } return (axis_n3ds * SHRT_MAX) / axis_max; } static inline bool joystick_state_changed(short before, short after) { - return (before / N3DS_ANALOG_POS_FACTOR) != (after / N3DS_ANALOG_POS_FACTOR); + return (before / N3DS_ANALOG_POS_FACTOR) != + (after / N3DS_ANALOG_POS_FACTOR); } static inline bool gamepad_state_changed() { - if ((previous_state.buttons != gamepad_state.buttons) || - (previous_state.leftTrigger != gamepad_state.leftTrigger) || - (previous_state.rightTrigger != gamepad_state.rightTrigger)) { - return true; - } - - if (joystick_state_changed(previous_state.leftStickX, gamepad_state.leftStickX) || - joystick_state_changed(previous_state.leftStickY, gamepad_state.leftStickY)){ - return true; - } - - if (joystick_state_changed(previous_state.rightStickX, gamepad_state.rightStickX) || - joystick_state_changed(previous_state.rightStickY, gamepad_state.rightStickY)) { - return true; - } - - return false; + if ((previous_state.buttons != gamepad_state.buttons) || + (previous_state.leftTrigger != gamepad_state.leftTrigger) || + (previous_state.rightTrigger != gamepad_state.rightTrigger)) { + return true; + } + + if (joystick_state_changed(previous_state.leftStickX, + gamepad_state.leftStickX) || + joystick_state_changed(previous_state.leftStickY, + gamepad_state.leftStickY)) { + return true; + } + + if (joystick_state_changed(previous_state.rightStickX, + gamepad_state.rightStickX) || + joystick_state_changed(previous_state.rightStickY, + gamepad_state.rightStickY)) { + return true; + } + + return false; +} + +static inline bool accelerometer_state_changed() { + if ((previous_state.accel_vector_x != gamepad_state.accel_vector_x) || + (previous_state.accel_vector_y != gamepad_state.accel_vector_y) || + (previous_state.accel_vector_z != gamepad_state.accel_vector_z)) { + return true; + } + return false; +} + +static inline bool gyroscope_state_changed() { + if ((previous_state.gyro_rate_x != gamepad_state.gyro_rate_x) || + (previous_state.gyro_rate_y != gamepad_state.gyro_rate_y) || + (previous_state.gyro_rate_z != gamepad_state.gyro_rate_z)) { + return true; + } + return false; } static inline bool change_touchpad_pressed(touchPosition touch) { - return touch.py >= 205 && touch.px >= 285; + if (gamepad_state.ttype == DS_TOUCH) { + return false; + } + return touch.py >= 205 && touch.px >= 285; } static void touch_gamepad_handler(touchPosition touch) { - if (touch.py >= 120) { - gamepad_state.buttons |= SPECIAL_FLAG; - return; - } - - if (touch.px < 235) - gamepad_state.buttons |= LS_CLK_FLAG; - if (touch.px > 104) - gamepad_state.buttons |= RS_CLK_FLAG; + if (touch.py >= 120) { + gamepad_state.buttons |= SPECIAL_FLAG; + return; + } + + if (touch.px < 235) + gamepad_state.buttons |= LS_CLK_FLAG; + if (touch.px > 104) + gamepad_state.buttons |= RS_CLK_FLAG; } static void touch_mouse_handler(touchPosition touch) { - if (touch.py < 175) { - gamepad_state.touchpad_active = true; - gamepad_state.touchpadX = touch.px; - gamepad_state.touchpadY = touch.py; - } - else if (touch.py >= 205 && touch.px <= 35) { - gamepad_state.key_active = true; - } - else if (touch.px > 160) { - gamepad_state.mouse_buttons = BUTTON_RIGHT; - } - else { + if (touch.py < 175) { + gamepad_state.touchpad_active = true; + gamepad_state.touchpadX = touch.px; + gamepad_state.touchpadY = touch.py; + } else if (touch.py >= 205 && touch.px <= 35) { + gamepad_state.key_active = true; + } else if (touch.px > 160) { + gamepad_state.mouse_buttons = BUTTON_RIGHT; + } else { + gamepad_state.mouse_buttons = BUTTON_LEFT; + } +} + +static void touch_ds_handler(touchPosition touch) { + gamepad_state.ds_touch_active = true; gamepad_state.mouse_buttons = BUTTON_LEFT; - } + LiSendMousePositionEvent(touch.px, touch.py + GSP_SCREEN_WIDTH, + GSP_SCREEN_HEIGHT_BOTTOM, 2 * GSP_SCREEN_WIDTH); } -void n3dsinput_set_touch(enum N3dsTouchType ttype) -{ - if (gamepad_state.ttype == ttype) { - return; - } +void n3dsinput_set_touch(enum N3dsTouchType ttype) { + if (gamepad_state.ttype == ttype) { + return; + } - gamepad_state.ttype = ttype; - u8* gfxbtmadr = gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL); - switch (gamepad_state.ttype) { + gamepad_state.ttype = ttype; + u8 *gfxbtmadr = gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL); + switch (gamepad_state.ttype) { case GAMEPAD: - memcpy(gfxbtmadr, gamepad_bgr, gamepad_bgr_size); - gamepad_state.ttype_handler = touch_gamepad_handler; - break; + memcpy(gfxbtmadr, gamepad_bgr, gamepad_bgr_size); + gamepad_state.ttype_handler = touch_gamepad_handler; + break; case MOUSEPAD: - memcpy(gfxbtmadr, touchpad_bgr, touchpad_bgr_size); - gamepad_state.ttype_handler = touch_mouse_handler; - break; + memcpy(gfxbtmadr, touchpad_bgr, touchpad_bgr_size); + gamepad_state.ttype_handler = touch_mouse_handler; + break; + case DS_TOUCH: + gamepad_state.ttype_handler = touch_ds_handler; + break; default: - GSPGPU_FramebufferFormat px_fmt = gfxGetScreenFormat(GFX_BOTTOM); - int pixel_size = gspGetBytesPerPixel(px_fmt); - memset(gfxbtmadr, 0, GSP_SCREEN_HEIGHT_BOTTOM * GSP_SCREEN_WIDTH * pixel_size); - gamepad_state.ttype_handler = NULL; - break; - } - gfxFlushBuffers(); - gfxScreenSwapBuffers(GFX_BOTTOM, false); + GSPGPU_FramebufferFormat px_fmt = gfxGetScreenFormat(GFX_BOTTOM); + int pixel_size = gspGetBytesPerPixel(px_fmt); + memset(gfxbtmadr, 0, + GSP_SCREEN_HEIGHT_BOTTOM * GSP_SCREEN_WIDTH * pixel_size); + gamepad_state.ttype_handler = NULL; + break; + } + gfxFlushBuffers(); + gfxScreenSwapBuffers(GFX_BOTTOM, false); } static inline void n3dsinput_cycle_touch() { - enum N3dsTouchType new_type = gamepad_state.ttype + 1; - if (new_type == DEBUG_PRINT) { - new_type = 0; - } - n3dsinput_set_touch(new_type); + enum N3dsTouchType new_type = gamepad_state.ttype + 1; + if (new_type == DISABLED) { + new_type = 0; + } + n3dsinput_set_touch(new_type); } static inline void n3dsinput_handle_touch() { - if (gamepad_state.ttype_handler == NULL) { - // Debug mode - return; - } - - touchPosition touch; - hidTouchRead(&touch); - if (change_touchpad_pressed(touch)) { - n3dsinput_cycle_touch(); - } - else { - gamepad_state.ttype_handler(touch); - } + if (gamepad_state.ttype_handler == NULL) { + // Debug mode + return; + } + + touchPosition touch; + hidTouchRead(&touch); + if (change_touchpad_pressed(touch)) { + n3dsinput_cycle_touch(); + } else { + gamepad_state.ttype_handler(touch); + } } int n3dsinput_handle_event() { - hidScanInput(); - u32 kDown = hidKeysDown(); - u32 kUp = hidKeysUp(); - previous_state = gamepad_state; - - if (kDown) { - gamepad_state.buttons |= n3ds_to_li_buttons(kDown); - gamepad_state.leftTrigger |= n3ds_to_li_trigger(kDown, SWAP_ZL); - gamepad_state.rightTrigger |= n3ds_to_li_trigger(kDown, SWAP_ZR); - } - if (kUp) { - gamepad_state.buttons &= ~n3ds_to_li_buttons(kUp); - gamepad_state.leftTrigger &= ~n3ds_to_li_trigger(kUp, SWAP_ZL); - gamepad_state.rightTrigger &= ~n3ds_to_li_trigger(kUp, SWAP_ZR); - } - - if ((gamepad_state.buttons & QUIT_BUTTONS) == QUIT_BUTTONS) - return 1; - - circlePosition cpad_pos; - hidCircleRead(&cpad_pos); - gamepad_state.leftStickX = scale_n3ds_axis(cpad_pos.dx, N3DS_ANALOG_MAX); - gamepad_state.leftStickY = scale_n3ds_axis(cpad_pos.dy, N3DS_ANALOG_MAX); - - circlePosition cstick_pos; - hidCstickRead(&cstick_pos); - gamepad_state.rightStickX = scale_n3ds_axis(cstick_pos.dx, N3DS_C_STICK_MAX); - gamepad_state.rightStickY = scale_n3ds_axis(cstick_pos.dy, N3DS_C_STICK_MAX); - - if (kDown & KEY_TOUCH) { - n3dsinput_handle_touch(); - if (gamepad_state.key_active) { - LiSendKeyboardEvent(0x5B, KEY_ACTION_DOWN, 0); + hidScanInput(); + u32 kDown = hidKeysDown(); + u32 kUp = hidKeysUp(); + previous_state = gamepad_state; + + if (kDown) { + gamepad_state.buttons |= n3ds_to_li_buttons(kDown); + gamepad_state.leftTrigger |= n3ds_to_li_trigger(kDown, SWAP_ZL); + gamepad_state.rightTrigger |= n3ds_to_li_trigger(kDown, SWAP_ZR); } - else if (gamepad_state.mouse_buttons) { - LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, gamepad_state.mouse_buttons); + if (kUp) { + gamepad_state.buttons &= ~n3ds_to_li_buttons(kUp); + gamepad_state.leftTrigger &= ~n3ds_to_li_trigger(kUp, SWAP_ZL); + gamepad_state.rightTrigger &= ~n3ds_to_li_trigger(kUp, SWAP_ZR); } - } - else if (kUp & KEY_TOUCH) { - gamepad_state.buttons &= ~TOUCH_GAMEPAD_BUTTONS; - gamepad_state.touchpad_active = false; - if (gamepad_state.key_active) { - LiSendKeyboardEvent(0x5B, KEY_ACTION_UP, 0); - gamepad_state.key_active = false; + + if ((gamepad_state.buttons & QUIT_BUTTONS) == QUIT_BUTTONS) + return 1; + + circlePosition cpad_pos; + hidCircleRead(&cpad_pos); + gamepad_state.leftStickX = scale_n3ds_axis(cpad_pos.dx, N3DS_ANALOG_MAX); + gamepad_state.leftStickY = scale_n3ds_axis(cpad_pos.dy, N3DS_ANALOG_MAX); + + circlePosition cstick_pos; + hidCstickRead(&cstick_pos); + gamepad_state.rightStickX = + scale_n3ds_axis(cstick_pos.dx, N3DS_C_STICK_MAX); + gamepad_state.rightStickY = + scale_n3ds_axis(cstick_pos.dy, N3DS_C_STICK_MAX); + + if (kDown & KEY_TOUCH) { + n3dsinput_handle_touch(); + if (gamepad_state.key_active) { + LiSendKeyboardEvent(0x5B, KEY_ACTION_DOWN, 0); + } else if (gamepad_state.mouse_buttons) { + LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, + gamepad_state.mouse_buttons); + } + } else if (kUp & KEY_TOUCH) { + gamepad_state.buttons &= ~TOUCH_GAMEPAD_BUTTONS; + gamepad_state.touchpad_active = false; + gamepad_state.ds_touch_active = false; + if (gamepad_state.key_active) { + LiSendKeyboardEvent(0x5B, KEY_ACTION_UP, 0); + gamepad_state.key_active = false; + } else if (gamepad_state.mouse_buttons) { + LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, + gamepad_state.mouse_buttons); + gamepad_state.mouse_buttons = 0; + } + } else if (gamepad_state.touchpad_active) { + touchPosition touch; + hidTouchRead(&touch); + gamepad_state.touchpadX = touch.px; + gamepad_state.touchpadY = touch.py; + short deltaX = N3DS_MOUSEPAD_SENSITIVITY * + (gamepad_state.touchpadX - previous_state.touchpadX); + short deltaY = N3DS_MOUSEPAD_SENSITIVITY * + (gamepad_state.touchpadY - previous_state.touchpadY); + LiSendMouseMoveEvent(deltaX, deltaY); + } else if (gamepad_state.ds_touch_active) { + touchPosition touch; + hidTouchRead(&touch); + LiSendMousePositionEvent(touch.px, touch.py + GSP_SCREEN_WIDTH, + GSP_SCREEN_HEIGHT_BOTTOM, + 2 * GSP_SCREEN_WIDTH); } - else if (gamepad_state.mouse_buttons) { - LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, gamepad_state.mouse_buttons); - gamepad_state.mouse_buttons = 0; + + if (gamepad_state_changed()) { + LiSendMultiControllerEvent( + 0, activeGamepadMask, gamepad_state.buttons, + gamepad_state.leftTrigger, gamepad_state.rightTrigger, + gamepad_state.leftStickX, gamepad_state.leftStickY, + gamepad_state.rightStickX, gamepad_state.rightStickY); } - } - else if (gamepad_state.touchpad_active) { - touchPosition touch; - hidTouchRead(&touch); - gamepad_state.touchpadX = touch.px; - gamepad_state.touchpadY = touch.py; - short deltaX = N3DS_MOUSEPAD_SENSITIVITY * (gamepad_state.touchpadX - previous_state.touchpadX); - short deltaY = N3DS_MOUSEPAD_SENSITIVITY * (gamepad_state.touchpadY - previous_state.touchpadY); - LiSendMouseMoveEvent(deltaX, deltaY); - } - - if (gamepad_state_changed()) { - LiSendMultiControllerEvent(0, activeGamepadMask, gamepad_state.buttons, gamepad_state.leftTrigger, gamepad_state.rightTrigger, gamepad_state.leftStickX, gamepad_state.leftStickY, gamepad_state.rightStickX, gamepad_state.rightStickY); - } - - return 0; + + if (enable_accel) { + accelVector accel_vector; + hidAccelRead(&accel_vector); + gamepad_state.accel_vector_x = floor(accel_vector.x / accel_coeff); + gamepad_state.accel_vector_y = floor(accel_vector.y / accel_coeff); + gamepad_state.accel_vector_z = floor(accel_vector.z / accel_coeff); + if (accelerometer_state_changed()) { + LiSendControllerMotionEvent( + 0, LI_MOTION_TYPE_ACCEL, gamepad_state.accel_vector_x, + gamepad_state.accel_vector_y, gamepad_state.accel_vector_z); + } + } + + if (enable_gyro) { + angularRate gyro_rate; + hidGyroRead(&gyro_rate); + gamepad_state.gyro_rate_x = floor(-1 * gyro_rate.x / gyro_coeff); + gamepad_state.gyro_rate_y = floor(gyro_rate.y / gyro_coeff); + gamepad_state.gyro_rate_z = floor(-1 * gyro_rate.z / gyro_coeff); + if (gyroscope_state_changed()) { + LiSendControllerMotionEvent( + 0, LI_MOTION_TYPE_GYRO, gamepad_state.gyro_rate_x, + gamepad_state.gyro_rate_y, gamepad_state.gyro_rate_z); + } + } + + return 0; } diff --git a/src/input/n3ds_input.h b/src/input/n3ds_input.h index c4f02dd9..c6cea5f8 100644 --- a/src/input/n3ds_input.h +++ b/src/input/n3ds_input.h @@ -19,7 +19,9 @@ #include -enum N3dsTouchType { GAMEPAD, MOUSEPAD, DEBUG_PRINT }; +enum N3dsTouchType { GAMEPAD, MOUSEPAD, DISABLED, DS_TOUCH }; +extern bool enable_gyro; +extern bool enable_accel; void n3dsinput_init(bool set_face_swap, bool set_trigger_swap); void n3dsinput_cleanup(); diff --git a/src/input/sdl.h b/src/input/sdl.h index 6928e355..ef0abde2 100644 --- a/src/input/sdl.h +++ b/src/input/sdl.h @@ -22,83 +22,6 @@ extern int sdl_gamepads; -static const short keyCodes1[] = { - 0, //SDLK_EXCLAIM - 0, //SDLK_QUOTEDBL - 0, //SDLK_HASH - 0, //SDLK_DOLLAR - 0, //SDLK_PERCENT - 0, //SDLK_AMPERSAND - 0xDE, //SDLK_QUOTE - 0, //SDLK_LEFTPAREN - 0, //SDLK_RIGHTPAREN - 0, //SDLK_ASTERISK - 0, //SDLK_PLUS - 0xBC, //SDLK_COMMA - 0xBD, //SDLK_MINUS - 0xBE, //SDLK_PERIOD - 0xBF, //SDLK_SLASH -}; - -static const short keyCodes2[] = { - 0, //SDLK_COLON - 0xBA, //SDLK_SEMICOLON - 0, //SDLK_LESS - 0xBB, //SDLK_EQUALS - 0, //SDLK_GREATER - 0, //SDLK_QUESTION - 0, //SDLK_AT -}; - -static const short keyCodes3[] = { - 0xDB, //SDLK_LEFTBRACKET - 0xDC, //SDLK_BACKSLASH - 0xDD, //SDLK_RIGHTBRACKET - 0, //SDLK_CARET - 0, //SDLK_UNDERSCORE - 0xC0, //SDLK_BACKQUOTE -}; - -static const short keyCodes4[] = { - 0x14, //SDLK_CAPSLOCK - 0x70, //SDLK_F1 - 0x71, //SDLK_F2 - 0x72, //SDLK_F3 - 0x73, //SDLK_F4 - 0x74, //SDLK_F5 - 0x75, //SDLK_F6 - 0x76, //SDLK_F7 - 0x77, //SDLK_F8 - 0x78, //SDLK_F9 - 0x79, //SDLK_F10 - 0x7A, //SDLK_F11 - 0x7B, //SDLK_F12 - 0, //SDLK_PRINTSCREEN - 0x91, //SDLK_SCROLLLOCK - 0x13, //SDLK_PAUSE - 0x2D, //SDLK_INSERT - 0x24, //SDLK_HOME - 0x21, //SDLK_PAGEUP - 0, //Not used - 0x23, //SDLK_END - 0x22, //SDLK_PAGEDOWN - 0x27, //SDLK_RIGHT - 0x25, //SDLK_LEFT - 0x28, //SDLK_DOWN - 0x26, //SDLK_UP -}; - -static const short keyCodes5[] = { - 0xA2, //SDLK_LCTRL - 0xA0, //SDLK_LSHIFT - 0xA4, //SDLK_LALT - 0x5B, //SDLK_LGUI - 0xA3, //SDLK_RCTRL - 0xA1, //SDLK_RSHIFT - 0xA5, //SDLK_RALT - 0x5C, //SDLK_RGUI -}; - void sdlinput_init(char* mappings); int sdlinput_handle_event(SDL_Window* window, SDL_Event* event); void sdlinput_rumble(unsigned short controller_id, unsigned short low_freq_motor, unsigned short high_freq_motor); diff --git a/src/input/sdl_input.c b/src/input/sdl_input.c index 8177d750..c2305501 100644 --- a/src/input/sdl_input.c +++ b/src/input/sdl_input.c @@ -62,11 +62,232 @@ typedef struct _GAMEPAD_STATE { static GAMEPAD_STATE gamepads[MAX_GAMEPADS]; -static int keyboard_modifiers; static int activeGamepadMask = 0; int sdl_gamepads = 0; +#define VK_0 0x30 +#define VK_A 0x41 + +// These are real Windows VK_* codes +#ifndef VK_F1 +#define VK_F1 0x70 +#define VK_F13 0x7C +#define VK_NUMPAD0 0x60 +#endif + +int vk_for_sdl_scancode(SDL_Scancode scancode) { + // Set keycode. We explicitly use scancode here because GFE will try to correct + // for AZERTY layouts on the host but it depends on receiving VK_ values matching + // a QWERTY layout to work. + if (scancode >= SDL_SCANCODE_1 && scancode <= SDL_SCANCODE_9) { + // SDL defines SDL_SCANCODE_0 > SDL_SCANCODE_9, so we need to handle that manually + return (scancode - SDL_SCANCODE_1) + VK_0 + 1; + } + else if (scancode >= SDL_SCANCODE_A && scancode <= SDL_SCANCODE_Z) { + return (scancode - SDL_SCANCODE_A) + VK_A; + } + else if (scancode >= SDL_SCANCODE_F1 && scancode <= SDL_SCANCODE_F12) { + return (scancode - SDL_SCANCODE_F1) + VK_F1; + } + else if (scancode >= SDL_SCANCODE_F13 && scancode <= SDL_SCANCODE_F24) { + return (scancode - SDL_SCANCODE_F13) + VK_F13; + } + else if (scancode >= SDL_SCANCODE_KP_1 && scancode <= SDL_SCANCODE_KP_9) { + // SDL defines SDL_SCANCODE_KP_0 > SDL_SCANCODE_KP_9, so we need to handle that manually + return (scancode - SDL_SCANCODE_KP_1) + VK_NUMPAD0 + 1; + } + else { + switch (scancode) { + case SDL_SCANCODE_BACKSPACE: + return 0x08; + + case SDL_SCANCODE_TAB: + return 0x09; + + case SDL_SCANCODE_CLEAR: + return 0x0C; + + case SDL_SCANCODE_KP_ENTER: + case SDL_SCANCODE_RETURN: + return 0x0D; + + case SDL_SCANCODE_PAUSE: + return 0x13; + + case SDL_SCANCODE_CAPSLOCK: + return 0x14; + + case SDL_SCANCODE_ESCAPE: + return 0x1B; + + case SDL_SCANCODE_SPACE: + return 0x20; + + case SDL_SCANCODE_PAGEUP: + return 0x21; + + case SDL_SCANCODE_PAGEDOWN: + return 0x22; + + case SDL_SCANCODE_END: + return 0x23; + + case SDL_SCANCODE_HOME: + return 0x24; + + case SDL_SCANCODE_LEFT: + return 0x25; + + case SDL_SCANCODE_UP: + return 0x26; + + case SDL_SCANCODE_RIGHT: + return 0x27; + + case SDL_SCANCODE_DOWN: + return 0x28; + + case SDL_SCANCODE_SELECT: + return 0x29; + + case SDL_SCANCODE_EXECUTE: + return 0x2B; + + case SDL_SCANCODE_PRINTSCREEN: + return 0x2C; + + case SDL_SCANCODE_INSERT: + return 0x2D; + + case SDL_SCANCODE_DELETE: + return 0x2E; + + case SDL_SCANCODE_HELP: + return 0x2F; + + case SDL_SCANCODE_KP_0: + // See comment above about why we only handle SDL_SCANCODE_KP_0 here + return VK_NUMPAD0; + + case SDL_SCANCODE_0: + // See comment above about why we only handle SDL_SCANCODE_0 here + return VK_0; + + case SDL_SCANCODE_KP_MULTIPLY: + return 0x6A; + + case SDL_SCANCODE_KP_PLUS: + return 0x6B; + + case SDL_SCANCODE_KP_COMMA: + return 0x6C; + + case SDL_SCANCODE_KP_MINUS: + return 0x6D; + + case SDL_SCANCODE_KP_PERIOD: + return 0x6E; + + case SDL_SCANCODE_KP_DIVIDE: + return 0x6F; + + case SDL_SCANCODE_NUMLOCKCLEAR: + return 0x90; + + case SDL_SCANCODE_SCROLLLOCK: + return 0x91; + + case SDL_SCANCODE_LSHIFT: + return 0xA0; + + case SDL_SCANCODE_RSHIFT: + return 0xA1; + + case SDL_SCANCODE_LCTRL: + return 0xA2; + + case SDL_SCANCODE_RCTRL: + return 0xA3; + + case SDL_SCANCODE_LALT: + return 0xA4; + + case SDL_SCANCODE_RALT: + return 0xA5; + + case SDL_SCANCODE_LGUI: + return 0x5B; + + case SDL_SCANCODE_RGUI: + return 0x5C; + + case SDL_SCANCODE_APPLICATION: + return 0x5D; + + case SDL_SCANCODE_AC_BACK: + return 0xA6; + + case SDL_SCANCODE_AC_FORWARD: + return 0xA7; + + case SDL_SCANCODE_AC_REFRESH: + return 0xA8; + + case SDL_SCANCODE_AC_STOP: + return 0xA9; + + case SDL_SCANCODE_AC_SEARCH: + return 0xAA; + + case SDL_SCANCODE_AC_BOOKMARKS: + return 0xAB; + + case SDL_SCANCODE_AC_HOME: + return 0xAC; + + case SDL_SCANCODE_SEMICOLON: + return 0xBA; + + case SDL_SCANCODE_EQUALS: + return 0xBB; + + case SDL_SCANCODE_COMMA: + return 0xBC; + + case SDL_SCANCODE_MINUS: + return 0xBD; + + case SDL_SCANCODE_PERIOD: + return 0xBE; + + case SDL_SCANCODE_SLASH: + return 0xBF; + + case SDL_SCANCODE_GRAVE: + return 0xC0; + + case SDL_SCANCODE_LEFTBRACKET: + return 0xDB; + + case SDL_SCANCODE_BACKSLASH: + return 0xDC; + + case SDL_SCANCODE_RIGHTBRACKET: + return 0xDD; + + case SDL_SCANCODE_APOSTROPHE: + return 0xDE; + + case SDL_SCANCODE_NONUSBACKSLASH: + return 0xE2; + + default: + return 0; + } + } +} + static void send_controller_arrival(PGAMEPAD_STATE state) { #if SDL_VERSION_ATLEAST(2, 0, 18) unsigned int supportedButtonFlags = 0; @@ -277,57 +498,30 @@ int sdlinput_handle_event(SDL_Window* window, SDL_Event* event) { return 0; case SDL_KEYDOWN: case SDL_KEYUP: - button = event->key.keysym.sym; - if (button >= 0x21 && button <= 0x2f) - button = keyCodes1[button - 0x21]; - else if (button >= 0x3a && button <= 0x40) - button = keyCodes2[button - 0x3a]; - else if (button >= 0x5b && button <= 0x60) - button = keyCodes3[button - 0x5b]; - else if (button >= 0x40000039 && button < 0x40000039 + sizeof(keyCodes4)) - button = keyCodes4[button - 0x40000039]; - else if (button >= 0x400000E0 && button <= 0x400000E7) - button = keyCodes5[button - 0x400000E0]; - else if (button >= 0x61 && button <= 0x7a) - button -= 0x20; - else if (button == 0x7f) - button = 0x2e; - - int modifier = 0; - switch (event->key.keysym.sym) { - case SDLK_RSHIFT: - case SDLK_LSHIFT: - modifier = MODIFIER_SHIFT; - break; - case SDLK_RALT: - case SDLK_LALT: - modifier = MODIFIER_ALT; - break; - case SDLK_RCTRL: - case SDLK_LCTRL: - modifier = MODIFIER_CTRL; - break; - case SDLK_RGUI: - case SDLK_LGUI: - modifier = MODIFIER_META; - break; - } + button = vk_for_sdl_scancode(event->key.keysym.scancode); - if (modifier != 0) { - if (event->type==SDL_KEYDOWN) - keyboard_modifiers |= modifier; - else - keyboard_modifiers &= ~modifier; + int modifiers = 0; + if (event->key.keysym.mod & KMOD_CTRL) { + modifiers |= MODIFIER_CTRL; + } + if (event->key.keysym.mod & KMOD_ALT) { + modifiers |= MODIFIER_ALT; + } + if (event->key.keysym.mod & KMOD_SHIFT) { + modifiers |= MODIFIER_SHIFT; + } + if (event->key.keysym.mod & KMOD_GUI) { + modifiers |= MODIFIER_META; } - LiSendKeyboardEvent(0x80 << 8 | button, event->type==SDL_KEYDOWN?KEY_ACTION_DOWN:KEY_ACTION_UP, keyboard_modifiers); + LiSendKeyboardEvent(0x80 << 8 | button, event->type==SDL_KEYDOWN?KEY_ACTION_DOWN:KEY_ACTION_UP, modifiers); // Quit the stream if all the required quit keys are down - if ((keyboard_modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == QUIT_KEY && event->type==SDL_KEYUP) + if ((modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == QUIT_KEY && event->type==SDL_KEYUP) return SDL_QUIT_APPLICATION; - else if ((keyboard_modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == FULLSCREEN_KEY && event->type==SDL_KEYUP) + else if ((modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == FULLSCREEN_KEY && event->type==SDL_KEYUP) return SDL_TOGGLE_FULLSCREEN; - else if ((keyboard_modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == UNGRAB_KEY && event->type==SDL_KEYUP) + else if ((modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == UNGRAB_KEY && event->type==SDL_KEYUP) return SDL_GetRelativeMouseMode() ? SDL_MOUSE_UNGRAB : SDL_MOUSE_GRAB; break; case SDL_FINGERDOWN: diff --git a/src/main.c b/src/main.c index 07d032c5..37b28b39 100644 --- a/src/main.c +++ b/src/main.c @@ -197,7 +197,7 @@ static void help() { printf("\t-4k\t\t\tUse 3840x2160 resolution\n"); printf("\t-width \t\tHorizontal resolution (default 1280)\n"); printf("\t-height \tVertical resolution (default 720)\n"); - #if defined(HAVE_PI) | defined(HAVE_MMAL) + #ifdef HAVE_EMBEDDED printf("\t-rotate \tRotate display: 0/90/180/270 (default 0)\n"); #endif printf("\t-fps \t\tSpecify the fps to use (default 60)\n"); diff --git a/src/n3ds/n3ds_connection.c b/src/n3ds/n3ds_connection.c index f439e2f1..6c504176 100644 --- a/src/n3ds/n3ds_connection.c +++ b/src/n3ds/n3ds_connection.c @@ -18,6 +18,7 @@ */ #include "n3ds_connection.h" +#include "../input/n3ds_input.h" #include <3ds.h> #include @@ -26,6 +27,7 @@ bool n3ds_connection_closed = false; bool n3ds_connection_debug = false; +bool n3ds_enable_motion = false; static void connection_terminated(int errorCode) { switch (errorCode) { @@ -49,6 +51,8 @@ static void connection_terminated(int errorCode) { break; } + HIDUSER_DisableAccelerometer(); + HIDUSER_DisableGyroscope(); n3ds_connection_closed = true; } @@ -74,6 +78,33 @@ static void connection_status_update(int status) { } } +static void set_motion_event_state(unsigned short controllerNumber, unsigned char motionType, unsigned short reportRateHz) { + if (!n3ds_enable_motion){ + return; + } + + switch (motionType) { + case LI_MOTION_TYPE_ACCEL: + enable_accel = (reportRateHz > 0); + if (enable_accel) { + HIDUSER_EnableAccelerometer(); + } + else { + HIDUSER_DisableAccelerometer(); + } + break; + case LI_MOTION_TYPE_GYRO: + enable_gyro = (reportRateHz > 0); + if (enable_gyro) { + HIDUSER_EnableGyroscope(); + } + else { + HIDUSER_DisableGyroscope(); + } + break; + } +} + CONNECTION_LISTENER_CALLBACKS n3ds_connection_callbacks = { .stageStarting = NULL, .stageComplete = NULL, @@ -85,6 +116,6 @@ CONNECTION_LISTENER_CALLBACKS n3ds_connection_callbacks = { .connectionStatusUpdate = connection_status_update, .setHdrMode = NULL, .rumbleTriggers = NULL, - .setMotionEventState = NULL, + .setMotionEventState = set_motion_event_state, .setControllerLED = NULL, }; diff --git a/src/n3ds/n3ds_connection.h b/src/n3ds/n3ds_connection.h index 7192c3cc..c5d9b3e6 100644 --- a/src/n3ds/n3ds_connection.h +++ b/src/n3ds/n3ds_connection.h @@ -21,4 +21,5 @@ extern bool n3ds_connection_closed; extern bool n3ds_connection_debug; +extern bool n3ds_enable_motion; extern CONNECTION_LISTENER_CALLBACKS n3ds_connection_callbacks; diff --git a/src/n3ds_main.c b/src/n3ds_main.c index 4e455e0d..f55c38bf 100644 --- a/src/n3ds_main.c +++ b/src/n3ds_main.c @@ -19,9 +19,9 @@ #ifdef __3DS__ +#include "config.h" #include "loop.h" #include "platform_main.h" -#include "config.h" #include "n3ds/n3ds_connection.h" #include "n3ds/pair_record.h" @@ -38,23 +38,23 @@ #include #include +#include +#include +#include +#include +#include +#include #include #include -#include #include -#include -#include -#include #include -#include -#include -#include -#include +#include +#include -#define SOC_ALIGN 0x1000 +#define SOC_ALIGN 0x1000 // 0x40000 for each enet host (2 hosts total) // 0x40000 for each platform socket (2 sockets total) -#define SOC_BUFFERSIZE 0x100000 +#define SOC_BUFFERSIZE 0x100000 #define MAX_INPUT_CHAR 60 #define MAX_APP_LIST 30 @@ -64,579 +64,588 @@ static u32 *SOC_buffer = NULL; static PrintConsole topScreen; static PrintConsole bottomScreen; -static inline void wait_for_button(char* prompt) { - if (prompt == NULL) { - printf("\nPress any button to continue\n"); - } - else { - printf("\n%s\n", prompt); - } - while (aptMainLoop()) - { - gfxSwapBuffers(); - gfxFlushBuffers(); - gspWaitForVBlank(); - - hidScanInput(); - u32 kDown = hidKeysDown(); - - if (kDown) - break; - } +static inline void wait_for_button(char *prompt) { + if (prompt == NULL) { + printf("\nPress any button to continue\n"); + } else { + printf("\n%s\n", prompt); + } + while (aptMainLoop()) { + gfxSwapBuffers(); + gfxFlushBuffers(); + gspWaitForVBlank(); + + hidScanInput(); + u32 kDown = hidKeysDown(); + + if (kDown) + break; + } } -static void n3ds_exit_handler(void) -{ - // Allow users to decide when to exit - wait_for_button("Press any button to quit"); - - NDMU_UnlockState(); - NDMU_LeaveExclusiveState(); - ndmuExit(); - irrstExit(); - SOCU_ShutdownSockets(); - SOCU_CloseSockets(); - socExit(); - free(SOC_buffer); - romfsExit(); - aptExit(); - gfxExit(); - acExit(); +static void n3ds_exit_handler(void) { + // Allow users to decide when to exit + wait_for_button("Press any button to quit"); + + NDMU_UnlockState(); + NDMU_LeaveExclusiveState(); + ndmuExit(); + irrstExit(); + SOCU_ShutdownSockets(); + SOCU_CloseSockets(); + socExit(); + free(SOC_buffer); + romfsExit(); + aptExit(); + gfxExit(); + acExit(); } -static int console_selection_prompt(char* prompt, char** options, int option_count, int default_idx) -{ - int option_idx = default_idx; - int last_option_idx = -1; - while (aptMainLoop()) - { - if (option_idx != last_option_idx) - { - consoleClear(); - if (prompt) { - printf("%s\n", prompt); - } - printf("Press up/down to select\n"); - printf("Press A to confirm\n"); - printf("Press B to go back\n\n"); - - for (int i = 0; i < option_count; i++) { - if (i == option_idx) { - printf(">%s\n", options[i]); - } - else { - printf("%s\n", options[i]); +static int console_selection_prompt(char *prompt, char **options, + int option_count, int default_idx) { + int option_idx = default_idx; + int last_option_idx = -1; + while (aptMainLoop()) { + if (option_idx != last_option_idx) { + consoleClear(); + if (prompt) { + printf("%s\n", prompt); + } + printf("Press up/down to select\n"); + printf("Press A to confirm\n"); + printf("Press B to go back\n\n"); + + for (int i = 0; i < option_count; i++) { + if (i == option_idx) { + printf(">%s\n", options[i]); + } else { + printf("%s\n", options[i]); + } + } + last_option_idx = option_idx; } - } - last_option_idx = option_idx; - } - gfxSwapBuffers(); - gfxFlushBuffers(); - gspWaitForVBlank(); + gfxSwapBuffers(); + gfxFlushBuffers(); + gspWaitForVBlank(); - hidScanInput(); - u32 kDown = hidKeysDown(); + hidScanInput(); + u32 kDown = hidKeysDown(); - if (kDown & KEY_A) { - consoleClear(); - return option_idx; - } - if (kDown & KEY_B) { - consoleClear(); - return -1; - } - if (kDown & KEY_DOWN) { - if (option_idx < option_count - 1) { - option_idx++; - } - } - else if (kDown & KEY_UP) { - if (option_idx > 0) { - option_idx--; - } + if (kDown & KEY_A) { + consoleClear(); + return option_idx; + } + if (kDown & KEY_B) { + consoleClear(); + return -1; + } + if (kDown & KEY_DOWN) { + if (option_idx < option_count - 1) { + option_idx++; + } + } else if (kDown & KEY_UP) { + if (option_idx > 0) { + option_idx--; + } + } } - } - exit(0); + exit(0); } -static char * prompt_for_action(PSERVER_DATA server) -{ - if (server->paired) { - char* actions[] = { - "stream", - "quit stream", - "stream settings", - "unpair", - }; +static char *prompt_for_action(PSERVER_DATA server) { + if (server->paired) { + char *actions[] = { + "stream", + "quit stream", + "stream settings", + "unpair", + }; + int actions_len = sizeof(actions) / sizeof(actions[0]); + int idx = console_selection_prompt("Select an action", actions, + actions_len, 0); + if (idx < 0) { + return NULL; + } + return actions[idx]; + } + char *actions[] = {"pair"}; int actions_len = sizeof(actions) / sizeof(actions[0]); - int idx = console_selection_prompt("Select an action", actions, actions_len, 0); + int idx = + console_selection_prompt("Select an action", actions, actions_len, 0); if (idx < 0) { - return NULL; + return NULL; } return actions[idx]; - } - char* actions[] = {"pair"}; - int actions_len = sizeof(actions) / sizeof(actions[0]); - int idx = console_selection_prompt("Select an action", actions, actions_len, 0); - if (idx < 0) { - return NULL; - } - return actions[idx]; -} - -static char * prompt_for_address() -{ - char* address_list[MAX_PAIRED_DEVICES + 1]; - int address_count = 0; - list_paired_addresses(address_list, &address_count); - - address_list[address_count] = "new"; - int idx = console_selection_prompt("Select a server address", address_list, address_count + 1, 0); - if (idx < 0) { - return NULL; - } - else if (strcmp(address_list[idx], "new") != 0) { - return address_list[idx]; - } - - // Prompt users for a custom address - SwkbdState swkbd; - char* addr_buff = malloc(MAX_INPUT_CHAR); - swkbdInit(&swkbd, SWKBD_TYPE_NORMAL, 3, -1); - swkbdSetHintText(&swkbd, "Address of host to connect to"); - swkbdInputText(&swkbd, addr_buff, MAX_INPUT_CHAR); - addr_buff = realloc(addr_buff, strlen(addr_buff) + 1); - return addr_buff; } -static inline char * prompt_for_boolean(char* prompt, bool default_val) -{ - char* options[] = { - "true", - "false", - }; - int options_len = sizeof(options) / sizeof(options[0]); - int idx = console_selection_prompt(prompt, options, options_len, default_val ? 0 : 1); - if (idx < 0) { - idx = default_val ? 0 : 1; - } - return options[idx]; -} +static char *prompt_for_address() { + char *address_list[MAX_PAIRED_DEVICES + 1]; + int address_count = 0; + list_paired_addresses(address_list, &address_count); -static void prompt_for_stream_settings(PCONFIGURATION config) -{ - char* setting_names[] = { - "width", - "height", - "fps", - "bitrate", - "packetsize", - "nosops", - "localaudio", - "quitappafter", - "viewonly", - "hwdecode", - "swapfacebuttons", - "swaptriggersandshoulders", - "debug", - }; - char argument_ids[] = { - 'c', - 'd', - 'v', - 'g', - 'h', - 'l', - 'n', - '1', - '2', - '8', - 'A', - 'B', - 'Z', - }; - int settings_len = sizeof(argument_ids); - char* setting_buff = malloc(MAX_INPUT_CHAR); - char* prompt_buff = malloc(200); - int idx = 0; - while (1) { - sprintf(prompt_buff, "Select a setting"); - if (config->stream.width != GSP_SCREEN_HEIGHT_TOP && \ - config->stream.width != GSP_SCREEN_HEIGHT_TOP_2X) { - strcat(prompt_buff, "\n\nWARNING: Using an unsupported width may cause issues (3DS supports 400 or 800)\n"); - } - if (config->stream.height != GSP_SCREEN_WIDTH) { - strcat(prompt_buff, "\n\nWARNING: Using an unsupported height may cause issues (3DS supports 240)\n"); - } - idx = console_selection_prompt(prompt_buff, setting_names, settings_len, idx); + address_list[address_count] = "new"; + int idx = console_selection_prompt("Select a server address", address_list, + address_count + 1, 0); if (idx < 0) { - break; + return NULL; + } else if (strcmp(address_list[idx], "new") != 0) { + return address_list[idx]; } + // Prompt users for a custom address SwkbdState swkbd; - memset(setting_buff, 0, MAX_INPUT_CHAR); - if (strcmp("width", setting_names[idx]) == 0) { - swkbdInit(&swkbd, SWKBD_TYPE_NUMPAD, 1, 8); - sprintf(setting_buff, "%d", config->stream.width); - swkbdSetInitialText(&swkbd, setting_buff); - swkbdInputText(&swkbd, setting_buff, MAX_INPUT_CHAR); - } - else if (strcmp("height", setting_names[idx]) == 0) { - swkbdInit(&swkbd, SWKBD_TYPE_NUMPAD, 1, 8); - sprintf(setting_buff, "%d", config->stream.height); - swkbdSetInitialText(&swkbd, setting_buff); - swkbdInputText(&swkbd, setting_buff, MAX_INPUT_CHAR); - } - else if (strcmp("fps", setting_names[idx]) == 0) { - swkbdInit(&swkbd, SWKBD_TYPE_NUMPAD, 1, 8); - sprintf(setting_buff, "%d", config->stream.fps); - swkbdSetInitialText(&swkbd, setting_buff); - swkbdInputText(&swkbd, setting_buff, MAX_INPUT_CHAR); - } - else if (strcmp("bitrate", setting_names[idx]) == 0) { - swkbdInit(&swkbd, SWKBD_TYPE_NUMPAD, 1, 8); - sprintf(setting_buff, "%d", config->stream.bitrate); - swkbdSetInitialText(&swkbd, setting_buff); - swkbdInputText(&swkbd, setting_buff, MAX_INPUT_CHAR); - } - else if (strcmp("packetsize", setting_names[idx]) == 0) { - swkbdInit(&swkbd, SWKBD_TYPE_NUMPAD, 1, 8); - sprintf(setting_buff, "%d", config->stream.packetSize); - swkbdSetInitialText(&swkbd, setting_buff); - swkbdInputText(&swkbd, setting_buff, MAX_INPUT_CHAR); - } - else if (strcmp("nosops", setting_names[idx]) == 0) { - char* bool_str = prompt_for_boolean("Disable sops", !config->sops); - if (bool_str != NULL) { - sprintf(setting_buff, bool_str); - } - } - else if (strcmp("localaudio", setting_names[idx]) == 0) { - char* bool_str = prompt_for_boolean("Enable local audio", config->localaudio); - if (bool_str != NULL) { - sprintf(setting_buff, bool_str); - } - } - else if (strcmp("quitappafter", setting_names[idx]) == 0) { - char* bool_str = prompt_for_boolean("Quit app after streaming", config->quitappafter); - if (bool_str != NULL) { - sprintf(setting_buff, bool_str); - } - } - else if (strcmp("viewonly", setting_names[idx]) == 0) { - char* bool_str = prompt_for_boolean("Disable controller input", config->viewonly); - if (bool_str != NULL) { - sprintf(setting_buff, bool_str); - } - } - else if (strcmp("hwdecode", setting_names[idx]) == 0) { - char* bool_str = prompt_for_boolean("Use hardware video decoder", config->hwdecode); - if (bool_str != NULL) { - sprintf(setting_buff, bool_str); - } - } - else if (strcmp("swapfacebuttons", setting_names[idx]) == 0) { - char* bool_str = prompt_for_boolean("Swaps A/B and X/Y to match Xbox controller layout", config->swap_face_buttons); - if (bool_str != NULL) { - sprintf(setting_buff, bool_str); - } - } - else if (strcmp("swaptriggersandshoulders", setting_names[idx]) == 0) { - char* bool_str = prompt_for_boolean("Swaps L/ZL and R/ZR for a more natural feel", config->swap_triggers_and_shoulders); - if (bool_str != NULL) { - sprintf(setting_buff, bool_str); - } - } - else if (strcmp("debug", setting_names[idx]) == 0) { - char* bool_str = prompt_for_boolean("Enable debug logs", config->debug_level); - if (bool_str != NULL) { - sprintf(setting_buff, bool_str); - } + char *addr_buff = malloc(MAX_INPUT_CHAR); + swkbdInit(&swkbd, SWKBD_TYPE_NORMAL, 3, -1); + swkbdSetHintText(&swkbd, "Address of host to connect to"); + swkbdInputText(&swkbd, addr_buff, MAX_INPUT_CHAR); + addr_buff = realloc(addr_buff, strlen(addr_buff) + 1); + return addr_buff; +} + +static inline char *prompt_for_boolean(char *prompt, bool default_val) { + char *options[] = { + "true", + "false", + }; + int options_len = sizeof(options) / sizeof(options[0]); + int idx = console_selection_prompt(prompt, options, options_len, + default_val ? 0 : 1); + if (idx < 0) { + idx = default_val ? 0 : 1; } + return options[idx]; +} - parse_argument(argument_ids[idx], setting_buff, config); - } +static void prompt_for_stream_settings(PCONFIGURATION config) { + char *setting_names[] = { + "width", + "height", + "fps", + "dual_screen", + "motion_controls", + "bitrate", + "packetsize", + "nosops", + "localaudio", + "quitappafter", + "viewonly", + "hwdecode", + "swapfacebuttons", + "swaptriggersandshoulders", + "debug", + }; + char argument_ids[] = { + 'c', 'd', 'v', '9', 'e', 'g', 'h', 'l', + 'n', '1', '2', '8', 'A', 'B', 'Z', + }; + int settings_len = sizeof(argument_ids); + char *setting_buff = malloc(MAX_INPUT_CHAR); + char *prompt_buff = malloc(200); + int idx = 0; + while (1) { + sprintf(prompt_buff, "Select a setting"); + if (config->stream.width != GSP_SCREEN_HEIGHT_TOP && + config->stream.width != GSP_SCREEN_HEIGHT_TOP_2X) { + strcat(prompt_buff, "\n\nWARNING: Using an unsupported width may " + "cause issues (3DS supports 400 or 800)\n"); + } + if (config->stream.height != GSP_SCREEN_WIDTH) { + strcat(prompt_buff, "\n\nWARNING: Using an unsupported height may " + "cause issues (3DS supports 240)\n"); + } + idx = console_selection_prompt(prompt_buff, setting_names, settings_len, + idx); + if (idx < 0) { + break; + } - // Update the config file - char* config_file_path = (char*) MOONLIGHT_3DS_PATH "/moonlight.conf"; - config_save(config_file_path, config); - free(setting_buff); + SwkbdState swkbd; + memset(setting_buff, 0, MAX_INPUT_CHAR); + if (strcmp("width", setting_names[idx]) == 0) { + swkbdInit(&swkbd, SWKBD_TYPE_NUMPAD, 1, 8); + sprintf(setting_buff, "%d", config->stream.width); + swkbdSetInitialText(&swkbd, setting_buff); + swkbdInputText(&swkbd, setting_buff, MAX_INPUT_CHAR); + } else if (strcmp("height", setting_names[idx]) == 0) { + swkbdInit(&swkbd, SWKBD_TYPE_NUMPAD, 1, 8); + sprintf(setting_buff, "%d", config->stream.height); + swkbdSetInitialText(&swkbd, setting_buff); + swkbdInputText(&swkbd, setting_buff, MAX_INPUT_CHAR); + } else if (strcmp("dual_screen", setting_names[idx]) == 0) { + char *bool_str = + prompt_for_boolean("Enable Dual Screens", config->dual_screen); + if (bool_str != NULL) { + sprintf(setting_buff, bool_str); + } + } else if (strcmp("motion_controls", setting_names[idx]) == 0) { + char *bool_str = prompt_for_boolean("Enable Motion Controls", + config->motion_controls); + if (bool_str != NULL) { + sprintf(setting_buff, bool_str); + } + } else if (strcmp("fps", setting_names[idx]) == 0) { + swkbdInit(&swkbd, SWKBD_TYPE_NUMPAD, 1, 8); + sprintf(setting_buff, "%d", config->stream.fps); + swkbdSetInitialText(&swkbd, setting_buff); + swkbdInputText(&swkbd, setting_buff, MAX_INPUT_CHAR); + } else if (strcmp("bitrate", setting_names[idx]) == 0) { + swkbdInit(&swkbd, SWKBD_TYPE_NUMPAD, 1, 8); + sprintf(setting_buff, "%d", config->stream.bitrate); + swkbdSetInitialText(&swkbd, setting_buff); + swkbdInputText(&swkbd, setting_buff, MAX_INPUT_CHAR); + } else if (strcmp("packetsize", setting_names[idx]) == 0) { + swkbdInit(&swkbd, SWKBD_TYPE_NUMPAD, 1, 8); + sprintf(setting_buff, "%d", config->stream.packetSize); + swkbdSetInitialText(&swkbd, setting_buff); + swkbdInputText(&swkbd, setting_buff, MAX_INPUT_CHAR); + } else if (strcmp("nosops", setting_names[idx]) == 0) { + char *bool_str = prompt_for_boolean("Disable sops", !config->sops); + if (bool_str != NULL) { + sprintf(setting_buff, bool_str); + } + } else if (strcmp("localaudio", setting_names[idx]) == 0) { + char *bool_str = + prompt_for_boolean("Enable local audio", config->localaudio); + if (bool_str != NULL) { + sprintf(setting_buff, bool_str); + } + } else if (strcmp("quitappafter", setting_names[idx]) == 0) { + char *bool_str = prompt_for_boolean("Quit app after streaming", + config->quitappafter); + if (bool_str != NULL) { + sprintf(setting_buff, bool_str); + } + } else if (strcmp("viewonly", setting_names[idx]) == 0) { + char *bool_str = prompt_for_boolean("Disable controller input", + config->viewonly); + if (bool_str != NULL) { + sprintf(setting_buff, bool_str); + } + } else if (strcmp("hwdecode", setting_names[idx]) == 0) { + char *bool_str = prompt_for_boolean("Use hardware video decoder", + config->hwdecode); + if (bool_str != NULL) { + sprintf(setting_buff, bool_str); + } + } else if (strcmp("swapfacebuttons", setting_names[idx]) == 0) { + char *bool_str = prompt_for_boolean( + "Swaps A/B and X/Y to match Xbox controller layout", + config->swap_face_buttons); + if (bool_str != NULL) { + sprintf(setting_buff, bool_str); + } + } else if (strcmp("swaptriggersandshoulders", setting_names[idx]) == + 0) { + char *bool_str = prompt_for_boolean( + "Swaps L/ZL and R/ZR for a more natural feel", + config->swap_triggers_and_shoulders); + if (bool_str != NULL) { + sprintf(setting_buff, bool_str); + } + } else if (strcmp("debug", setting_names[idx]) == 0) { + char *bool_str = + prompt_for_boolean("Enable debug logs", config->debug_level); + if (bool_str != NULL) { + sprintf(setting_buff, bool_str); + } + } + + parse_argument(argument_ids[idx], setting_buff, config); + } + + // Update the config file + char *config_file_path = (char *)MOONLIGHT_3DS_PATH "/moonlight.conf"; + config_save(config_file_path, config); + free(setting_buff); } -static void init_3ds() -{ - Result status = 0; - acInit(); - gfxInit(GSP_RGB565_OES, GSP_BGR8_OES, false); - consoleInit(GFX_TOP, &topScreen); - consoleSelect(&topScreen); - atexit(n3ds_exit_handler); - - osSetSpeedupEnable(true); - aptSetSleepAllowed(false); - aptInit(); - - SOC_buffer = (u32*)memalign(SOC_ALIGN, SOC_BUFFERSIZE); - status = socInit(SOC_buffer, SOC_BUFFERSIZE); - if (R_FAILED(status)) - { - printf("socInit: %08lX\n", status); - exit(1); - } - - status = ndmuInit(); - status |= NDMU_EnterExclusiveState(NDM_EXCLUSIVE_STATE_INFRASTRUCTURE); - status |= NDMU_LockState(); - if (R_FAILED(status)) - { - printf ("Warning: failed to enter exclusive NDM state: %08lX\n", status); - wait_for_button(NULL); - } +static void init_3ds() { + Result status = 0; + acInit(); + gfxInit(GSP_RGB565_OES, GSP_BGR8_OES, false); + consoleInit(GFX_TOP, &topScreen); + consoleSelect(&topScreen); + atexit(n3ds_exit_handler); + + osSetSpeedupEnable(true); + aptSetSleepAllowed(false); + aptInit(); + + SOC_buffer = (u32 *)memalign(SOC_ALIGN, SOC_BUFFERSIZE); + status = socInit(SOC_buffer, SOC_BUFFERSIZE); + if (R_FAILED(status)) { + printf("socInit: %08lX\n", status); + exit(1); + } + + status = ndmuInit(); + status |= NDMU_EnterExclusiveState(NDM_EXCLUSIVE_STATE_INFRASTRUCTURE); + status |= NDMU_LockState(); + if (R_FAILED(status)) { + printf("Warning: failed to enter exclusive NDM state: %08lX\n", status); + wait_for_button(NULL); + } } -static int prompt_for_app_id(PSERVER_DATA server) -{ - PAPP_LIST list = NULL; - if (gs_applist(server, &list) != GS_OK) { - fprintf(stderr, "Can't get app list\n"); - return -1; - } - - char* app_names[MAX_APP_LIST]; - int app_ids[MAX_APP_LIST]; - int idx = 0; - for (idx = 0; idx < MAX_APP_LIST; idx++) { - if (list == NULL) { - break; +static int prompt_for_app_id(PSERVER_DATA server) { + PAPP_LIST list = NULL; + if (gs_applist(server, &list) != GS_OK) { + fprintf(stderr, "Can't get app list\n"); + return -1; + } + + char *app_names[MAX_APP_LIST]; + int app_ids[MAX_APP_LIST]; + int idx = 0; + for (idx = 0; idx < MAX_APP_LIST; idx++) { + if (list == NULL) { + break; + } + + printf("%d. %s\n", idx, list->name); + app_names[idx] = malloc(strlen(list->name)); + strcpy(app_names[idx], list->name); + app_ids[idx] = list->id; + list = list->next; } - printf("%d. %s\n", idx, list->name); - app_names[idx] = malloc(strlen(list->name)); - strcpy(app_names[idx], list->name); - app_ids[idx] = list->id; - list = list->next; - } - - int id_idx = console_selection_prompt("Select an app", app_names, idx, 0); - if (id_idx == -1) { - return -1; - } - return app_ids[id_idx]; + int id_idx = console_selection_prompt("Select an app", app_names, idx, 0); + if (id_idx == -1) { + return -1; + } + return app_ids[id_idx]; } static inline void stream_loop(PCONFIGURATION config) { - bool done = false; - while(!done && aptMainLoop()) { - done = n3ds_connection_closed; - if (!config->viewonly) { - done |= n3dsinput_handle_event(); + bool done = false; + while (!done && aptMainLoop()) { + done = n3ds_connection_closed; + if (!config->viewonly) { + done |= n3dsinput_handle_event(); + } + hidWaitForAnyEvent(true, 0, 1000000000); } - hidWaitForEvent(HIDEVENT_PAD0, true); - } } static void stream(PSERVER_DATA server, PCONFIGURATION config, int appId) { - int gamepad_mask = 1; - int ret = gs_start_app(server, &config->stream, appId, config->sops, config->localaudio, gamepad_mask); - if (ret < 0) { - if (ret == GS_NOT_SUPPORTED_4K) - fprintf(stderr, "Server doesn't support 4K\n"); - else if (ret == GS_NOT_SUPPORTED_MODE) - fprintf(stderr, "Server doesn't support %dx%d (%d fps) or remove --nounsupported option\n", config->stream.width, config->stream.height, config->stream.fps); - else if (ret == GS_NOT_SUPPORTED_SOPS_RESOLUTION) - fprintf(stderr, "Optimal Playable Settings isn't supported for the resolution %dx%d, use supported resolution or add --nosops option\n", config->stream.width, config->stream.height); - else if (ret == GS_ERROR) - fprintf(stderr, "Gamestream error: %s\n", gs_error); - else - fprintf(stderr, "Errorcode starting app: %d\n", ret); - exit(-1); - } - - n3ds_audio_disabled = config->localaudio; - n3ds_connection_debug = config->debug_level; - - int drFlags = 0; - if (config->fullscreen) - drFlags |= DISPLAY_FULLSCREEN; - - switch (config->rotate) { - case 0: - break; - case 90: - drFlags |= DISPLAY_ROTATE_90; - break; - case 180: - drFlags |= DISPLAY_ROTATE_180; - break; - case 270: - drFlags |= DISPLAY_ROTATE_270; - break; - default: - printf("Ignoring invalid rotation value: %d\n", config->rotate); - } - - PDECODER_RENDERER_CALLBACKS video_callbacks = config->hwdecode ? &decoder_callbacks_n3ds_mvd : &decoder_callbacks_n3ds; - - printf("Loading...\nStream %dx%d, %dfps, %dkbps, sops=%d, localaudio=%d, quitappafter=%d,\ - viewonly=%d, rotate=%d, encryption=%x, hwdecode=%d, swapfacebuttons=%d, swaptriggersandshoulders=%d, debug=%d\n", - config->stream.width, - config->stream.height, - config->stream.fps, - config->stream.bitrate, - config->sops, - config->localaudio, - config->quitappafter, - config->viewonly, - config->rotate, - config->stream.encryptionFlags, - config->hwdecode, - config->swap_face_buttons, - config->swap_triggers_and_shoulders, - config->debug_level - ); - - int status = LiStartConnection(&server->serverInfo, &config->stream, &n3ds_connection_callbacks, video_callbacks, &audio_callbacks_n3ds, NULL, drFlags, config->audio_device, 0); - - if (status != 0) { - n3ds_connection_callbacks.connectionTerminated(status); - exit(status); - } - - consoleClear(); - if (config->debug_level) { - consoleInit(GFX_BOTTOM, &bottomScreen); - consoleSelect(&bottomScreen); - printf("Connected!\n"); - } - else { - n3dsinput_set_touch(GAMEPAD); - } - - stream_loop(config); - - LiStopConnection(); - - if (config->quitappafter) { - printf("Sending app quit request ...\n"); - gs_quit_app(server); - } -} + int gamepad_mask = 1; + int ret = gs_start_app(server, &config->stream, appId, config->sops, + config->localaudio, gamepad_mask); + if (ret < 0) { + if (ret == GS_NOT_SUPPORTED_4K) + fprintf(stderr, "Server doesn't support 4K\n"); + else if (ret == GS_NOT_SUPPORTED_MODE) + fprintf(stderr, + "Server doesn't support %dx%d (%d fps) or remove " + "--nounsupported option\n", + config->stream.width, config->stream.height, + config->stream.fps); + else if (ret == GS_NOT_SUPPORTED_SOPS_RESOLUTION) + fprintf( + stderr, + "Optimal Playable Settings isn't supported for the resolution " + "%dx%d, use supported resolution or add --nosops option\n", + config->stream.width, config->stream.height); + else if (ret == GS_ERROR) + fprintf(stderr, "Gamestream error: %s\n", gs_error); + else + fprintf(stderr, "Errorcode starting app: %d\n", ret); + exit(-1); + } -int main(int argc, char* argv[]) { - init_3ds(); + n3ds_audio_disabled = config->localaudio; + n3ds_connection_debug = config->debug_level; - CONFIGURATION config; - config_parse(argc, argv, &config); + int drFlags = 0; + if (config->fullscreen) + drFlags |= DISPLAY_FULLSCREEN; - while (aptMainLoop()) { - config.address = prompt_for_address(); - if (config.address == NULL) { - continue; + switch (config->rotate) { + case 0: + break; + case 90: + drFlags |= DISPLAY_ROTATE_90; + break; + case 180: + drFlags |= DISPLAY_ROTATE_180; + break; + case 270: + drFlags |= DISPLAY_ROTATE_270; + break; + default: + printf("Ignoring invalid rotation value: %d\n", config->rotate); } - SERVER_DATA server; - printf("Connecting to %s...\n", config.address); - - int ret; - if ((ret = gs_init(&server, config.address, config.port, config.key_dir, config.debug_level, config.unsupported)) == GS_OUT_OF_MEMORY) { - fprintf(stderr, "Not enough memory\n"); - exit(-1); - } else if (ret == GS_ERROR) { - fprintf(stderr, "Gamestream error: %s\n", gs_error); - exit(-1); - } else if (ret == GS_INVALID) { - fprintf(stderr, "Invalid data received from server: %s\n", gs_error); - exit(-1); - } else if (ret == GS_UNSUPPORTED_VERSION) { - fprintf(stderr, "Unsupported version: %s\n", gs_error); - exit(-1); - } else if (ret != GS_OK) { - fprintf(stderr, "Can't connect to server %s\n", config.address); - wait_for_button(NULL); - continue; + n3ds_enable_motion = config->motion_controls; + PDECODER_RENDERER_CALLBACKS video_callbacks = + config->hwdecode ? &decoder_callbacks_n3ds_mvd + : &decoder_callbacks_n3ds; + + printf( + "Loading...\nStream %dx%d, %dfps, %dkbps, sops=%d, localaudio=%d, quitappafter=%d,\ + viewonly=%d, rotate=%d, encryption=%x, hwdecode=%d, swapfacebuttons=%d, swaptriggersandshoulders=%d,\ + dual_screen=%d, motion_controls=%d, debug=%d\n", + config->stream.width, config->stream.height, config->stream.fps, + config->stream.bitrate, config->sops, config->localaudio, + config->quitappafter, config->viewonly, config->rotate, + config->stream.encryptionFlags, config->hwdecode, + config->swap_face_buttons, config->swap_triggers_and_shoulders, + config->dual_screen, config->motion_controls, config->debug_level); + + int status = LiStartConnection(&server->serverInfo, &config->stream, + &n3ds_connection_callbacks, video_callbacks, + &audio_callbacks_n3ds, NULL, drFlags, + config->audio_device, 0); + + if (status != 0) { + n3ds_connection_callbacks.connectionTerminated(status); + exit(status); } - if (config.debug_level > 0) { - printf("GPU: %s, GFE: %s (%s, %s)\n", server.gpuType, server.serverInfo.serverInfoGfeVersion, server.gsVersion, server.serverInfo.serverInfoAppVersion); - printf("Server codec flags: 0x%x\n", server.serverInfo.serverCodecModeSupport); - } - if (server.paired) { - add_pair_address(config.address); + consoleClear(); + if (config->debug_level) { + consoleInit(GFX_BOTTOM, &bottomScreen); + consoleSelect(&bottomScreen); + printf("Connected!\n"); + } else if (config->dual_screen) { + enable_dual_display = true; + gfxExit(); + gfxInit(GSP_RGB565_OES, GSP_RGB565_OES, false); + n3dsinput_set_touch(DS_TOUCH); + } else { + n3dsinput_set_touch(GAMEPAD); } - else { - remove_pair_address(config.address); + + stream_loop(config); + + LiStopConnection(); + + if (config->quitappafter) { + printf("Sending app quit request ...\n"); + gs_quit_app(server); } +} - while (aptMainLoop()) { - config.action = prompt_for_action(&server); - if (config.action == NULL) { - break; - } - else if (strcmp("stream", config.action) == 0) { - int appId = prompt_for_app_id(&server); - if (appId == -1) { - continue; - } +int main(int argc, char *argv[]) { + init_3ds(); - config.stream.supportedVideoFormats = VIDEO_FORMAT_H264; + CONFIGURATION config; + config_parse(argc, argv, &config); - if (config.viewonly) { - if (config.debug_level > 0) - printf("View-only mode enabled, no input will be sent to the host computer\n"); - } else { - n3dsinput_init(config.swap_face_buttons, config.swap_triggers_and_shoulders); + while (aptMainLoop()) { + config.address = prompt_for_address(); + if (config.address == NULL) { + continue; } - stream(&server, &config, appId); - if (!config.viewonly) { - n3dsinput_cleanup(); + SERVER_DATA server; + printf("Connecting to %s...\n", config.address); + + int ret; + if ((ret = gs_init(&server, config.address, config.port, config.key_dir, + config.debug_level, config.unsupported)) == + GS_OUT_OF_MEMORY) { + fprintf(stderr, "Not enough memory\n"); + exit(-1); + } else if (ret == GS_ERROR) { + fprintf(stderr, "Gamestream error: %s\n", gs_error); + exit(-1); + } else if (ret == GS_INVALID) { + fprintf(stderr, "Invalid data received from server: %s\n", + gs_error); + exit(-1); + } else if (ret == GS_UNSUPPORTED_VERSION) { + fprintf(stderr, "Unsupported version: %s\n", gs_error); + exit(-1); + } else if (ret != GS_OK) { + fprintf(stderr, "Can't connect to server %s\n", config.address); + wait_for_button(NULL); + continue; } - // Exit app after streaming has closed - exit(0); - } - else if (strcmp("pair", config.action) == 0) { - char pin[5]; - if (config.pin > 0 && config.pin <= 9999) { - sprintf(pin, "%04d", config.pin); - } else { - sprintf(pin, "%d%d%d%d", (unsigned)random() % 10, (unsigned)random() % 10, (unsigned)random() % 10, (unsigned)random() % 10); - } - printf("Please enter the following PIN on the target PC:\n%s\n", pin); - fflush(stdout); - if (gs_pair(&server, &pin[0]) != GS_OK) { - fprintf(stderr, "Failed to pair to server: %s\n", gs_error); - } else { - printf("Succesfully paired\n"); - add_pair_address(config.address); - break; + + if (config.debug_level > 0) { + printf("GPU: %s, GFE: %s (%s, %s)\n", server.gpuType, + server.serverInfo.serverInfoGfeVersion, server.gsVersion, + server.serverInfo.serverInfoAppVersion); + printf("Server codec flags: 0x%x\n", + server.serverInfo.serverCodecModeSupport); } - } - else if (strcmp("stream settings", config.action) == 0) { - prompt_for_stream_settings(&config); - continue; - } - else if (strcmp("unpair", config.action) == 0) { - if (gs_unpair(&server) != GS_OK) { - fprintf(stderr, "Failed to unpair to server: %s\n", gs_error); + if (server.paired) { + add_pair_address(config.address); } else { - printf("Succesfully unpaired\n"); - remove_pair_address(config.address); - break; + remove_pair_address(config.address); } - } - else if (strcmp("quit stream", config.action) == 0) { - printf("Sending app quit request ...\n"); - gs_quit_app(&server); - } - else - fprintf(stderr, "%s is not a valid action\n", config.action); - wait_for_button(NULL); + while (aptMainLoop()) { + config.action = prompt_for_action(&server); + if (config.action == NULL) { + break; + } else if (strcmp("stream", config.action) == 0) { + int appId = prompt_for_app_id(&server); + if (appId == -1) { + continue; + } + + config.stream.supportedVideoFormats = VIDEO_FORMAT_H264; + + if (config.viewonly) { + if (config.debug_level > 0) + printf("View-only mode enabled, no input will be sent " + "to the host computer\n"); + } else { + n3dsinput_init(config.swap_face_buttons, + config.swap_triggers_and_shoulders); + } + stream(&server, &config, appId); + + if (!config.viewonly) { + n3dsinput_cleanup(); + } + // Exit app after streaming has closed + exit(0); + } else if (strcmp("pair", config.action) == 0) { + char pin[5]; + if (config.pin > 0 && config.pin <= 9999) { + sprintf(pin, "%04d", config.pin); + } else { + sprintf(pin, "%d%d%d%d", (unsigned)random() % 10, + (unsigned)random() % 10, (unsigned)random() % 10, + (unsigned)random() % 10); + } + printf("Please enter the following PIN on the target PC:\n%s\n", + pin); + fflush(stdout); + if (gs_pair(&server, &pin[0]) != GS_OK) { + fprintf(stderr, "Failed to pair to server: %s\n", gs_error); + } else { + printf("Succesfully paired\n"); + add_pair_address(config.address); + break; + } + } else if (strcmp("stream settings", config.action) == 0) { + prompt_for_stream_settings(&config); + continue; + } else if (strcmp("unpair", config.action) == 0) { + if (gs_unpair(&server) != GS_OK) { + fprintf(stderr, "Failed to unpair to server: %s\n", + gs_error); + } else { + printf("Succesfully unpaired\n"); + remove_pair_address(config.address); + break; + } + } else if (strcmp("quit stream", config.action) == 0) { + printf("Sending app quit request ...\n"); + gs_quit_app(&server); + } else + fprintf(stderr, "%s is not a valid action\n", config.action); + + wait_for_button(NULL); + } } - } - return 0; + return 0; } #endif diff --git a/src/platform_main.h b/src/platform_main.h index c3fd7484..1393da40 100644 --- a/src/platform_main.h +++ b/src/platform_main.h @@ -16,6 +16,8 @@ * You should have received a copy of the GNU General Public License * along with Moonlight; if not, see . */ +#ifndef PLATFORM_MAIN_H +#define PLATFORM_MAIN_H #include @@ -39,3 +41,5 @@ char* platform_name(enum platform system); void platform_start(enum platform system); void platform_stop(enum platform system); + +#endif diff --git a/src/util.c b/src/util.c index c62b34ff..3eb5088c 100644 --- a/src/util.c +++ b/src/util.c @@ -29,53 +29,6 @@ #include <3ds.h> #endif -#ifdef HAVE_GETAUXVAL -#include - -#ifndef HWCAP2_AES -#define HWCAP2_AES (1 << 0) -#endif -#endif - -#if defined(__linux__) && defined(__riscv) -#if __has_include() -#include -#else -#include - -#if __has_include() -#include -#include -#else -#define __NR_riscv_hwprobe 258 -struct riscv_hwprobe { - int64_t key; - uint64_t value; -}; -#define RISCV_HWPROBE_KEY_IMA_EXT_0 4 -#endif - -// RISC-V Scalar AES [E]ncryption and [D]ecryption -#ifndef RISCV_HWPROBE_EXT_ZKND -#define RISCV_HWPROBE_EXT_ZKND (1 << 11) -#define RISCV_HWPROBE_EXT_ZKNE (1 << 12) -#endif - -// RISC-V Vector AES -#ifndef RISCV_HWPROBE_EXT_ZVKNED -#define RISCV_HWPROBE_EXT_ZVKNED (1 << 21) -#endif - -static int __riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count, - size_t cpu_count, unsigned long *cpus, - unsigned int flags) -{ - return syscall(__NR_riscv_hwprobe, pairs, pair_count, cpu_count, cpus, flags); -} - -#endif -#endif - int write_bool(char *path, bool val) { int fd = open(path, O_RDWR); @@ -115,44 +68,6 @@ bool ensure_buf_size(void **buf, size_t *buf_size, size_t required_size) { return true; } -bool has_fast_aes() { -#ifndef __has_builtin -#define __has_builtin(x) 0 -#endif - -#if defined(HAVE_GETAUXVAL) && (defined(__arm__) || defined(__aarch64__)) - #if defined(__arm__) && defined(HWCAP2_AES) - return !!(getauxval(AT_HWCAP2) & HWCAP2_AES); - #elif defined(__aarch64__) - return !!(getauxval(AT_HWCAP) & HWCAP_AES); - #else - return false; - #endif -#elif __has_builtin(__builtin_cpu_supports) && (defined(__i386__) || defined(__x86_64__)) - return __builtin_cpu_supports("aes"); -#elif defined(__BUILTIN_CPU_SUPPORTS__) && defined(__powerpc__) - return __builtin_cpu_supports("vcrypto"); -#elif defined(__linux__) && defined(__riscv) - struct riscv_hwprobe pairs[1] = { - { RISCV_HWPROBE_KEY_IMA_EXT_0, 0 }, - }; - - // If this syscall is not implemented, we'll get -ENOSYS - // and the value field will remain zero. - __riscv_hwprobe(pairs, sizeof(pairs) / sizeof(struct riscv_hwprobe), 0, NULL, 0); - - return (pairs[0].value & (RISCV_HWPROBE_EXT_ZKNE | RISCV_HWPROBE_EXT_ZKND)) == - (RISCV_HWPROBE_EXT_ZKNE | RISCV_HWPROBE_EXT_ZKND) || - (pairs[0].value & RISCV_HWPROBE_EXT_ZVKNED); -#elif __SIZEOF_SIZE_T__ == 4 - #warning Unknown 32-bit platform. Assuming AES is slow on this CPU. - return false; -#else - #warning Unknown 64-bit platform. Assuming AES is fast on this CPU. - return true; -#endif -} - #ifdef __3DS__ bool ensure_linear_buf_size(void **buf, size_t *buf_size, size_t required_size) { if (*buf_size >= required_size) diff --git a/src/video/n3ds_video.c b/src/video/n3ds_video.c index 57cddb22..99429099 100644 --- a/src/video/n3ds_video.c +++ b/src/video/n3ds_video.c @@ -17,213 +17,431 @@ * along with Moonlight; if not, see . */ -#include "video.h" #include "ffmpeg.h" +#include "video.h" #include "../util.h" #include <3ds.h> -#include #include +#include #define SLICES_PER_FRAME 1 #define N3DS_BUFFER_FRAMES 1 -static void* ffmpeg_buffer; +static void *ffmpeg_buffer; static size_t ffmpeg_buffer_size; static int image_width, image_height, surface_width, surface_height, pixel_size; -static u8* img_buffer; +static u8 *img_buffer; static int offset_lut_size; static int *dest_offset_lut; static int *src_offset_lut; -static inline int get_dest_offset(int x, int y, int dest_height) -{ - return dest_height - y - 1 + dest_height * x; +static int offset_lut_size_3d; +static int *dest_offset_lut_3d; +static int *src_offset_lut_3d_l; +static int *src_offset_lut_3d_r; + +static int offset_lut_size_ds_bottom; +static int *dest_offset_lut_ds_bottom; +static int *src_offset_lut_ds_top; +static int *src_offset_lut_ds_bottom; +bool enable_dual_display = false; + +static inline int get_dest_offset(int x, int y, int dest_height) { + return dest_height - y - 1 + dest_height * x; } -static inline int get_source_offset(int x, int y, int src_width, int src_height, int dest_width, int dest_height) -{ - return (x * src_width / dest_width) + (y * src_height / dest_height) * src_width; +static inline int get_source_offset(int x, int y, int src_width, int src_height, + int dest_width, int dest_height) { + return (x * src_width / dest_width) + + (y * src_height / dest_height) * src_width; } -static int n3ds_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { - if (ffmpeg_init(videoFormat, width, height, 0, N3DS_BUFFER_FRAMES, SLICES_PER_FRAME) < 0) { - fprintf(stderr, "Couldn't initialize video decoding\n"); - return -1; - } +static inline int get_source_offset_3d_l(int x, int y, int src_width, + int src_height, int dest_width, + int dest_height) { + return (x * (src_width / 2) / dest_width) + + (y * src_height / dest_height) * src_width; +} - ensure_buf_size(&ffmpeg_buffer, &ffmpeg_buffer_size, INITIAL_DECODER_BUFFER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE); +static inline int get_source_offset_3d_r(int x, int y, int src_width, + int src_height, int dest_width, + int dest_height) { + return ((x * (src_width / 2) / dest_width) + (src_width / 2)) + + (y * src_height / dest_height) * src_width; +} - if(y2rInit()) - { - fprintf(stderr, "Failed to initialize Y2R\n"); - return -1; - } - Y2RU_ConversionParams y2r_parameters; - y2r_parameters.input_format = INPUT_YUV420_INDIV_8; - y2r_parameters.output_format = OUTPUT_RGB_16_565; - y2r_parameters.rotation = ROTATION_NONE; - y2r_parameters.block_alignment = BLOCK_LINE; - y2r_parameters.input_line_width = width; - y2r_parameters.input_lines = height; - y2r_parameters.standard_coefficient = COEFFICIENT_ITU_R_BT_709_SCALING; - y2r_parameters.alpha = 0xFF; - int status = Y2RU_SetConversionParams(&y2r_parameters); - if (status) { - fprintf(stderr, "Failed to set Y2RU params\n"); - return -1; - } - - surface_height = GSP_SCREEN_WIDTH; - if (width > GSP_SCREEN_HEIGHT_TOP) { - gfxSetWide(true); - surface_width = GSP_SCREEN_HEIGHT_TOP_2X; - } - else { - gfxSetWide(false); - surface_width = GSP_SCREEN_HEIGHT_TOP; - } - - GSPGPU_FramebufferFormat px_fmt = gfxGetScreenFormat(GFX_TOP); - image_width = width; - image_height = height; - pixel_size = gspGetBytesPerPixel(px_fmt); - - img_buffer = linearAlloc(width * height * pixel_size); - if (!img_buffer) { - fprintf(stderr, "Out of memory!\n"); - return -1; - } +static inline int get_source_offset_ds_top(int x, int y, int src_width, + int src_height, int dest_width, + int dest_height) { + return (x * src_width / dest_width) + + (y * (src_height / 2) / dest_height) * src_width; +} + +static inline int get_source_offset_ds_bottom(int x, int y, int src_width, + int src_height, int dest_width, + int dest_height) { + return (x * src_width / dest_width) + + ((y * (src_height / 2) / dest_height) + (src_height / 2)) * + src_width; +} + +static inline void ensure_3d_enabled() { + if (!gfxIs3D()) { + gfxSetWide(false); + gfxSet3D(true); + } +} - return init_px_to_framebuffer(surface_width, surface_height, image_width, image_height, pixel_size); +static inline void ensure_3d_disabled() { + if (gfxIs3D()) { + gfxSet3D(false); + } + if (surface_width == GSP_SCREEN_HEIGHT_TOP_2X) { + gfxSetWide(true); + } +} + +static int n3ds_setup(int videoFormat, int width, int height, int redrawRate, + void *context, int drFlags) { + if (ffmpeg_init(videoFormat, width, height, 0, N3DS_BUFFER_FRAMES, + SLICES_PER_FRAME) < 0) { + fprintf(stderr, "Couldn't initialize video decoding\n"); + return -1; + } + + ensure_buf_size(&ffmpeg_buffer, &ffmpeg_buffer_size, + INITIAL_DECODER_BUFFER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE); + + if (y2rInit()) { + fprintf(stderr, "Failed to initialize Y2R\n"); + return -1; + } + Y2RU_ConversionParams y2r_parameters; + y2r_parameters.input_format = INPUT_YUV420_INDIV_8; + y2r_parameters.output_format = OUTPUT_RGB_16_565; + y2r_parameters.rotation = ROTATION_NONE; + y2r_parameters.block_alignment = BLOCK_LINE; + y2r_parameters.input_line_width = width; + y2r_parameters.input_lines = height; + y2r_parameters.standard_coefficient = COEFFICIENT_ITU_R_BT_709_SCALING; + y2r_parameters.alpha = 0xFF; + int status = Y2RU_SetConversionParams(&y2r_parameters); + if (status) { + fprintf(stderr, "Failed to set Y2RU params\n"); + return -1; + } + + surface_height = GSP_SCREEN_WIDTH; + if (width > GSP_SCREEN_HEIGHT_TOP) { + surface_width = GSP_SCREEN_HEIGHT_TOP_2X; + } else { + surface_width = GSP_SCREEN_HEIGHT_TOP; + } + + GSPGPU_FramebufferFormat px_fmt = gfxGetScreenFormat(GFX_TOP); + image_width = width; + image_height = height; + pixel_size = gspGetBytesPerPixel(px_fmt); + + img_buffer = linearAlloc(width * height * pixel_size); + if (!img_buffer) { + fprintf(stderr, "Out of memory!\n"); + return -1; + } + + return init_px_to_framebuffer(surface_width, surface_height, image_width, + image_height, pixel_size); } static void n3ds_cleanup() { - ffmpeg_destroy(); - y2rExit(); - linearFree(img_buffer); - deinit_px_to_framebuffer(); -} - -int init_px_to_framebuffer(int dest_width, - int dest_height, - int src_width, - int src_height, - int px_size) { - // Generate LUTs so we don't have to calculate pixel rotation while streaming. - offset_lut_size = dest_width * dest_height; - src_offset_lut = malloc(sizeof(int) * offset_lut_size); - if (!src_offset_lut) { - fprintf(stderr, "Out of memory!\n"); - return -1; - } - dest_offset_lut = malloc(sizeof(int) * offset_lut_size); - if (!dest_offset_lut) { - fprintf(stderr, "Out of memory!\n"); - return -1; - } + ffmpeg_destroy(); + y2rExit(); + linearFree(img_buffer); + deinit_px_to_framebuffer(); +} - int i = 0; - for (int y = 0; y < dest_height; ++y) { - for (int x = 0; x < dest_width; ++x) { - src_offset_lut[i] = px_size * get_source_offset(x, y, src_width, src_height, dest_width, dest_height); - dest_offset_lut[i] = px_size * get_dest_offset(x, y, dest_height); - i++; +static inline int init_px_to_framebuffer_2d(int dest_width, int dest_height, + int src_width, int src_height, + int px_size) { + // Generate LUTs so we don't have to calculate pixel rotation while + // streaming. + offset_lut_size = dest_width * dest_height; + src_offset_lut = malloc(sizeof(int) * offset_lut_size); + if (!src_offset_lut) { + fprintf(stderr, "Out of memory!\n"); + return -1; } - } - return 0; + dest_offset_lut = malloc(sizeof(int) * offset_lut_size); + if (!dest_offset_lut) { + fprintf(stderr, "Out of memory!\n"); + return -1; + } + + int i = 0; + for (int y = 0; y < dest_height; ++y) { + for (int x = 0; x < dest_width; ++x) { + src_offset_lut[i] = + px_size * get_source_offset(x, y, src_width, src_height, + dest_width, dest_height); + dest_offset_lut[i] = px_size * get_dest_offset(x, y, dest_height); + i++; + } + } + return 0; +} + +static inline int init_px_to_framebuffer_3d(int dest_width, int dest_height, + int src_width, int src_height, + int px_size) { + // Generate LUTs so we don't have to calculate pixel rotation while + // streaming. + offset_lut_size_3d = dest_width * dest_height; + src_offset_lut_3d_l = malloc(sizeof(int) * offset_lut_size_3d); + if (!src_offset_lut_3d_l) { + fprintf(stderr, "Out of memory!\n"); + return -1; + } + src_offset_lut_3d_r = malloc(sizeof(int) * offset_lut_size_3d); + if (!src_offset_lut_3d_r) { + fprintf(stderr, "Out of memory!\n"); + return -1; + } + dest_offset_lut_3d = malloc(sizeof(int) * offset_lut_size_3d); + if (!dest_offset_lut_3d) { + fprintf(stderr, "Out of memory!\n"); + return -1; + } + + int i = 0; + for (int y = 0; y < dest_height; ++y) { + for (int x = 0; x < dest_width; ++x) { + src_offset_lut_3d_l[i] = + px_size * get_source_offset_3d_l(x, y, src_width, src_height, + dest_width, dest_height); + src_offset_lut_3d_r[i] = + px_size * get_source_offset_3d_r(x, y, src_width, src_height, + dest_width, dest_height); + dest_offset_lut_3d[i] = + px_size * get_dest_offset(x, y, dest_height); + i++; + } + } + return 0; +} + +static inline int init_px_to_framebuffer_ds(int dest_width, int dest_height, + int src_width, int src_height, + int px_size) { + // Generate LUTs so we don't have to calculate pixel rotation while + // streaming. + offset_lut_size_ds_bottom = GSP_SCREEN_HEIGHT_BOTTOM * dest_height; + src_offset_lut_ds_top = malloc(sizeof(int) * offset_lut_size); + if (!src_offset_lut_ds_top) { + fprintf(stderr, "Out of memory!\n"); + return -1; + } + src_offset_lut_ds_bottom = malloc(sizeof(int) * offset_lut_size_ds_bottom); + if (!src_offset_lut_ds_bottom) { + fprintf(stderr, "Out of memory!\n"); + return -1; + } + dest_offset_lut_ds_bottom = malloc(sizeof(int) * offset_lut_size_ds_bottom); + if (!dest_offset_lut_ds_bottom) { + fprintf(stderr, "Out of memory!\n"); + return -1; + } + + int i = 0; + for (int y = 0; y < dest_height; ++y) { + for (int x = 0; x < dest_width; ++x) { + src_offset_lut_ds_top[i] = + px_size * get_source_offset_ds_top(x, y, src_width, src_height, + dest_width, dest_height); + i++; + } + } + + i = 0; + for (int y = 0; y < dest_height; ++y) { + for (int x = 0; x < GSP_SCREEN_HEIGHT_BOTTOM; ++x) { + src_offset_lut_ds_bottom[i] = + px_size * get_source_offset_ds_bottom( + x, y, src_width, src_height, + GSP_SCREEN_HEIGHT_BOTTOM, dest_height); + dest_offset_lut_ds_bottom[i] = + px_size * get_dest_offset(x, y, dest_height); + i++; + } + } + return 0; +} + +int init_px_to_framebuffer(int dest_width, int dest_height, int src_width, + int src_height, int px_size) { + surface_width = dest_width; + surface_height = dest_height; + int ret = init_px_to_framebuffer_2d(dest_width, dest_height, src_width, + src_height, px_size); + if (ret == 0) { + ret = init_px_to_framebuffer_3d(GSP_SCREEN_HEIGHT_TOP, dest_height, + src_width, src_height, px_size); + } + if (ret == 0) { + ret = init_px_to_framebuffer_ds(dest_width, dest_height, src_width, + src_height, px_size); + } + return ret; } void deinit_px_to_framebuffer() { - if (src_offset_lut) - free(src_offset_lut); - if (dest_offset_lut) - free(dest_offset_lut); -} - -void write_px_to_framebuffer(uint8_t* dest, - uint8_t* source, - int px_size) { - for (int i = 0; i < offset_lut_size; i++) { - memcpy(dest + dest_offset_lut[i], source + src_offset_lut[i], px_size); - } -} - -static inline int write_yuv_to_framebuffer(u8 *dest, const u8 **source, int width, int height, int px_size) { - Handle conversion_finish_event_handle; - int status = 0; - - status = Y2RU_SetSendingY(source[0], width * height, width, 0); - if (status) { - fprintf(stderr, "Y2RU_SetSendingY failed\n"); - goto y2ru_failed; - } - - status = Y2RU_SetSendingU(source[1], width * height / 4, width / 2, 0); - if (status) { - fprintf(stderr, "Y2RU_SetSendingU failed\n"); - goto y2ru_failed; - } - - status = Y2RU_SetSendingV(source[2], width * height / 4, width / 2, 0); - if (status) { - fprintf(stderr, "Y2RU_SetSendingV failed\n"); - goto y2ru_failed; - } - - status = Y2RU_SetReceiving(img_buffer, width * height * px_size, 8, 0); - if (status) { - fprintf(stderr, "Y2RU_SetReceiving failed\n"); - goto y2ru_failed; - } - - status = Y2RU_StartConversion(); - if (status) { - fprintf(stderr, "Y2RU_StartConversion failed\n"); - goto y2ru_failed; - } - - status = Y2RU_GetTransferEndEvent(&conversion_finish_event_handle); - if (status) { - fprintf(stderr, "Y2RU_GetTransferEndEvent failed\n"); - goto y2ru_failed; - } - - svcWaitSynchronization(conversion_finish_event_handle, 10000000);//Wait up to 10ms. - svcCloseHandle(conversion_finish_event_handle); - write_px_to_framebuffer(dest, img_buffer, px_size); - return DR_OK; - - y2ru_failed: - return -1; + if (src_offset_lut) + free(src_offset_lut); + if (src_offset_lut_3d_l) + free(src_offset_lut_3d_l); + if (src_offset_lut_3d_r) + free(src_offset_lut_3d_r); + if (src_offset_lut_ds_top) + free(src_offset_lut_ds_top); + if (src_offset_lut_ds_bottom) + free(src_offset_lut_ds_bottom); + if (dest_offset_lut) + free(dest_offset_lut); + if (dest_offset_lut_3d) + free(dest_offset_lut_3d); + if (dest_offset_lut_ds_bottom) + free(dest_offset_lut_ds_bottom); +} + +static inline void write_px_to_framebuffer_2D(uint8_t *source, int px_size) { + u8 *dest = gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL); + for (int i = 0; i < offset_lut_size; i++) { + memcpy(dest + dest_offset_lut[i], source + src_offset_lut[i], px_size); + } + gfxScreenSwapBuffers(GFX_TOP, false); +} + +static inline void write_px_to_framebuffer_3D(uint8_t *source, int px_size) { + u8 *dest = gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL); + for (int i = 0; i < offset_lut_size_3d; i++) { + memcpy(dest + dest_offset_lut_3d[i], source + src_offset_lut_3d_l[i], + px_size); + } + + dest = gfxGetFramebuffer(GFX_TOP, GFX_RIGHT, NULL, NULL); + for (int i = 0; i < offset_lut_size_3d; i++) { + memcpy(dest + dest_offset_lut_3d[i], source + src_offset_lut_3d_r[i], + px_size); + } + gfxScreenSwapBuffers(GFX_TOP, true); +} + +static inline void write_px_to_framebuffer_DS(uint8_t *source, int px_size) { + u8 *dest = gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL); + for (int i = 0; i < offset_lut_size; i++) { + memcpy(dest + dest_offset_lut[i], source + src_offset_lut_ds_top[i], + px_size); + } + + dest = gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL); + for (int i = 0; i < offset_lut_size_ds_bottom; i++) { + memcpy(dest + dest_offset_lut_ds_bottom[i], + source + src_offset_lut_ds_bottom[i], px_size); + } + gfxSwapBuffers(); +} + +void write_px_to_framebuffer(uint8_t *source, int px_size) { + if (enable_dual_display) { + ensure_3d_disabled(); + write_px_to_framebuffer_DS(source, px_size); + } else if (osGet3DSliderState() > 0.0) { + ensure_3d_enabled(); + write_px_to_framebuffer_3D(source, px_size); + } else { + ensure_3d_disabled(); + write_px_to_framebuffer_2D(source, px_size); + } +} + +static inline int write_yuv_to_framebuffer(const u8 **source, int width, + int height, int px_size) { + Handle conversion_finish_event_handle; + int status = 0; + + status = Y2RU_SetSendingY(source[0], width * height, width, 0); + if (status) { + fprintf(stderr, "Y2RU_SetSendingY failed\n"); + goto y2ru_failed; + } + + status = Y2RU_SetSendingU(source[1], width * height / 4, width / 2, 0); + if (status) { + fprintf(stderr, "Y2RU_SetSendingU failed\n"); + goto y2ru_failed; + } + + status = Y2RU_SetSendingV(source[2], width * height / 4, width / 2, 0); + if (status) { + fprintf(stderr, "Y2RU_SetSendingV failed\n"); + goto y2ru_failed; + } + + status = Y2RU_SetReceiving(img_buffer, width * height * px_size, 8, 0); + if (status) { + fprintf(stderr, "Y2RU_SetReceiving failed\n"); + goto y2ru_failed; + } + + status = Y2RU_StartConversion(); + if (status) { + fprintf(stderr, "Y2RU_StartConversion failed\n"); + goto y2ru_failed; + } + + status = Y2RU_GetTransferEndEvent(&conversion_finish_event_handle); + if (status) { + fprintf(stderr, "Y2RU_GetTransferEndEvent failed\n"); + goto y2ru_failed; + } + + svcWaitSynchronization(conversion_finish_event_handle, + 10000000); // Wait up to 10ms. + svcCloseHandle(conversion_finish_event_handle); + write_px_to_framebuffer(img_buffer, px_size); + return DR_OK; + +y2ru_failed: + return -1; } static int n3ds_submit_decode_unit(PDECODE_UNIT decodeUnit) { - PLENTRY entry = decodeUnit->bufferList; - int length = 0; + PLENTRY entry = decodeUnit->bufferList; + int length = 0; - ensure_buf_size(&ffmpeg_buffer, &ffmpeg_buffer_size, decodeUnit->fullLength + AV_INPUT_BUFFER_PADDING_SIZE); + ensure_buf_size(&ffmpeg_buffer, &ffmpeg_buffer_size, + decodeUnit->fullLength + AV_INPUT_BUFFER_PADDING_SIZE); - while (entry != NULL) { - memcpy(ffmpeg_buffer+length, entry->data, entry->length); - length += entry->length; - entry = entry->next; - } - ffmpeg_decode(ffmpeg_buffer, length); + while (entry != NULL) { + memcpy(ffmpeg_buffer + length, entry->data, entry->length); + length += entry->length; + entry = entry->next; + } + ffmpeg_decode(ffmpeg_buffer, length); - AVFrame* frame = ffmpeg_get_frame(false); - u8 *gfxtopadr = gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL); - int status = write_yuv_to_framebuffer(gfxtopadr, frame->data, image_width, image_height, pixel_size); - gfxScreenSwapBuffers(GFX_TOP, false); + AVFrame *frame = ffmpeg_get_frame(false); + int status = write_yuv_to_framebuffer(frame->data, image_width, + image_height, pixel_size); - return status; + return status; } DECODER_RENDERER_CALLBACKS decoder_callbacks_n3ds = { - .setup = n3ds_setup, - .cleanup = n3ds_cleanup, - .submitDecodeUnit = n3ds_submit_decode_unit, - .capabilities = CAPABILITY_DIRECT_SUBMIT | CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC, + .setup = n3ds_setup, + .cleanup = n3ds_cleanup, + .submitDecodeUnit = n3ds_submit_decode_unit, + .capabilities = + CAPABILITY_DIRECT_SUBMIT | CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC, }; diff --git a/src/video/n3ds_video_mvd.c b/src/video/n3ds_video_mvd.c index 011a7cc3..cbca6399 100644 --- a/src/video/n3ds_video_mvd.c +++ b/src/video/n3ds_video_mvd.c @@ -17,198 +17,204 @@ * along with Moonlight; if not, see . */ -#include "video.h" #include "../util.h" +#include "video.h" #include <3ds.h> #include #include -#include #include -#include #include +#include +#include #define N3DS_DEC_BUFF_SIZE 23 // Best performing transfer size (optimized through experimentation) #define N3DS_YUYV_XFER_UNIT 800 -// Wait up to 20ms for YUYV conversion to complete (optimized through experimentation) +// Wait up to 20ms for YUYV conversion to complete (optimized through +// experimentation) #define N3DS_YUYV_CONV_WAIT_NS 20000000 // General decoder and renderer state -static void* nal_unit_buffer; +static void *nal_unit_buffer; static size_t nal_unit_buffer_size; static MVDSTD_Config mvdstd_config; Handle conversion_finish_event_handle = NULL; static int image_width, image_height, surface_width, surface_height, pixel_size; -static u8* yuv_img_buffer; -static u8* rgb_img_buffer; +static u8 *yuv_img_buffer; +static u8 *rgb_img_buffer; static bool first_frame = true; -static int n3ds_init(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { - bool is_new_3ds; - APT_CheckNew3DS(&is_new_3ds); - if (!is_new_3ds) { - fprintf(stderr, "Hardware decoding is only available on the New 3DS\n"); - return -1; - } - - int status = mvdstdInit(MVDMODE_VIDEOPROCESSING, MVD_INPUT_H264, MVD_OUTPUT_YUYV422, width * height * N3DS_DEC_BUFF_SIZE, NULL); - if (status) { - fprintf(stderr, "mvdstdInit failed: %d\n", status); - mvdstdExit(); - return -1; - } - - if(y2rInit()) - { - fprintf(stderr, "Failed to initialize Y2R\n"); - return -1; - } - Y2RU_ConversionParams y2r_parameters; - y2r_parameters.input_format = INPUT_YUV422_BATCH; - y2r_parameters.output_format = OUTPUT_RGB_16_565; - y2r_parameters.rotation = ROTATION_NONE; - y2r_parameters.block_alignment = BLOCK_LINE; - y2r_parameters.input_line_width = width; - y2r_parameters.input_lines = height; - y2r_parameters.standard_coefficient = COEFFICIENT_ITU_R_BT_709_SCALING; - y2r_parameters.alpha = 0xFF; - status = Y2RU_SetConversionParams(&y2r_parameters); - if (status) { - fprintf(stderr, "Failed to set Y2RU params\n"); - return -1; - } - status = Y2RU_SetTransferEndInterrupt(true); - if (status) { - fprintf(stderr, "Failed to enable Y2RU interrupt\n"); - return -1; - } - - surface_height = GSP_SCREEN_WIDTH; - if (width > GSP_SCREEN_HEIGHT_TOP) { - gfxSetWide(true); - surface_width = GSP_SCREEN_HEIGHT_TOP_2X; - } - else { - gfxSetWide(false); - surface_width = GSP_SCREEN_HEIGHT_TOP; - } - - GSPGPU_FramebufferFormat px_fmt = gfxGetScreenFormat(GFX_TOP); - image_width = width; - image_height = height; - pixel_size = gspGetBytesPerPixel(px_fmt); - yuv_img_buffer = linearAlloc(width * height * pixel_size); - if (!yuv_img_buffer) { - fprintf(stderr, "Out of memory!\n"); - return -1; - } - rgb_img_buffer = linearAlloc(width * height * pixel_size); - if (!rgb_img_buffer) { - fprintf(stderr, "Out of memory!\n"); - return -1; - } - - ensure_linear_buf_size(&nal_unit_buffer, &nal_unit_buffer_size, INITIAL_DECODER_BUFFER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE); - mvdstdGenerateDefaultConfig(&mvdstd_config, image_width, image_height, image_width, image_height, NULL, yuv_img_buffer, NULL); - MVDSTD_SetConfig(&mvdstd_config); - - return init_px_to_framebuffer(surface_width, surface_height, image_width, image_height, pixel_size); +static int n3ds_init(int videoFormat, int width, int height, int redrawRate, + void *context, int drFlags) { + bool is_new_3ds; + APT_CheckNew3DS(&is_new_3ds); + if (!is_new_3ds) { + fprintf(stderr, "Hardware decoding is only available on the New 3DS\n"); + return -1; + } + + int status = + mvdstdInit(MVDMODE_VIDEOPROCESSING, MVD_INPUT_H264, MVD_OUTPUT_YUYV422, + width * height * N3DS_DEC_BUFF_SIZE, NULL); + if (status) { + fprintf(stderr, "mvdstdInit failed: %d\n", status); + mvdstdExit(); + return -1; + } + + if (y2rInit()) { + fprintf(stderr, "Failed to initialize Y2R\n"); + return -1; + } + Y2RU_ConversionParams y2r_parameters; + y2r_parameters.input_format = INPUT_YUV422_BATCH; + y2r_parameters.output_format = OUTPUT_RGB_16_565; + y2r_parameters.rotation = ROTATION_NONE; + y2r_parameters.block_alignment = BLOCK_LINE; + y2r_parameters.input_line_width = width; + y2r_parameters.input_lines = height; + y2r_parameters.standard_coefficient = COEFFICIENT_ITU_R_BT_709_SCALING; + y2r_parameters.alpha = 0xFF; + status = Y2RU_SetConversionParams(&y2r_parameters); + if (status) { + fprintf(stderr, "Failed to set Y2RU params\n"); + return -1; + } + status = Y2RU_SetTransferEndInterrupt(true); + if (status) { + fprintf(stderr, "Failed to enable Y2RU interrupt\n"); + return -1; + } + + surface_height = GSP_SCREEN_WIDTH; + if (width > GSP_SCREEN_HEIGHT_TOP) { + surface_width = GSP_SCREEN_HEIGHT_TOP_2X; + } else { + surface_width = GSP_SCREEN_HEIGHT_TOP; + } + + GSPGPU_FramebufferFormat px_fmt = gfxGetScreenFormat(GFX_TOP); + image_width = width; + image_height = height; + pixel_size = gspGetBytesPerPixel(px_fmt); + yuv_img_buffer = linearAlloc(width * height * pixel_size); + if (!yuv_img_buffer) { + fprintf(stderr, "Out of memory!\n"); + return -1; + } + rgb_img_buffer = linearAlloc(width * height * pixel_size); + if (!rgb_img_buffer) { + fprintf(stderr, "Out of memory!\n"); + return -1; + } + + ensure_linear_buf_size(&nal_unit_buffer, &nal_unit_buffer_size, + INITIAL_DECODER_BUFFER_SIZE + + AV_INPUT_BUFFER_PADDING_SIZE); + mvdstdGenerateDefaultConfig(&mvdstd_config, image_width, image_height, + image_width, image_height, NULL, yuv_img_buffer, + NULL); + MVDSTD_SetConfig(&mvdstd_config); + + return init_px_to_framebuffer(surface_width, surface_height, image_width, + image_height, pixel_size); } // This function must be called after // decoding is finished static void n3ds_destroy(void) { - y2rExit(); - mvdstdExit(); - linearFree(nal_unit_buffer); - linearFree(yuv_img_buffer); - linearFree(rgb_img_buffer); - deinit_px_to_framebuffer(); + y2rExit(); + mvdstdExit(); + linearFree(nal_unit_buffer); + linearFree(yuv_img_buffer); + linearFree(rgb_img_buffer); + deinit_px_to_framebuffer(); } -static inline int yuv_to_rgb(u8 *dest, const u8 *source, int width, int height, int px_size) { - int status = Y2RU_SetSendingYUYV(source, width * height * 2, N3DS_YUYV_XFER_UNIT, 0); - if (status) { - fprintf(stderr, "Y2RU_SetSendingYUYV failed\n"); - goto y2ru_failed; - } - - status = Y2RU_SetReceiving(dest, width * height * px_size, 8, 0); - if (status) { - fprintf(stderr, "Y2RU_SetReceiving failed\n"); - goto y2ru_failed; - } - - status = Y2RU_StartConversion(); - if (status) { - fprintf(stderr, "Y2RU_StartConversion failed\n"); - goto y2ru_failed; - } - - status = Y2RU_GetTransferEndEvent(&conversion_finish_event_handle); - if (status) { - fprintf(stderr, "Y2RU_GetTransferEndEvent failed\n"); - goto y2ru_failed; - } - return DR_OK; - - y2ru_failed: - return -1; +static inline int yuv_to_rgb(u8 *dest, const u8 *source, int width, int height, + int px_size) { + int status = + Y2RU_SetSendingYUYV(source, width * height * 2, N3DS_YUYV_XFER_UNIT, 0); + if (status) { + fprintf(stderr, "Y2RU_SetSendingYUYV failed\n"); + goto y2ru_failed; + } + + status = Y2RU_SetReceiving(dest, width * height * px_size, 8, 0); + if (status) { + fprintf(stderr, "Y2RU_SetReceiving failed\n"); + goto y2ru_failed; + } + + status = Y2RU_StartConversion(); + if (status) { + fprintf(stderr, "Y2RU_StartConversion failed\n"); + goto y2ru_failed; + } + + status = Y2RU_GetTransferEndEvent(&conversion_finish_event_handle); + if (status) { + fprintf(stderr, "Y2RU_GetTransferEndEvent failed\n"); + goto y2ru_failed; + } + return DR_OK; + +y2ru_failed: + return -1; } // packets must be decoded in order // indata must be inlen + AV_INPUT_BUFFER_PADDING_SIZE in length -static inline int n3ds_decode(unsigned char* indata, int inlen) { - int ret = mvdstdProcessVideoFrame(indata, inlen, 1, NULL); - if(ret!=MVD_STATUS_PARAMSET && ret!=MVD_STATUS_INCOMPLETEPROCESSING) - { - mvdstdRenderVideoFrame(&mvdstd_config, true); - } - return 0; +static inline int n3ds_decode(unsigned char *indata, int inlen) { + int ret = mvdstdProcessVideoFrame(indata, inlen, 1, NULL); + if (ret != MVD_STATUS_PARAMSET && ret != MVD_STATUS_INCOMPLETEPROCESSING) { + mvdstdRenderVideoFrame(&mvdstd_config, true); + } + return 0; } static int n3ds_submit_decode_unit(PDECODE_UNIT decodeUnit) { - PLENTRY entry = decodeUnit->bufferList; - int length = 0; - - ensure_linear_buf_size(&nal_unit_buffer, &nal_unit_buffer_size, decodeUnit->fullLength + AV_INPUT_BUFFER_PADDING_SIZE); - - while (entry != NULL) { - memcpy(nal_unit_buffer+length, entry->data, entry->length); - length += entry->length; - entry = entry->next; - } - GSPGPU_FlushDataCache(nal_unit_buffer, length); - - if (conversion_finish_event_handle != NULL) { - u8 *gfxtopadr = gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL); - - svcWaitSynchronization(conversion_finish_event_handle, N3DS_YUYV_CONV_WAIT_NS); - svcCloseHandle(conversion_finish_event_handle); - - write_px_to_framebuffer(gfxtopadr, rgb_img_buffer, pixel_size); - gfxScreenSwapBuffers(GFX_TOP, false); - } - - - n3ds_decode(nal_unit_buffer, length); - yuv_to_rgb(rgb_img_buffer, yuv_img_buffer, image_width, image_height, pixel_size); - - // If MVD never gets an IDR frame, everything shows up gray - if (first_frame) { - first_frame = false; - return DR_NEED_IDR; - } - return DR_OK; + PLENTRY entry = decodeUnit->bufferList; + int length = 0; + + ensure_linear_buf_size(&nal_unit_buffer, &nal_unit_buffer_size, + decodeUnit->fullLength + + AV_INPUT_BUFFER_PADDING_SIZE); + + while (entry != NULL) { + memcpy(nal_unit_buffer + length, entry->data, entry->length); + length += entry->length; + entry = entry->next; + } + GSPGPU_FlushDataCache(nal_unit_buffer, length); + + if (conversion_finish_event_handle != NULL) { + svcWaitSynchronization(conversion_finish_event_handle, + N3DS_YUYV_CONV_WAIT_NS); + svcCloseHandle(conversion_finish_event_handle); + + write_px_to_framebuffer(rgb_img_buffer, pixel_size); + } + + n3ds_decode(nal_unit_buffer, length); + yuv_to_rgb(rgb_img_buffer, yuv_img_buffer, image_width, image_height, + pixel_size); + + // If MVD never gets an IDR frame, everything shows up gray + if (first_frame) { + first_frame = false; + return DR_NEED_IDR; + } + return DR_OK; } DECODER_RENDERER_CALLBACKS decoder_callbacks_n3ds_mvd = { - .setup = n3ds_init, - .cleanup = n3ds_destroy, - .submitDecodeUnit = n3ds_submit_decode_unit, - .capabilities = CAPABILITY_DIRECT_SUBMIT, + .setup = n3ds_init, + .cleanup = n3ds_destroy, + .submitDecodeUnit = n3ds_submit_decode_unit, + .capabilities = CAPABILITY_DIRECT_SUBMIT, }; diff --git a/src/video/rk.c b/src/video/rk.c index a39b595b..2c2bdfed 100644 --- a/src/video/rk.c +++ b/src/video/rk.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -161,33 +160,34 @@ void *display_thread(void *param) { while (!frm_eos) { int _fb_id; - ret = pthread_mutex_lock(&mutex); - assert(!ret); + pthread_mutex_lock(&mutex); while (fb_id == 0) { pthread_cond_wait(&cond, &mutex); - assert(!ret); if (fb_id == 0 && frm_eos) { - ret = pthread_mutex_unlock(&mutex); - assert(!ret); + pthread_mutex_unlock(&mutex); return NULL; } } _fb_id = fb_id; fb_id = 0; - ret = pthread_mutex_unlock(&mutex); - assert(!ret); + pthread_mutex_unlock(&mutex); if (atomic) { // We may need to modeset to apply colorspace changes when toggling HDR set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "FB_ID", _fb_id); ret = drmModeAtomicCommit(fd, drm_request, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); + if (ret) { + perror("drmModeAtomicCommit"); + } } else { ret = drmModeSetPlane(fd, plane_id, crtc_id, _fb_id, 0, fb_x, fb_y, fb_width, fb_height, 0, 0, frm_width << 16, frm_height << 16); + if (ret) { + perror("drmModeSetPlane"); + } } - assert(!ret); } return NULL; @@ -250,10 +250,11 @@ void *frame_thread(void *param) { dmcd.bpp = 8; // hor_stride is already adjusted for 10 vs 8 bit dmcd.width = hor_stride; dmcd.height = ver_stride * 2; // documentation say not v*2/3 but v*2 (additional info included) - do { - ret = ioctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &dmcd); - } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); - assert(!ret); + ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &dmcd); + if (ret) { + perror("drmIoctl(DRM_IOCTL_MODE_CREATE_DUMB)"); + exit(EXIT_FAILURE); + } assert(dmcd.pitch == dmcd.width); assert(dmcd.size == dmcd.pitch * dmcd.height); frame_to_drm[i].handle = dmcd.handle; @@ -262,10 +263,11 @@ void *frame_thread(void *param) { struct drm_prime_handle dph = {0}; dph.handle = dmcd.handle; dph.fd = -1; - do { - ret = ioctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &dph); - } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); - assert(!ret); + ret = drmIoctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &dph); + if (ret) { + perror("drmIoctl(DRM_IOCTL_PRIME_HANDLE_TO_FD)"); + exit(EXIT_FAILURE); + } MppBufferInfo info = {0}; info.type = MPP_BUFFER_TYPE_DRM; info.size = dmcd.width * dmcd.height; @@ -283,7 +285,10 @@ void *frame_thread(void *param) { offsets[1] = pitches[0] * ver_stride; pitches[1] = dmcd.pitch; ret = drmModeAddFB2(fd, frm_width, frm_height, pixel_format, handles, pitches, offsets, &frame_to_drm[i].fb_id, 0); - assert(!ret); + if (ret) { + perror("drmModeAddFB2"); + exit(EXIT_FAILURE); + } } // register external frame group ret = mpi_api->control(mpi_ctx, MPP_DEC_SET_EXT_BUF_GROUP, mpi_frm_grp); @@ -320,14 +325,10 @@ void *frame_thread(void *param) { } assert(i != MAX_FRAMES); // send DRM FB to display thread - ret = pthread_mutex_lock(&mutex); - assert(!ret); + pthread_mutex_lock(&mutex); fb_id = frame_to_drm[i].fb_id; - ret = pthread_cond_signal(&cond); - assert(!ret); - ret = pthread_mutex_unlock(&mutex); - assert(!ret); - + pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); } else { fprintf(stderr, "Frame no buff\n"); } @@ -384,7 +385,10 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte } resources = drmModeGetResources(fd); - assert(resources); + if (!resources) { + perror("drmModeGetResources"); + return -1; + } // find active monitor for (i = 0; i < resources->count_connectors; ++i) { @@ -432,7 +436,10 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte for (i = 0; i < resources->count_crtcs; ++i) { if (resources->crtcs[i] == encoder->crtc_id) { crtc = drmModeGetCrtc(fd, resources->crtcs[i]); - assert(crtc); + if (!crtc) { + perror("drmModeGetCrtc"); + continue; + } break; } } @@ -443,15 +450,25 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte uint32_t crtc_bit = (1 << i); ret = drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); - assert(!ret); + if (ret) { + perror("drmSetClientCap(DRM_CLIENT_CAP_UNIVERSAL_PLANES)"); + } if (atomic) { ret = drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1); - assert(!ret); - drm_request = drmModeAtomicAlloc(); - assert(drm_request); + if (ret) { + perror("drmSetClientCap(DRM_CLIENT_CAP_ATOMIC)"); + atomic = false; + } + else { + drm_request = drmModeAtomicAlloc(); + assert(drm_request); + } } plane_resources = drmModeGetPlaneResources(fd); - assert(plane_resources); + if (!plane_resources) { + perror("drmModeGetPlaneResources"); + return -1; + } // search for OVERLAY (for active connector, unused, NV12 support) for (i = 0; i < plane_resources->count_planes; i++) { @@ -507,7 +524,29 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte } drmModeFreePlane(ovr); } - assert(plane_id); + + if (!plane_id) { + fprintf(stderr, "Unable to find suitable plane\n"); + return -1; + } + + // DRM defines rotation in degrees counter-clockwise while we define + // rotation in degrees clockwise, so we swap the 90 and 270 cases + int displayRotation = drFlags & DISPLAY_ROTATE_MASK; + switch (displayRotation) { + case DISPLAY_ROTATE_90: + set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "rotation", DRM_MODE_ROTATE_270); + break; + case DISPLAY_ROTATE_180: + set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "rotation", DRM_MODE_ROTATE_180); + break; + case DISPLAY_ROTATE_270: + set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "rotation", DRM_MODE_ROTATE_90); + break; + default: + set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "rotation", DRM_MODE_ROTATE_0); + break; + } // hide cursor by move in left lower corner drmModeMoveCursor(fd, crtc_id, 0, crtc_height); @@ -540,15 +579,11 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte ret = mpi_api->control(mpi_ctx, MPP_SET_OUTPUT_BLOCK, ¶m); assert(!ret); - ret = pthread_mutex_init(&mutex, NULL); - assert(!ret); - ret = pthread_cond_init(&cond, NULL); - assert(!ret); + pthread_mutex_init(&mutex, NULL); + pthread_cond_init(&cond, NULL); - ret = pthread_create(&tid_frame, NULL, frame_thread, NULL); - assert(!ret); - ret = pthread_create(&tid_display, NULL, display_thread, NULL); - assert(!ret); + pthread_create(&tid_frame, NULL, frame_thread, NULL); + pthread_create(&tid_display, NULL, display_thread, NULL); return 0; } @@ -559,26 +594,19 @@ void rk_cleanup() { int ret; frm_eos = 1; - ret = pthread_mutex_lock(&mutex); - assert(!ret); - ret = pthread_cond_signal(&cond); - assert(!ret); - ret = pthread_mutex_unlock(&mutex); - assert(!ret); + pthread_mutex_lock(&mutex); + pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); - ret = pthread_join(tid_display, NULL); - assert(!ret); + pthread_join(tid_display, NULL); - ret = pthread_cond_destroy(&cond); - assert(!ret); - ret = pthread_mutex_destroy(&mutex); - assert(!ret); + pthread_cond_destroy(&cond); + pthread_mutex_destroy(&mutex); ret = mpi_api->reset(mpi_ctx); assert(!ret); - ret = pthread_join(tid_frame, NULL); - assert(!ret); + pthread_join(tid_frame, NULL); if (mpi_frm_grp) { ret = mpp_buffer_group_put(mpi_frm_grp); @@ -586,13 +614,15 @@ void rk_cleanup() { mpi_frm_grp = NULL; for (i = 0; i < MAX_FRAMES; i++) { ret = drmModeRmFB(fd, frame_to_drm[i].fb_id); - assert(!ret); + if (ret) { + perror("drmModeRmFB"); + } struct drm_mode_destroy_dumb dmdd = {0}; dmdd.handle = frame_to_drm[i].handle; - do { - ret = ioctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dmdd); - } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); - assert(!ret); + ret = drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dmdd); + if (ret) { + perror("drmIoctl(DRM_IOCTL_MODE_DESTROY_DUMB)"); + } } } diff --git a/src/video/video.h b/src/video/video.h index 820f5733..a6a9bb13 100644 --- a/src/video/video.h +++ b/src/video/video.h @@ -33,7 +33,7 @@ #define INIT_VDPAU 2 #define INIT_VAAPI 3 -#define INITIAL_DECODER_BUFFER_SIZE (256*1024) +#define INITIAL_DECODER_BUFFER_SIZE (256 * 1024) #ifdef HAVE_X11 int x11_init(bool vdpau, bool vaapi); @@ -46,15 +46,11 @@ extern DECODER_RENDERER_CALLBACKS decoder_callbacks_x11_vdpau; #endif #endif #ifdef __3DS__ -int init_px_to_framebuffer(int dest_width, - int dest_height, - int src_width, - int src_height, - int px_size); +extern bool enable_dual_display; +int init_px_to_framebuffer(int dest_width, int dest_height, int src_width, + int src_height, int px_size); void deinit_px_to_framebuffer(); -void write_px_to_framebuffer(uint8_t* dest, - uint8_t* source, - int px_size); +void write_px_to_framebuffer(uint8_t *source, int px_size); extern DECODER_RENDERER_CALLBACKS decoder_callbacks_n3ds; extern DECODER_RENDERER_CALLBACKS decoder_callbacks_n3ds_mvd; diff --git a/third_party/SDL_GameControllerDB b/third_party/SDL_GameControllerDB index dbcf31a6..ae51c994 160000 --- a/third_party/SDL_GameControllerDB +++ b/third_party/SDL_GameControllerDB @@ -1 +1 @@ -Subproject commit dbcf31a6709ec8354b5963b1bb411721e07bd846 +Subproject commit ae51c9942f0d985b2161d6b22986f041fc98ce5c diff --git a/third_party/moonlight-common-c b/third_party/moonlight-common-c index ec171fd7..eb215615 160000 --- a/third_party/moonlight-common-c +++ b/third_party/moonlight-common-c @@ -1 +1 @@ -Subproject commit ec171fd7cab4cb8b7eb6416f1ad9ad62e339efb2 +Subproject commit eb21561541874d9e9dbd7da26000c6dd55050748