From c2f09326702a723c3365c49a25d30047beecb154 Mon Sep 17 00:00:00 2001 From: Johannes Baiter Date: Tue, 1 Nov 2022 10:37:20 +0100 Subject: [PATCH] gui: Support for DualSense haptics and trigger effects Haptics with PulseAudio does not seem to be working properly, so using Pipewire as a backend is recommended (and picked by default, if available via an SDL hint). --- .appveyor.yml | 2 +- gui/CMakeLists.txt | 2 +- gui/include/controllermanager.h | 35 ++++++ gui/include/settings.h | 3 + gui/include/settingsdialog.h | 2 + gui/include/streamsession.h | 7 ++ gui/src/controllermanager.cpp | 52 ++++++++- gui/src/settingsdialog.cpp | 10 ++ gui/src/streamsession.cpp | 167 ++++++++++++++++++++++++++++- scripts/appveyor-win.sh | 10 +- scripts/build-sdl2.sh | 4 +- scripts/kitware-archive-latest.asc | 120 ++++++++++----------- 12 files changed, 343 insertions(+), 71 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 7dc4799..db3c3a0 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -37,7 +37,7 @@ for: install: - git submodule update --init --recursive - sudo pip3 install protobuf - - brew install qt@5 opus openssl@1.1 nasm sdl2 protobuf + - HOMEBREW_NO_AUTO_UPDATE=1 brew install qt@5 opus openssl@1.1 nasm sdl2 protobuf - scripts/build-ffmpeg.sh build_script: diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 5e51b55..443150f 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -67,12 +67,12 @@ if(CHIAKI_ENABLE_CLI) endif() target_link_libraries(chiaki Qt5::Core Qt5::Widgets Qt5::Gui Qt5::Concurrent Qt5::Multimedia Qt5::OpenGL Qt5::Svg) +target_link_libraries(chiaki SDL2::SDL2) if(APPLE) target_link_libraries(chiaki Qt5::MacExtras) target_compile_definitions(chiaki PRIVATE CHIAKI_GUI_ENABLE_QT_MACEXTRAS) endif() if(CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER) - target_link_libraries(chiaki SDL2::SDL2) target_compile_definitions(chiaki PRIVATE CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER) endif() if(CHIAKI_ENABLE_SETSU) diff --git a/gui/include/controllermanager.h b/gui/include/controllermanager.h index 9e89fcc..eee0120 100644 --- a/gui/include/controllermanager.h +++ b/gui/include/controllermanager.h @@ -77,6 +77,7 @@ class Controller : public QObject int id; ChiakiOrientationTracker orientation_tracker; ChiakiControllerState state; + bool is_dualsense; #ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER QMap, uint8_t> touch_ids; @@ -91,9 +92,43 @@ class Controller : public QObject QString GetName(); ChiakiControllerState GetState(); void SetRumble(uint8_t left, uint8_t right); + void SetTriggerEffects(uint8_t type_left, const uint8_t *data_left, uint8_t type_right, const uint8_t *data_right); + bool IsDualSense(); signals: void StateChanged(); }; +/* PS5 trigger effect documentation: + https://controllers.fandom.com/wiki/Sony_DualSense#FFB_Trigger_Modes + + Taken from SDL2, licensed under the zlib license, + Copyright (C) 1997-2022 Sam Lantinga + https://github.com/libsdl-org/SDL/blob/release-2.24.1/test/testgamecontroller.c#L263-L289 +*/ +typedef struct +{ + Uint8 ucEnableBits1; /* 0 */ + Uint8 ucEnableBits2; /* 1 */ + Uint8 ucRumbleRight; /* 2 */ + Uint8 ucRumbleLeft; /* 3 */ + Uint8 ucHeadphoneVolume; /* 4 */ + Uint8 ucSpeakerVolume; /* 5 */ + Uint8 ucMicrophoneVolume; /* 6 */ + Uint8 ucAudioEnableBits; /* 7 */ + Uint8 ucMicLightMode; /* 8 */ + Uint8 ucAudioMuteBits; /* 9 */ + Uint8 rgucRightTriggerEffect[11]; /* 10 */ + Uint8 rgucLeftTriggerEffect[11]; /* 21 */ + Uint8 rgucUnknown1[6]; /* 32 */ + Uint8 ucLedFlags; /* 38 */ + Uint8 rgucUnknown2[2]; /* 39 */ + Uint8 ucLedAnim; /* 41 */ + Uint8 ucLedBrightness; /* 42 */ + Uint8 ucPadLights; /* 43 */ + Uint8 ucLedRed; /* 44 */ + Uint8 ucLedGreen; /* 45 */ + Uint8 ucLedBlue; /* 46 */ +} DS5EffectsState_t; + #endif // CHIAKI_CONTROLLERMANAGER_H diff --git a/gui/include/settings.h b/gui/include/settings.h index c2320b8..00f38ab 100644 --- a/gui/include/settings.h +++ b/gui/include/settings.h @@ -63,6 +63,9 @@ class Settings : public QObject void SetLogVerbose(bool enabled) { settings.setValue("settings/log_verbose", enabled); } uint32_t GetLogLevelMask(); + bool GetDualSenseEnabled() const { return settings.value("settings/dualsense_enabled", false).toBool(); } + void SetDualSenseEnabled(bool enabled) { settings.setValue("settings/dualsense_enabled", enabled); } + ChiakiVideoResolutionPreset GetResolution() const; void SetResolution(ChiakiVideoResolutionPreset resolution); diff --git a/gui/include/settingsdialog.h b/gui/include/settingsdialog.h index d564bef..00af648 100644 --- a/gui/include/settingsdialog.h +++ b/gui/include/settingsdialog.h @@ -20,6 +20,7 @@ class SettingsDialog : public QDialog QCheckBox *log_verbose_check_box; QComboBox *disconnect_action_combo_box; + QCheckBox *dualsense_check_box; QComboBox *resolution_combo_box; QComboBox *fps_combo_box; @@ -37,6 +38,7 @@ class SettingsDialog : public QDialog private slots: void LogVerboseChanged(); + void DualSenseChanged(); void DisconnectActionSelected(); void ResolutionSelected(); diff --git a/gui/include/streamsession.h b/gui/include/streamsession.h index e139f45..4b0ba01 100644 --- a/gui/include/streamsession.h +++ b/gui/include/streamsession.h @@ -56,6 +56,7 @@ struct StreamSessionConnectInfo bool fullscreen; TransformMode transform_mode; bool enable_keyboard; + bool enable_dualsense; StreamSessionConnectInfo( Settings *settings, @@ -101,17 +102,23 @@ class StreamSession : public QObject unsigned int audio_buffer_size; QAudioOutput *audio_output; QIODevice *audio_io; + SDL_AudioDeviceID haptics_output; + uint8_t *haptics_resampler_buf; QMap key_map; void PushAudioFrame(int16_t *buf, size_t samples_count); + void PushHapticsFrame(uint8_t *buf, size_t buf_size); #if CHIAKI_GUI_ENABLE_SETSU void HandleSetsuEvent(SetsuEvent *event); #endif private slots: void InitAudio(unsigned int channels, unsigned int rate); + void InitHaptics(); void Event(ChiakiEvent *event); + void DisconnectHaptics(); + void ConnectHaptics(); public: explicit StreamSession(const StreamSessionConnectInfo &connect_info, QObject *parent = nullptr); diff --git a/gui/src/controllermanager.cpp b/gui/src/controllermanager.cpp index b1f38e7..8cc501f 100644 --- a/gui/src/controllermanager.cpp +++ b/gui/src/controllermanager.cpp @@ -68,6 +68,12 @@ static QSet chiaki_motion_controller_guids({ "030000008f0e00001431000000000000", }); +static QSet> chiaki_dualsense_controller_ids({ + // in format (vendor id, product id) + QPair(0x054c, 0x0ce6), // DualSense controller + QPair(0x054c, 0x0df2), // DualSense Edge controller +}); + static ControllerManager *instance = nullptr; #define UPDATE_INTERVAL_MS 4 @@ -84,6 +90,15 @@ ControllerManager::ControllerManager(QObject *parent) { #ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER SDL_SetMainReady(); +#ifdef SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1"); +#endif +#ifdef SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); +#endif +#ifdef SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS + SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); +#endif if(SDL_Init(SDL_INIT_GAMECONTROLLER) < 0) { const char *err = SDL_GetError(); @@ -225,7 +240,8 @@ void ControllerManager::ControllerClosed(Controller *controller) open_controllers.remove(controller->GetDeviceID()); } -Controller::Controller(int device_id, ControllerManager *manager) : QObject(manager) +Controller::Controller(int device_id, ControllerManager *manager) + : QObject(manager), is_dualsense(false) { this->id = device_id; this->manager = manager; @@ -244,8 +260,10 @@ Controller::Controller(int device_id, ControllerManager *manager) : QObject(mana SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE); if(SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO)) SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE); - break; #endif + auto controller_id = QPair(SDL_GameControllerGetVendor(controller), SDL_GameControllerGetProduct(controller)); + is_dualsense = chiaki_dualsense_controller_ids.contains(controller_id); + break; } } #endif @@ -255,7 +273,12 @@ Controller::~Controller() { #ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER if(controller) + { + // Clear trigger effects, SDL doesn't do it automatically + const uint8_t clear_effect[10] = { 0 }; + this->SetTriggerEffects(0x05, clear_effect, 0x05, clear_effect); SDL_GameControllerClose(controller); + } #endif manager->ControllerClosed(this); } @@ -487,3 +510,28 @@ void Controller::SetRumble(uint8_t left, uint8_t right) SDL_GameControllerRumble(controller, (uint16_t)left << 8, (uint16_t)right << 8, 5000); #endif } + +void Controller::SetTriggerEffects(uint8_t type_left, const uint8_t *data_left, uint8_t type_right, const uint8_t *data_right) +{ +#if defined(CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER) && SDL_VERSION_ATLEAST(2, 0, 16) + if(!is_dualsense || !controller) + return; + DS5EffectsState_t state; + SDL_zero(state); + state.ucEnableBits1 |= (0x04 /* left trigger */ | 0x08 /* right trigger */); + state.rgucLeftTriggerEffect[0] = type_left; + SDL_memcpy(state.rgucLeftTriggerEffect + 1, data_left, 10); + state.rgucRightTriggerEffect[0] = type_right; + SDL_memcpy(state.rgucRightTriggerEffect + 1, data_right, 10); + SDL_GameControllerSendEffect(controller, &state, sizeof(state)); +#endif +} + +bool Controller::IsDualSense() +{ +#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER + if(!controller) + return false; + return is_dualsense; +#endif +} diff --git a/gui/src/settingsdialog.cpp b/gui/src/settingsdialog.cpp index 3f8856f..1770932 100644 --- a/gui/src/settingsdialog.cpp +++ b/gui/src/settingsdialog.cpp @@ -69,6 +69,11 @@ SettingsDialog::SettingsDialog(Settings *settings, QWidget *parent) : QDialog(pa log_verbose_check_box->setChecked(settings->GetLogVerbose()); connect(log_verbose_check_box, &QCheckBox::stateChanged, this, &SettingsDialog::LogVerboseChanged); + dualsense_check_box = new QCheckBox(this); + general_layout->addRow(tr("Extended DualSense Support:\nEnable haptics and adaptive triggers\nfor attached DualSense controllers.\nThis is currently experimental."), dualsense_check_box); + dualsense_check_box->setChecked(settings->GetDualSenseEnabled()); + connect(dualsense_check_box, &QCheckBox::stateChanged, this, &SettingsDialog::DualSenseChanged); + auto log_directory_label = new QLineEdit(GetLogBaseDir(), this); log_directory_label->setReadOnly(true); general_layout->addRow(tr("Log Directory:"), log_directory_label); @@ -322,6 +327,11 @@ void SettingsDialog::LogVerboseChanged() settings->SetLogVerbose(log_verbose_check_box->isChecked()); } +void SettingsDialog::DualSenseChanged() +{ + settings->SetDualSenseEnabled(dualsense_check_box->isChecked()); +} + void SettingsDialog::FPSSelected() { settings->SetFPS((ChiakiVideoFPSPreset)fps_combo_box->currentData().toInt()); diff --git a/gui/src/streamsession.cpp b/gui/src/streamsession.cpp index f5b7ace..42c9959 100644 --- a/gui/src/streamsession.cpp +++ b/gui/src/streamsession.cpp @@ -14,6 +14,12 @@ #define SETSU_UPDATE_INTERVAL_MS 4 +#ifdef Q_OS_LINUX +#define DUALSENSE_AUDIO_DEVICE_NEEDLE "DualSense" +#else +#define DUALSENSE_AUDIO_DEVICE_NEEDLE "Wireless Controller" +#endif + StreamSessionConnectInfo::StreamSessionConnectInfo( Settings *settings, ChiakiTarget target, @@ -39,10 +45,12 @@ StreamSessionConnectInfo::StreamSessionConnectInfo( this->fullscreen = fullscreen; this->transform_mode = transform_mode; this->enable_keyboard = false; // TODO: from settings + this->enable_dualsense = settings->GetDualSenseEnabled(); } static void AudioSettingsCb(uint32_t channels, uint32_t rate, void *user); static void AudioFrameCb(int16_t *buf, size_t samples_count, void *user); +static void HapticsFrameCb(uint8_t *buf, size_t buf_size, void *user); static void EventCb(ChiakiEvent *event, void *user); #if CHIAKI_GUI_ENABLE_SETSU static void SessionSetsuCb(SetsuEvent *event, void *user); @@ -57,7 +65,9 @@ StreamSession::StreamSession(const StreamSessionConnectInfo &connect_info, QObje pi_decoder(nullptr), #endif audio_output(nullptr), - audio_io(nullptr) + audio_io(nullptr), + haptics_output(0), + haptics_resampler_buf(nullptr) { connected = false; ChiakiErrorCode err; @@ -116,6 +126,7 @@ StreamSession::StreamSession(const StreamSessionConnectInfo &connect_info, QObje chiaki_connect_info.video_profile = connect_info.video_profile; chiaki_connect_info.video_profile_auto_downgrade = true; chiaki_connect_info.enable_keyboard = false; + chiaki_connect_info.enable_dualsense = connect_info.enable_dualsense; #if CHIAKI_LIB_ENABLE_PI_DECODER if(connect_info.decoder == Decoder::Pi && chiaki_connect_info.video_profile.codec != CHIAKI_CODEC_H264) @@ -144,6 +155,14 @@ StreamSession::StreamSession(const StreamSessionConnectInfo &connect_info, QObje chiaki_opus_decoder_get_sink(&opus_decoder, &audio_sink); chiaki_session_set_audio_sink(&session, &audio_sink); + if(connect_info.enable_dualsense) + { + ChiakiAudioSink haptics_sink; + haptics_sink.user = this; + haptics_sink.frame_cb = HapticsFrameCb; + chiaki_session_set_haptics_sink(&session, &haptics_sink); + } + #if CHIAKI_LIB_ENABLE_PI_DECODER if(pi_decoder) chiaki_session_set_video_sample_cb(&session, chiaki_pi_decoder_video_sample_cb, pi_decoder); @@ -181,6 +200,10 @@ StreamSession::StreamSession(const StreamSessionConnectInfo &connect_info, QObje #endif key_map = connect_info.key_map; + if(connect_info.enable_dualsense) + { + InitHaptics(); + } UpdateGamepads(); } @@ -208,6 +231,16 @@ StreamSession::~StreamSession() chiaki_ffmpeg_decoder_fini(ffmpeg_decoder); delete ffmpeg_decoder; } + if(haptics_output > 0) + { + SDL_CloseAudioDevice(haptics_output); + haptics_output = 0; + } + if(haptics_resampler_buf) + { + free(haptics_resampler_buf); + haptics_resampler_buf = nullptr; + } } void StreamSession::Start() @@ -312,6 +345,8 @@ void StreamSession::UpdateGamepads() { CHIAKI_LOGI(log.GetChiakiLog(), "Controller %d disconnected", controller->GetDeviceID()); controllers.remove(controller_id); + if(controller->IsDualSense()) + DisconnectHaptics(); delete controller; } } @@ -330,6 +365,11 @@ void StreamSession::UpdateGamepads() CHIAKI_LOGI(log.GetChiakiLog(), "Controller %d opened: \"%s\"", controller_id, controller->GetName().toLocal8Bit().constData()); connect(controller, &Controller::StateChanged, this, &StreamSession::SendFeedbackState); controllers[controller_id] = controller; + if(controller->IsDualSense()) + { + // Connect haptics audio device with a delay to give the sound system time to set up + QTimer::singleShot(1000, this, &StreamSession::ConnectHaptics); + } } } @@ -388,6 +428,82 @@ void StreamSession::InitAudio(unsigned int channels, unsigned int rate) channels, rate, audio_output->bufferSize()); } +void StreamSession::InitHaptics() +{ + haptics_output = 0; + haptics_resampler_buf = nullptr; +#ifdef Q_OS_LINUX + // Haptics work most reliably with Pipewire, so try to use that if available + SDL_SetHint("SDL_AUDIODRIVER", "pipewire"); +#endif + + if(SDL_Init(SDL_INIT_AUDIO) < 0) + { + CHIAKI_LOGE(log.GetChiakiLog(), "Could not initialize SDL Audio for haptics output: %s", SDL_GetError()); + return; + } + +#ifdef Q_OS_LINUX + if(!strstr(SDL_GetCurrentAudioDriver(), "pipewire")) + { + CHIAKI_LOGW( + log.GetChiakiLog(), + "Haptics output is not using Pipewire, this may not work reliably. (was: '%s')", + SDL_GetCurrentAudioDriver()); + } +#endif + + SDL_AudioCVT cvt; + SDL_BuildAudioCVT(&cvt, AUDIO_S16LSB, 4, 3000, AUDIO_S16LSB, 4, 48000); + cvt.len = 240; // 10 16bit stereo samples + haptics_resampler_buf = (uint8_t*) calloc(cvt.len * cvt.len_mult, sizeof(uint8_t)); +} + +void StreamSession::DisconnectHaptics() +{ + if(this->haptics_output > 0) + { + SDL_CloseAudioDevice(haptics_output); + this->haptics_output = 0; + } +} + +void StreamSession::ConnectHaptics() +{ + if(this->haptics_output > 0) + { + CHIAKI_LOGW(this->log.GetChiakiLog(), "Haptics already connected to an attached DualSense controller, ignoring additional controllers."); + return; + } + + SDL_AudioSpec want, have; + SDL_zero(want); + want.freq = 48000; + want.format = AUDIO_S16LSB; + want.channels = 4; + want.samples = 480; // 10ms buffer + want.callback = NULL; + + const char *device_name = nullptr; + for(int i=0; i < SDL_GetNumAudioDevices(0); i++) + { + device_name = SDL_GetAudioDeviceName(i, 0); + if(!device_name || !strstr(device_name, DUALSENSE_AUDIO_DEVICE_NEEDLE)) + continue; + haptics_output = SDL_OpenAudioDevice(device_name, 0, &want, &have, 0); + if(haptics_output == 0) + { + CHIAKI_LOGE(log.GetChiakiLog(), "Could not open SDL Audio Device %s for haptics output: %s", device_name, SDL_GetError()); + continue; + } + SDL_PauseAudioDevice(haptics_output, 0); + CHIAKI_LOGI(log.GetChiakiLog(), "Haptics Audio Device '%s' opened with %d channels @ %d Hz, buffer size %u (driver=%s)", device_name, have.channels, have.freq, have.size, SDL_GetCurrentAudioDriver()); + return; + } + CHIAKI_LOGW(log.GetChiakiLog(), "DualSense features were enabled and a DualSense is connected, but could not find the DualSense audio device!"); + return; +} + void StreamSession::PushAudioFrame(int16_t *buf, size_t samples_count) { if(!audio_io) @@ -395,6 +511,35 @@ void StreamSession::PushAudioFrame(int16_t *buf, size_t samples_count) audio_io->write((const char *)buf, static_cast(samples_count * 2 * 2)); } +void StreamSession::PushHapticsFrame(uint8_t *buf, size_t buf_size) +{ + if(haptics_output == 0) + return; + SDL_AudioCVT cvt; + // Haptics samples are coming in at 3KHZ, but the DualSense expects 48KHZ + SDL_BuildAudioCVT(&cvt, AUDIO_S16LSB, 4, 3000, AUDIO_S16LSB, 4, 48000); + cvt.len = buf_size * 2; + cvt.buf = haptics_resampler_buf; + // Remix to 4 channels + for (int i=0; i < buf_size; i+=4) + { + SDL_memset(haptics_resampler_buf + i * 2, 0, 4); + SDL_memcpy(haptics_resampler_buf + (i * 2) + 4, buf + i, 4); + } + // Resample to 48kHZ + if(SDL_ConvertAudio(&cvt) != 0) + { + CHIAKI_LOGE(log.GetChiakiLog(), "Failed to resample haptics audio: %s", SDL_GetError()); + return; + } + + if(SDL_QueueAudio(haptics_output, cvt.buf, cvt.len_cvt) < 0) + { + CHIAKI_LOGE(log.GetChiakiLog(), "Failed to submit haptics audio to device: %s", SDL_GetError()); + return; + } +} + void StreamSession::Event(ChiakiEvent *event) { switch(event->type) @@ -418,6 +563,19 @@ void StreamSession::Event(ChiakiEvent *event) }); break; } + case CHIAKI_EVENT_TRIGGER_EFFECTS: { + uint8_t type_left = event->trigger_effects.type_left; + uint8_t data_left[10]; + memcpy(data_left, event->trigger_effects.left, 10); + uint8_t data_right[10]; + memcpy(data_right, event->trigger_effects.right, 10); + uint8_t type_right = event->trigger_effects.type_right; + QMetaObject::invokeMethod(this, [this, type_left, data_left, type_right, data_right]() { + for(auto controller : controllers) + controller->SetTriggerEffects(type_left, data_left, type_right, data_right); + }); + break; + } default: break; } @@ -544,6 +702,7 @@ class StreamSessionPrivate } static void PushAudioFrame(StreamSession *session, int16_t *buf, size_t samples_count) { session->PushAudioFrame(buf, samples_count); } + static void PushHapticsFrame(StreamSession *session, uint8_t *buf, size_t buf_size) { session->PushHapticsFrame(buf, buf_size); } static void Event(StreamSession *session, ChiakiEvent *event) { session->Event(event); } #if CHIAKI_GUI_ENABLE_SETSU static void HandleSetsuEvent(StreamSession *session, SetsuEvent *event) { session->HandleSetsuEvent(event); } @@ -563,6 +722,12 @@ static void AudioFrameCb(int16_t *buf, size_t samples_count, void *user) StreamSessionPrivate::PushAudioFrame(session, buf, samples_count); } +static void HapticsFrameCb(uint8_t *buf, size_t buf_size, void *user) +{ + auto session = reinterpret_cast(user); + StreamSessionPrivate::PushHapticsFrame(session, buf, buf_size); +} + static void EventCb(ChiakiEvent *event, void *user) { auto session = reinterpret_cast(user); diff --git a/scripts/appveyor-win.sh b/scripts/appveyor-win.sh index a7e63a0..7bbc0f3 100755 --- a/scripts/appveyor-win.sh +++ b/scripts/appveyor-win.sh @@ -30,14 +30,15 @@ ninja ninja install cd ../.. -wget https://mirror.firedaemon.com/OpenSSL/openssl-1.1.1q.zip && 7z x openssl-1.1.1q.zip +wget https://download.firedaemon.com/FireDaemon-OpenSSL/openssl-1.1.1s.zip && 7z x openssl-1.1.*.zip -wget https://www.libsdl.org/release/SDL2-devel-2.0.10-VC.zip && 7z x SDL2-devel-2.0.10-VC.zip -export SDL_ROOT="$BUILD_ROOT/SDL2-2.0.10" +wget https://www.libsdl.org/release/SDL2-devel-2.26.2-VC.zip && 7z x SDL2-devel-2.26.2-VC.zip +export SDL_ROOT="$BUILD_ROOT/SDL2-2.26.2" export SDL_ROOT=${SDL_ROOT//[\\]//} echo "set(SDL2_INCLUDE_DIRS \"$SDL_ROOT/include\") set(SDL2_LIBRARIES \"$SDL_ROOT/lib/x64/SDL2.lib\") -set(SDL2_LIBDIR \"$SDL_ROOT/lib/x64\")" > "$SDL_ROOT/SDL2Config.cmake" +set(SDL2_LIBDIR \"$SDL_ROOT/lib/x64\") +include($SDL_ROOT/cmake/sdl2-config-version.cmake)" > "$SDL_ROOT/SDL2Config.cmake" mkdir protoc && cd protoc wget https://github.com/protocolbuffers/protobuf/releases/download/v3.9.1/protoc-3.9.1-win64.zip && 7z x protoc-3.9.1-win64.zip @@ -55,6 +56,7 @@ echo "-- Configure" mkdir build && cd build + cmake \ -G Ninja \ -DCMAKE_C_COMPILER=cl \ diff --git a/scripts/build-sdl2.sh b/scripts/build-sdl2.sh index 725bbf4..716b486 100755 --- a/scripts/build-sdl2.sh +++ b/scripts/build-sdl2.sh @@ -22,14 +22,14 @@ mkdir -p build && cd build || exit 1 cmake \ -DCMAKE_INSTALL_PREFIX="$ROOT/sdl2-prefix" \ -DSDL_ATOMIC=OFF \ - -DSDL_AUDIO=OFF \ + -DSDL_AUDIO=ON \ -DSDL_CPUINFO=OFF \ -DSDL_EVENTS=ON \ -DSDL_FILE=OFF \ -DSDL_FILESYSTEM=OFF \ -DSDL_HAPTIC=ON \ -DSDL_JOYSTICK=ON \ - -DSDL_LOADSO=OFF \ + -DSDL_LOADSO=ON \ -DSDL_RENDER=OFF \ -DSDL_SHARED=ON \ -DSDL_STATIC=OFF \ diff --git a/scripts/kitware-archive-latest.asc b/scripts/kitware-archive-latest.asc index 6b3a357..2c95d3e 100644 --- a/scripts/kitware-archive-latest.asc +++ b/scripts/kitware-archive-latest.asc @@ -1,64 +1,64 @@ -----BEGIN PGP PUBLIC KEY BLOCK----- -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 -=cUKW +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 +=8BHh -----END PGP PUBLIC KEY BLOCK-----