mirror of
https://github.com/moonlight-stream/moonlight-embedded.git
synced 2026-04-07 08:26:05 +00:00
Merge tag 'v2.4.0' into raspbian/jessie
This commit is contained in:
@@ -1,19 +1,14 @@
|
||||
project(moonlight-embedded C)
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project(moonlight-embedded VERSION 2.4.0 LANGUAGES C)
|
||||
SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
||||
include(${CMAKE_ROOT}/Modules/GNUInstallDirs.cmake)
|
||||
|
||||
set(MOONLIGHT_MAJOR_VERSION 2)
|
||||
set(MOONLIGHT_MINOR_VERSION 3)
|
||||
set(MOONLIGHT_PATCH_VERSION 0)
|
||||
set(MOONLIGHT_VERSION ${MOONLIGHT_MAJOR_VERSION}.${MOONLIGHT_MINOR_VERSION}.${MOONLIGHT_PATCH_VERSION})
|
||||
|
||||
aux_source_directory(./src SRC_LIST)
|
||||
aux_source_directory(./src/input SRC_LIST)
|
||||
list(APPEND SRC_LIST ./src/input/evdev.c ./src/input/mapping.c ./src/input/udev.c)
|
||||
|
||||
set(MOONLIGHT_DEFINITIONS)
|
||||
|
||||
find_package(ALSA REQUIRED)
|
||||
find_package(ALSA)
|
||||
find_package(Opus REQUIRED)
|
||||
find_package(Broadcom)
|
||||
find_package(Freescale)
|
||||
@@ -25,36 +20,47 @@ pkg_check_modules(UDEV REQUIRED libudev)
|
||||
pkg_check_modules(SDL sdl2>=2.0.4)
|
||||
pkg_check_modules(AVCODEC libavcodec)
|
||||
pkg_check_modules(AVUTIL libavutil)
|
||||
pkg_check_modules(XLIB x11-xcb)
|
||||
pkg_check_modules(XLIB x11)
|
||||
pkg_check_modules(LIBVA vdpau)
|
||||
pkg_check_modules(PULSE libpulse-simple)
|
||||
pkg_check_modules(CEC libcec>=3.0.0)
|
||||
pkg_check_modules(EGL egl)
|
||||
pkg_check_modules(GLES glesv2)
|
||||
|
||||
if(AVCODEC_FOUND AND AVUTIL_FOUND AND SDL_FOUND)
|
||||
set(SOFTWARE_FOUND TRUE)
|
||||
if(XLIB_FOUND AND LIBVA_FOUND)
|
||||
set(VDPAU_FOUND TRUE)
|
||||
else()
|
||||
set(VDPAU_FOUND FALSE)
|
||||
set(VDPAU_FOUND FALSE)
|
||||
set(SOFTWARE_FOUND FALSE)
|
||||
|
||||
if(AVCODEC_FOUND AND AVUTIL_FOUND)
|
||||
if(EGL_FOUND AND GLES_FOUND AND XLIB_FOUND)
|
||||
set(X11_FOUND TRUE)
|
||||
if(XLIB_FOUND AND LIBVA_FOUND)
|
||||
set(VDPAU_FOUND TRUE)
|
||||
endif()
|
||||
endif()
|
||||
if(SDL_FOUND OR X11_FOUND)
|
||||
set(SOFTWARE_FOUND TRUE)
|
||||
endif()
|
||||
else()
|
||||
set(SOFTWARE_FOUND FALSE)
|
||||
endif()
|
||||
|
||||
SET(MOONLIGHT_COMMON_INCLUDE_DIR ./third_party/moonlight-common-c/src)
|
||||
SET(GAMESTREAM_INCLUDE_DIR ./libgamestream)
|
||||
|
||||
if(CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS LC_DEBUG)
|
||||
list(APPEND MOONLIGHT_OPTIONS DEBUG)
|
||||
elseif(NOT AMLOGIC_FOUND AND NOT BROADCOM_FOUND AND NOT FREESCALE_FOUND AND NOT SOFTWARE_FOUND)
|
||||
if(NOT AMLOGIC_FOUND AND NOT BROADCOM_FOUND AND NOT FREESCALE_FOUND AND NOT SOFTWARE_FOUND)
|
||||
message(FATAL_ERROR "No video output available")
|
||||
endif()
|
||||
|
||||
if (SOFTWARE_FOUND)
|
||||
list(APPEND SRC_LIST ./src/video/ffmpeg.c ./src/video/sdl.c ./src/audio/sdl.c)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_SDL)
|
||||
list(APPEND MOONLIGHT_OPTIONS SDL)
|
||||
list(APPEND SRC_LIST ./src/video/ffmpeg.c)
|
||||
if (SDL_FOUND)
|
||||
list(APPEND SRC_LIST ./src/video/sdl.c ./src/audio/sdl.c ./src/input/sdl.c)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_SDL)
|
||||
list(APPEND MOONLIGHT_OPTIONS SDL)
|
||||
endif()
|
||||
if (X11_FOUND)
|
||||
list(APPEND SRC_LIST ./src/video/x11.c ./src/video/egl.c ./src/input/x11.c)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_X11)
|
||||
list(APPEND MOONLIGHT_OPTIONS X11)
|
||||
endif()
|
||||
if(VDPAU_FOUND)
|
||||
list(APPEND SRC_LIST ./src/video/ffmpeg_vdpau.c)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_VDPAU)
|
||||
@@ -62,20 +68,28 @@ if (SOFTWARE_FOUND)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (AMLOGIC_FOUND OR BROADCOM_FOUND OR FREESCALE_FOUND OR CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
if (AMLOGIC_FOUND OR BROADCOM_FOUND OR FREESCALE_FOUND OR X11_FOUND)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_EMBEDDED)
|
||||
list(APPEND MOONLIGHT_OPTIONS EMBEDDED)
|
||||
endif()
|
||||
|
||||
if (ALSA_FOUND)
|
||||
list(APPEND SRC_LIST ./src/audio/alsa.c)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_ALSA)
|
||||
list(APPEND MOONLIGHT_OPTIONS ALSA)
|
||||
endif()
|
||||
|
||||
if (PULSE_FOUND)
|
||||
list(APPEND SRC_LIST ./src/audio/pulse.c)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_PULSE)
|
||||
list(APPEND MOONLIGHT_OPTIONS PULSE)
|
||||
endif()
|
||||
|
||||
include_directories("${PROJECT_BINARY_DIR}")
|
||||
if (CEC_FOUND)
|
||||
list(APPEND SRC_LIST ./src/input/cec.c)
|
||||
endif()
|
||||
|
||||
list(APPEND SRC_LIST ./src/audio/alsa.c ./src/audio/fake.c ./src/video/fake.c)
|
||||
include_directories("${PROJECT_BINARY_DIR}")
|
||||
|
||||
add_subdirectory(libgamestream)
|
||||
|
||||
@@ -120,15 +134,30 @@ if(FREESCALE_FOUND)
|
||||
install(TARGETS moonlight-imx DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
endif()
|
||||
|
||||
if(SDL_FOUND)
|
||||
target_include_directories(moonlight PRIVATE ${SDL_INCLUDE_DIRS})
|
||||
target_link_libraries(moonlight ${SDL_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(X11_FOUND)
|
||||
target_include_directories(moonlight PRIVATE ${XLIB_INCLUDE_DIRS} ${EGL_INCLUDE_DIRS} ${GLES_INCLUDE_DIRS})
|
||||
target_link_libraries(moonlight ${XLIB_LIBRARIES} ${EGL_LIBRARIES} ${GLES_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if (SOFTWARE_FOUND)
|
||||
target_include_directories(moonlight PRIVATE ${SDL_INCLUDE_DIRS} ${AVCODEC_INCLUDE_DIRS} ${AVUTIL_INCLUDE_DIRS})
|
||||
target_link_libraries(moonlight ${SDL_LIBRARIES} ${AVCODEC_LIBRARIES} ${AVUTIL_LIBRARIES})
|
||||
target_include_directories(moonlight PRIVATE ${AVCODEC_INCLUDE_DIRS} ${AVUTIL_INCLUDE_DIRS})
|
||||
target_link_libraries(moonlight ${AVCODEC_LIBRARIES} ${AVUTIL_LIBRARIES})
|
||||
if(VDPAU_FOUND)
|
||||
target_include_directories(moonlight PRIVATE ${XLIB_INCLUDE_DIRS} ${LIBVA_INCLUDE_DIRS})
|
||||
target_link_libraries(moonlight ${XLIB_LIBRARIES} ${LIBVA_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (ALSA_FOUND)
|
||||
target_include_directories(moonlight PRIVATE ${ALSA_INCLUDE_DIR})
|
||||
target_link_libraries(moonlight ${ALSA_LIBRARY})
|
||||
endif()
|
||||
|
||||
if (PULSE_FOUND)
|
||||
target_include_directories(moonlight PRIVATE ${PULSE_INCLUDE_DIRS})
|
||||
target_link_libraries(moonlight ${PULSE_LIBRARIES})
|
||||
@@ -138,10 +167,10 @@ configure_file("./src/configuration.h.in" "${PROJECT_BINARY_DIR}/configuration.h
|
||||
|
||||
set_property(TARGET moonlight PROPERTY COMPILE_DEFINITIONS ${MOONLIGHT_DEFINITIONS})
|
||||
target_include_directories(moonlight PRIVATE ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR} ${OPUS_INCLUDE_DIRS} ${EVDEV_INCLUDE_DIRS} ${UDEV_INCLUDE_DIRS})
|
||||
target_link_libraries(moonlight ${EVDEV_LIBRARIES} ${ALSA_LIBRARY} ${OPUS_LIBRARY} ${UDEV_LIBRARIES} ${CMAKE_DL_LIBS})
|
||||
target_link_libraries(moonlight ${EVDEV_LIBRARIES} ${OPUS_LIBRARY} ${UDEV_LIBRARIES} ${CMAKE_DL_LIBS})
|
||||
|
||||
add_subdirectory(docs)
|
||||
|
||||
install(TARGETS moonlight DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
install(DIRECTORY mappings DESTINATION ${CMAKE_INSTALL_DATADIR}/moonlight)
|
||||
install(FILES gamecontrollerdb.txt DESTINATION ${CMAKE_INSTALL_DATADIR}/moonlight)
|
||||
install(FILES moonlight.conf DESTINATION ${CMAKE_INSTALL_SYSCONFDIR})
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Moonlight Embedded is an open source implementation of NVIDIA's GameStream, as used by the NVIDIA Shield, but built for Linux.
|
||||
|
||||
Moonlight Embedded allows you to stream your full collection of Steam games from
|
||||
Moonlight Embedded allows you to stream your full collection of games from
|
||||
your powerful Windows desktop to your (embedded) Linux system, like Raspberry Pi, CuBox-i and ODROID.
|
||||
|
||||
## Documentation
|
||||
@@ -11,7 +11,7 @@ More information about installing and runnning Moonlight Embedded is available o
|
||||
|
||||
## Requirements
|
||||
|
||||
* [GFE compatible](http://shield.nvidia.com/play-pc-games/) computer with GTX 600/700/900 series GPU (for the PC you're streaming from)
|
||||
* [GFE compatible](http://shield.nvidia.com/play-pc-games/) computer with GTX 600/700/900/1000 series GPU (for the PC you're streaming from)
|
||||
* High-end wireless router (802.11n dual-band recommended) or wired network
|
||||
* Geforce Experience 2.1.1 or higher
|
||||
|
||||
|
||||
2
cmake/FindAmlogic.cmake
Normal file → Executable file
2
cmake/FindAmlogic.cmake
Normal file → Executable file
@@ -1,7 +1,7 @@
|
||||
find_path(AMLOGIC_INCLUDE_DIR
|
||||
NAMES codec.h
|
||||
DOC "Amlogic include directory"
|
||||
PATHS /usr/local/include/amcodec /usr/include/amcodec)
|
||||
PATHS /usr/local/include/amcodec /usr/include/amcodec /usr/include/)
|
||||
mark_as_advanced(AMLOGIC_INCLUDE_DIR)
|
||||
|
||||
find_library(AMAVUTILS_LIBRARY
|
||||
|
||||
@@ -11,10 +11,6 @@ Usage: I<moonlight> E<lt>actionE<gt> [options] [host]
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<map>
|
||||
|
||||
Create a mapping file for a gamepad.
|
||||
|
||||
=item B<pair>
|
||||
|
||||
Pair this computer with the host.
|
||||
@@ -64,6 +60,10 @@ This is the default option.
|
||||
|
||||
Use the resolution 1920x1080 for streaming.
|
||||
|
||||
=item B<-4k>
|
||||
|
||||
Use the resolution 3840x2160 for streaming.
|
||||
|
||||
=item B<-width> [I<WIDTH>]
|
||||
|
||||
Change the horizontal resolution to I<WIDTH>
|
||||
@@ -72,22 +72,15 @@ Change the horizontal resolution to I<WIDTH>
|
||||
|
||||
Change the vertical resolution to I<HEIGHT>
|
||||
|
||||
=item B<-30fps>
|
||||
|
||||
Use 30 fps for streaming.
|
||||
|
||||
=item B<-60fps>
|
||||
|
||||
Use 60 fps for streaming.
|
||||
This is the default configuration.
|
||||
|
||||
=item B<-fps> [I<FPS>]
|
||||
|
||||
Change the number of frame per second to I<FPS>
|
||||
Change the number of frame per second to I<FPS>.
|
||||
Defaults to 60fps for 720p and 30fps for 1080p and higher.
|
||||
Only 30 and 60 fps are currently supported by Gamestream.
|
||||
|
||||
=item B<-bitrate> [I<BITRATE>]
|
||||
|
||||
Change bitrate to I<BITRATE> kbps.
|
||||
Change bitrate to I<BITRATE> Kbps.
|
||||
By default the bitrate depends on the selected resolution and fps.
|
||||
For resolution 1080p and 60 fps and higher 20 Mbps is used.
|
||||
For resolution 1080p or 60 fps and higher 10 Mbps is used
|
||||
@@ -95,18 +88,17 @@ For other configurations 5 Mbps is used by default.
|
||||
|
||||
=item B<-packetsize> [I<PACKETSIZE>]
|
||||
|
||||
Change the network packetsize to I<PACKETSIZE>.
|
||||
Change the network packetsize to I<PACKETSIZE> bytes.
|
||||
The packetsize should the smaller than the MTU of the network.
|
||||
This value must be a multiply of 16.
|
||||
By default a safe value of 1024 is used.
|
||||
|
||||
=item B<-hevc>
|
||||
=item B<-codec> [I<CODEC>]
|
||||
|
||||
Request a h265/HEVC from the server.
|
||||
Will still use h264 if server doesn't support HEVC.
|
||||
|
||||
=item B<-h264>
|
||||
|
||||
Request a h264 from the server even if server and video decoder supports HEVC.
|
||||
Select codec to use.
|
||||
Can be 'auto', 'h264', 'h265' or 'hevc'.
|
||||
Not all video decoders do support H.265/HEVC.
|
||||
Will still use H.264 if server doesn't support HEVC.
|
||||
|
||||
=item B<-remote>
|
||||
|
||||
@@ -123,7 +115,11 @@ Stop GFE from changing the graphical settings of the requested game or applicati
|
||||
|
||||
=item B<-localaudio>
|
||||
|
||||
Play the audio on the host instead of this computer.
|
||||
Play the audio on the host computer instead of this device.
|
||||
|
||||
=item B<-surround>
|
||||
|
||||
Enable 5.1 surround sound instead of stereo.
|
||||
|
||||
=item B<-keydir> [I<DIRECTORY>]
|
||||
|
||||
@@ -132,21 +128,43 @@ By default the encryption keys are stored in $XDG_CACHE_DIR/moonlight or ~/.cach
|
||||
|
||||
=item B<-mapping> [I<MAPPING>]
|
||||
|
||||
Use I<MAPPING> as the mapping file for all inputs specified after this B<-mapping>.
|
||||
If no B<-input> is specified after the B<-mapping> this mapping is used for autoloaded inputs.
|
||||
Use I<MAPPING> as the mapping file for all inputs.
|
||||
This mapping file should have the same format as the gamecontrollerdb.txt for SDL2.
|
||||
By default the gamecontrollerdb.txt provided by Moonlight Embedded is used.
|
||||
|
||||
=item B<-platform> [I<PLATFORM>]
|
||||
|
||||
Select platform for audio and video output and input.
|
||||
<PLATFORM> can be pi, imx, aml, x11, x11_vdpau, sdl or fake.
|
||||
|
||||
=item B<-unsupported>
|
||||
|
||||
Try streaming if GFE version is unsupported
|
||||
|
||||
=item B<-verbose>
|
||||
|
||||
Enable verbose output
|
||||
|
||||
=item B<-debug>
|
||||
|
||||
Enable verbose and debug output
|
||||
|
||||
=item B<-input> [I<INPUT>]
|
||||
|
||||
Enable the I<INPUT> device.
|
||||
By default all available input devices are enabled.
|
||||
Only evdev devices /dev/input/event* are supported.
|
||||
To use a different gamepad mapping then the default the B<-mapping> should be specified before the B<-input>.
|
||||
|
||||
=item B<-audio> [I<DEVICE>]
|
||||
|
||||
Use <DEVICE> as audio output device.
|
||||
The default value is 'sysdefault' for ALSA and 'hdmi' for OMX on the Raspberry Pi.
|
||||
|
||||
=item B<-windowed>
|
||||
|
||||
Display the stream in a window instead of fullscreen.
|
||||
Only available when X11 or SDL platform is used.
|
||||
|
||||
=back
|
||||
|
||||
=head1 CONFIG FILE
|
||||
@@ -163,7 +181,7 @@ A documented example configuration file can be found at /etc/moonlight/moonlight
|
||||
|
||||
=head1 COMMENTS
|
||||
|
||||
Use Ctrl+Alt+Shift+Q to quit the streaming session.
|
||||
Use Ctrl+Alt+Shift+Q or Play+Back+LeftShoulder+RightShoulder to quit the streaming session.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
|
||||
158
gamecontrollerdb.txt
Normal file
158
gamecontrollerdb.txt
Normal file
@@ -0,0 +1,158 @@
|
||||
03000000022000000090000011010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,
|
||||
05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,
|
||||
05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,
|
||||
03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,
|
||||
03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,
|
||||
03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,
|
||||
03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,
|
||||
0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,
|
||||
030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||
030000000d0f00006e00000011010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||
030000000d0f00006600000011010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||
030000000d0f00005f00000011010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||
030000000d0f00005e00000011010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||
03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,
|
||||
030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||
030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||
030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||
030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||
030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||
030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||
030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||
030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||
03000000380700005032000011010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||
03000000380700005082000011010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||
03000000380700008433000011010000,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||
03000000380700008483000011010000,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||
03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,
|
||||
03000000380700008034000011010000,Mad Catz fightstick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||
03000000380700008084000011010000,Mad Catz fightstick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||
03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||
03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,
|
||||
03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,
|
||||
050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,
|
||||
050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,
|
||||
030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,
|
||||
05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,
|
||||
030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,
|
||||
050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,
|
||||
030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,
|
||||
030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
|
||||
050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
|
||||
030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
|
||||
03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||
030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||
050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||
030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||
050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||
030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||
030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
|
||||
050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
|
||||
030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
|
||||
050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
|
||||
030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
|
||||
03000000321500000010000011010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||
03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||
03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,
|
||||
050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,
|
||||
0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||
030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||
03000000a30600000cff000010010000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,
|
||||
03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||
050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||
050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,
|
||||
05000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,
|
||||
03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||
0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
|
||||
03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,
|
||||
030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
030000006d04000016c2000011010000,Logitech F310 Gamepad (DInput),x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Linux,
|
||||
030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,
|
||||
030000004c050000c405000011010000,Sony DualShock 4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux,
|
||||
050000004c050000c405000000010000,Sony DualShock 4 BT,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||
030000004c050000cc09000011010000,Sony DualShock 4 V2,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux,
|
||||
050000004c050000cc09000000010000,Sony DualShock 4 V2 BT,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux,
|
||||
030000004c050000a00b000011010000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux,
|
||||
030000006f0e00003001000001010000,EA Sports PS3 Controller,platform:Linux,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,
|
||||
03000000de280000ff11000001000000,Valve Streaming Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,y:b0,x:b3,start:b9,guide:,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,platform:Linux,
|
||||
03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a4,lefttrigger:b6,righttrigger:b7,platform:Linux,
|
||||
030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,y:b3,x:b1,start:b9,guide:,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Linux,
|
||||
030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5,
|
||||
030000008f0e00000300000010010000,GreenAsia Inc. USB Joystick ,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,
|
||||
030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick ,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,
|
||||
030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:b13,dpleft:b11,dpdown:b14,dpright:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,
|
||||
030000006d04000016c2000010010000,Logitech Logitech Dual Action,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,
|
||||
03000000260900008888000000010000,GameCube {WiseGroup USB box},a:b0,b:b2,y:b3,x:b1,start:b7,leftshoulder:,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,rightstick:,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Linux,
|
||||
030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,y:b4,x:b3,start:b8,guide:b5,back:b2,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b9,righttrigger:b10,platform:Linux,
|
||||
030000006d04000018c2000010010000,Logitech Logitech RumblePad 2 USB,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,
|
||||
05000000d6200000ad0d000001000000,Moga Pro,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b6,leftstick:b7,rightstick:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,
|
||||
030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,platform:Linux,a:b1,b:b2,x:b0,y:b3,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,
|
||||
030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,platform:Linux,a:b1,b:b2,x:b0,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,
|
||||
0300000000f000000300000000010000,RetroUSB.com RetroPad,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Linux,
|
||||
0300000000f00000f100000000010000,RetroUSB.com Super RetroPort,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Linux,
|
||||
030000006f0e00001f01000000010000,Generic X-Box pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,
|
||||
03000000280400000140000000010000,Gravis GamePad Pro USB ,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftx:a0,lefty:a1,
|
||||
030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b6,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:a2,rightshoulder:b2,righttrigger:a5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,
|
||||
030000005e0400008502000000010000,Microsoft X-Box pad (Japan),platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b6,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:a2,rightshoulder:b2,righttrigger:a5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,
|
||||
030000006f0e00001e01000011010000,Rock Candy Gamepad for PS3,platform:Linux,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,guide:b12,leftshoulder:b4,rightshoulder:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,
|
||||
03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,platform:Linux,a:b2,b:b1,y:b0,x:b3,start:b8,back:b9,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b4,righttrigger:b5,
|
||||
030000008916000000fd000024010000,Razer Onza Tournament,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:b13,dpleft:b11,dpdown:b14,dpright:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,
|
||||
030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,y:b3,x:b1,start:b10,guide:b8,back:b9,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Linux,
|
||||
03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,
|
||||
060000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,y:b12,x:b15,start:b3,guide:b16,back:b0,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpleft:b7,dpdown:b6,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9,platform:Linux,
|
||||
050000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,y:b12,x:b15,start:b3,guide:b16,back:b0,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpleft:b7,dpdown:b6,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9,platform:Linux,
|
||||
03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick ,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a3,rightx:a1,righty:a4,
|
||||
03000000666600000488000000010000,Super Joy Box 5 Pro,platform:Linux,a:b2,b:b1,x:b3,y:b0,back:b9,start:b8,leftshoulder:b6,rightshoulder:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b4,righttrigger:b5,dpup:b12,dpleft:b15,dpdown:b14,dpright:b13,
|
||||
05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,platform:Linux,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,
|
||||
05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,platform:Linux,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,
|
||||
030000008916000001fd000024010000,Razer Onza Classic Edition,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:b11,dpdown:b14,dpright:b12,dpup:b13,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,
|
||||
030000005e040000d102000001010000,Microsoft X-Box One pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,
|
||||
030000005e040000dd02000003020000,Microsoft X-Box One pad v2,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,platform:Linux,
|
||||
03000000790000001100000010010000,RetroLink Saturn Classic Controller,platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b5,guide:b2,start:b8,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,
|
||||
050000007e0500003003000001000000,Nintendo Wii U Pro Controller,platform:Linux,a:b0,b:b1,x:b3,y:b2,back:b8,start:b9,guide:b10,leftshoulder:b4,rightshoulder:b5,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,dpup:b13,dpleft:b15,dpdown:b14,dpright:b16,
|
||||
030000005e0400008e02000004010000,Microsoft X-Box 360 pad,platform:Linux,a:b0,b:b1,x:b2,y:b3,back:b6,start:b7,guide:b8,leftshoulder:b4,rightshoulder:b5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,
|
||||
030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,
|
||||
030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7
|
||||
03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,
|
||||
0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),platform:Linux,a:b0,b:b1,x:b2,y:b3,start:b7,back:b6,guide:b8,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftshoulder:b4,rightshoulder:b5,lefttrigger:a5,righttrigger:a4,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a2,righty:a3,
|
||||
03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Linux,
|
||||
030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,
|
||||
030000006f0e00001304000000010000,Generic X-Box pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:a0,rightstick:a3,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,
|
||||
03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,
|
||||
03000000830500006020000010010000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,x:b3,y:b2,back:b6,start:b7,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Linux,
|
||||
03000000bd12000015d0000010010000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Linux,
|
||||
03000000790000001100000010010000,Retrolink Classic Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Linux,
|
||||
03000000c9110000f055000011010000,HJC Game GAMEPAD,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b11,rightshoulder:b5,rightx:a2,start:b9,righty:a3,dpleft:h0.8,lefttrigger:b6,x:b2,dpup:h0.1,back:b8,leftstick:b10,leftshoulder:b4,y:b3,a:b0,dpright:h0.2,righttrigger:b7,b:b1,platform:Linux,
|
||||
03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,y:b3,x:b0,start:b12,guide:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,platform:Linux,
|
||||
03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,y:b3,x:b0,start:b9,guide:,back:,leftstick:,rightstick:,leftshoulder:,dpleft:b15,dpdown:b14,dpright:b13,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,rightshoulder:b7,dpup:b12,platform:Linux,
|
||||
030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,platform:Linux,x:b0,a:b2,b:b3,y:b1,back:b10,guide:b12,start:b11,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,
|
||||
030000006f0e00004601000001010000,Rock Candy Wired Controller for Xbox One,platform:Linux,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,guide:b8,leftstick:b9,rightstick:b10,lefttrigger:a2,righttrigger:a5,leftx:a0,lefty:a1,rightx:a3,righty:a4,
|
||||
03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,
|
||||
030000006f0e00003901000020060000,Afterglow Wired Controller for Xbox One,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,platform:Linux,
|
||||
030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,platform:Linux,a:b0,b:b2,x:b1,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,
|
||||
05000000102800000900000000010000,8Bitdo SFC30 GamePad,platform:Linux,x:b4,a:b1,b:b0,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,
|
||||
03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,platform:Linux,a:b1,b:b2,y:b3,x:b0,start:b12,guide:b9,back:b8,leftshoulder:b4,rightshoulder:b5,lefttrigger:b6,righttrigger:b7,leftx:a0,lefty:a1,
|
||||
030000000d0f00000d00000000010000,hori,platform:Linux,a:b0,b:b6,y:b2,x:b1,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,start:b9,guide:b10,back:b8,leftshoulder:b3,rightshoulder:b7,leftx:b4,lefty:b5,
|
||||
03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,
|
||||
03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,platform:Linux,a:b0,b:b1,y:b2,x:b3,start:b9,back:b8,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,lefttrigger:b6,righttrigger:b7,
|
||||
03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),platform:Linux,a:b3,b:b4,y:b1,x:b0,start:b7,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,
|
||||
05000000010000000100000003000000,Nintendo Wiimote,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b9,guide:b10,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,
|
||||
030000005e0400008e02000062230000,Microsoft X-Box 360 pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,
|
||||
03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,y:b1,x:b0,leftstick:b8,rightstick:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b6,righttrigger:b7,platform:Linux,
|
||||
030000006f0e00000103000000020000,Logic3 Controller,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,
|
||||
05000000380700006652000025010000,Mad Catz C.T.R.L.R ,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,
|
||||
030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,
|
||||
03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,lefttrigger:a2,righttrigger:a5,
|
||||
05000000a00500003232000001000000,8Bitdo Zero GamePad,platform:Linux,a:b0,b:b1,x:b3,y:b4,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,
|
||||
030000001008000001e5000010010000,NEXT Classic USB Game Controller,a:b0,b:b1,back:b8,start:b9,rightx:a2,righty:a3,leftx:a0,lefty:a1,platform:Linux,
|
||||
03000000100800000300000010010000,USB Gamepad,platform:Linux,a:b2,b:b1,x:b3,y:b0,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,
|
||||
05000000ac0500003232000001000000,VR-BOX,platform:Linux,a:b0,b:b1,x:b2,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,
|
||||
03000000780000000600000010010000,Microntek USB Joystick,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftx:a0,lefty:a1,
|
||||
@@ -1,3 +1,5 @@
|
||||
set(SO_VERSION 1)
|
||||
|
||||
find_package(LibUUID REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
find_package(CURL REQUIRED)
|
||||
@@ -19,8 +21,8 @@ add_library(gamestream SHARED ${GAMESTREAM_SRC_LIST})
|
||||
set_property(TARGET gamestream PROPERTY C_STANDARD 99)
|
||||
target_link_libraries(gamestream moonlight-common)
|
||||
|
||||
set_target_properties(gamestream PROPERTIES SOVERSION 0 VERSION ${MOONLIGHT_VERSION})
|
||||
set_target_properties(moonlight-common PROPERTIES SOVERSION 0 VERSION ${MOONLIGHT_VERSION})
|
||||
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(moonlight-common PRIVATE ../third_party/moonlight-common-c/reedsolomon ${ENET_INCLUDE_DIRS})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Iwan Timmer
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -179,7 +179,6 @@ static int load_server_status(PSERVER_DATA server) {
|
||||
char *pairedText = NULL;
|
||||
char *currentGameText = NULL;
|
||||
char *stateText = NULL;
|
||||
char *heightText = NULL;
|
||||
char *serverCodecModeSupportText = NULL;
|
||||
|
||||
ret = GS_INVALID;
|
||||
@@ -217,25 +216,28 @@ static int load_server_status(PSERVER_DATA server) {
|
||||
if (xml_search(data->memory, data->size, "state", &stateText) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "Height", &heightText) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "ServerCodecModeSupport", &serverCodecModeSupportText) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "gputype", &server->gpuType) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "GsVersion", &server->gsVersion) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "GfeVersion", (char**) &server->serverInfo.serverInfoGfeVersion) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_modelist(data->memory, data->size, &server->modes) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
// These fields are present on all version of GFE that this client supports
|
||||
if (!strlen(currentGameText) || !strlen(pairedText) || !strlen(server->serverInfo.serverInfoAppVersion) || !strlen(stateText))
|
||||
goto cleanup;
|
||||
|
||||
server->paired = pairedText != NULL && strcmp(pairedText, "1") == 0;
|
||||
server->currentGame = currentGameText == NULL ? 0 : atoi(currentGameText);
|
||||
server->supports4K = heightText != NULL && serverCodecModeSupportText != NULL && atoi(heightText) >= 2160;
|
||||
server->supports4K = serverCodecModeSupportText != NULL;
|
||||
server->serverMajorVersion = atoi(server->serverInfo.serverInfoAppVersion);
|
||||
|
||||
if (strstr(stateText, "_SERVER_AVAILABLE")) {
|
||||
@@ -256,9 +258,6 @@ static int load_server_status(PSERVER_DATA server) {
|
||||
if (currentGameText != NULL)
|
||||
free(currentGameText);
|
||||
|
||||
if (heightText != NULL)
|
||||
free(heightText);
|
||||
|
||||
if (serverCodecModeSupportText != NULL)
|
||||
free(serverCodecModeSupportText);
|
||||
|
||||
@@ -494,12 +493,15 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
||||
char client_secret_data[16];
|
||||
RAND_bytes(client_secret_data, 16);
|
||||
|
||||
const ASN1_BIT_STRING *asnSignature;
|
||||
X509_get0_signature(&asnSignature, NULL, cert);
|
||||
|
||||
char challenge_response[16 + 256 + 16];
|
||||
char challenge_response_hash[32];
|
||||
char challenge_response_hash_enc[32];
|
||||
char challenge_response_hex[65];
|
||||
memcpy(challenge_response, challenge_response_data + hash_length, 16);
|
||||
memcpy(challenge_response + 16, cert->signature->data, 256);
|
||||
memcpy(challenge_response + 16, asnSignature->data, 256);
|
||||
memcpy(challenge_response + 16 + 256, client_secret_data, 16);
|
||||
if (server->serverMajorVersion >= 7)
|
||||
SHA256(challenge_response, 16 + 256 + 16, challenge_response_hash);
|
||||
@@ -635,6 +637,18 @@ int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, b
|
||||
char* result = NULL;
|
||||
char uuid_str[37];
|
||||
|
||||
PDISPLAY_MODE mode = server->modes;
|
||||
bool correct_mode = false;
|
||||
while (mode != NULL) {
|
||||
if (mode->width == config->width && mode->height == config->height && mode->refresh == config->fps)
|
||||
correct_mode = true;
|
||||
|
||||
mode = mode->next;
|
||||
}
|
||||
|
||||
if (!correct_mode)
|
||||
return GS_NOT_SUPPORTED_MODE;
|
||||
|
||||
if (config->height >= 2160 && !server->supports4K)
|
||||
return GS_NOT_SUPPORTED_4K;
|
||||
|
||||
@@ -713,7 +727,7 @@ int gs_quit_app(PSERVER_DATA server) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gs_init(PSERVER_DATA server, char *address, const char *keyDirectory) {
|
||||
int gs_init(PSERVER_DATA server, char *address, const char *keyDirectory, int log_level) {
|
||||
mkdirtree(keyDirectory);
|
||||
if (load_unique_id(keyDirectory) != GS_OK)
|
||||
return GS_FAILED;
|
||||
@@ -721,7 +735,7 @@ int gs_init(PSERVER_DATA server, char *address, const char *keyDirectory) {
|
||||
if (load_cert(keyDirectory))
|
||||
return GS_FAILED;
|
||||
|
||||
http_init(keyDirectory);
|
||||
http_init(keyDirectory, log_level);
|
||||
|
||||
LiInitializeServerInformation(&server->serverInfo);
|
||||
server->serverInfo.address = address;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015, 2016 Iwan Timmer
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -35,10 +35,12 @@ typedef struct _SERVER_DATA {
|
||||
bool supports4K;
|
||||
int currentGame;
|
||||
int serverMajorVersion;
|
||||
char* gsVersion;
|
||||
PDISPLAY_MODE modes;
|
||||
SERVER_INFORMATION serverInfo;
|
||||
} SERVER_DATA, *PSERVER_DATA;
|
||||
|
||||
int gs_init(PSERVER_DATA server, char* address, const char *keyDirectory);
|
||||
int gs_init(PSERVER_DATA server, char* address, const char *keyDirectory, int logLevel);
|
||||
int gs_start_app(PSERVER_DATA server, PSTREAM_CONFIGURATION config, int appId, bool sops, bool localaudio);
|
||||
int gs_applist(PSERVER_DATA server, PAPP_LIST *app_list);
|
||||
int gs_unpair(PSERVER_DATA server);
|
||||
|
||||
@@ -27,5 +27,6 @@
|
||||
#define GS_IO_ERROR -5
|
||||
#define GS_NOT_SUPPORTED_4K -6
|
||||
#define GS_UNSUPPORTED_VERSION -7
|
||||
#define GS_NOT_SUPPORTED_MODE -8
|
||||
|
||||
const char* gs_error;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "http.h"
|
||||
#include "errors.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
@@ -28,6 +29,8 @@ static CURL *curl;
|
||||
static const char *pCertFile = "./client.pem";
|
||||
static const char *pKeyFile = "./key.pem";
|
||||
|
||||
static bool debug;
|
||||
|
||||
static size_t _write_curl(void *contents, size_t size, size_t nmemb, void *userp)
|
||||
{
|
||||
size_t realsize = size * nmemb;
|
||||
@@ -44,8 +47,9 @@ static size_t _write_curl(void *contents, size_t size, size_t nmemb, void *userp
|
||||
return realsize;
|
||||
}
|
||||
|
||||
int http_init(const char* keyDirectory) {
|
||||
int http_init(const char* keyDirectory, int logLevel) {
|
||||
curl = curl_easy_init();
|
||||
debug = logLevel >= 2;
|
||||
if (!curl)
|
||||
return GS_FAILED;
|
||||
|
||||
@@ -73,6 +77,9 @@ int http_request(char* url, PHTTP_DATA data) {
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, data);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
|
||||
if (debug)
|
||||
printf("Request %s\n", url);
|
||||
|
||||
if (data->size > 0) {
|
||||
free(data->memory);
|
||||
data->memory = malloc(1);
|
||||
@@ -89,6 +96,9 @@ int http_request(char* url, PHTTP_DATA data) {
|
||||
} else if (data->memory == NULL) {
|
||||
return GS_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (debug)
|
||||
printf("Response:\n%s\n\n", data->memory);
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ typedef struct _HTTP_DATA {
|
||||
size_t size;
|
||||
} HTTP_DATA, *PHTTP_DATA;
|
||||
|
||||
int http_init(const char* keyDirectory);
|
||||
int http_init(const char* keyDirectory, int logLevel);
|
||||
PHTTP_DATA http_create_data();
|
||||
int http_request(char* url, PHTTP_DATA data);
|
||||
void http_free_data(PHTTP_DATA data);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/conf.h>
|
||||
#include <openssl/pkcs12.h>
|
||||
@@ -42,7 +43,7 @@ CERT_KEY_PAIR mkcert_generate() {
|
||||
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
|
||||
bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
|
||||
|
||||
SSLeay_add_all_algorithms();
|
||||
OpenSSL_add_all_algorithms();
|
||||
ERR_load_crypto_strings();
|
||||
|
||||
mkcert(&x509, &pkey, NUM_BITS, SERIAL, NUM_YEARS);
|
||||
@@ -54,7 +55,6 @@ CERT_KEY_PAIR mkcert_generate() {
|
||||
#endif
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
|
||||
CRYPTO_mem_leaks(bio_err);
|
||||
BIO_free(bio_err);
|
||||
|
||||
return (CERT_KEY_PAIR) {x509, pkey, p12};
|
||||
@@ -103,8 +103,22 @@ int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years) {
|
||||
} else {
|
||||
x = *x509p;
|
||||
}
|
||||
|
||||
if ((rsa = RSA_new()) == NULL)
|
||||
goto err;
|
||||
|
||||
rsa = RSA_generate_key(bits, RSA_F4, NULL, NULL);
|
||||
BIGNUM* bne = BN_new();
|
||||
if (bne == NULL) {
|
||||
abort();
|
||||
goto err;
|
||||
}
|
||||
|
||||
BN_set_word(bne, RSA_F4);
|
||||
if (RSA_generate_key_ex(rsa, bits, bne, NULL) == 0) {
|
||||
abort();
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!EVP_PKEY_assign_RSA(pk, rsa)) {
|
||||
abort();
|
||||
goto err;
|
||||
|
||||
@@ -22,5 +22,5 @@
|
||||
#define GS_SPS_BITSTREAM_FIXUP 0x01
|
||||
#define GS_SPS_BASELINE_HACK 0x02
|
||||
|
||||
void gs_sps_init();
|
||||
void gs_sps_init(int width, int height);
|
||||
PLENTRY gs_sps_fix(PLENTRY *entry, int flags);
|
||||
|
||||
@@ -79,6 +79,37 @@ static void XMLCALL _xml_end_applist_element(void *userData, const char *name) {
|
||||
}
|
||||
}
|
||||
|
||||
static void XMLCALL _xml_start_mode_element(void *userData, const char *name, const char **atts) {
|
||||
struct xml_query *search = (struct xml_query*) userData;
|
||||
if (strcmp("DisplayMode", name) == 0) {
|
||||
PDISPLAY_MODE mode = calloc(1, sizeof(DISPLAY_MODE));
|
||||
if (mode != NULL) {
|
||||
mode->next = (PDISPLAY_MODE) search->data;
|
||||
search->data = mode;
|
||||
}
|
||||
} else if (search->data != NULL && (strcmp("Height", name) == 0 || strcmp("Width", name) == 0 || strcmp("RefreshRate", name) == 0)) {
|
||||
search->memory = malloc(1);
|
||||
search->size = 0;
|
||||
search->start = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void XMLCALL _xml_end_mode_element(void *userData, const char *name) {
|
||||
struct xml_query *search = (struct xml_query*) userData;
|
||||
if (search->data != NULL && search->start) {
|
||||
PDISPLAY_MODE mode = (PDISPLAY_MODE) search->data;
|
||||
if (strcmp("Width", name) == 0)
|
||||
mode->width = atoi(search->memory);
|
||||
else if (strcmp("Height", name) == 0)
|
||||
mode->height = atoi(search->memory);
|
||||
else if (strcmp("RefreshRate", name) == 0)
|
||||
mode->refresh = atoi(search->memory);
|
||||
|
||||
free(search->memory);
|
||||
search->start = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void XMLCALL _xml_write_data(void *userData, const XML_Char *s, int len) {
|
||||
struct xml_query *search = (struct xml_query*) userData;
|
||||
if (search->start > 0) {
|
||||
@@ -141,3 +172,24 @@ int xml_applist(char* data, size_t len, PAPP_LIST *app_list) {
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
int xml_modelist(char* data, size_t len, PDISPLAY_MODE *mode_list) {
|
||||
struct xml_query query = {0};
|
||||
query.memory = calloc(1, 1);
|
||||
XML_Parser parser = XML_ParserCreate("UTF-8");
|
||||
XML_SetUserData(parser, &query);
|
||||
XML_SetElementHandler(parser, _xml_start_mode_element, _xml_end_mode_element);
|
||||
XML_SetCharacterDataHandler(parser, _xml_write_data);
|
||||
if (! XML_Parse(parser, data, len, 1)) {
|
||||
int code = XML_GetErrorCode(parser);
|
||||
gs_error = XML_ErrorString(code);
|
||||
XML_ParserFree(parser);
|
||||
return GS_INVALID;
|
||||
}
|
||||
|
||||
XML_ParserFree(parser);
|
||||
*mode_list = (PDISPLAY_MODE) query.data;
|
||||
|
||||
return GS_OK;
|
||||
|
||||
}
|
||||
|
||||
@@ -26,5 +26,13 @@ typedef struct _APP_LIST {
|
||||
struct _APP_LIST *next;
|
||||
} APP_LIST, *PAPP_LIST;
|
||||
|
||||
typedef struct _DISPLAY_MODE {
|
||||
unsigned int height;
|
||||
unsigned int width;
|
||||
unsigned int refresh;
|
||||
struct _DISPLAY_MODE *next;
|
||||
} DISPLAY_MODE, *PDISPLAY_MODE;
|
||||
|
||||
int xml_search(char* data, size_t len, char* node, char** result);
|
||||
int xml_applist(char* data, size_t len, PAPP_LIST *app_list);
|
||||
int xml_modelist(char* data, size_t len, PDISPLAY_MODE *mode_list);
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
abs_x = 0
|
||||
abs_y = 1
|
||||
abs_z = -1
|
||||
reverse_x = false
|
||||
reverse_y = true
|
||||
abs_rx = 2
|
||||
abs_ry = 5
|
||||
abs_rz = -1
|
||||
reverse_rx = false
|
||||
reverse_ry = true
|
||||
abs_dpad_x = 16
|
||||
abs_dpad_y = 17
|
||||
reverse_dpad_x = false
|
||||
reverse_dpad_y = false
|
||||
btn_east = 306
|
||||
btn_south = 305
|
||||
btn_north = 307
|
||||
btn_west = 304
|
||||
btn_select = 312
|
||||
btn_start = 313
|
||||
btn_thumbl = 314
|
||||
btn_thumbr = 315
|
||||
btn_tl = 308
|
||||
btn_tr = 309
|
||||
btn_tl2 = 310
|
||||
btn_tr2 = 311
|
||||
btn_dpad_up = -1
|
||||
btn_dpad_down = -1
|
||||
btn_dpad_left = -1
|
||||
btn_dpad_right = -1
|
||||
@@ -1,32 +0,0 @@
|
||||
abs_x = 0
|
||||
abs_y = 1
|
||||
abs_z = 2
|
||||
abs_rx = 3
|
||||
abs_ry = 4
|
||||
abs_rz = 5
|
||||
abs_deadzone = 0
|
||||
abs_dpad_y = 16
|
||||
abs_dpad_x = 17
|
||||
reverse_x = false
|
||||
reverse_y = true
|
||||
reverse_rx = false
|
||||
reverse_ry = true
|
||||
reverse_dpad_y = false
|
||||
reverse_dpad_x = false
|
||||
btn_south = 304
|
||||
btn_east = 305
|
||||
btn_north = 307
|
||||
btn_west = 308
|
||||
btn_select = 314
|
||||
btn_start = 315
|
||||
btn_mode = 316
|
||||
btn_thumbl = 317
|
||||
btn_thumbr = 318
|
||||
btn_tl = 310
|
||||
btn_tr = 311
|
||||
btn_tl2 = 312
|
||||
btn_tr2 = 313
|
||||
btn_dpad_up = 544
|
||||
btn_dpad_down = 545
|
||||
btn_dpad_left = 546
|
||||
btn_dpad_right = 547
|
||||
@@ -1,32 +0,0 @@
|
||||
abs_x = 0
|
||||
abs_y = 1
|
||||
abs_z = 50
|
||||
abs_rx = 2
|
||||
abs_ry = 5
|
||||
abs_rz = 51
|
||||
abs_deadzone = 0
|
||||
abs_dpad_x = -1
|
||||
abs_dpad_y = -1
|
||||
reverse_x = false
|
||||
reverse_y = true
|
||||
reverse_rx = false
|
||||
reverse_ry = true
|
||||
reverse_dpad_x = false
|
||||
reverse_dpad_y = false
|
||||
btn_west = 303
|
||||
btn_south = 302
|
||||
btn_north = 300
|
||||
btn_east = 301
|
||||
btn_select = 288
|
||||
btn_start = 291
|
||||
btn_mode = 704
|
||||
btn_thumbl = 289
|
||||
btn_thumbr = 290
|
||||
btn_tl = 298
|
||||
btn_tr = 299
|
||||
btn_tl2 = 296
|
||||
btn_tr2 = 297
|
||||
btn_dpad_up = 292
|
||||
btn_dpad_down = 294
|
||||
btn_dpad_left = 295
|
||||
btn_dpad_right = 293
|
||||
@@ -1,32 +0,0 @@
|
||||
abs_x = 0
|
||||
abs_y = 1
|
||||
abs_z = 3
|
||||
abs_rx = 2
|
||||
abs_ry = 5
|
||||
abs_rz = 4
|
||||
abs_deadzone = 0
|
||||
abs_dpad_x = 16
|
||||
abs_dpad_y = 17
|
||||
reverse_x = false
|
||||
reverse_y = true
|
||||
reverse_rx = false
|
||||
reverse_ry = true
|
||||
reverse_dpad_x = false
|
||||
reverse_dpad_y = false
|
||||
btn_west = 304
|
||||
btn_south = 305
|
||||
btn_north = 307
|
||||
btn_east = 306
|
||||
btn_select = 312
|
||||
btn_start = 313
|
||||
btn_mode = 316
|
||||
btn_thumbl = 314
|
||||
btn_thumbr = 315
|
||||
btn_tl = 308
|
||||
btn_tr = 309
|
||||
btn_tl2 = 310
|
||||
btn_tr2 = 311
|
||||
btn_dpad_up = -1
|
||||
btn_dpad_down = -1
|
||||
btn_dpad_left = -1
|
||||
btn_dpad_right = -1
|
||||
@@ -1,31 +0,0 @@
|
||||
abs_x = 0
|
||||
abs_y = 1
|
||||
abs_z = -1
|
||||
reverse_x = false
|
||||
reverse_y = true
|
||||
abs_rx = 2
|
||||
abs_ry = 5
|
||||
abs_rz = -1
|
||||
reverse_rx = false
|
||||
reverse_ry = true
|
||||
abs_dpad_x = 16
|
||||
abs_dpad_y = 17
|
||||
reverse_dpad_x = false
|
||||
reverse_dpad_y = false
|
||||
btn_north = 291
|
||||
btn_east = 290
|
||||
btn_south = 289
|
||||
btn_west = 288
|
||||
btn_select = 297
|
||||
btn_start = 296
|
||||
btn_mode = 0
|
||||
btn_thumbl = 298
|
||||
btn_thumbr = 299
|
||||
btn_tl = 292
|
||||
btn_tr = 293
|
||||
btn_tl2 = 294
|
||||
btn_tr2 = 295
|
||||
btn_dpad_up = -1
|
||||
btn_dpad_down = -1
|
||||
btn_dpad_left = -1
|
||||
btn_dpad_right = -1
|
||||
@@ -1,32 +0,0 @@
|
||||
abs_x = 0
|
||||
abs_y = 1
|
||||
abs_z = -1
|
||||
reverse_x = false
|
||||
reverse_y = true
|
||||
abs_rx = 3
|
||||
abs_ry = 4
|
||||
abs_rz = -1
|
||||
reverse_rx = false
|
||||
reverse_ry = true
|
||||
abs_deadzone = 24088
|
||||
abs_dpad_x = -1
|
||||
abs_dpad_y = -1
|
||||
reverse_dpad_x = false
|
||||
reverse_dpad_y = false
|
||||
btn_north = 308
|
||||
btn_east = 304
|
||||
btn_south = 305
|
||||
btn_west = 307
|
||||
btn_select = 314
|
||||
btn_start = 315
|
||||
btn_mode = 316
|
||||
btn_thumbl = 317
|
||||
btn_thumbr = 318
|
||||
btn_tl = 310
|
||||
btn_tr = 311
|
||||
btn_tl2 = 312
|
||||
btn_tr2 = 313
|
||||
btn_dpad_up = 544
|
||||
btn_dpad_down = 545
|
||||
btn_dpad_left = 546
|
||||
btn_dpad_right = 547
|
||||
@@ -1,32 +0,0 @@
|
||||
abs_x = 0
|
||||
abs_y = 1
|
||||
abs_z = 10
|
||||
abs_rx = 3
|
||||
abs_ry = 4
|
||||
abs_rz = 9
|
||||
abs_deadzone = 0
|
||||
abs_dpad_x = 17
|
||||
abs_dpad_y = 16
|
||||
reverse_x = false
|
||||
reverse_y = true
|
||||
reverse_rx = false
|
||||
reverse_ry = true
|
||||
reverse_dpad_x = false
|
||||
reverse_dpad_y = false
|
||||
btn_east = 305
|
||||
btn_south = 304
|
||||
btn_north = 308
|
||||
btn_west = 307
|
||||
btn_select = 314
|
||||
btn_start = 315
|
||||
btn_mode = 316
|
||||
btn_thumbl = 317
|
||||
btn_thumbr = 318
|
||||
btn_tl = 310
|
||||
btn_tr = 311
|
||||
btn_tl2 = 312
|
||||
btn_tr2 = 313
|
||||
btn_dpad_up = -1
|
||||
btn_dpad_down = -1
|
||||
btn_dpad_left = -1
|
||||
btn_dpad_right = -1
|
||||
@@ -17,8 +17,8 @@
|
||||
## Size of network packets should be lower than MTU
|
||||
#packetsize = 1024
|
||||
|
||||
## Use of h265/HEVC video codec
|
||||
#h265 = false
|
||||
## Select video codec (auto/h264/h265)
|
||||
#codec = auto
|
||||
|
||||
## Default started application on host
|
||||
#app = Steam
|
||||
@@ -26,7 +26,7 @@
|
||||
## Default used mapping for streaming
|
||||
## Searched for in $XDG_DATA_DIRS/moonlight or /usr/share/moonlight and /usr/local/share/moonlight
|
||||
## Mapping can also be user overrided in $XDG_CONFIG_DIR/moonlight or ~/.config/moonlight or current directory
|
||||
#mapping = mappings/default.conf
|
||||
#mapping = gamecontrollerdb.txt
|
||||
|
||||
## Enable selected input devices
|
||||
## By default all available input devices should be used
|
||||
@@ -34,7 +34,7 @@
|
||||
## To use a different mapping then default another mapping should be declared above the input
|
||||
#input = /dev/input/event1
|
||||
|
||||
## Let GFE change graphical game settings for optimal performance and quality
|
||||
## Enable GFE for changing graphical game settings for optimal performance and quality
|
||||
#sops = true
|
||||
|
||||
## Play audio on host instead of streaming to client
|
||||
@@ -48,13 +48,20 @@
|
||||
## aml - hardware video decoder for ODROID-C1/C2
|
||||
## omx - hardware video decoder for Raspberry Pi
|
||||
## imx - hardware video decoder for i.MX6 devices
|
||||
## sdl - software decoder
|
||||
## fake - save to file (only available in debug builds)
|
||||
## x11 - software decoder
|
||||
## sdl - software decoder with SDL input and audio
|
||||
## fake - no audio and video
|
||||
#platform = default
|
||||
|
||||
## Directory to store encryption keys
|
||||
## By default keys are stored in $XDG_CACHE_DIR/moonlight or ~/.cache/moonlight
|
||||
#keydir = /dir/to/keys
|
||||
|
||||
## Enable QOS settings to optimize for internet instead of local network
|
||||
#remote = false
|
||||
|
||||
## Enable 5.1 surround sound
|
||||
#surround = false
|
||||
|
||||
## Load additional configuration files
|
||||
#config = /path/to/config
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015, 2016 Iwan Timmer
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,24 +17,21 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../audio.h"
|
||||
#include "audio.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <opus_multistream.h>
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#define CHECK_RETURN(f) if ((rc = f) < 0) { printf("Alsa error code %d\n", rc); exit(-1); }
|
||||
|
||||
#define MAX_CHANNEL_COUNT 6
|
||||
#define FRAME_SIZE 240
|
||||
#define CHECK_RETURN(f) if ((rc = f) < 0) { printf("Alsa error code %d\n", rc); return -1; }
|
||||
|
||||
static snd_pcm_t *handle;
|
||||
static OpusMSDecoder* decoder;
|
||||
static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT];
|
||||
|
||||
static void alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) {
|
||||
static int alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) {
|
||||
int rc;
|
||||
unsigned char alsaMapping[6];
|
||||
unsigned char alsaMapping[MAX_CHANNEL_COUNT];
|
||||
|
||||
/* The supplied mapping array has order: FL-FR-C-LFE-RL-RR
|
||||
* ALSA expects the order: FL-FR-RL-RR-C-LFE
|
||||
@@ -49,19 +46,15 @@ static void alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGU
|
||||
alsaMapping[5] = opusConfig->mapping[3];
|
||||
}
|
||||
|
||||
decoder = opus_multistream_decoder_create(opusConfig->sampleRate,
|
||||
opusConfig->channelCount,
|
||||
opusConfig->streams,
|
||||
opusConfig->coupledStreams,
|
||||
alsaMapping,
|
||||
&rc);
|
||||
decoder = opus_multistream_decoder_create(opusConfig->sampleRate, opusConfig->channelCount, opusConfig->streams, opusConfig->coupledStreams, alsaMapping, &rc);
|
||||
|
||||
snd_pcm_hw_params_t *hw_params;
|
||||
snd_pcm_sw_params_t *sw_params;
|
||||
snd_pcm_uframes_t period_size = FRAME_SIZE * opusConfig->channelCount * 2;
|
||||
snd_pcm_uframes_t buffer_size = 12 * period_size;
|
||||
snd_pcm_uframes_t period_size = FRAME_SIZE * FRAME_BUFFER;
|
||||
snd_pcm_uframes_t buffer_size = 2 * period_size;
|
||||
unsigned int sampleRate = opusConfig->sampleRate;
|
||||
|
||||
char* audio_device = (char*) context;
|
||||
if (audio_device == NULL)
|
||||
audio_device = "sysdefault";
|
||||
|
||||
@@ -75,20 +68,22 @@ static void alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGU
|
||||
CHECK_RETURN(snd_pcm_hw_params_set_format(handle, hw_params, SND_PCM_FORMAT_S16_LE));
|
||||
CHECK_RETURN(snd_pcm_hw_params_set_rate_near(handle, hw_params, &sampleRate, NULL));
|
||||
CHECK_RETURN(snd_pcm_hw_params_set_channels(handle, hw_params, opusConfig->channelCount));
|
||||
CHECK_RETURN(snd_pcm_hw_params_set_buffer_size_near(handle, hw_params, &buffer_size));
|
||||
CHECK_RETURN(snd_pcm_hw_params_set_period_size_near(handle, hw_params, &period_size, NULL));
|
||||
CHECK_RETURN(snd_pcm_hw_params_set_buffer_size_near(handle, hw_params, &buffer_size));
|
||||
CHECK_RETURN(snd_pcm_hw_params(handle, hw_params));
|
||||
snd_pcm_hw_params_free(hw_params);
|
||||
|
||||
/* Set software parameters */
|
||||
CHECK_RETURN(snd_pcm_sw_params_malloc(&sw_params));
|
||||
CHECK_RETURN(snd_pcm_sw_params_current(handle, sw_params));
|
||||
CHECK_RETURN(snd_pcm_sw_params_set_start_threshold(handle, sw_params, buffer_size - period_size));
|
||||
CHECK_RETURN(snd_pcm_sw_params_set_avail_min(handle, sw_params, period_size));
|
||||
CHECK_RETURN(snd_pcm_sw_params_set_start_threshold(handle, sw_params, 1));
|
||||
CHECK_RETURN(snd_pcm_sw_params(handle, sw_params));
|
||||
snd_pcm_sw_params_free(sw_params);
|
||||
|
||||
CHECK_RETURN(snd_pcm_prepare(handle));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void alsa_renderer_cleanup() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015 Iwan Timmer
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -21,16 +21,17 @@
|
||||
|
||||
#include <Limelight.h>
|
||||
|
||||
extern const char* audio_device;
|
||||
#define MAX_CHANNEL_COUNT 6
|
||||
#define FRAME_SIZE 240
|
||||
#define FRAME_BUFFER 12
|
||||
|
||||
#ifdef HAVE_ALSA
|
||||
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_alsa;
|
||||
#endif
|
||||
#ifdef HAVE_SDL
|
||||
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_sdl;
|
||||
#endif
|
||||
#ifdef HAVE_PULSE
|
||||
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_pulse;
|
||||
bool audio_pulse_init();
|
||||
#endif
|
||||
#ifdef HAVE_PI
|
||||
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_omx;
|
||||
bool audio_pulse_init(char* audio_device);
|
||||
#endif
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015, 2016 Iwan Timmer
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../audio.h"
|
||||
#include "audio.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -25,9 +25,6 @@
|
||||
#include "bcm_host.h"
|
||||
#include "ilclient.h"
|
||||
|
||||
#define MAX_CHANNEL_COUNT 6
|
||||
#define FRAME_SIZE 240
|
||||
|
||||
static OpusMSDecoder* decoder;
|
||||
ILCLIENT_T* handle;
|
||||
COMPONENT_T* component;
|
||||
@@ -35,10 +32,10 @@ static OMX_BUFFERHEADERTYPE *buf;
|
||||
static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT];
|
||||
static int channelCount;
|
||||
|
||||
static void omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) {
|
||||
static int omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) {
|
||||
int rc, error;
|
||||
OMX_ERRORTYPE err;
|
||||
unsigned char omxMapping[6];
|
||||
unsigned char omxMapping[MAX_CHANNEL_COUNT];
|
||||
char* componentName = "audio_render";
|
||||
|
||||
channelCount = opusConfig->channelCount;
|
||||
@@ -52,27 +49,22 @@ static void omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGUR
|
||||
omxMapping[3] = opusConfig->mapping[2];
|
||||
}
|
||||
|
||||
decoder = opus_multistream_decoder_create(opusConfig->sampleRate,
|
||||
opusConfig->channelCount,
|
||||
opusConfig->streams,
|
||||
opusConfig->coupledStreams,
|
||||
omxMapping,
|
||||
&rc);
|
||||
decoder = opus_multistream_decoder_create(opusConfig->sampleRate, opusConfig->channelCount, opusConfig->streams, opusConfig->coupledStreams, omxMapping, &rc);
|
||||
|
||||
handle = ilclient_init();
|
||||
if (handle == NULL) {
|
||||
fprintf(stderr, "IL client init failed\n");
|
||||
exit(1);
|
||||
fprintf(stderr, "IL client init failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ilclient_create_component(handle, &component, componentName, ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS) != 0) {
|
||||
fprintf(stderr, "Component create failed\n");
|
||||
exit(1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ilclient_change_component_state(component, OMX_StateIdle)!= 0) {
|
||||
fprintf(stderr, "Couldn't change state to Idle\n");
|
||||
exit(1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// must be before we enable buffers
|
||||
@@ -104,33 +96,34 @@ static void omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGUR
|
||||
|
||||
switch(channelCount) {
|
||||
case 1:
|
||||
sPCMMode.eChannelMapping[0] = OMX_AUDIO_ChannelCF;
|
||||
break;
|
||||
sPCMMode.eChannelMapping[0] = OMX_AUDIO_ChannelCF;
|
||||
break;
|
||||
case 8:
|
||||
sPCMMode.eChannelMapping[7] = OMX_AUDIO_ChannelRS;
|
||||
sPCMMode.eChannelMapping[7] = OMX_AUDIO_ChannelRS;
|
||||
case 7:
|
||||
sPCMMode.eChannelMapping[6] = OMX_AUDIO_ChannelLS;
|
||||
sPCMMode.eChannelMapping[6] = OMX_AUDIO_ChannelLS;
|
||||
case 6:
|
||||
sPCMMode.eChannelMapping[5] = OMX_AUDIO_ChannelRR;
|
||||
sPCMMode.eChannelMapping[5] = OMX_AUDIO_ChannelRR;
|
||||
case 5:
|
||||
sPCMMode.eChannelMapping[4] = OMX_AUDIO_ChannelLR;
|
||||
sPCMMode.eChannelMapping[4] = OMX_AUDIO_ChannelLR;
|
||||
case 4:
|
||||
sPCMMode.eChannelMapping[3] = OMX_AUDIO_ChannelLFE;
|
||||
sPCMMode.eChannelMapping[3] = OMX_AUDIO_ChannelLFE;
|
||||
case 3:
|
||||
sPCMMode.eChannelMapping[2] = OMX_AUDIO_ChannelCF;
|
||||
sPCMMode.eChannelMapping[2] = OMX_AUDIO_ChannelCF;
|
||||
case 2:
|
||||
sPCMMode.eChannelMapping[1] = OMX_AUDIO_ChannelRF;
|
||||
sPCMMode.eChannelMapping[0] = OMX_AUDIO_ChannelLF;
|
||||
break;
|
||||
sPCMMode.eChannelMapping[1] = OMX_AUDIO_ChannelRF;
|
||||
sPCMMode.eChannelMapping[0] = OMX_AUDIO_ChannelLF;
|
||||
break;
|
||||
}
|
||||
|
||||
err = OMX_SetParameter(ilclient_get_handle(component), OMX_IndexParamAudioPcm, &sPCMMode);
|
||||
if(err != OMX_ErrorNone){
|
||||
fprintf(stderr, "PCM mode unsupported\n");
|
||||
return;
|
||||
fprintf(stderr, "PCM mode unsupported\n");
|
||||
return -1;
|
||||
}
|
||||
OMX_CONFIG_BRCMAUDIODESTINATIONTYPE arDest;
|
||||
|
||||
char* audio_device = (char*) context;
|
||||
if (audio_device == NULL)
|
||||
audio_device = "hdmi";
|
||||
|
||||
@@ -144,7 +137,7 @@ static void omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGUR
|
||||
err = OMX_SetParameter(ilclient_get_handle(component), OMX_IndexConfigBrcmAudioDestination, &arDest);
|
||||
if (err != OMX_ErrorNone) {
|
||||
fprintf(stderr, "Error on setting audio destination\nomx option must be set to hdmi or local\n");
|
||||
exit(1);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,9 +147,11 @@ static void omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGUR
|
||||
|
||||
err = ilclient_change_component_state(component, OMX_StateExecuting);
|
||||
if (err < 0) {
|
||||
fprintf(stderr, "Couldn't change state to Executing\n");
|
||||
exit(1);
|
||||
fprintf(stderr, "Couldn't change state to Executing\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void omx_renderer_cleanup() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015 Iwan Timmer
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../audio.h"
|
||||
#include "audio.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -26,33 +26,30 @@
|
||||
#include <pulse/simple.h>
|
||||
#include <pulse/error.h>
|
||||
|
||||
#define MAX_CHANNEL_COUNT 6
|
||||
#define FRAME_SIZE 240
|
||||
|
||||
static OpusMSDecoder* decoder;
|
||||
static pa_simple *dev = NULL;
|
||||
static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT];
|
||||
static int channelCount;
|
||||
|
||||
bool audio_pulse_init() {
|
||||
pa_sample_spec spec = {
|
||||
.format = PA_SAMPLE_S16LE,
|
||||
.rate = 44000,
|
||||
.channels = 2
|
||||
};
|
||||
bool audio_pulse_init(char* audio_device) {
|
||||
pa_sample_spec spec = {
|
||||
.format = PA_SAMPLE_S16LE,
|
||||
.rate = 44000,
|
||||
.channels = 2
|
||||
};
|
||||
|
||||
int error;
|
||||
dev = pa_simple_new(NULL, "Moonlight Embedded", PA_STREAM_PLAYBACK, NULL, "Streaming", &spec, NULL, NULL, &error);
|
||||
if (dev) {
|
||||
pa_simple_free(dev);
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
int error;
|
||||
dev = pa_simple_new(audio_device, "Moonlight Embedded", PA_STREAM_PLAYBACK, NULL, "Streaming", &spec, NULL, NULL, &error);
|
||||
|
||||
if (dev)
|
||||
pa_simple_free(dev);
|
||||
|
||||
return (bool) dev;
|
||||
}
|
||||
|
||||
static void pulse_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) {
|
||||
static int pulse_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) {
|
||||
int rc, error;
|
||||
unsigned char alsaMapping[6];
|
||||
unsigned char alsaMapping[MAX_CHANNEL_COUNT];
|
||||
|
||||
channelCount = opusConfig->channelCount;
|
||||
|
||||
@@ -69,12 +66,7 @@ static void pulse_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIG
|
||||
alsaMapping[5] = opusConfig->mapping[3];
|
||||
}
|
||||
|
||||
decoder = opus_multistream_decoder_create(opusConfig->sampleRate,
|
||||
opusConfig->channelCount,
|
||||
opusConfig->streams,
|
||||
opusConfig->coupledStreams,
|
||||
alsaMapping,
|
||||
&rc);
|
||||
decoder = opus_multistream_decoder_create(opusConfig->sampleRate, opusConfig->channelCount, opusConfig->streams, opusConfig->coupledStreams, alsaMapping, &rc);
|
||||
|
||||
pa_sample_spec spec = {
|
||||
.format = PA_SAMPLE_S16LE,
|
||||
@@ -82,12 +74,15 @@ static void pulse_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIG
|
||||
.channels = opusConfig->channelCount
|
||||
};
|
||||
|
||||
dev = pa_simple_new(NULL, "Moonlight Embedded", PA_STREAM_PLAYBACK, NULL, "Streaming", &spec, NULL, NULL, &error);
|
||||
char* audio_device = (char*) context;
|
||||
dev = pa_simple_new(audio_device, "Moonlight Embedded", PA_STREAM_PLAYBACK, NULL, "Streaming", &spec, NULL, NULL, &error);
|
||||
|
||||
if (!dev) {
|
||||
printf("Pulseaudio error: %s\n", pa_strerror(error));
|
||||
exit(-1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pulse_renderer_decode_and_play_sample(char* data, int length) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015 Iwan Timmer
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../audio.h"
|
||||
#include "audio.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_audio.h>
|
||||
@@ -25,22 +25,14 @@
|
||||
#include <stdio.h>
|
||||
#include <opus_multistream.h>
|
||||
|
||||
#define MAX_CHANNEL_COUNT 6
|
||||
#define FRAME_SIZE 240
|
||||
|
||||
static OpusMSDecoder* decoder;
|
||||
static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT];
|
||||
static SDL_AudioDeviceID dev;
|
||||
static int channelCount;
|
||||
|
||||
static void sdl_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) {
|
||||
static int sdl_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) {
|
||||
int rc;
|
||||
decoder = opus_multistream_decoder_create(opusConfig->sampleRate,
|
||||
opusConfig->channelCount,
|
||||
opusConfig->streams,
|
||||
opusConfig->coupledStreams,
|
||||
opusConfig->mapping,
|
||||
&rc);
|
||||
decoder = opus_multistream_decoder_create(opusConfig->sampleRate, opusConfig->channelCount, opusConfig->streams, opusConfig->coupledStreams, opusConfig->mapping, &rc);
|
||||
|
||||
channelCount = opusConfig->channelCount;
|
||||
|
||||
@@ -56,11 +48,14 @@ static void sdl_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGUR
|
||||
dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, SDL_AUDIO_ALLOW_FORMAT_CHANGE);
|
||||
if (dev == 0) {
|
||||
printf("Failed to open audio: %s\n", SDL_GetError());
|
||||
return -1;
|
||||
} else {
|
||||
if (have.format != want.format) // we let this one thing change.
|
||||
printf("We didn't get requested audio format.\n");
|
||||
SDL_PauseAudioDevice(dev, 0); // start audio playing.
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sdl_renderer_cleanup() {
|
||||
|
||||
83
src/config.c
83
src/config.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015, 2016 Iwan Timmer
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,9 +17,10 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "input/evdev.h"
|
||||
#include "config.h"
|
||||
#include "audio.h"
|
||||
|
||||
#include "input/evdev.h"
|
||||
#include "audio/audio.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -37,16 +38,13 @@
|
||||
#define write_config_bool(fd, key, value) fprintf(fd, "%s = %s\n", key, value?"true":"false");
|
||||
|
||||
bool inputAdded = false;
|
||||
static bool mapped = true;
|
||||
const char* audio_device = NULL;
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"720", no_argument, NULL, 'a'},
|
||||
{"1080", no_argument, NULL, 'b'},
|
||||
{"4k", no_argument, NULL, '0'},
|
||||
{"width", required_argument, NULL, 'c'},
|
||||
{"height", required_argument, NULL, 'd'},
|
||||
{"30fps", no_argument, NULL, 'e'},
|
||||
{"60fps", no_argument, NULL, 'f'},
|
||||
{"bitrate", required_argument, NULL, 'g'},
|
||||
{"packetsize", required_argument, NULL, 'h'},
|
||||
{"app", required_argument, NULL, 'i'},
|
||||
@@ -63,10 +61,10 @@ static struct option long_options[] = {
|
||||
{"windowed", no_argument, NULL, 't'},
|
||||
{"surround", no_argument, NULL, 'u'},
|
||||
{"fps", required_argument, NULL, 'v'},
|
||||
{"forcehw", no_argument, NULL, 'w'},
|
||||
{"hevc", no_argument, NULL, 'x'},
|
||||
{"h264", no_argument, NULL, 'z'},
|
||||
{"codec", required_argument, NULL, 'x'},
|
||||
{"unsupported", no_argument, NULL, 'y'},
|
||||
{"verbose", no_argument, NULL, 'z'},
|
||||
{"debug", no_argument, NULL, 'Z'},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
@@ -126,18 +124,16 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) {
|
||||
config->stream.width = 1920;
|
||||
config->stream.height = 1080;
|
||||
break;
|
||||
case '0':
|
||||
config->stream.width = 3840;
|
||||
config->stream.height = 2160;
|
||||
break;
|
||||
case 'c':
|
||||
config->stream.width = atoi(value);
|
||||
break;
|
||||
case 'd':
|
||||
config->stream.height = atoi(value);
|
||||
break;
|
||||
case 'e':
|
||||
config->stream.fps = 30;
|
||||
break;
|
||||
case 'f':
|
||||
config->stream.fps = 60;
|
||||
break;
|
||||
case 'g':
|
||||
config->stream.bitrate = atoi(value);
|
||||
break;
|
||||
@@ -152,11 +148,9 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) {
|
||||
perror("Too many inputs specified");
|
||||
exit(-1);
|
||||
}
|
||||
config->inputs[config->inputsCount].path = value;
|
||||
config->inputs[config->inputsCount].mapping = config->mapping;
|
||||
config->inputs[config->inputsCount] = value;
|
||||
config->inputsCount++;
|
||||
inputAdded = true;
|
||||
mapped = true;
|
||||
break;
|
||||
case 'k':
|
||||
config->mapping = get_path(value, getenv("XDG_DATA_DIRS"));
|
||||
@@ -164,13 +158,12 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) {
|
||||
fprintf(stderr, "Unable to open custom mapping file: %s\n", value);
|
||||
exit(-1);
|
||||
}
|
||||
mapped = false;
|
||||
break;
|
||||
case 'l':
|
||||
config->sops = false;
|
||||
break;
|
||||
case 'm':
|
||||
audio_device = value;
|
||||
config->audio_device = value;
|
||||
break;
|
||||
case 'n':
|
||||
config->localaudio = true;
|
||||
@@ -201,18 +194,23 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) {
|
||||
case 'v':
|
||||
config->stream.fps = atoi(value);
|
||||
break;
|
||||
case 'w':
|
||||
config->forcehw = true;
|
||||
break;
|
||||
case 'x':
|
||||
config->codec = CODEC_HEVC;
|
||||
break;
|
||||
case 'z':
|
||||
config->codec = CODEC_H264;
|
||||
if (strcasecmp(value, "auto") == 0)
|
||||
config->codec = CODEC_UNSPECIFIED;
|
||||
else if (strcasecmp(value, "h264") == 0)
|
||||
config->codec = CODEC_H264;
|
||||
if (strcasecmp(value, "h265") == 0 || strcasecmp(value, "hevc") == 0)
|
||||
config->codec = CODEC_HEVC;
|
||||
break;
|
||||
case 'y':
|
||||
config->unsupported_version = true;
|
||||
break;
|
||||
case 'z':
|
||||
config->debug_level = 1;
|
||||
break;
|
||||
case 'Z':
|
||||
config->debug_level = 2;
|
||||
break;
|
||||
case 1:
|
||||
if (config->action == NULL)
|
||||
config->action = value;
|
||||
@@ -246,8 +244,11 @@ bool config_file_parse(char* filename, PCONFIGURATION config) {
|
||||
config->localaudio = strcmp("true", value) == 0;
|
||||
} else {
|
||||
for (int i=0;long_options[i].name != NULL;i++) {
|
||||
if (long_options[i].has_arg == required_argument && strcmp(long_options[i].name, key) == 0) {
|
||||
parse_argument(long_options[i].val, value, config);
|
||||
if (strcmp(long_options[i].name, key) == 0) {
|
||||
if (long_options[i].has_arg == required_argument)
|
||||
parse_argument(long_options[i].val, value, config);
|
||||
else if (strcmp("true", value) == 0)
|
||||
parse_argument(long_options[i].val, NULL, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -289,27 +290,28 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
|
||||
|
||||
config->stream.width = 1280;
|
||||
config->stream.height = 720;
|
||||
config->stream.fps = 60;
|
||||
config->stream.fps = -1;
|
||||
config->stream.bitrate = -1;
|
||||
config->stream.packetSize = 1024;
|
||||
config->stream.streamingRemotely = 0;
|
||||
config->stream.audioConfiguration = AUDIO_CONFIGURATION_STEREO;
|
||||
config->stream.supportsHevc = false;
|
||||
|
||||
config->platform = "default";
|
||||
config->debug_level = 0;
|
||||
config->platform = "auto";
|
||||
config->app = "Steam";
|
||||
config->action = NULL;
|
||||
config->address = NULL;
|
||||
config->config_file = NULL;
|
||||
config->audio_device = NULL;
|
||||
config->sops = true;
|
||||
config->localaudio = false;
|
||||
config->fullscreen = true;
|
||||
config->unsupported_version = false;
|
||||
config->forcehw = false;
|
||||
config->codec = CODEC_UNSPECIFIED;
|
||||
|
||||
config->inputsCount = 0;
|
||||
config->mapping = get_path("mappings/default.conf", getenv("XDG_DATA_DIRS"));
|
||||
config->mapping = get_path("gamecontrollerdb.txt", getenv("XDG_DATA_DIRS"));
|
||||
config->key_dir[0] = 0;
|
||||
|
||||
char* config_file = get_path("moonlight.conf", "/etc");
|
||||
@@ -342,6 +344,9 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
|
||||
}
|
||||
}
|
||||
|
||||
if (config->stream.fps == -1)
|
||||
config->stream.fps = config->stream.height >= 1080 ? 30 : 60;
|
||||
|
||||
if (config->stream.bitrate == -1) {
|
||||
if (config->stream.height >= 1080 && config->stream.fps >= 60)
|
||||
config->stream.bitrate = 20000;
|
||||
@@ -350,14 +355,4 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
|
||||
else
|
||||
config->stream.bitrate = 5000;
|
||||
}
|
||||
|
||||
if (inputAdded) {
|
||||
if (!mapped) {
|
||||
fprintf(stderr, "Mapping option should be followed by the input to be mapped.\n");
|
||||
exit(-1);
|
||||
} else if (config->mapping == NULL) {
|
||||
fprintf(stderr, "Please specify mapping file as default mapping could not be found.\n");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
src/config.h
12
src/config.h
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015, 2016 Iwan Timmer
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -25,26 +25,22 @@
|
||||
|
||||
enum codecs { CODEC_UNSPECIFIED, CODEC_H264, CODEC_HEVC };
|
||||
|
||||
struct input_config {
|
||||
char* path;
|
||||
char* mapping;
|
||||
};
|
||||
|
||||
typedef struct _CONFIGURATION {
|
||||
STREAM_CONFIGURATION stream;
|
||||
int debug_level;
|
||||
char* app;
|
||||
char* action;
|
||||
char* address;
|
||||
char* mapping;
|
||||
char* platform;
|
||||
char* audio_device;
|
||||
char* config_file;
|
||||
char key_dir[4096];
|
||||
bool sops;
|
||||
bool localaudio;
|
||||
bool fullscreen;
|
||||
bool forcehw;
|
||||
bool unsupported_version;
|
||||
struct input_config inputs[MAX_INPUTS];
|
||||
char* inputs[MAX_INPUTS];
|
||||
int inputsCount;
|
||||
enum codecs codec;
|
||||
} CONFIGURATION, *PCONFIGURATION;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015 Iwan Timmer
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,8 +17,8 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define VERSION_MAJOR @MOONLIGHT_MAJOR_VERSION@
|
||||
#define VERSION_MINOR @MOONLIGHT_MINOR_VERSION@
|
||||
#define VERSION_PATCH @MOONLIGHT_PATCH_VERSION@
|
||||
#define VERSION_MAJOR @PROJECT_VERSION_MAJOR@
|
||||
#define VERSION_MINOR @PROJECT_VERSION_MINOR@
|
||||
#define VERSION_PATCH @PROJECT_VERSION_PATCH@
|
||||
|
||||
#define COMPILE_OPTIONS "@MOONLIGHT_OPTIONS@"
|
||||
|
||||
@@ -18,12 +18,17 @@
|
||||
*/
|
||||
|
||||
#include "connection.h"
|
||||
#include "global.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <signal.h>
|
||||
|
||||
static void connection_connection_terminated() {
|
||||
quit();
|
||||
pthread_t main_thread_id = 0;
|
||||
bool connection_debug;
|
||||
|
||||
static void connection_terminated() {
|
||||
if (main_thread_id != 0)
|
||||
pthread_kill(main_thread_id, SIGTERM);
|
||||
}
|
||||
|
||||
static void connection_display_message(const char *msg) {
|
||||
@@ -34,12 +39,20 @@ static void connection_display_transient_message(const char *msg) {
|
||||
printf("%s\n", msg);
|
||||
}
|
||||
|
||||
static void connection_log_message(const char* format, ...) {
|
||||
va_list arglist;
|
||||
va_start(arglist, format);
|
||||
vprintf(format, arglist);
|
||||
va_end(arglist);
|
||||
}
|
||||
|
||||
CONNECTION_LISTENER_CALLBACKS connection_callbacks = {
|
||||
.stageStarting = NULL,
|
||||
.stageComplete = NULL,
|
||||
.stageFailed = NULL,
|
||||
.connectionStarted = NULL,
|
||||
.connectionTerminated = connection_connection_terminated,
|
||||
.connectionTerminated = connection_terminated,
|
||||
.displayMessage = connection_display_message,
|
||||
.displayTransientMessage = connection_display_transient_message,
|
||||
.logMessage = connection_log_message,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015 Iwan Timmer
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,4 +19,9 @@
|
||||
|
||||
#include <Limelight.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
extern CONNECTION_LISTENER_CALLBACKS connection_callbacks;
|
||||
extern pthread_t main_thread_id;
|
||||
extern bool connection_debug;
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_LIBCEC
|
||||
|
||||
#include <Limelight.h>
|
||||
|
||||
#include <ceccloader.h>
|
||||
@@ -111,4 +109,3 @@ void cec_init() {
|
||||
|
||||
g_iface.set_active_source(g_iface.connection, g_config.deviceTypes.types[0]);
|
||||
}
|
||||
#endif /* HAVE_LIBCEC */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015 Iwan Timmer
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,11 +17,11 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../loop.h"
|
||||
#include "../global.h"
|
||||
#include "evdev.h"
|
||||
|
||||
#include "keyboard.h"
|
||||
#include "mapping.h"
|
||||
|
||||
#include "../loop.h"
|
||||
|
||||
#include "libevdev/libevdev.h"
|
||||
#include <Limelight.h>
|
||||
@@ -38,6 +38,13 @@
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <endian.h>
|
||||
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#define int16_to_le(val) val
|
||||
#else
|
||||
#define int16_to_le(val) ((((val) >> 8) & 0x00FF) | (((val) << 8) & 0xFF00))
|
||||
#endif
|
||||
|
||||
struct input_abs_parms {
|
||||
int min, max;
|
||||
@@ -48,7 +55,10 @@ struct input_abs_parms {
|
||||
|
||||
struct input_device {
|
||||
struct libevdev *dev;
|
||||
struct mapping map;
|
||||
struct mapping* map;
|
||||
int key_map[KEY_MAX];
|
||||
int abs_map[ABS_MAX];
|
||||
int hats_state[3][2];
|
||||
int fd;
|
||||
char modifiers;
|
||||
__s32 mouseDeltaX, mouseDeltaY, mouseScroll;
|
||||
@@ -59,9 +69,16 @@ struct input_device {
|
||||
short rightStickX, rightStickY;
|
||||
bool gamepadModified;
|
||||
struct input_abs_parms xParms, yParms, rxParms, ryParms, zParms, rzParms;
|
||||
struct input_abs_parms dpadxParms, dpadyParms;
|
||||
};
|
||||
|
||||
#define HAT_UP 1
|
||||
#define HAT_RIGHT 2
|
||||
#define HAT_DOWN 4
|
||||
#define HAT_LEFT 8
|
||||
static const int hat_constants[3][3] = {{HAT_UP | HAT_LEFT, HAT_UP, HAT_UP | HAT_RIGHT}, {HAT_LEFT, 0, HAT_RIGHT}, {HAT_LEFT | HAT_DOWN, HAT_DOWN, HAT_RIGHT}};
|
||||
|
||||
#define set_hat(flags, flag, hat, hat_flag) flags = (-((hat & hat_flag) > 0) ^ flags) & flag
|
||||
|
||||
static struct input_device* devices = NULL;
|
||||
static int numDevices = 0;
|
||||
static int assignedControllerIds = 0;
|
||||
@@ -74,13 +91,14 @@ static bool grabbingDevices;
|
||||
|
||||
#define QUIT_MODIFIERS (MODIFIER_SHIFT|MODIFIER_ALT|MODIFIER_CTRL)
|
||||
#define QUIT_KEY KEY_Q
|
||||
#define QUIT_BUTTONS (PLAY_FLAG|BACK_FLAG|LB_FLAG|RB_FLAG)
|
||||
|
||||
static bool (*handler) (struct input_event*, struct input_device*);
|
||||
|
||||
static void evdev_init_parms(struct input_device *dev, struct input_abs_parms *parms, int code) {
|
||||
parms->flat = libevdev_get_abs_flat(dev->dev, code);
|
||||
parms->min = libevdev_get_abs_minimum(dev->dev, code);
|
||||
parms->max = libevdev_get_abs_maximum(dev->dev, code);
|
||||
parms->flat = libevdev_get_abs_flat(dev->dev, dev->abs_map[code]);
|
||||
parms->min = libevdev_get_abs_minimum(dev->dev, dev->abs_map[code]);
|
||||
parms->max = libevdev_get_abs_maximum(dev->dev, dev->abs_map[code]);
|
||||
parms->avg = (parms->min+parms->max)/2;
|
||||
parms->range = parms->max - parms->avg;
|
||||
parms->diff = parms->max - parms->min;
|
||||
@@ -120,15 +138,6 @@ static char evdev_convert_value_byte(struct input_event *ev, struct input_device
|
||||
return (ev->value - parms->flat - parms->min) * UCHAR_MAX / (parms->diff - parms->flat);
|
||||
}
|
||||
|
||||
static int evdev_convert_value_direction(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms, bool reverse) {
|
||||
if (ev->value > (parms->avg+parms->range/4))
|
||||
return reverse?-1:1;
|
||||
else if (ev->value < (parms->avg-parms->range/4))
|
||||
return reverse?1:-1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool evdev_handle_event(struct input_event *ev, struct input_device *dev) {
|
||||
bool gamepadModified = false;
|
||||
|
||||
@@ -195,6 +204,8 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
|
||||
} else {
|
||||
int mouseCode = 0;
|
||||
short gamepadCode = 0;
|
||||
int index = dev->key_map[ev->code - BTN_MISC];
|
||||
|
||||
switch (ev->code) {
|
||||
case BTN_LEFT:
|
||||
mouseCode = BUTTON_LEFT;
|
||||
@@ -206,35 +217,35 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
|
||||
mouseCode = BUTTON_RIGHT;
|
||||
break;
|
||||
default:
|
||||
if (ev->code == dev->map.btn_south)
|
||||
if (index == dev->map->btn_a)
|
||||
gamepadCode = A_FLAG;
|
||||
else if (ev->code == dev->map.btn_west)
|
||||
else if (index == dev->map->btn_x)
|
||||
gamepadCode = X_FLAG;
|
||||
else if (ev->code == dev->map.btn_north)
|
||||
else if (index == dev->map->btn_y)
|
||||
gamepadCode = Y_FLAG;
|
||||
else if (ev->code == dev->map.btn_east)
|
||||
else if (index == dev->map->btn_b)
|
||||
gamepadCode = B_FLAG;
|
||||
else if (ev->code == dev->map.btn_dpad_up)
|
||||
else if (index == dev->map->btn_dpup)
|
||||
gamepadCode = UP_FLAG;
|
||||
else if (ev->code == dev->map.btn_dpad_down)
|
||||
else if (index == dev->map->btn_dpdown)
|
||||
gamepadCode = DOWN_FLAG;
|
||||
else if (ev->code == dev->map.btn_dpad_right)
|
||||
else if (index == dev->map->btn_dpright)
|
||||
gamepadCode = RIGHT_FLAG;
|
||||
else if (ev->code == dev->map.btn_dpad_left)
|
||||
else if (index == dev->map->btn_dpleft)
|
||||
gamepadCode = LEFT_FLAG;
|
||||
else if (ev->code == dev->map.btn_thumbl)
|
||||
else if (index == dev->map->btn_leftstick)
|
||||
gamepadCode = LS_CLK_FLAG;
|
||||
else if (ev->code == dev->map.btn_thumbr)
|
||||
else if (index == dev->map->btn_rightstick)
|
||||
gamepadCode = RS_CLK_FLAG;
|
||||
else if (ev->code == dev->map.btn_tl)
|
||||
else if (index == dev->map->btn_leftshoulder)
|
||||
gamepadCode = LB_FLAG;
|
||||
else if (ev->code == dev->map.btn_tr)
|
||||
else if (index == dev->map->btn_rightshoulder)
|
||||
gamepadCode = RB_FLAG;
|
||||
else if (ev->code == dev->map.btn_start)
|
||||
else if (index == dev->map->btn_start)
|
||||
gamepadCode = PLAY_FLAG;
|
||||
else if (ev->code == dev->map.btn_select)
|
||||
else if (index == dev->map->btn_back)
|
||||
gamepadCode = BACK_FLAG;
|
||||
else if (ev->code == dev->map.btn_mode)
|
||||
else if (index == dev->map->btn_guide)
|
||||
gamepadCode = SPECIAL_FLAG;
|
||||
}
|
||||
|
||||
@@ -248,9 +259,9 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
|
||||
dev->buttonFlags |= gamepadCode;
|
||||
else
|
||||
dev->buttonFlags &= ~gamepadCode;
|
||||
} else if (ev->code == dev->map.btn_tl2)
|
||||
} else if (index == dev->map->btn_lefttrigger)
|
||||
dev->leftTrigger = ev->value?UCHAR_MAX:0;
|
||||
else if (ev->code == dev->map.btn_tr2)
|
||||
else if (index == dev->map->btn_righttrigger)
|
||||
dev->rightTrigger = ev->value?UCHAR_MAX:0;
|
||||
else {
|
||||
fprintf(stderr, "Unmapped button: %d\n", ev->code);
|
||||
@@ -274,48 +285,51 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
|
||||
break;
|
||||
case EV_ABS:
|
||||
gamepadModified = true;
|
||||
if (ev->code == dev->map.abs_x)
|
||||
dev->leftStickX = evdev_convert_value(ev, dev, &dev->xParms, dev->map.reverse_x);
|
||||
else if (ev->code == dev->map.abs_y)
|
||||
dev->leftStickY = evdev_convert_value(ev, dev, &dev->yParms, dev->map.reverse_y);
|
||||
else if (ev->code == dev->map.abs_rx)
|
||||
dev->rightStickX = evdev_convert_value(ev, dev, &dev->rxParms, dev->map.reverse_rx);
|
||||
else if (ev->code == dev->map.abs_ry)
|
||||
dev->rightStickY = evdev_convert_value(ev, dev, &dev->ryParms, dev->map.reverse_ry);
|
||||
else if (ev->code == dev->map.abs_z)
|
||||
dev->leftTrigger = evdev_convert_value_byte(ev, dev, &dev->zParms);
|
||||
else if (ev->code == dev->map.abs_rz)
|
||||
dev->rightTrigger = evdev_convert_value_byte(ev, dev, &dev->rzParms);
|
||||
else if (ev->code == dev->map.abs_dpad_x) {
|
||||
int dir = evdev_convert_value_direction(ev, dev, &dev->dpadxParms, dev->map.reverse_dpad_x);
|
||||
if (dir == 1) {
|
||||
dev->buttonFlags |= RIGHT_FLAG;
|
||||
dev->buttonFlags &= ~LEFT_FLAG;
|
||||
} else if (dir == 0) {
|
||||
dev->buttonFlags &= ~RIGHT_FLAG;
|
||||
dev->buttonFlags &= ~LEFT_FLAG;
|
||||
} else {
|
||||
dev->buttonFlags &= ~RIGHT_FLAG;
|
||||
dev->buttonFlags |= LEFT_FLAG;
|
||||
}
|
||||
} else if (ev->code == dev->map.abs_dpad_y) {
|
||||
int dir = evdev_convert_value_direction(ev, dev, &dev->dpadyParms, dev->map.reverse_dpad_y);
|
||||
if (dir == 1) {
|
||||
dev->buttonFlags |= DOWN_FLAG;
|
||||
dev->buttonFlags &= ~UP_FLAG;
|
||||
} else if (dir == 0) {
|
||||
dev->buttonFlags &= ~DOWN_FLAG;
|
||||
dev->buttonFlags &= ~UP_FLAG;
|
||||
} else {
|
||||
dev->buttonFlags &= ~DOWN_FLAG;
|
||||
dev->buttonFlags |= UP_FLAG;
|
||||
}
|
||||
} else
|
||||
gamepadModified = false;
|
||||
int index = dev->abs_map[ev->code];
|
||||
int hat_index = (ev->code - ABS_HAT0X) / 2;
|
||||
int har_dir_index = (ev->code - ABS_HAT0X) % 2;
|
||||
|
||||
break;
|
||||
switch (ev->code) {
|
||||
case ABS_HAT0X:
|
||||
case ABS_HAT0Y:
|
||||
case ABS_HAT1X:
|
||||
case ABS_HAT1Y:
|
||||
case ABS_HAT2X:
|
||||
case ABS_HAT2Y:
|
||||
case ABS_HAT3X:
|
||||
case ABS_HAT3Y:
|
||||
dev->hats_state[hat_index][har_dir_index] = ev->value < 0 ? 0 : (ev->value == 0 ? 1 : 2);
|
||||
int hat_state = hat_constants[dev->hats_state[hat_index][0]][dev->hats_state[hat_index][1]];
|
||||
if (hat_index == dev->map->hat_dpup)
|
||||
set_hat(dev->buttonFlags, UP_FLAG, hat_state, dev->map->hat_dir_dpup);
|
||||
if (hat_index == dev->map->hat_dpdown)
|
||||
set_hat(dev->buttonFlags, DOWN_FLAG, hat_state, dev->map->hat_dir_dpdown);
|
||||
if (hat_index == dev->map->hat_dpright)
|
||||
set_hat(dev->buttonFlags, HAT_RIGHT, hat_state, dev->map->hat_dir_dpright);
|
||||
if (hat_index == dev->map->hat_dpleft)
|
||||
set_hat(dev->buttonFlags, HAT_LEFT, hat_state, dev->map->hat_dir_dpleft);
|
||||
break;
|
||||
default:
|
||||
if (index == dev->map->abs_leftx)
|
||||
dev->leftStickX = evdev_convert_value(ev, dev, &dev->xParms, dev->map->reverse_leftx);
|
||||
else if (index == dev->map->abs_lefty)
|
||||
dev->leftStickY = evdev_convert_value(ev, dev, &dev->yParms, !dev->map->reverse_lefty);
|
||||
else if (index == dev->map->abs_rightx)
|
||||
dev->rightStickX = evdev_convert_value(ev, dev, &dev->rxParms, dev->map->reverse_rightx);
|
||||
else if (index == dev->map->abs_righty)
|
||||
dev->rightStickY = evdev_convert_value(ev, dev, &dev->ryParms, !dev->map->reverse_righty);
|
||||
else if (index == dev->map->abs_lefttrigger)
|
||||
dev->leftTrigger = evdev_convert_value_byte(ev, dev, &dev->zParms);
|
||||
else if (index == dev->map->abs_righttrigger)
|
||||
dev->rightTrigger = evdev_convert_value_byte(ev, dev, &dev->rzParms);
|
||||
else
|
||||
gamepadModified = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (gamepadModified && (dev->buttonFlags & QUIT_BUTTONS) == QUIT_BUTTONS)
|
||||
return false;
|
||||
|
||||
dev->gamepadModified |= gamepadModified;
|
||||
return true;
|
||||
}
|
||||
@@ -380,7 +394,7 @@ static int evdev_handle(int fd) {
|
||||
return LOOP_OK;
|
||||
}
|
||||
|
||||
void evdev_create(const char* device, char* mapFile) {
|
||||
void evdev_create(const char* device, struct mapping* mappings) {
|
||||
int fd = open(device, O_RDONLY|O_NONBLOCK);
|
||||
if (fd <= 0) {
|
||||
fprintf(stderr, "Failed to open device %s\n", device);
|
||||
@@ -388,6 +402,31 @@ void evdev_create(const char* device, char* mapFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct libevdev *evdev = libevdev_new();
|
||||
libevdev_set_fd(evdev, fd);
|
||||
|
||||
int16_t guid[8] = {0};
|
||||
guid[0] = int16_to_le(libevdev_get_id_bustype(evdev));
|
||||
guid[2] = int16_to_le(libevdev_get_id_vendor(evdev));
|
||||
guid[4] = int16_to_le(libevdev_get_id_product(evdev));
|
||||
guid[6] = int16_to_le(libevdev_get_id_version(evdev));
|
||||
|
||||
char str_guid[33];
|
||||
char* buf = str_guid;
|
||||
for (int i = 0; i < 16; i++)
|
||||
buf += sprintf(buf, "%02x", ((unsigned char*) guid)[i]);
|
||||
|
||||
while (mappings != NULL && strncmp(str_guid, mappings->guid, 32) != 0)
|
||||
mappings = mappings->next;
|
||||
|
||||
if (mappings == NULL) {
|
||||
fprintf(stderr, "No mapping available for %s\n", device);
|
||||
fflush(stderr);
|
||||
close(fd);
|
||||
libevdev_free(evdev);
|
||||
return;
|
||||
}
|
||||
|
||||
int dev = numDevices;
|
||||
numDevices++;
|
||||
|
||||
@@ -404,21 +443,35 @@ void evdev_create(const char* device, char* mapFile) {
|
||||
|
||||
memset(&devices[dev], 0, sizeof(devices[0]));
|
||||
devices[dev].fd = fd;
|
||||
devices[dev].dev = libevdev_new();
|
||||
libevdev_set_fd(devices[dev].dev, devices[dev].fd);
|
||||
devices[dev].dev = evdev;
|
||||
devices[dev].map = mappings;
|
||||
|
||||
if (mapFile != NULL)
|
||||
mapping_load(mapFile, &(devices[dev].map));
|
||||
int nbuttons = 0;
|
||||
for (int i = BTN_JOYSTICK; i < KEY_MAX; ++i) {
|
||||
if (libevdev_has_event_code(devices[dev].dev, EV_KEY, i))
|
||||
devices[dev].key_map[i - BTN_MISC] = nbuttons++;
|
||||
}
|
||||
for (int i = BTN_MISC; i < BTN_JOYSTICK; ++i) {
|
||||
if (libevdev_has_event_code(devices[dev].dev, EV_KEY, i))
|
||||
devices[dev].key_map[i - BTN_MISC] = nbuttons++;
|
||||
}
|
||||
|
||||
int naxes = 0;
|
||||
for (int i = 0; i < ABS_MAX; ++i) {
|
||||
/* Skip hats */
|
||||
if (i == ABS_HAT0X)
|
||||
i = ABS_HAT3Y;
|
||||
else if (libevdev_has_event_code(devices[dev].dev, EV_ABS, i))
|
||||
devices[dev].abs_map[i] = naxes++;
|
||||
}
|
||||
|
||||
devices[dev].controllerId = -1;
|
||||
evdev_init_parms(&devices[dev], &(devices[dev].xParms), devices[dev].map.abs_x);
|
||||
evdev_init_parms(&devices[dev], &(devices[dev].yParms), devices[dev].map.abs_y);
|
||||
evdev_init_parms(&devices[dev], &(devices[dev].zParms), devices[dev].map.abs_z);
|
||||
evdev_init_parms(&devices[dev], &(devices[dev].rxParms), devices[dev].map.abs_rx);
|
||||
evdev_init_parms(&devices[dev], &(devices[dev].ryParms), devices[dev].map.abs_ry);
|
||||
evdev_init_parms(&devices[dev], &(devices[dev].rzParms), devices[dev].map.abs_rz);
|
||||
evdev_init_parms(&devices[dev], &(devices[dev].dpadxParms), devices[dev].map.abs_dpad_x);
|
||||
evdev_init_parms(&devices[dev], &(devices[dev].dpadyParms), devices[dev].map.abs_dpad_y);
|
||||
evdev_init_parms(&devices[dev], &(devices[dev].xParms), devices[dev].map->abs_leftx);
|
||||
evdev_init_parms(&devices[dev], &(devices[dev].yParms), devices[dev].map->abs_lefty);
|
||||
evdev_init_parms(&devices[dev], &(devices[dev].zParms), devices[dev].map->abs_lefttrigger);
|
||||
evdev_init_parms(&devices[dev], &(devices[dev].rxParms), devices[dev].map->abs_rightx);
|
||||
evdev_init_parms(&devices[dev], &(devices[dev].ryParms), devices[dev].map->abs_righty);
|
||||
evdev_init_parms(&devices[dev], &(devices[dev].rzParms), devices[dev].map->abs_righttrigger);
|
||||
|
||||
if (grabbingDevices) {
|
||||
if (ioctl(fd, EVIOCGRAB, 1) < 0) {
|
||||
@@ -429,85 +482,6 @@ void evdev_create(const char* device, char* mapFile) {
|
||||
loop_add_fd(devices[dev].fd, &evdev_handle, POLLIN);
|
||||
}
|
||||
|
||||
static void evdev_map_key(char* keyName, short* key) {
|
||||
printf("Press %s\n", keyName);
|
||||
currentKey = key;
|
||||
currentAbs = NULL;
|
||||
*key = -1;
|
||||
loop_main();
|
||||
|
||||
usleep(250000);
|
||||
evdev_drain();
|
||||
}
|
||||
|
||||
static void evdev_map_abs(char* keyName, short* abs, bool* reverse) {
|
||||
printf("Move %s\n", keyName);
|
||||
currentKey = NULL;
|
||||
currentAbs = abs;
|
||||
currentReverse = reverse;
|
||||
*abs = -1;
|
||||
loop_main();
|
||||
|
||||
usleep(250000);
|
||||
evdev_drain();
|
||||
}
|
||||
|
||||
static void evdev_map_abskey(char* keyName, short* key, short* abs, bool* reverse) {
|
||||
printf("Press %s\n", keyName);
|
||||
currentKey = key;
|
||||
currentAbs = abs;
|
||||
currentReverse = reverse;
|
||||
*key = -1;
|
||||
*abs = -1;
|
||||
*currentReverse = false;
|
||||
loop_main();
|
||||
|
||||
usleep(250000);
|
||||
evdev_drain();
|
||||
}
|
||||
|
||||
void evdev_map(char* fileName) {
|
||||
struct mapping map;
|
||||
|
||||
handler = evdev_handle_mapping_event;
|
||||
|
||||
evdev_map_abs("Left Stick Right", &(map.abs_x), &(map.reverse_x));
|
||||
evdev_map_abs("Left Stick Up", &(map.abs_y), &(map.reverse_y));
|
||||
evdev_map_key("Left Stick Button", &(map.btn_thumbl));
|
||||
|
||||
evdev_map_abs("Right Stick Right", &(map.abs_rx), &(map.reverse_rx));
|
||||
evdev_map_abs("Right Stick Up", &(map.abs_ry), &(map.reverse_ry));
|
||||
evdev_map_key("Right Stick Button", &(map.btn_thumbr));
|
||||
|
||||
evdev_map_abskey("D-Pad Right", &(map.btn_dpad_right), &(map.abs_dpad_x), &(map.reverse_dpad_x));
|
||||
if (map.btn_dpad_right >= 0)
|
||||
evdev_map_key("D-Pad Left", &(map.btn_dpad_left));
|
||||
else
|
||||
map.btn_dpad_left = -1;
|
||||
|
||||
evdev_map_abskey("D-Pad Down", &(map.btn_dpad_down), &(map.abs_dpad_y), &(map.reverse_dpad_y));
|
||||
if (map.btn_dpad_down >= 0)
|
||||
evdev_map_key("D-Pad Up", &(map.btn_dpad_up));
|
||||
else
|
||||
map.btn_dpad_up = -1;
|
||||
|
||||
evdev_map_key("Button X (1)", &(map.btn_west));
|
||||
evdev_map_key("Button A (2)", &(map.btn_south));
|
||||
evdev_map_key("Button B (3)", &(map.btn_east));
|
||||
evdev_map_key("Button Y (4)", &(map.btn_north));
|
||||
evdev_map_key("Back Button", &(map.btn_select));
|
||||
evdev_map_key("Start Button", &(map.btn_start));
|
||||
evdev_map_key("Special Button", &(map.btn_mode));
|
||||
|
||||
bool ignored;
|
||||
evdev_map_abskey("Left Trigger", &(map.btn_tl2), &(map.abs_z), &ignored);
|
||||
evdev_map_abskey("Right Trigger", &(map.btn_tr2), &(map.abs_rz), &ignored);
|
||||
|
||||
evdev_map_key("Left Bumper", &(map.btn_tl));
|
||||
evdev_map_key("Right Bumper", &(map.btn_tr));
|
||||
mapping_save(fileName, &map);
|
||||
}
|
||||
|
||||
void evdev_start() {
|
||||
// After grabbing, the only way to quit via the keyboard
|
||||
// is via the special key combo that the input handling
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015 Iwan Timmer
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,9 +17,10 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
void evdev_create(const char* device, char* mapFile);
|
||||
#include "mapping.h"
|
||||
|
||||
void evdev_create(const char* device, struct mapping* mappings);
|
||||
void evdev_loop();
|
||||
void evdev_map(char* fileName);
|
||||
|
||||
void evdev_init();
|
||||
void evdev_start();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015 Iwan Timmer
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -23,10 +23,9 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define write_config(fd, key, value) fprintf(fd, "%s = %hd\n", key, value)
|
||||
#define write_config_bool(fd, key, value) fprintf(fd, "%s = %s\n", key, value?"true":"false");
|
||||
|
||||
void mapping_load(char* fileName, struct mapping* map) {
|
||||
struct mapping* mapping_load(char* fileName) {
|
||||
struct mapping* mappings = NULL;
|
||||
struct mapping* map = NULL;
|
||||
FILE* fd = fopen(fileName, "r");
|
||||
if (fd == NULL) {
|
||||
fprintf(stderr, "Can't open mapping file: %s\n", fileName);
|
||||
@@ -36,135 +35,117 @@ void mapping_load(char* fileName, struct mapping* map) {
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
while (getline(&line, &len, fd) != -1) {
|
||||
char *key = NULL, *value = NULL;
|
||||
if (sscanf(line, "%ms = %ms", &key, &value) == 2) {
|
||||
long int_value = strtol(value, NULL, 10);
|
||||
if (strcmp("abs_x", key) == 0)
|
||||
map->abs_x = int_value;
|
||||
else if (strcmp("abs_y", key) == 0)
|
||||
map->abs_y = int_value;
|
||||
else if (strcmp("abs_z", key) == 0)
|
||||
map->abs_z = int_value;
|
||||
else if (strcmp("abs_rx", key) == 0)
|
||||
map->abs_rx = int_value;
|
||||
else if (strcmp("abs_ry", key) == 0)
|
||||
map->abs_ry = int_value;
|
||||
else if (strcmp("abs_rz", key) == 0)
|
||||
map->abs_rz = int_value;
|
||||
else if (strcmp("abs_deadzone", key) == 0)
|
||||
map->abs_deadzone = int_value;
|
||||
else if (strcmp("abs_dpad_x", key) == 0)
|
||||
map->abs_dpad_x = int_value;
|
||||
else if (strcmp("abs_dpad_y", key) == 0)
|
||||
map->abs_dpad_y = int_value;
|
||||
else if (strcmp("btn_south", key) == 0)
|
||||
map->btn_south = int_value;
|
||||
else if (strcmp("btn_north", key) == 0)
|
||||
map->btn_north = int_value;
|
||||
else if (strcmp("btn_east", key) == 0)
|
||||
map->btn_east = int_value;
|
||||
else if (strcmp("btn_west", key) == 0)
|
||||
map->btn_west = int_value;
|
||||
else if (strcmp("btn_select", key) == 0)
|
||||
map->btn_select = int_value;
|
||||
else if (strcmp("btn_start", key) == 0)
|
||||
map->btn_start = int_value;
|
||||
else if (strcmp("btn_mode", key) == 0)
|
||||
map->btn_mode = int_value;
|
||||
else if (strcmp("btn_thumbl", key) == 0)
|
||||
map->btn_thumbl = int_value;
|
||||
else if (strcmp("btn_thumbr", key) == 0)
|
||||
map->btn_thumbr = int_value;
|
||||
else if (strcmp("btn_tl", key) == 0)
|
||||
map->btn_tl = int_value;
|
||||
else if (strcmp("btn_tr", key) == 0)
|
||||
map->btn_tr = int_value;
|
||||
else if (strcmp("btn_tl2", key) == 0)
|
||||
map->btn_tl2 = int_value;
|
||||
else if (strcmp("btn_tr2", key) == 0)
|
||||
map->btn_tr2 = int_value;
|
||||
else if (strcmp("btn_dpad_up", key) == 0)
|
||||
map->btn_dpad_up = int_value;
|
||||
else if (strcmp("btn_dpad_down", key) == 0)
|
||||
map->btn_dpad_down = int_value;
|
||||
else if (strcmp("btn_dpad_left", key) == 0)
|
||||
map->btn_dpad_left = int_value;
|
||||
else if (strcmp("btn_dpad_right", key) == 0)
|
||||
map->btn_dpad_right = int_value;
|
||||
else if (strcmp("reverse_x", key) == 0)
|
||||
map->reverse_x = strcmp("true", value) == 0;
|
||||
else if (strcmp("reverse_y", key) == 0)
|
||||
map->reverse_y = strcmp("true", value) == 0;
|
||||
else if (strcmp("reverse_rx", key) == 0)
|
||||
map->reverse_rx = strcmp("true", value) == 0;
|
||||
else if (strcmp("reverse_ry", key) == 0)
|
||||
map->reverse_ry = strcmp("true", value) == 0;
|
||||
else if (strcmp("reverse_dpad_x", key) == 0)
|
||||
map->reverse_dpad_x = strcmp("true", value) == 0;
|
||||
else if (strcmp("reverse_dpad_y", key) == 0)
|
||||
map->reverse_dpad_y = strcmp("true", value) == 0;
|
||||
else
|
||||
fprintf(stderr, "Can't map (%s)\n", key);
|
||||
}
|
||||
if (key != NULL)
|
||||
free(key);
|
||||
char* strpoint;
|
||||
char* guid = strtok_r(line, ",", &strpoint);
|
||||
char* name = strtok_r(NULL, ",", &strpoint);
|
||||
if (guid == NULL || name == NULL)
|
||||
continue;
|
||||
|
||||
if (value != NULL)
|
||||
free(value);
|
||||
struct mapping* newmap = malloc(sizeof(struct mapping));
|
||||
if (newmap == NULL) {
|
||||
fprintf(stderr, "Not enough memory");
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (mappings == NULL)
|
||||
mappings = newmap;
|
||||
else
|
||||
map->next = newmap;
|
||||
|
||||
map = newmap;
|
||||
|
||||
strncpy(map->guid, guid, sizeof(map->guid));
|
||||
strncpy(map->name, name, sizeof(map->name));
|
||||
|
||||
char* option;
|
||||
while ((option = strtok_r(NULL, ",", &strpoint)) != NULL) {
|
||||
char *key = NULL, *value = NULL;
|
||||
int ret;
|
||||
if ((ret = sscanf(option, "%m[^:]:%ms", &key, &value)) == 2) {
|
||||
int int_value, direction_value;
|
||||
char flag = 0;
|
||||
if (strcmp("platform", key) == 0)
|
||||
strncpy(map->platform, value, sizeof(map->platform));
|
||||
else if (sscanf(value, "b%d", &int_value) == 1) {
|
||||
if (strcmp("a", key) == 0)
|
||||
map->btn_a = int_value;
|
||||
else if (strcmp("y", key) == 0)
|
||||
map->btn_y = int_value;
|
||||
else if (strcmp("x", key) == 0)
|
||||
map->btn_x = int_value;
|
||||
else if (strcmp("b", key) == 0)
|
||||
map->btn_b = int_value;
|
||||
else if (strcmp("back", key) == 0)
|
||||
map->btn_back = int_value;
|
||||
else if (strcmp("start", key) == 0)
|
||||
map->btn_start = int_value;
|
||||
else if (strcmp("guide", key) == 0)
|
||||
map->btn_guide = int_value;
|
||||
else if (strcmp("dpup", key) == 0)
|
||||
map->btn_dpup = int_value;
|
||||
else if (strcmp("dpdown", key) == 0)
|
||||
map->btn_dpdown = int_value;
|
||||
else if (strcmp("dpleft", key) == 0)
|
||||
map->btn_dpleft = int_value;
|
||||
else if (strcmp("dpright", key) == 0)
|
||||
map->btn_dpright = int_value;
|
||||
else if (strcmp("leftstick", key) == 0)
|
||||
map->btn_leftstick = int_value;
|
||||
else if (strcmp("rightstick", key) == 0)
|
||||
map->btn_rightstick = int_value;
|
||||
else if (strcmp("leftshoulder", key) == 0)
|
||||
map->btn_leftshoulder = int_value;
|
||||
else if (strcmp("rightshoulder", key) == 0)
|
||||
map->btn_rightshoulder = int_value;
|
||||
else if (strcmp("lefttrigger", key) == 0)
|
||||
map->btn_lefttrigger = int_value;
|
||||
else if (strcmp("righttrigger", key) == 0)
|
||||
map->btn_righttrigger = int_value;
|
||||
} else if (sscanf(value, "a%d%c", &int_value, &flag) >= 1) {
|
||||
if (strcmp("leftx", key) == 0) {
|
||||
map->abs_leftx = int_value;
|
||||
map->reverse_leftx = flag == '~';
|
||||
} else if (strcmp("lefty", key) == 0) {
|
||||
map->abs_lefty = int_value;
|
||||
map->reverse_lefty = flag == '~';
|
||||
} else if (strcmp("rightx", key) == 0) {
|
||||
map->abs_rightx = int_value;
|
||||
map->reverse_rightx = flag == '~';
|
||||
} else if (strcmp("righty", key) == 0) {
|
||||
map->abs_righty = int_value;
|
||||
map->reverse_righty = flag == '~';
|
||||
} else if (strcmp("lefttrigger", key) == 0)
|
||||
map->abs_lefttrigger = int_value;
|
||||
else if (strcmp("righttrigger", key) == 0)
|
||||
map->abs_righttrigger = int_value;
|
||||
} else if (sscanf(value, "h%d.%d", &int_value, &direction_value) == 2) {
|
||||
if (strcmp("dpright", key) == 0) {
|
||||
map->hat_dpright = int_value;
|
||||
map->hat_dir_dpright = direction_value;
|
||||
} else if (strcmp("dpleft", key) == 0) {
|
||||
map->hat_dpleft = int_value;
|
||||
map->hat_dir_dpleft = direction_value;
|
||||
} else if (strcmp("dpup", key) == 0) {
|
||||
map->hat_dpup = int_value;
|
||||
map->hat_dir_dpup = direction_value;
|
||||
} else if (strcmp("dpdown", key) == 0) {
|
||||
map->hat_dpdown = int_value;
|
||||
map->hat_dir_dpdown = direction_value;
|
||||
}
|
||||
} else
|
||||
fprintf(stderr, "Can't map (%s)\n", option);
|
||||
} else if (ret == 0 && option[0] != '\n')
|
||||
fprintf(stderr, "Can't map (%s)\n", option);
|
||||
|
||||
if (key != NULL)
|
||||
free(key);
|
||||
|
||||
if (value != NULL)
|
||||
free(value);
|
||||
}
|
||||
map->guid[32] = '\0';
|
||||
map->name[256] = '\0';
|
||||
map->platform[32] = '\0';
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
|
||||
void mapping_save(char* fileName, struct mapping* map) {
|
||||
FILE* fd = fopen(fileName, "w");
|
||||
if (fd == NULL) {
|
||||
fprintf(stderr, "Can't open mapping file: %s\n", fileName);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
write_config(fd, "abs_x", map->abs_x);
|
||||
write_config(fd, "abs_y", map->abs_y);
|
||||
write_config(fd, "abs_z", map->abs_z);
|
||||
|
||||
write_config_bool(fd, "reverse_x", map->reverse_x);
|
||||
write_config_bool(fd, "reverse_y", map->reverse_y);
|
||||
|
||||
write_config(fd, "abs_rx", map->abs_rx);
|
||||
write_config(fd, "abs_ry", map->abs_ry);
|
||||
write_config(fd, "abs_rz", map->abs_rz);
|
||||
|
||||
write_config_bool(fd, "reverse_rx", map->reverse_rx);
|
||||
write_config_bool(fd, "reverse_ry", map->reverse_ry);
|
||||
|
||||
write_config(fd, "abs_deadzone", map->abs_deadzone);
|
||||
|
||||
write_config(fd, "abs_dpad_x", map->abs_dpad_x);
|
||||
write_config(fd, "abs_dpad_y", map->abs_dpad_y);
|
||||
|
||||
write_config_bool(fd, "reverse_dpad_x", map->reverse_dpad_x);
|
||||
write_config_bool(fd, "reverse_dpad_y", map->reverse_dpad_y);
|
||||
|
||||
write_config(fd, "btn_north", map->btn_north);
|
||||
write_config(fd, "btn_east", map->btn_east);
|
||||
write_config(fd, "btn_south", map->btn_south);
|
||||
write_config(fd, "btn_west", map->btn_west);
|
||||
|
||||
write_config(fd, "btn_select", map->btn_select);
|
||||
write_config(fd, "btn_start", map->btn_start);
|
||||
write_config(fd, "btn_mode", map->btn_mode);
|
||||
|
||||
write_config(fd, "btn_thumbl", map->btn_thumbl);
|
||||
write_config(fd, "btn_thumbr", map->btn_thumbr);
|
||||
|
||||
write_config(fd, "btn_tl", map->btn_tl);
|
||||
write_config(fd, "btn_tr", map->btn_tr);
|
||||
write_config(fd, "btn_tl2", map->btn_tl2);
|
||||
write_config(fd, "btn_tr2", map->btn_tr2);
|
||||
|
||||
write_config(fd, "btn_dpad_up", map->btn_dpad_up);
|
||||
write_config(fd, "btn_dpad_down", map->btn_dpad_down);
|
||||
write_config(fd, "btn_dpad_left", map->btn_dpad_left);
|
||||
write_config(fd, "btn_dpad_right", map->btn_dpad_right);
|
||||
|
||||
fclose(fd);
|
||||
|
||||
return mappings;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015 Iwan Timmer
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,27 +17,34 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct mapping {
|
||||
short abs_x, abs_y, abs_z;
|
||||
short abs_rx, abs_ry, abs_rz;
|
||||
char guid[33];
|
||||
char platform[33];
|
||||
char name[257];
|
||||
|
||||
bool reverse_x, reverse_y;
|
||||
bool reverse_rx, reverse_ry;
|
||||
bool reverse_leftx, reverse_lefty;
|
||||
bool reverse_rightx, reverse_righty;
|
||||
|
||||
short abs_leftx, abs_lefty;
|
||||
short abs_rightx, abs_righty;
|
||||
|
||||
short abs_deadzone;
|
||||
short hat_dpright, hat_dpleft, hat_dpup, hat_dpdown;
|
||||
short hat_dir_dpright, hat_dir_dpleft, hat_dir_dpup, hat_dir_dpdown;
|
||||
short btn_dpup, btn_dpdown, btn_dpleft, btn_dpright;
|
||||
|
||||
short btn_a, btn_x, btn_y, btn_b;
|
||||
short btn_back, btn_start, btn_guide;
|
||||
short btn_leftstick, btn_rightstick;
|
||||
short btn_leftshoulder, btn_rightshoulder;
|
||||
|
||||
short abs_lefttrigger, abs_righttrigger;
|
||||
short btn_lefttrigger, btn_righttrigger;
|
||||
|
||||
short abs_dpad_x, abs_dpad_y;
|
||||
bool reverse_dpad_x, reverse_dpad_y;
|
||||
|
||||
short btn_south, btn_east, btn_north, btn_west;
|
||||
short btn_select, btn_start, btn_mode;
|
||||
short btn_thumbl, btn_thumbr;
|
||||
short btn_tl, btn_tr, btn_tl2, btn_tr2;
|
||||
|
||||
short btn_dpad_up, btn_dpad_down, btn_dpad_left, btn_dpad_right;
|
||||
struct mapping* next;
|
||||
};
|
||||
|
||||
void mapping_load(char* fileName, struct mapping* map);
|
||||
void mapping_save(char* fileName, struct mapping* map);
|
||||
struct mapping* mapping_load(char* fileName);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015 Iwan Timmer
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,15 +17,14 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_SDL
|
||||
|
||||
#include "sdlinput.h"
|
||||
#include "sdl.h"
|
||||
#include "../sdl.h"
|
||||
|
||||
#include <Limelight.h>
|
||||
|
||||
#define ACTION_MODIFIERS (MODIFIER_SHIFT|MODIFIER_ALT|MODIFIER_CTRL)
|
||||
#define QUIT_KEY SDLK_q
|
||||
#define QUIT_BUTTONS (PLAY_FLAG|BACK_FLAG|LB_FLAG|RB_FLAG)
|
||||
#define FULLSCREEN_KEY SDLK_f
|
||||
|
||||
typedef struct _GAMEPAD_STATE {
|
||||
@@ -43,11 +42,11 @@ static GAMEPAD_STATE gamepads[4];
|
||||
static int keyboard_modifiers;
|
||||
static int activeGamepadMask = 0;
|
||||
|
||||
void sdlinput_init() {
|
||||
void sdlinput_init(char* mappings) {
|
||||
memset(gamepads, 0, sizeof(gamepads));
|
||||
|
||||
SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
|
||||
SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt");
|
||||
SDL_GameControllerAddMappingsFromFile(mappings);
|
||||
|
||||
for (int i = 0; i < SDL_NumJoysticks(); ++i) {
|
||||
if (SDL_IsGameController(i)) {
|
||||
@@ -103,10 +102,20 @@ int sdlinput_handle_event(SDL_Event* event) {
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
button = event->key.keysym.sym;
|
||||
if (button >= (0x40000000 + 0x39) && button < (0x40000000 + 0x39 + sizeof(keyCodes)))
|
||||
button = keyCodes[button - 0x40000039];
|
||||
else if (button >= 0x61)
|
||||
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) {
|
||||
@@ -224,10 +233,11 @@ int sdlinput_handle_event(SDL_Event* event) {
|
||||
else
|
||||
gamepad->buttons &= ~button;
|
||||
|
||||
if ((gamepad->buttons & QUIT_BUTTONS) == QUIT_BUTTONS)
|
||||
return SDL_QUIT_APPLICATION;
|
||||
|
||||
LiSendMultiControllerEvent(gamepad->id, activeGamepadMask, gamepad->buttons, gamepad->leftTrigger, gamepad->rightTrigger, gamepad->leftStickX, gamepad->leftStickY, gamepad->rightStickX, gamepad->rightStickY);
|
||||
break;
|
||||
}
|
||||
return SDL_NOTHING;
|
||||
}
|
||||
|
||||
#endif /* HAVE_SDL */
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015 Iwan Timmer
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,12 +17,47 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_SDL
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <SDL.h>
|
||||
|
||||
static const short keyCodes[] = {
|
||||
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
|
||||
@@ -51,7 +86,16 @@ static const short keyCodes[] = {
|
||||
0x26, //SDLK_UP
|
||||
};
|
||||
|
||||
void sdlinput_init();
|
||||
int sdlinput_handle_event(SDL_Event* event);
|
||||
static const short keyCodes5[] = {
|
||||
0x11, //SDLK_LCTRL
|
||||
0x10, //SDLK_LSHIFT
|
||||
0x12, //SDLK_LALT
|
||||
0x5B, //SDLK_LGUI
|
||||
0x11, //SDLK_LRCTRL
|
||||
0x10, //SDLK_RSHIFT
|
||||
0x12, //SDLK_RALT
|
||||
0x5C, //SDLK_RGUI
|
||||
};
|
||||
|
||||
#endif /* HAVE_SDL */
|
||||
void sdlinput_init(char* mappings);
|
||||
int sdlinput_handle_event(SDL_Event* event);
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015 Iwan Timmer
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "../loop.h"
|
||||
|
||||
#include "udev.h"
|
||||
#include "evdev.h"
|
||||
|
||||
#include <libudev.h>
|
||||
@@ -31,7 +32,7 @@
|
||||
#include <poll.h>
|
||||
|
||||
static bool autoadd;
|
||||
static char* defaultMapfile;
|
||||
static struct mapping* defaultMappings;
|
||||
|
||||
static struct udev *udev;
|
||||
static struct udev_monitor *udev_mon;
|
||||
@@ -45,7 +46,7 @@ static int udev_handle(int fd) {
|
||||
const char *devnode = udev_device_get_devnode(dev);
|
||||
int id;
|
||||
if (devnode != NULL && sscanf(devnode, "/dev/input/event%d", &id) == 1) {
|
||||
evdev_create(devnode, defaultMapfile);
|
||||
evdev_create(devnode, defaultMappings);
|
||||
}
|
||||
}
|
||||
udev_device_unref(dev);
|
||||
@@ -53,7 +54,7 @@ static int udev_handle(int fd) {
|
||||
return LOOP_OK;
|
||||
}
|
||||
|
||||
void udev_init(bool autoload, char* mapfile) {
|
||||
void udev_init(bool autoload, struct mapping* mappings) {
|
||||
udev = udev_new();
|
||||
if (!udev) {
|
||||
fprintf(stderr, "Can't create udev\n");
|
||||
@@ -74,7 +75,7 @@ void udev_init(bool autoload, char* mapfile) {
|
||||
const char *devnode = udev_device_get_devnode(dev);
|
||||
int id;
|
||||
if (devnode != NULL && sscanf(devnode, "/dev/input/event%d", &id) == 1) {
|
||||
evdev_create(devnode, mapfile);
|
||||
evdev_create(devnode, mappings);
|
||||
}
|
||||
udev_device_unref(dev);
|
||||
}
|
||||
@@ -86,7 +87,7 @@ void udev_init(bool autoload, char* mapfile) {
|
||||
udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "input", NULL);
|
||||
udev_monitor_enable_receiving(udev_mon);
|
||||
|
||||
defaultMapfile = mapfile;
|
||||
defaultMappings = mappings;
|
||||
|
||||
int udev_fd = udev_monitor_get_fd(udev_mon);
|
||||
loop_add_fd(udev_fd, &udev_handle, POLLIN);
|
||||
|
||||
@@ -17,5 +17,7 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
void udev_init(bool autoload, char* mapfile);
|
||||
#include "mapping.h"
|
||||
|
||||
void udev_init(bool autoload, struct mapping* mappings);
|
||||
void evdev_destroy();
|
||||
|
||||
152
src/input/x11.c
Normal file
152
src/input/x11.c
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Moonlight is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "x11.h"
|
||||
#include "keyboard.h"
|
||||
|
||||
#include "../loop.h"
|
||||
|
||||
#include <Limelight.h>
|
||||
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xutil.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <poll.h>
|
||||
|
||||
#define MODIFIERS (MODIFIER_SHIFT|MODIFIER_ALT|MODIFIER_CTRL)
|
||||
|
||||
static Display *display;
|
||||
static Window window;
|
||||
|
||||
static Atom wm_deletemessage;
|
||||
|
||||
static int last_x = -1, last_y = -1;
|
||||
static int keyboard_modifiers;
|
||||
|
||||
static const char data[1] = {0};
|
||||
static Cursor cursor;
|
||||
static bool grabbed = True;
|
||||
|
||||
static int x11_handler(int fd) {
|
||||
XEvent event;
|
||||
int button = 0;
|
||||
int motion_x, motion_y;
|
||||
|
||||
while (XPending(display)) {
|
||||
XNextEvent(display, &event);
|
||||
switch (event.type) {
|
||||
case KeyPress:
|
||||
case KeyRelease:
|
||||
if (event.xkey.keycode >= 8 && event.xkey.keycode < (sizeof(keyCodes)/sizeof(keyCodes[0]) + 8)) {
|
||||
if ((keyboard_modifiers & MODIFIERS) == MODIFIERS && event.type == KeyRelease) {
|
||||
if (event.xkey.keycode == 0x18)
|
||||
return LOOP_RETURN;
|
||||
else {
|
||||
grabbed = !grabbed;
|
||||
XDefineCursor(display, window, grabbed ? cursor : 0);
|
||||
}
|
||||
}
|
||||
|
||||
int modifier = 0;
|
||||
switch (event.xkey.keycode) {
|
||||
case 0x32:
|
||||
case 0x3e:
|
||||
modifier = MODIFIER_SHIFT;
|
||||
break;
|
||||
case 0x40:
|
||||
case 0x6c:
|
||||
modifier = MODIFIER_ALT;
|
||||
break;
|
||||
case 0x25:
|
||||
case 0x69:
|
||||
modifier = MODIFIER_CTRL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (modifier != 0) {
|
||||
if (event.type == KeyPress)
|
||||
keyboard_modifiers |= modifier;
|
||||
else
|
||||
keyboard_modifiers &= ~modifier;
|
||||
}
|
||||
|
||||
short code = 0x80 << 8 | keyCodes[event.xkey.keycode - 8];
|
||||
LiSendKeyboardEvent(code, event.type == KeyPress ? KEY_ACTION_DOWN : KEY_ACTION_UP, keyboard_modifiers);
|
||||
}
|
||||
break;
|
||||
case ButtonPress:
|
||||
case ButtonRelease:
|
||||
switch (event.xbutton.button) {
|
||||
case Button1:
|
||||
button = BUTTON_LEFT;
|
||||
break;
|
||||
case Button2:
|
||||
button = BUTTON_MIDDLE;
|
||||
break;
|
||||
case Button3:
|
||||
button = BUTTON_RIGHT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (button != 0)
|
||||
LiSendMouseButtonEvent(event.type==ButtonPress ? BUTTON_ACTION_PRESS : BUTTON_ACTION_RELEASE, button);
|
||||
break;
|
||||
case MotionNotify:
|
||||
motion_x = event.xmotion.x - last_x;
|
||||
motion_y = event.xmotion.y - last_y;
|
||||
if (abs(motion_x) > 0 || abs(motion_y) > 0) {
|
||||
if (last_x >= 0 && last_y >= 0)
|
||||
LiSendMouseMoveEvent(motion_x, motion_y);
|
||||
|
||||
if (grabbed)
|
||||
XWarpPointer(display, None, window, 0, 0, 0, 0, 640, 360);
|
||||
}
|
||||
|
||||
last_x = grabbed ? 640 : event.xmotion.x;
|
||||
last_y = grabbed ? 360 : event.xmotion.y;
|
||||
break;
|
||||
case ClientMessage:
|
||||
if (event.xclient.data.l[0] == wm_deletemessage)
|
||||
return LOOP_RETURN;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return LOOP_OK;
|
||||
}
|
||||
|
||||
void x11_input_init(Display* x11_display, Window x11_window) {
|
||||
display = x11_display;
|
||||
window = x11_window;
|
||||
|
||||
wm_deletemessage = XInternAtom(display, "WM_DELETE_WINDOW", False);
|
||||
XSetWMProtocols(display, window, &wm_deletemessage, 1);
|
||||
|
||||
/* make a blank cursor */
|
||||
XColor dummy;
|
||||
Pixmap blank = XCreateBitmapFromData(display, window, data, 1, 1);
|
||||
cursor = XCreatePixmapCursor(display, blank, blank, &dummy, &dummy, 0, 0);
|
||||
XFreePixmap(display, blank);
|
||||
XDefineCursor(display, window, cursor);
|
||||
|
||||
loop_add_fd(ConnectionNumber(display), x11_handler, POLLIN | POLLERR | POLLHUP);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015 Iwan Timmer
|
||||
* Copyright (C) 2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,8 +17,6 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
extern pthread_t main_thread_id;
|
||||
|
||||
void quit();
|
||||
void x11_input_init(Display* display, Window window);
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "loop.h"
|
||||
|
||||
#include "global.h"
|
||||
#include "connection.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/signalfd.h>
|
||||
|
||||
120
src/main.c
120
src/main.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015, 2016 Iwan Timmer
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -18,23 +18,30 @@
|
||||
*/
|
||||
|
||||
#include "loop.h"
|
||||
#include "client.h"
|
||||
#include "connection.h"
|
||||
#include "configuration.h"
|
||||
#include "audio.h"
|
||||
#include "video.h"
|
||||
#include "discover.h"
|
||||
#include "config.h"
|
||||
#include "platform.h"
|
||||
#include "sdl.h"
|
||||
|
||||
#include "audio/audio.h"
|
||||
#include "video/video.h"
|
||||
|
||||
#include "input/mapping.h"
|
||||
#include "input/evdev.h"
|
||||
#include "input/udev.h"
|
||||
#ifdef HAVE_LIBCEC
|
||||
#include "input/cec.h"
|
||||
#include "input/sdlinput.h"
|
||||
#endif
|
||||
#ifdef HAVE_SDL
|
||||
#include "input/sdl.h"
|
||||
#endif
|
||||
|
||||
#include <Limelight.h>
|
||||
|
||||
#include <client.h>
|
||||
#include <discover.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
@@ -87,6 +94,8 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys
|
||||
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)\n", config->stream.width, config->stream.height, config->stream.fps);
|
||||
else
|
||||
fprintf(stderr, "Errorcode starting app: %d\n", ret);
|
||||
exit(-1);
|
||||
@@ -96,11 +105,13 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys
|
||||
if (config->fullscreen)
|
||||
drFlags |= DISPLAY_FULLSCREEN;
|
||||
|
||||
if (config->forcehw)
|
||||
drFlags |= FORCE_HARDWARE_ACCELERATION;
|
||||
if (config->debug_level > 0) {
|
||||
printf("Stream %d x %d, %d fps, %d kbps\n", config->stream.width, config->stream.height, config->stream.fps, config->stream.bitrate);
|
||||
connection_debug = true;
|
||||
}
|
||||
|
||||
printf("Stream %d x %d, %d fps, %d kbps\n", config->stream.width, config->stream.height, config->stream.fps, config->stream.bitrate);
|
||||
LiStartConnection(&server->serverInfo, &config->stream, &connection_callbacks, platform_get_video(system), platform_get_audio(system), NULL, drFlags);
|
||||
platform_start(system);
|
||||
LiStartConnection(&server->serverInfo, &config->stream, &connection_callbacks, platform_get_video(system), platform_get_audio(system, config->audio_device), NULL, drFlags, config->audio_device, 0);
|
||||
|
||||
if (IS_EMBEDDED(system)) {
|
||||
evdev_start();
|
||||
@@ -113,13 +124,14 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys
|
||||
#endif
|
||||
|
||||
LiStopConnection();
|
||||
platform_stop(system);
|
||||
}
|
||||
|
||||
static void help() {
|
||||
printf("Moonlight Embedded %d.%d.%d\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
|
||||
printf("Usage: moonlight [action] (options) [host]\n");
|
||||
printf(" moonlight [configfile]\n");
|
||||
printf("\n Actions\n\n");
|
||||
printf("\tmap\t\t\tCreate mapping file for gamepad\n");
|
||||
printf("\tpair\t\t\tPair device with computer\n");
|
||||
printf("\tunpair\t\t\tUnpair device with computer\n");
|
||||
printf("\tstream\t\t\tStream computer to device\n");
|
||||
@@ -129,35 +141,37 @@ static void help() {
|
||||
printf("\n Global Options\n\n");
|
||||
printf("\t-config <config>\tLoad configuration file\n");
|
||||
printf("\t-save <config>\t\tSave configuration file\n");
|
||||
printf("\t-verbose\t\tEnable verbose output\n");
|
||||
printf("\t-debug\t\t\tEnable verbose and debug output\n");
|
||||
printf("\n Streaming options\n\n");
|
||||
printf("\t-720\t\t\tUse 1280x720 resolution [default]\n");
|
||||
printf("\t-1080\t\t\tUse 1920x1080 resolution\n");
|
||||
printf("\t-4k\t\t\tUse 3840x2160 resolution\n");
|
||||
printf("\t-width <width>\t\tHorizontal resolution (default 1280)\n");
|
||||
printf("\t-height <height>\tVertical resolution (default 720)\n");
|
||||
printf("\t-30fps\t\t\tUse 30fps\n");
|
||||
printf("\t-60fps\t\t\tUse 60fps [default]\n");
|
||||
printf("\t-fps <fps>\t\tSpecify the fps to use (default -1)\n");
|
||||
printf("\t-bitrate <bitrate>\tSpecify the bitrate in Kbps\n");
|
||||
printf("\t-packetsize <size>\tSpecify the maximum packetsize in bytes\n");
|
||||
printf("\t-hevc\t\t\tUse the high efficiency video coding (HEVC)\n");
|
||||
printf("\t-h264\t\t\tUse the advanced video coding (H264)\n");
|
||||
printf("\t-codec <codec>\t\tSelect used codec: auto/h264/h265 (default auto)\n");
|
||||
printf("\t-remote\t\t\tEnable remote optimizations\n");
|
||||
printf("\t-app <app>\t\tName of app to stream\n");
|
||||
printf("\t-nosops\t\t\tDon't allow GFE to modify game settings\n");
|
||||
printf("\t-localaudio\t\tPlay audio locally\n");
|
||||
printf("\t-surround\t\tStream 5.1 surround sound (requires GFE 2.7)\n");
|
||||
printf("\t-keydir <directory>\tLoad encryption keys from directory\n");
|
||||
#ifdef HAVE_SDL
|
||||
printf("\n Video options (SDL Only)\n\n");
|
||||
printf("\t-mapping <file>\t\tUse <file> as gamepad mappings configuration file\n");
|
||||
printf("\t-platform <system>\tSpecify system used for audio, video and input: pi/imx/aml/x11/x11_vdpau/sdl/fake (default auto)\n");
|
||||
printf("\t-unsupported\t\tTry streaming if GFE version is unsupported\n");
|
||||
#if defined(HAVE_SDL) || defined(HAVE_X11)
|
||||
printf("\n WM options (SDL and X11 only)\n\n");
|
||||
printf("\t-windowed\t\tDisplay screen in a window\n");
|
||||
#endif
|
||||
#ifdef HAVE_EMBEDDED
|
||||
printf("\n I/O options\n\n");
|
||||
printf("\t-mapping <file>\t\tUse <file> as gamepad mapping configuration file (use before -input)\n");
|
||||
printf("\n I/O options (Not for SDL)\n\n");
|
||||
printf("\t-input <device>\t\tUse <device> as input. Can be used multiple times\n");
|
||||
printf("\t-audio <device>\t\tUse <device> as audio output device\n");
|
||||
printf("\t-forcehw \t\tTry to use video hardware acceleration\n");
|
||||
#endif
|
||||
printf("\nUse Ctrl+Alt+Shift+Q to exit streaming session\n\n");
|
||||
printf("\nUse Ctrl+Alt+Shift+Q or Play+Back+LeftShoulder+RightShoulder to exit streaming session\n\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -169,33 +183,14 @@ static void pair_check(PSERVER_DATA server) {
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
printf("Moonlight Embedded %d.%d.%d (%s)\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, COMPILE_OPTIONS);
|
||||
|
||||
CONFIGURATION config;
|
||||
config_parse(argc, argv, &config);
|
||||
|
||||
if (config.action == NULL || strcmp("help", config.action) == 0)
|
||||
help();
|
||||
|
||||
enum platform system = platform_check(config.platform);
|
||||
if (system == 0) {
|
||||
fprintf(stderr, "Platform '%s' not found\n", config.platform);
|
||||
exit(-1);
|
||||
}
|
||||
config.stream.supportsHevc = config.codec != CODEC_H264 && (config.codec == CODEC_HEVC || platform_supports_hevc(system));
|
||||
|
||||
if (strcmp("map", config.action) == 0) {
|
||||
if (config.address == NULL) {
|
||||
perror("No filename for mapping");
|
||||
exit(-1);
|
||||
}
|
||||
udev_init(!inputAdded, config.mapping);
|
||||
for (int i=0;i<config.inputsCount;i++)
|
||||
evdev_create(config.inputs[i].path, config.inputs[i].mapping);
|
||||
|
||||
evdev_map(config.address);
|
||||
exit(0);
|
||||
}
|
||||
if (config.debug_level > 0)
|
||||
printf("Moonlight Embedded %d.%d.%d (%s)\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, COMPILE_OPTIONS);
|
||||
|
||||
if (config.address == NULL) {
|
||||
config.address = malloc(MAX_ADDRESS_SIZE);
|
||||
@@ -221,7 +216,7 @@ int main(int argc, char* argv[]) {
|
||||
printf("Connect to %s...\n", config.address);
|
||||
|
||||
int ret;
|
||||
if ((ret = gs_init(&server, config.address, config.key_dir)) == GS_OUT_OF_MEMORY) {
|
||||
if ((ret = gs_init(&server, config.address, config.key_dir, config.debug_level)) == GS_OUT_OF_MEMORY) {
|
||||
fprintf(stderr, "Not enough memory\n");
|
||||
exit(-1);
|
||||
} else if (ret == GS_INVALID) {
|
||||
@@ -237,28 +232,57 @@ int main(int argc, char* argv[]) {
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
printf("NVIDIA %s, GFE %s (protocol version %d)\n", server.gpuType, server.serverInfo.serverInfoGfeVersion, server.serverMajorVersion);
|
||||
if (config.debug_level > 0)
|
||||
printf("NVIDIA %s, GFE %s (%s, %s)\n", server.gpuType, server.serverInfo.serverInfoGfeVersion, server.gsVersion, server.serverInfo.serverInfoAppVersion);
|
||||
|
||||
if (strcmp("list", config.action) == 0) {
|
||||
pair_check(&server);
|
||||
applist(&server);
|
||||
} else if (strcmp("stream", config.action) == 0) {
|
||||
pair_check(&server);
|
||||
enum platform system = platform_check(config.platform);
|
||||
if (config.debug_level > 0)
|
||||
printf("Platform %s\n", platform_name(system));
|
||||
|
||||
if (system == 0) {
|
||||
fprintf(stderr, "Platform '%s' not found\n", config.platform);
|
||||
exit(-1);
|
||||
} else if (system == SDL && config.audio_device != NULL) {
|
||||
fprintf(stderr, "You can't select a audio device for SDL\n");
|
||||
exit(-1);
|
||||
}
|
||||
config.stream.supportsHevc = config.codec != CODEC_H264 && (config.codec == CODEC_HEVC || platform_supports_hevc(system));
|
||||
|
||||
if (IS_EMBEDDED(system)) {
|
||||
if (config.mapping == NULL) {
|
||||
fprintf(stderr, "Please specify mapping file as default mapping could not be found.\n");
|
||||
exit(-1);
|
||||
}
|
||||
struct mapping* mappings = mapping_load(config.mapping);
|
||||
|
||||
for (int i=0;i<config.inputsCount;i++) {
|
||||
printf("Add input %s (mapping %s)...\n", config.inputs[i].path, config.inputs[i].mapping);
|
||||
evdev_create(config.inputs[i].path, config.inputs[i].mapping);
|
||||
if (config.debug_level > 0)
|
||||
printf("Add input %s...\n", config.inputs[i]);
|
||||
|
||||
evdev_create(config.inputs[i], mappings);
|
||||
}
|
||||
|
||||
udev_init(!inputAdded, config.mapping);
|
||||
udev_init(!inputAdded, mappings);
|
||||
evdev_init();
|
||||
#ifdef HAVE_LIBCEC
|
||||
cec_init();
|
||||
#endif /* HAVE_LIBCEC */
|
||||
}
|
||||
#ifdef HAVE_SDL
|
||||
else if (system == SDL)
|
||||
else if (system == SDL) {
|
||||
if (config.inputsCount > 0) {
|
||||
fprintf(stderr, "You can't select input devices as SDL will automatically use all available controllers\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
sdl_init(config.stream.width, config.stream.height, config.fullscreen);
|
||||
sdlinput_init(config.mapping);
|
||||
}
|
||||
#endif
|
||||
|
||||
stream(&server, &config, system);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015, 2016 Iwan Timmer
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -20,7 +20,11 @@
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "platform.h"
|
||||
#include "audio.h"
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#include "audio/audio.h"
|
||||
#include "video/video.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -30,7 +34,7 @@
|
||||
typedef bool(*ImxInit)();
|
||||
|
||||
enum platform platform_check(char* name) {
|
||||
bool std = strcmp(name, "default") == 0;
|
||||
bool std = strcmp(name, "auto") == 0;
|
||||
#ifdef HAVE_IMX
|
||||
if (std || strcmp(name, "imx") == 0) {
|
||||
void *handle = dlopen("libmoonlight-imx.so", RTLD_NOW | RTLD_GLOBAL);
|
||||
@@ -55,6 +59,16 @@ enum platform platform_check(char* name) {
|
||||
return AML;
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_X11
|
||||
if (std || strcmp(name, "x11") == 0 || strcmp(name, "x11_vdpau") == 0) {
|
||||
int x11 = x11_init(strcmp(name, "x11") != 0);
|
||||
#ifdef HAVE_VDPAU
|
||||
if (strcmp(name, "x11") != 0 && x11 == 0)
|
||||
return X11_VDPAU;
|
||||
#endif
|
||||
return X11;
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_SDL
|
||||
if (std || strcmp(name, "sdl") == 0)
|
||||
return SDL;
|
||||
@@ -65,8 +79,48 @@ enum platform platform_check(char* name) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void platform_start(enum platform system) {
|
||||
switch (system) {
|
||||
#ifdef HAVE_AML
|
||||
case AML:
|
||||
blank_fb("/sys/class/graphics/fb0/blank", true);
|
||||
blank_fb("/sys/class/graphics/fb1/blank", true);
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_PI
|
||||
case PI:
|
||||
blank_fb("/sys/class/graphics/fb0/blank", true);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void platform_stop(enum platform system) {
|
||||
switch (system) {
|
||||
#ifdef HAVE_AML
|
||||
case AML:
|
||||
blank_fb("/sys/class/graphics/fb0/blank", false);
|
||||
blank_fb("/sys/class/graphics/fb1/blank", false);
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_PI
|
||||
case PI:
|
||||
blank_fb("/sys/class/graphics/fb0/blank", false);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
DECODER_RENDERER_CALLBACKS* platform_get_video(enum platform system) {
|
||||
switch (system) {
|
||||
#ifdef HAVE_X11
|
||||
case X11:
|
||||
return &decoder_callbacks_x11;
|
||||
#ifdef HAVE_VDPAU
|
||||
case X11_VDPAU:
|
||||
return &decoder_callbacks_x11_vdpau;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef HAVE_SDL
|
||||
case SDL:
|
||||
return &decoder_callbacks_sdl;
|
||||
@@ -83,13 +137,11 @@ DECODER_RENDERER_CALLBACKS* platform_get_video(enum platform system) {
|
||||
case AML:
|
||||
return (PDECODER_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "decoder_callbacks_aml");
|
||||
#endif
|
||||
case FAKE:
|
||||
return &decoder_callbacks_fake;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system) {
|
||||
AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system, char* audio_device) {
|
||||
switch (system) {
|
||||
#ifdef HAVE_SDL
|
||||
case SDL:
|
||||
@@ -102,12 +154,12 @@ AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system) {
|
||||
#endif
|
||||
default:
|
||||
#ifdef HAVE_PULSE
|
||||
if (audio_pulse_init())
|
||||
if (audio_pulse_init(audio_device))
|
||||
return &audio_callbacks_pulse;
|
||||
#endif
|
||||
#ifdef HAVE_ALSA
|
||||
return &audio_callbacks_alsa;
|
||||
case FAKE:
|
||||
return &audio_callbacks_fake;
|
||||
#endif
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -119,3 +171,24 @@ bool platform_supports_hevc(enum platform system) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
char* platform_name(enum platform system) {
|
||||
switch(system) {
|
||||
case PI:
|
||||
return "Raspberry Pi (Broadcom)";
|
||||
case IMX:
|
||||
return "i.MX6 (MXC Vivante)";
|
||||
case AML:
|
||||
return "AMLogic VPU";
|
||||
case X11:
|
||||
return "X Window System (software decoding)";
|
||||
case X11_VDPAU:
|
||||
return "X Window System (VDPAU)";
|
||||
case SDL:
|
||||
return "SDL2 (software decoding)";
|
||||
case FAKE:
|
||||
return "Fake (no a/v output)";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015, 2016 Iwan Timmer
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -26,16 +26,13 @@
|
||||
|
||||
#define IS_EMBEDDED(SYSTEM) SYSTEM != SDL
|
||||
|
||||
enum platform { NONE, SDL, PI, IMX, AML, FAKE };
|
||||
enum platform { NONE, SDL, X11, X11_VDPAU, PI, IMX, AML, FAKE };
|
||||
|
||||
enum platform platform_check(char*);
|
||||
PDECODER_RENDERER_CALLBACKS platform_get_video(enum platform system);
|
||||
PAUDIO_RENDERER_CALLBACKS platform_get_audio(enum platform system);
|
||||
PAUDIO_RENDERER_CALLBACKS platform_get_audio(enum platform system, char* audio_device);
|
||||
bool platform_supports_hevc(enum platform system);
|
||||
char* platform_name(enum platform system);
|
||||
|
||||
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_fake;
|
||||
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_fake;
|
||||
#ifdef HAVE_SDL
|
||||
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_sdl;
|
||||
void sdl_loop();
|
||||
#endif
|
||||
void platform_start(enum platform system);
|
||||
void platform_stop(enum platform system);
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#ifdef HAVE_SDL
|
||||
|
||||
#include "sdl.h"
|
||||
#include "input/sdlinput.h"
|
||||
#include "input/sdl.h"
|
||||
|
||||
#include <Limelight.h>
|
||||
|
||||
@@ -68,8 +68,6 @@ void sdl_init(int width, int height, bool fullscreen) {
|
||||
fprintf(stderr, "Couldn't create mutex\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sdlinput_init();
|
||||
}
|
||||
|
||||
void sdl_loop() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2016 Iwan Timmer
|
||||
* Copyright (C) 2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,28 +17,24 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../audio.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static FILE* fd;
|
||||
static const char* fileName = "fake.opus";
|
||||
int blank_fb(char *path, bool clear) {
|
||||
int fd = open(path, O_RDWR);
|
||||
|
||||
static void alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) {
|
||||
fd = fopen(fileName, "w");
|
||||
if(fd >= 0) {
|
||||
int ret = write(fd, clear ? "1" : "0", 1);
|
||||
if (ret < 0)
|
||||
fprintf(stderr, "Failed to clear framebuffer %s: %d\n", path, ret);
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
} else
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void alsa_renderer_cleanup() {
|
||||
fclose(fd);
|
||||
}
|
||||
|
||||
static void alsa_renderer_decode_and_play_sample(char* data, int length) {
|
||||
fwrite(data, length, 1, fd);
|
||||
}
|
||||
|
||||
AUDIO_RENDERER_CALLBACKS audio_callbacks_fake = {
|
||||
.init = alsa_renderer_init,
|
||||
.cleanup = alsa_renderer_cleanup,
|
||||
.decodeAndPlaySample = alsa_renderer_decode_and_play_sample,
|
||||
.capabilities = CAPABILITY_DIRECT_SUBMIT,
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015 Iwan Timmer
|
||||
* Copyright (C) 2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,5 +17,6 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define DISPLAY_FULLSCREEN 1
|
||||
#define FORCE_HARDWARE_ACCELERATION 2
|
||||
#include <stdbool.h>
|
||||
|
||||
int blank_fb(char *path, bool clear);
|
||||
50
src/video/aml.c
Normal file → Executable file
50
src/video/aml.c
Normal file → Executable file
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015, 2016 Iwan Timmer
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
* Copyright (C) 2016 OtherCrashOverride, Daniel Mehrwald
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
@@ -27,36 +27,15 @@
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <amcodec/codec.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <codec.h>
|
||||
|
||||
#define SYNC_OUTSIDE 0x02
|
||||
#define UCODE_IP_ONLY_PARAM 0x08
|
||||
|
||||
static codec_para_t codecParam = { 0 };
|
||||
|
||||
static int osd_blank(char *path,int cmd) {
|
||||
int fd;
|
||||
char bcmd[16];
|
||||
|
||||
fd = open(path, O_CREAT|O_RDWR | O_TRUNC, 0644);
|
||||
|
||||
if(fd>=0) {
|
||||
sprintf(bcmd,"%d",cmd);
|
||||
int ret = write(fd,bcmd,strlen(bcmd));
|
||||
if (ret < 0) {
|
||||
printf("osd_blank error during write: %x\n", ret);
|
||||
}
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void aml_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
|
||||
osd_blank("/sys/class/graphics/fb0/blank",1);
|
||||
osd_blank("/sys/class/graphics/fb1/blank",0);
|
||||
|
||||
int aml_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
|
||||
codecParam.stream_type = STREAM_TYPE_ES_VIDEO;
|
||||
codecParam.has_video = 1;
|
||||
codecParam.noblock = 0;
|
||||
@@ -86,7 +65,7 @@ void aml_setup(int videoFormat, int width, int height, int redrawRate, void* con
|
||||
break;
|
||||
default:
|
||||
printf("Video format not supported\n");
|
||||
exit(1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
codecParam.am_sysinfo.width = width;
|
||||
@@ -94,17 +73,22 @@ void aml_setup(int videoFormat, int width, int height, int redrawRate, void* con
|
||||
codecParam.am_sysinfo.rate = 96000 / redrawRate;
|
||||
codecParam.am_sysinfo.param = (void*) ((size_t) codecParam.am_sysinfo.param | SYNC_OUTSIDE);
|
||||
|
||||
int api = codec_init(&codecParam);
|
||||
if (api != 0) {
|
||||
fprintf(stderr, "codec_init error: %x\n", api);
|
||||
exit(1);
|
||||
int ret;
|
||||
if ((ret = codec_init(&codecParam)) != 0) {
|
||||
fprintf(stderr, "codec_init error: %x\n", ret);
|
||||
return -2;
|
||||
}
|
||||
|
||||
if ((ret = codec_set_freerun_mode(&codecParam, 1)) != 0) {
|
||||
fprintf(stderr, "Can't set Freerun mode: %x\n", ret);
|
||||
return -2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void aml_cleanup() {
|
||||
int api = codec_close(&codecParam);
|
||||
osd_blank("/sys/class/graphics/fb0/blank",0);
|
||||
osd_blank("/sys/class/graphics/fb1/blank",0);
|
||||
codec_close(&codecParam);
|
||||
}
|
||||
|
||||
int aml_submit_decode_unit(PDECODE_UNIT decodeUnit) {
|
||||
|
||||
199
src/video/egl.c
Normal file
199
src/video/egl.c
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Moonlight is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "egl.h"
|
||||
|
||||
#include <Limelight.h>
|
||||
|
||||
#include <GLES2/gl2.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static const EGLint context_attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
|
||||
static const char* texture_mappings[] = { "ymap", "umap", "vmap" };
|
||||
static const char* vertex_source = "\
|
||||
attribute vec2 position;\
|
||||
varying mediump vec2 tex_position;\
|
||||
\
|
||||
void main() {\
|
||||
gl_Position = vec4(position, 0, 1);\
|
||||
tex_position = vec2((position.x + 1.) / 2., (1. - position.y) / 2.);\
|
||||
}\
|
||||
";
|
||||
|
||||
static const char* fragment_source = "\
|
||||
uniform lowp sampler2D ymap;\
|
||||
uniform lowp sampler2D umap;\
|
||||
uniform lowp sampler2D vmap;\
|
||||
varying mediump vec2 tex_position;\
|
||||
\
|
||||
void main() {\
|
||||
mediump float y = texture2D(ymap, tex_position).r;\
|
||||
mediump float u = texture2D(umap, tex_position).r - .5;\n\
|
||||
mediump float v = texture2D(vmap, tex_position).r - .5;\n\
|
||||
lowp float r = y + 1.28033 * v;\
|
||||
lowp float g = y - .21482 * u - .38059 * v;\
|
||||
lowp float b = y + 2.12798 * u;\
|
||||
gl_FragColor = vec4(r, g, b, 1.0);\
|
||||
}\
|
||||
";
|
||||
|
||||
static const float vertices[] = {
|
||||
-1.f, 1.f,
|
||||
-1.f, -1.f,
|
||||
1.f, -1.f,
|
||||
1.f, 1.f
|
||||
};
|
||||
|
||||
static const GLuint elements[] = {
|
||||
0, 1, 2,
|
||||
2, 3, 0
|
||||
};
|
||||
|
||||
static EGLDisplay display;
|
||||
static EGLSurface surface;
|
||||
static EGLContext context;
|
||||
|
||||
static int width, height;
|
||||
static bool current;
|
||||
|
||||
static GLuint texture_id[3], texture_uniform[3];
|
||||
static GLuint shader_program;
|
||||
|
||||
void egl_init(EGLNativeDisplayType native_display, NativeWindowType native_window, int display_width, int display_height) {
|
||||
width = display_width;
|
||||
height = display_height;
|
||||
|
||||
// get an EGL display connection
|
||||
display = eglGetDisplay(native_display);
|
||||
if (display == EGL_NO_DISPLAY) {
|
||||
fprintf( stderr, "EGL: error get display\n" );
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// initialize the EGL display connection
|
||||
int major, minor;
|
||||
EGLBoolean result = eglInitialize(display, &major, &minor);
|
||||
if (result == EGL_FALSE) {
|
||||
fprintf( stderr, "EGL: error initialising display\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// get our config from the config class
|
||||
EGLConfig config = NULL;
|
||||
static const EGLint attribute_list[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE };
|
||||
|
||||
EGLint totalConfigsFound = 0;
|
||||
result = eglChooseConfig(display, attribute_list, &config, 1, &totalConfigsFound);
|
||||
if (result != EGL_TRUE || totalConfigsFound == 0) {
|
||||
fprintf(stderr, "EGL: Unable to query for available configs, found %d.\n", totalConfigsFound);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// bind the OpenGL API to the EGL
|
||||
result = eglBindAPI(EGL_OPENGL_ES_API);
|
||||
if (result == EGL_FALSE) {
|
||||
fprintf(stderr, "EGL: error binding API\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// create an EGL rendering context
|
||||
context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attributes);
|
||||
if (context == EGL_NO_CONTEXT) {
|
||||
fprintf(stderr, "EGL: couldn't get a valid context\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// finally we can create a new surface using this config and window
|
||||
surface = eglCreateWindowSurface(display, config, (NativeWindowType) native_window, NULL);
|
||||
eglMakeCurrent(display, surface, surface, context);
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
GLuint vbo;
|
||||
glGenBuffers(1, &vbo);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
|
||||
GLuint ebo;
|
||||
glGenBuffers(1, &ebo);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
|
||||
|
||||
GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(vertex_shader, 1, &vertex_source, NULL);
|
||||
glCompileShader(vertex_shader);
|
||||
GLint maxLength = 0;
|
||||
|
||||
GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(fragment_shader, 1, &fragment_source, NULL);
|
||||
glCompileShader(fragment_shader);
|
||||
|
||||
shader_program = glCreateProgram();
|
||||
glAttachShader(shader_program, vertex_shader);
|
||||
glAttachShader(shader_program, fragment_shader);
|
||||
|
||||
glLinkProgram(shader_program);
|
||||
glBindAttribLocation(shader_program, 0, "position");
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0);
|
||||
|
||||
glGenTextures(3, texture_id);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
glBindTexture(GL_TEXTURE_2D, texture_id[i]);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, i > 0 ? width / 2 : width, i > 0 ? height / 2 : height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0);
|
||||
|
||||
texture_uniform[i] = glGetUniformLocation(shader_program, texture_mappings[i]);
|
||||
}
|
||||
|
||||
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
}
|
||||
|
||||
void egl_draw(uint8_t* image[3]) {
|
||||
if (!current) {
|
||||
eglMakeCurrent(display, surface, surface, context);
|
||||
current = True;
|
||||
}
|
||||
|
||||
glUseProgram(shader_program);
|
||||
glEnableVertexAttribArray(0);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
glActiveTexture(GL_TEXTURE0 + i);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_id[i]);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, i > 0 ? width / 2 : width, i > 0 ? height / 2 : height, GL_LUMINANCE, GL_UNSIGNED_BYTE, image[i]);
|
||||
glUniform1i(texture_uniform[i], i);
|
||||
}
|
||||
|
||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
|
||||
|
||||
eglSwapBuffers(display, surface);
|
||||
}
|
||||
|
||||
void egl_destroy() {
|
||||
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
eglDestroySurface(display, surface);
|
||||
eglDestroyContext(display, context);
|
||||
eglTerminate(display);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015 Iwan Timmer
|
||||
* Copyright (C) 2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,11 +17,8 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <EGL/egl.h>
|
||||
|
||||
pthread_t main_thread_id;
|
||||
|
||||
void quit() {
|
||||
pthread_kill(main_thread_id, SIGTERM);
|
||||
}
|
||||
void egl_init(EGLNativeDisplayType native_display, NativeWindowType native_window, int display_width, int display_height);
|
||||
void egl_draw(uint8_t* image[3]);
|
||||
void egl_destroy();
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Moonlight is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Limelight.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static FILE* fd;
|
||||
static const char* fileName = "fake.h264";
|
||||
|
||||
void decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
|
||||
fd = fopen(fileName, "w");
|
||||
}
|
||||
|
||||
void decoder_renderer_cleanup() {
|
||||
fclose(fd);
|
||||
}
|
||||
|
||||
int decoder_renderer_submit_decode_unit(PDECODE_UNIT decodeUnit) {
|
||||
PLENTRY entry = decodeUnit->bufferList;
|
||||
while (entry != NULL) {
|
||||
fwrite(entry->data, entry->length, 1, fd);
|
||||
entry = entry->next;
|
||||
}
|
||||
return DR_OK;
|
||||
}
|
||||
|
||||
DECODER_RENDERER_CALLBACKS decoder_callbacks_fake = {
|
||||
.setup = decoder_renderer_setup,
|
||||
.cleanup = decoder_renderer_cleanup,
|
||||
.submitDecodeUnit = decoder_renderer_submit_decode_unit,
|
||||
.capabilities = CAPABILITY_DIRECT_SUBMIT,
|
||||
};
|
||||
@@ -40,8 +40,7 @@ static AVFrame** dec_frames;
|
||||
static int dec_frames_cnt;
|
||||
static int current_frame, next_frame;
|
||||
|
||||
enum decoders {SOFTWARE, VDPAU};
|
||||
enum decoders decoder_system;
|
||||
enum decoders ffmpeg_decoder;
|
||||
|
||||
#define BYTES_PER_PIXEL 4
|
||||
|
||||
@@ -66,12 +65,12 @@ int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer
|
||||
}
|
||||
|
||||
if (decoder != NULL)
|
||||
decoder_system = VDPAU;
|
||||
ffmpeg_decoder = VDPAU;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (decoder == NULL) {
|
||||
decoder_system = SOFTWARE;
|
||||
ffmpeg_decoder = SOFTWARE;
|
||||
switch (videoFormat) {
|
||||
case VIDEO_FORMAT_H264:
|
||||
decoder = avcodec_find_decoder_by_name("h264");
|
||||
@@ -133,7 +132,7 @@ int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer
|
||||
}
|
||||
|
||||
#ifdef HAVE_VDPAU
|
||||
if (decoder_system == VDPAU)
|
||||
if (ffmpeg_decoder == VDPAU)
|
||||
vdpau_init(decoder_ctx, width, height);
|
||||
#endif
|
||||
|
||||
@@ -156,16 +155,16 @@ void ffmpeg_destroy(void) {
|
||||
}
|
||||
}
|
||||
|
||||
AVFrame* ffmpeg_get_frame() {
|
||||
AVFrame* ffmpeg_get_frame(bool native_frame) {
|
||||
int err = avcodec_receive_frame(decoder_ctx, dec_frames[next_frame]);
|
||||
if (err == 0) {
|
||||
current_frame = next_frame;
|
||||
next_frame = (current_frame+1) % dec_frames_cnt;
|
||||
|
||||
if (decoder_system == SOFTWARE)
|
||||
if (ffmpeg_decoder == SOFTWARE || native_frame)
|
||||
return dec_frames[current_frame];
|
||||
#ifdef HAVE_VDPAU
|
||||
else if (decoder_system == VDPAU)
|
||||
else if (ffmpeg_decoder == VDPAU)
|
||||
return vdpau_get_frame(dec_frames[current_frame]);
|
||||
#endif
|
||||
} else if (err != AVERROR(EAGAIN)) {
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
|
||||
// Disables the deblocking filter at the cost of image quality
|
||||
@@ -34,9 +36,12 @@
|
||||
// Uses hardware acceleration
|
||||
#define HARDWARE_ACCELERATION 0x40
|
||||
|
||||
enum decoders {SOFTWARE, VDPAU};
|
||||
extern enum decoders ffmpeg_decoder;
|
||||
|
||||
int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer_count, int thread_count);
|
||||
void ffmpeg_destroy(void);
|
||||
|
||||
int ffmpeg_draw_frame(AVFrame *pict);
|
||||
AVFrame* ffmpeg_get_frame();
|
||||
AVFrame* ffmpeg_get_frame(bool native_frame);
|
||||
int ffmpeg_decode(unsigned char* indata, int inlen);
|
||||
|
||||
@@ -34,17 +34,26 @@ static AVFrame* cpu_frame;
|
||||
static VdpDevice vdp_device;
|
||||
static VdpDecoder vdp_decoder;
|
||||
static VdpVideoMixer vdp_mixer;
|
||||
static VdpPresentationQueue vdp_queue;
|
||||
static VdpPresentationQueueTarget vdp_queue_target;
|
||||
static VdpOutputSurface vdp_output;
|
||||
static struct vdpau_render_state* vdp_render_state[MAX_RENDER_STATES];
|
||||
static int vdp_render_states = 0;
|
||||
|
||||
static VdpGetProcAddress* vdp_get_proc_address;
|
||||
static VdpDeviceDestroy* vdp_device_destroy;
|
||||
static VdpDecoderCreate* vdp_decoder_create;
|
||||
static VdpDecoderRender* vdp_decoder_render;
|
||||
static VdpVideoSurfaceGetBitsYCbCr* vdp_video_surface_get_bits_y_cb_cr;
|
||||
static VdpVideoSurfaceCreate* vdp_video_surface_create;
|
||||
static VdpVideoMixerCreate* vdp_video_mixer_create;
|
||||
static VdpVideoMixerRender* vdp_video_mixer_render;
|
||||
static VdpOutputSurfaceCreate* vdp_output_surface_create;
|
||||
static VdpPresentationQueueCreate* vdp_presentation_queue_create;
|
||||
static VdpPresentationQueueDisplay* vdp_presentation_queue_display;
|
||||
static VdpPresentationQueueTargetCreateX11* vdp_presentation_queue_target_create_x11;
|
||||
|
||||
struct vdpau_render_state* vdp_get_free_render_state() {
|
||||
struct vdpau_render_state* vdp_get_free_render_state(int width, int height) {
|
||||
for (unsigned i = 0; i < vdp_render_states; i++) {
|
||||
struct vdpau_render_state* render_state = vdp_render_state[i];
|
||||
if (!render_state->state)
|
||||
@@ -60,7 +69,7 @@ struct vdpau_render_state* vdp_get_free_render_state() {
|
||||
vdp_render_states++;
|
||||
memset(render_state, 0, sizeof(struct vdpau_render_state));
|
||||
render_state->surface = VDP_INVALID_HANDLE;
|
||||
VdpStatus status = vdp_video_surface_create(vdp_device, VDP_CHROMA_TYPE_420, 1280, 720, &render_state->surface);
|
||||
VdpStatus status = vdp_video_surface_create(vdp_device, VDP_CHROMA_TYPE_420, width, height, &render_state->surface);
|
||||
return render_state;
|
||||
}
|
||||
|
||||
@@ -70,7 +79,7 @@ static void vdp_release_buffer(void* opaque, uint8_t *data) {
|
||||
}
|
||||
|
||||
static int vdp_get_buffer(AVCodecContext* context, AVFrame* frame, int flags) {
|
||||
struct vdpau_render_state* pRenderState = vdp_get_free_render_state();
|
||||
struct vdpau_render_state* pRenderState = vdp_get_free_render_state(frame->width, frame->height);
|
||||
frame->data[0] = (uint8_t*) pRenderState;
|
||||
frame->buf[0] = av_buffer_create(frame->data[0], 0, vdp_release_buffer, NULL, 0);
|
||||
|
||||
@@ -85,32 +94,33 @@ static enum AVPixelFormat vdp_get_format(AVCodecContext* context, const enum AVP
|
||||
static void vdp_draw_horiz_band(struct AVCodecContext* context, const AVFrame* frame, int offset[4], int y, int type, int height) {
|
||||
struct vdpau_render_state* render_state = (struct vdpau_render_state*)frame->data[0];
|
||||
|
||||
VdpStatus status = vdp_decoder_render(vdp_decoder, render_state->surface, (VdpPictureInfo const*)&(render_state->info), render_state->bitstream_buffers_used, render_state->bitstream_buffers);
|
||||
status = vdp_decoder_render(vdp_decoder, render_state->surface, (VdpPictureInfo const*)&(render_state->info), render_state->bitstream_buffers_used, render_state->bitstream_buffers);
|
||||
vdp_decoder_render(vdp_decoder, render_state->surface, (VdpPictureInfo const*)&(render_state->info), render_state->bitstream_buffers_used, render_state->bitstream_buffers);
|
||||
}
|
||||
|
||||
int vdpau_init(AVCodecContext* decoder_ctx, int width, int height) {
|
||||
if (vdp_device)
|
||||
return vdp_device;
|
||||
|
||||
Display* xdisplay = XOpenDisplay(0);
|
||||
if (!xdisplay)
|
||||
return -1;
|
||||
|
||||
VdpStatus status = vdp_device_create_x11(xdisplay, DefaultScreen(xdisplay), &vdp_device, &vdp_get_proc_address);
|
||||
int vdpau_init_lib(Display* display) {
|
||||
VdpStatus status = vdp_device_create_x11(display, DefaultScreen(display), &vdp_device, &vdp_get_proc_address);
|
||||
if (status != VDP_STATUS_OK)
|
||||
return -1;
|
||||
|
||||
vdp_get_proc_address(vdp_device, VDP_FUNC_ID_DEVICE_DESTROY, (void**)&vdp_device_destroy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vdpau_init(AVCodecContext* decoder_ctx, int width, int height) {
|
||||
vdp_get_proc_address(vdp_device, VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR, (void**)&vdp_video_surface_get_bits_y_cb_cr);
|
||||
vdp_get_proc_address(vdp_device, VDP_FUNC_ID_VIDEO_SURFACE_CREATE, (void**)&vdp_video_surface_create);
|
||||
vdp_get_proc_address(vdp_device, VDP_FUNC_ID_OUTPUT_SURFACE_CREATE, (void**)&vdp_output_surface_create);
|
||||
vdp_get_proc_address(vdp_device, VDP_FUNC_ID_DECODER_RENDER, (void**)&vdp_decoder_render);
|
||||
vdp_get_proc_address(vdp_device, VDP_FUNC_ID_DECODER_CREATE, (void**)&vdp_decoder_create);
|
||||
vdp_get_proc_address(vdp_device, VDP_FUNC_ID_VIDEO_MIXER_CREATE, (void**)&vdp_video_mixer_create);
|
||||
vdp_get_proc_address(vdp_device, VDP_FUNC_ID_VIDEO_MIXER_RENDER, (void**)&vdp_video_mixer_render);
|
||||
vdp_get_proc_address(vdp_device, VDP_FUNC_ID_PRESENTATION_QUEUE_CREATE, (void**)&vdp_presentation_queue_create);
|
||||
vdp_get_proc_address(vdp_device, VDP_FUNC_ID_PRESENTATION_QUEUE_TARGET_CREATE_X11, (void**)&vdp_presentation_queue_target_create_x11);
|
||||
vdp_get_proc_address(vdp_device, VDP_FUNC_ID_PRESENTATION_QUEUE_DISPLAY, (void**)&vdp_presentation_queue_display);
|
||||
|
||||
decoder_ctx->get_buffer2 = vdp_get_buffer;
|
||||
decoder_ctx->draw_horiz_band = vdp_draw_horiz_band;
|
||||
decoder_ctx->get_format = vdp_get_format;
|
||||
decoder_ctx->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
|
||||
|
||||
cpu_frame = av_frame_alloc();
|
||||
if (cpu_frame == NULL) {
|
||||
@@ -127,35 +137,19 @@ int vdpau_init(AVCodecContext* decoder_ctx, int width, int height) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = vdp_decoder_create(vdp_device, VDP_DECODER_PROFILE_H264_HIGH, width, height, 16, &vdp_decoder);
|
||||
VdpStatus status = vdp_decoder_create(vdp_device, VDP_DECODER_PROFILE_H264_HIGH, width, height, 16, &vdp_decoder);
|
||||
if (status != VDP_STATUS_OK) {
|
||||
printf("Can't create VDPAU decoder\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
VdpVideoMixerFeature features[] = {
|
||||
VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL,
|
||||
VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL,
|
||||
};
|
||||
VdpVideoMixerParameter params[] = {
|
||||
VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH,
|
||||
VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT,
|
||||
VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE,
|
||||
VDP_VIDEO_MIXER_PARAMETER_LAYERS
|
||||
};
|
||||
VdpChromaType chroma = VDP_CHROMA_TYPE_420;
|
||||
int numLayers = 0;
|
||||
void const* paramValues[] = { &width, &height, &chroma, &numLayers };
|
||||
|
||||
status = vdp_video_mixer_create(vdp_device, 0, features, 4, params, paramValues, &vdp_mixer);
|
||||
if (status != VDP_STATUS_OK) {
|
||||
printf("Can't create VDPAU mixer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return vdp_device;
|
||||
}
|
||||
|
||||
void vdpau_destroy() {
|
||||
vdp_device_destroy(vdp_device);
|
||||
}
|
||||
|
||||
AVFrame* vdpau_get_frame(AVFrame* dec_frame) {
|
||||
struct vdpau_render_state *render_state = (struct vdpau_render_state *)dec_frame->data[0];
|
||||
void *dest[3] = {
|
||||
@@ -169,6 +163,35 @@ AVFrame* vdpau_get_frame(AVFrame* dec_frame) {
|
||||
cpu_frame->linesize[1]
|
||||
};
|
||||
|
||||
VdpStatus status = vdp_video_surface_get_bits_y_cb_cr(render_state->surface, VDP_YCBCR_FORMAT_YV12, dest, pitches);
|
||||
vdp_video_surface_get_bits_y_cb_cr(render_state->surface, VDP_YCBCR_FORMAT_YV12, dest, pitches);
|
||||
return cpu_frame;
|
||||
}
|
||||
|
||||
int vdpau_init_presentation(Drawable win, int width, int height, int display_width, int display_height) {
|
||||
VdpVideoMixerParameter params[] = {
|
||||
VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH,
|
||||
VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT
|
||||
};
|
||||
void const* paramValues[] = { &width, &height };
|
||||
|
||||
if (vdp_video_mixer_create(vdp_device, 0, NULL, 2, params, paramValues, &vdp_mixer) != VDP_STATUS_OK)
|
||||
return -1;
|
||||
|
||||
if (vdp_output_surface_create(vdp_device, VDP_RGBA_FORMAT_B8G8R8A8, display_width, display_height, &vdp_output) != VDP_STATUS_OK)
|
||||
return -1;
|
||||
|
||||
if(vdp_presentation_queue_target_create_x11(vdp_device, win, &vdp_queue_target) != VDP_STATUS_OK)
|
||||
return -1;
|
||||
|
||||
if(vdp_presentation_queue_create(vdp_device, vdp_queue_target, &vdp_queue) != VDP_STATUS_OK)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vdpau_queue(AVFrame* dec_frame) {
|
||||
struct vdpau_render_state *render_state = (struct vdpau_render_state *)dec_frame->data[0];
|
||||
vdp_video_mixer_render(vdp_mixer, VDP_INVALID_HANDLE, 0, VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME, 0, (VdpVideoSurface*)VDP_INVALID_HANDLE, render_state->surface, 0,(VdpVideoSurface*)VDP_INVALID_HANDLE, NULL, vdp_output, NULL, NULL, 0, NULL);
|
||||
|
||||
vdp_presentation_queue_display(vdp_queue, vdp_output, 0, 0, 0);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,12 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
|
||||
int vdpau_init_lib(Display* display);
|
||||
int vdpau_init(AVCodecContext* decoder_ctx, int width, int height);
|
||||
void vdpau_destroy();
|
||||
AVFrame* vdpau_get_frame(AVFrame* dec_frame);
|
||||
int vdpau_init_presentation(Drawable win, int width, int height, int display_width, int display_height);
|
||||
void vdpau_queue(AVFrame* dec_frame);
|
||||
|
||||
@@ -17,9 +17,10 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../loop.h"
|
||||
#include "imx_vpu.h"
|
||||
|
||||
#include "../loop.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
@@ -114,10 +115,10 @@ static int frame_handle(int pipefd) {
|
||||
return LOOP_OK;
|
||||
}
|
||||
|
||||
static void decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
|
||||
int decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
|
||||
if (videoFormat != VIDEO_FORMAT_H264) {
|
||||
fprintf(stderr, "Video format not supported\n");
|
||||
exit(1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct mxcfb_gbl_alpha alpha;
|
||||
@@ -126,14 +127,14 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r
|
||||
|
||||
if (fd_fb < 0){
|
||||
fprintf(stderr, "Can't access framebuffer\n");
|
||||
exit(EXIT_FAILURE);
|
||||
return -2;
|
||||
}
|
||||
|
||||
alpha.alpha = 0;
|
||||
alpha.enable = 1;
|
||||
if (ioctl(fd_fb, MXCFB_SET_GBL_ALPHA, &alpha) < 0){
|
||||
fprintf(stderr, "Can't set framebuffer output\n");
|
||||
exit(EXIT_FAILURE);
|
||||
return -2;
|
||||
}
|
||||
|
||||
close(fd_fb);
|
||||
@@ -147,7 +148,7 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r
|
||||
fd = open(v4l_device, O_RDWR, 0);
|
||||
if (fd < 0){
|
||||
fprintf(stderr, "Can't access video output\n");
|
||||
exit(EXIT_FAILURE);
|
||||
return -2;
|
||||
}
|
||||
|
||||
struct v4l2_rect icrop = {0};
|
||||
@@ -166,7 +167,7 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r
|
||||
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
|
||||
if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) {
|
||||
fprintf(stderr, "Can't set source video format\n");
|
||||
exit(EXIT_FAILURE);
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (ioctl(fd, VIDIOC_G_FMT, &fmt) < 0) {
|
||||
@@ -183,12 +184,12 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r
|
||||
|
||||
if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
|
||||
fprintf(stderr, "Can't get video buffers\n");
|
||||
exit(EXIT_FAILURE);
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (reqbuf.count < regfbcount) {
|
||||
fprintf(stderr, "Not enough video buffers\n");
|
||||
exit(EXIT_FAILURE);
|
||||
return -2;
|
||||
}
|
||||
|
||||
for (int i = 0; i < regfbcount; i++) {
|
||||
@@ -198,7 +199,7 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r
|
||||
buf = calloc(1, sizeof(struct vpu_buf));
|
||||
if (buf == NULL) {
|
||||
fprintf(stderr, "Not enough memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
return -2;
|
||||
}
|
||||
|
||||
buffers[i] = buf;
|
||||
@@ -209,7 +210,7 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r
|
||||
|
||||
if (ioctl(fd, VIDIOC_QUERYBUF, &buffer) < 0) {
|
||||
fprintf(stderr, "Can't get video buffer\n");
|
||||
exit(EXIT_FAILURE);
|
||||
return -2;
|
||||
}
|
||||
buf->start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buffer.m.offset);
|
||||
|
||||
@@ -220,7 +221,7 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r
|
||||
*/
|
||||
if (ioctl(fd, VIDIOC_QUERYBUF, &buffer) < 0) {
|
||||
fprintf(stderr, "Can't set source video format\n");
|
||||
exit(EXIT_FAILURE);
|
||||
return -2;
|
||||
}
|
||||
|
||||
buf->offset = buffer.m.offset;
|
||||
@@ -228,7 +229,7 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r
|
||||
|
||||
if (buf->start == MAP_FAILED) {
|
||||
fprintf(stderr, "Failed to map video buffer\n");
|
||||
exit(EXIT_FAILURE);
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,13 +237,15 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r
|
||||
|
||||
if (pipe(pipefd) == -1 || pipe(clearpipefd) == -1) {
|
||||
fprintf(stderr, "Can't create communication channel between threads\n");
|
||||
exit(EXIT_FAILURE);
|
||||
return -2;
|
||||
}
|
||||
|
||||
loop_add_fd(pipefd[0], &frame_handle, POLLIN);
|
||||
|
||||
fcntl(clearpipefd[0], F_SETFL, O_NONBLOCK);
|
||||
fcntl(pipefd[0], F_SETFL, O_NONBLOCK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int decoder_renderer_submit_decode_unit(PDECODE_UNIT decodeUnit) {
|
||||
|
||||
@@ -28,10 +28,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
// Video decode on Raspberry Pi using OpenMAX IL though the ilcient helper library
|
||||
// Based upon video decode example from the Raspberry Pi firmware
|
||||
|
||||
#include "sps.h"
|
||||
|
||||
#include <Limelight.h>
|
||||
|
||||
#include <sps.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -53,10 +53,10 @@ static unsigned char *dest;
|
||||
static int port_settings_changed;
|
||||
static int first_packet;
|
||||
|
||||
static void decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
|
||||
int decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
|
||||
if (videoFormat != VIDEO_FORMAT_H264) {
|
||||
fprintf(stderr, "Video format not supported\n");
|
||||
exit(1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bcm_host_init();
|
||||
@@ -71,18 +71,18 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r
|
||||
|
||||
if((client = ilclient_init()) == NULL) {
|
||||
fprintf(stderr, "Can't initialize video\n");
|
||||
exit(EXIT_FAILURE);
|
||||
return -2;
|
||||
}
|
||||
|
||||
if(OMX_Init() != OMX_ErrorNone) {
|
||||
fprintf(stderr, "Can't initialize OMX\n");
|
||||
exit(EXIT_FAILURE);
|
||||
return -2;
|
||||
}
|
||||
|
||||
// create video_decode
|
||||
if(ilclient_create_component(client, &video_decode, "video_decode", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS) != 0){
|
||||
fprintf(stderr, "Can't create video decode\n");
|
||||
exit(EXIT_FAILURE);
|
||||
return -2;
|
||||
}
|
||||
|
||||
list[0] = video_decode;
|
||||
@@ -90,7 +90,7 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r
|
||||
// create video_render
|
||||
if(ilclient_create_component(client, &video_render, "video_render", ILCLIENT_DISABLE_ALL_PORTS) != 0){
|
||||
fprintf(stderr, "Can't create video renderer\n");
|
||||
exit(EXIT_FAILURE);
|
||||
return -2;
|
||||
}
|
||||
|
||||
list[1] = video_render;
|
||||
@@ -117,6 +117,31 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r
|
||||
if(OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamVideoPortFormat, &format) != OMX_ErrorNone ||
|
||||
OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamBrcmDataUnit, &unit) != OMX_ErrorNone) {
|
||||
fprintf(stderr, "Failed to set video parameters\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
OMX_CONFIG_LATENCYTARGETTYPE latencyTarget;
|
||||
memset(&latencyTarget, 0, sizeof(OMX_CONFIG_LATENCYTARGETTYPE));
|
||||
latencyTarget.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE);
|
||||
latencyTarget.nVersion.nVersion = OMX_VERSION;
|
||||
latencyTarget.nPortIndex = 90;
|
||||
latencyTarget.bEnabled = OMX_TRUE;
|
||||
latencyTarget.nFilter = 2;
|
||||
latencyTarget.nTarget = 4000;
|
||||
latencyTarget.nShift = 3;
|
||||
latencyTarget.nSpeedFactor = -135;
|
||||
latencyTarget.nInterFactor = 500;
|
||||
latencyTarget.nAdjCap = 20;
|
||||
|
||||
OMX_CONFIG_DISPLAYREGIONTYPE displayRegion;
|
||||
displayRegion.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE);
|
||||
displayRegion.nVersion.nVersion = OMX_VERSION;
|
||||
displayRegion.nPortIndex = 90;
|
||||
displayRegion.fullscreen = OMX_TRUE;
|
||||
displayRegion.mode = OMX_DISPLAY_SET_FULLSCREEN;
|
||||
|
||||
if(OMX_SetParameter(ILC_GET_HANDLE(video_render), OMX_IndexConfigLatencyTarget, &latencyTarget) != OMX_ErrorNone) {
|
||||
fprintf(stderr, "Failed to set video render parameters\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@@ -128,7 +153,7 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r
|
||||
port.nPortIndex = 130;
|
||||
if(OMX_GetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamPortDefinition, &port) != OMX_ErrorNone) {
|
||||
fprintf(stderr, "Failed to get decoder port definition\n");
|
||||
exit(EXIT_FAILURE);
|
||||
return -2;
|
||||
}
|
||||
|
||||
// Increase the buffer size to fit the largest possible frame
|
||||
@@ -143,8 +168,10 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r
|
||||
ilclient_change_component_state(video_decode, OMX_StateExecuting);
|
||||
} else {
|
||||
fprintf(stderr, "Can't setup video\n");
|
||||
exit(EXIT_FAILURE);
|
||||
return -2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void decoder_renderer_cleanup() {
|
||||
|
||||
@@ -17,36 +17,37 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../video.h"
|
||||
#include "../sdl.h"
|
||||
#include "video.h"
|
||||
#include "ffmpeg.h"
|
||||
|
||||
#include <Limelight.h>
|
||||
#include "../sdl.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_thread.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define DECODER_BUFFER_SIZE 92*1024
|
||||
|
||||
static char* ffmpeg_buffer;
|
||||
|
||||
static void sdl_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
|
||||
static int sdl_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
|
||||
int avc_flags = SLICE_THREADING;
|
||||
if (drFlags & FORCE_HARDWARE_ACCELERATION)
|
||||
avc_flags |= HARDWARE_ACCELERATION;
|
||||
|
||||
if (ffmpeg_init(videoFormat, width, height, avc_flags, SDL_BUFFER_FRAMES, 2) < 0) {
|
||||
if (ffmpeg_init(videoFormat, width, height, avc_flags, SDL_BUFFER_FRAMES, sysconf(_SC_NPROCESSORS_ONLN)) < 0) {
|
||||
fprintf(stderr, "Couldn't initialize video decoding\n");
|
||||
exit(1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ffmpeg_buffer = malloc(DECODER_BUFFER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
if (ffmpeg_buffer == NULL) {
|
||||
fprintf(stderr, "Not enough memory\n");
|
||||
exit(1);
|
||||
ffmpeg_destroy();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sdl_cleanup() {
|
||||
@@ -65,7 +66,7 @@ static int sdl_submit_decode_unit(PDECODE_UNIT decodeUnit) {
|
||||
ffmpeg_decode(ffmpeg_buffer, length);
|
||||
|
||||
if (SDL_LockMutex(mutex) == 0) {
|
||||
AVFrame* frame = ffmpeg_get_frame();
|
||||
AVFrame* frame = ffmpeg_get_frame(false);
|
||||
if (frame != NULL) {
|
||||
sdlNextFrame++;
|
||||
|
||||
@@ -92,5 +93,5 @@ DECODER_RENDERER_CALLBACKS decoder_callbacks_sdl = {
|
||||
.setup = sdl_setup,
|
||||
.cleanup = sdl_cleanup,
|
||||
.submitDecodeUnit = sdl_submit_decode_unit,
|
||||
.capabilities = CAPABILITY_SLICES_PER_FRAME(2) | CAPABILITY_REFERENCE_FRAME_INVALIDATION | CAPABILITY_DIRECT_SUBMIT,
|
||||
.capabilities = CAPABILITY_SLICES_PER_FRAME(4) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC | CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC | CAPABILITY_DIRECT_SUBMIT,
|
||||
};
|
||||
|
||||
36
src/video/video.h
Normal file
36
src/video/video.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Moonlight is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Limelight.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#define DISPLAY_FULLSCREEN 1
|
||||
#define ENABLE_HARDWARE_ACCELERATION 2
|
||||
|
||||
#ifdef HAVE_X11
|
||||
int x11_init(bool vdpau);
|
||||
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_x11;
|
||||
#ifdef HAVE_VDPAU
|
||||
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_x11_vdpau;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef HAVE_SDL
|
||||
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_sdl;
|
||||
#endif
|
||||
188
src/video/x11.c
Normal file
188
src/video/x11.c
Normal file
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Moonlight is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "video.h"
|
||||
#include "egl.h"
|
||||
#include "ffmpeg.h"
|
||||
#ifdef HAVE_VDPAU
|
||||
#include "ffmpeg_vdpau.h"
|
||||
#endif
|
||||
|
||||
#include "../input/x11.h"
|
||||
#include "../loop.h"
|
||||
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xutil.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
|
||||
#define DECODER_BUFFER_SIZE 92*1024
|
||||
|
||||
static char* ffmpeg_buffer = NULL;
|
||||
|
||||
static Display *display = NULL;
|
||||
|
||||
static int pipefd[2];
|
||||
|
||||
static int frame_handle(int pipefd) {
|
||||
AVFrame* frame = NULL;
|
||||
while (read(pipefd, &frame, sizeof(void*)) > 0);
|
||||
if (frame)
|
||||
egl_draw(frame->data);
|
||||
|
||||
return LOOP_OK;
|
||||
}
|
||||
|
||||
int x11_init(bool vdpau) {
|
||||
XInitThreads();
|
||||
display = XOpenDisplay(NULL);
|
||||
if (!display)
|
||||
return -1;
|
||||
|
||||
#ifdef HAVE_VDPAU
|
||||
if (vdpau && vdpau_init_lib(display) != 0)
|
||||
return -2;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int x11_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
|
||||
ffmpeg_buffer = malloc(DECODER_BUFFER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
if (ffmpeg_buffer == NULL) {
|
||||
fprintf(stderr, "Not enough memory\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!display) {
|
||||
fprintf(stderr, "Error: failed to open X display.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int display_width;
|
||||
int display_height;
|
||||
if (drFlags & DISPLAY_FULLSCREEN) {
|
||||
Screen* screen = DefaultScreenOfDisplay(display);
|
||||
display_width = WidthOfScreen(screen);
|
||||
display_height = HeightOfScreen(screen);
|
||||
} else {
|
||||
display_width = width;
|
||||
display_height = height;
|
||||
}
|
||||
|
||||
Window root = DefaultRootWindow(display);
|
||||
XSetWindowAttributes winattr = { .event_mask = PointerMotionMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask };
|
||||
Window window = XCreateWindow(display, root, 0, 0, display_width, display_height, 0, CopyFromParent, InputOutput, CopyFromParent, CWEventMask, &winattr);
|
||||
XMapWindow(display, window);
|
||||
XStoreName(display, window, "Moonlight");
|
||||
|
||||
if (drFlags & DISPLAY_FULLSCREEN) {
|
||||
Atom wm_state = XInternAtom(display, "_NET_WM_STATE", False);
|
||||
Atom fullscreen = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", False);
|
||||
|
||||
XEvent xev = {0};
|
||||
xev.type = ClientMessage;
|
||||
xev.xclient.window = window;
|
||||
xev.xclient.message_type = wm_state;
|
||||
xev.xclient.format = 32;
|
||||
xev.xclient.data.l[0] = 1;
|
||||
xev.xclient.data.l[1] = fullscreen;
|
||||
xev.xclient.data.l[2] = 0;
|
||||
|
||||
XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
|
||||
}
|
||||
XFlush(display);
|
||||
|
||||
int avc_flags = SLICE_THREADING;
|
||||
#ifdef HAVE_VDPAU
|
||||
if (drFlags & ENABLE_HARDWARE_ACCELERATION)
|
||||
avc_flags |= HARDWARE_ACCELERATION;
|
||||
#endif
|
||||
|
||||
if (ffmpeg_init(videoFormat, width, height, avc_flags, 2, 2) < 0) {
|
||||
fprintf(stderr, "Couldn't initialize video decoding\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ffmpeg_decoder == SOFTWARE) {
|
||||
egl_init(display, window, width, height);
|
||||
if (pipe(pipefd) == -1) {
|
||||
fprintf(stderr, "Can't create communication channel between threads\n");
|
||||
return -2;
|
||||
}
|
||||
loop_add_fd(pipefd[0], &frame_handle, POLLIN);
|
||||
fcntl(pipefd[0], F_SETFL, O_NONBLOCK);
|
||||
}
|
||||
#ifdef HAVE_VDPAU
|
||||
else if (ffmpeg_decoder == VDPAU)
|
||||
vdpau_init_presentation(window, width, height, display_width, display_height);
|
||||
#endif
|
||||
|
||||
x11_input_init(display, window);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int x11_setup_vdpau(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
|
||||
return x11_setup(videoFormat, width, height, redrawRate, context, drFlags | ENABLE_HARDWARE_ACCELERATION);
|
||||
}
|
||||
|
||||
void x11_cleanup() {
|
||||
ffmpeg_destroy();
|
||||
egl_destroy();
|
||||
}
|
||||
|
||||
int x11_submit_decode_unit(PDECODE_UNIT decodeUnit) {
|
||||
if (decodeUnit->fullLength < DECODER_BUFFER_SIZE) {
|
||||
PLENTRY entry = decodeUnit->bufferList;
|
||||
int length = 0;
|
||||
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(true);
|
||||
if (frame != NULL) {
|
||||
if (ffmpeg_decoder == SOFTWARE)
|
||||
write(pipefd[1], &frame, sizeof(void*));
|
||||
else if (ffmpeg_decoder == VDPAU)
|
||||
vdpau_queue(frame);
|
||||
}
|
||||
}
|
||||
|
||||
return DR_OK;
|
||||
}
|
||||
|
||||
DECODER_RENDERER_CALLBACKS decoder_callbacks_x11 = {
|
||||
.setup = x11_setup,
|
||||
.cleanup = x11_cleanup,
|
||||
.submitDecodeUnit = x11_submit_decode_unit,
|
||||
.capabilities = CAPABILITY_SLICES_PER_FRAME(4) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC | CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC | CAPABILITY_DIRECT_SUBMIT,
|
||||
};
|
||||
|
||||
DECODER_RENDERER_CALLBACKS decoder_callbacks_x11_vdpau = {
|
||||
.setup = x11_setup_vdpau,
|
||||
.cleanup = x11_cleanup,
|
||||
.submitDecodeUnit = x11_submit_decode_unit,
|
||||
.capabilities = CAPABILITY_DIRECT_SUBMIT,
|
||||
};
|
||||
2
third_party/moonlight-common-c
vendored
2
third_party/moonlight-common-c
vendored
Submodule third_party/moonlight-common-c updated: dcdfcd55e5...a1bdb36766
Reference in New Issue
Block a user