From 42bf4957e8f3e2a14b83276a6da36a496f11bf07 Mon Sep 17 00:00:00 2001 From: Robert Broglia Date: Fri, 19 Apr 2024 20:04:36 -0400 Subject: [PATCH] Misc tweaks and fixes * Imagine: Add support for NDK r27, which is now required * Imagine: Replace deprecated ALooper_pollAll() with ALooper_pollOnce() * Imagine: Replace ItemDelegate & ItemsDelegate menu item types with new ItemSourceDelegate * Imagine: Refactor Bluetooth classes * Imagine: Make PixelFormat::id public to allow passing as a non-type template parameter * Imagine: Remove unneeded ErrorCode type * Imagine: Use std::generic_category instead of system_category for POSIX file errors * Imagine: Check and catch exceptions in Android BluetoothAdapter startDiscovery() * Imagine: Improve RingBuffer's blocking API * EmuFramework: Refactor key event code into InputManager * EmuFramework: Move some video-related options into EmuVideoLayer * EmuFramework: Fix Bluetooth menu items incorrectly shown by default on Android 4.2+ * NES.emu: Fix incorrect paths for some internal palettes * Saturn.emu: Simplify VDP2 work queue code and flush commands when partially filled --- .github/workflows/build.yml | 6 +- EmuFramework/include/emuframework/Cheats.hh | 7 +- EmuFramework/include/emuframework/EmuApp.hh | 36 +- EmuFramework/include/emuframework/EmuInput.hh | 9 + .../include/emuframework/EmuOptions.hh | 17 + .../include/emuframework/EmuSystem.hh | 1 - EmuFramework/include/emuframework/EmuVideo.hh | 1 - .../include/emuframework/EmuVideoLayer.hh | 23 +- .../include/emuframework/InputManagerView.hh | 10 +- .../include/emuframework/MainMenuView.hh | 6 +- .../include/emuframework/OutSizeTracker.hh | 1 + .../include/emuframework/VideoImageEffect.hh | 1 - EmuFramework/include/emuframework/config.hh | 2 +- EmuFramework/src/ConfigFile.cc | 44 +-- EmuFramework/src/EmuApp.cc | 309 +----------------- EmuFramework/src/EmuInput.cc | 220 +++++++++++++ EmuFramework/src/EmuOptions.cc | 7 +- EmuFramework/src/EmuVideo.cc | 2 +- EmuFramework/src/EmuVideoLayer.cc | 41 ++- EmuFramework/src/gui/ButtonConfigView.cc | 25 +- EmuFramework/src/gui/Cheats.cc | 26 +- EmuFramework/src/gui/InputManagerView.cc | 89 ++--- EmuFramework/src/gui/MainMenuView.cc | 59 ++-- EmuFramework/src/gui/PlaceVideoView.cc | 4 +- EmuFramework/src/gui/RecentContentView.cc | 12 +- EmuFramework/src/gui/VideoOptionView.cc | 36 +- .../src/shared/mednafen-emuex/MDFNUtils.hh | 4 +- GBA.emu/src/main/Cheats.cc | 38 +-- GBA.emu/src/main/EmuCheatViews.hh | 1 + GBC.emu/src/main/Cheats.cc | 36 +- GBC.emu/src/main/EmuCheatViews.hh | 1 + MD.emu/src/main/Cheats.cc | 36 +- MD.emu/src/main/EmuCheatViews.hh | 1 + MSX.emu/src/main/EmuMenuViews.cc | 9 +- NEO.emu/src/main/EmuMenuViews.cc | 15 +- NES.emu/src/main/Cheats.cc | 72 ++-- NES.emu/src/main/EmuMenuViews.cc | 22 +- Saturn.emu/src/ss/vdp2_render.cpp | 17 +- Snes9x/src/main/Cheats.cc | 38 +-- Snes9x/src/main/EmuCheatViews.hh | 1 + imagine/doc/INSTALL-Android | 4 +- .../include/imagine/base/BaseApplication.hh | 4 +- imagine/include/imagine/base/Error.hh | 41 --- .../include/imagine/base/android/gralloc.h | 3 +- .../bluetooth/AndroidBluetoothAdapter.hh | 58 ++-- .../imagine/bluetooth/BluetoothAdapter.hh | 155 +++------ .../bluetooth/BluetoothInputDevScanner.hh | 21 -- .../imagine/bluetooth/BluetoothInputDevice.hh | 50 +++ .../bluetooth/BluezBluetoothAdapter.hh | 56 ++-- .../bluetooth/BtstackBluetoothAdapter.hh | 54 ++- .../include/imagine/bluetooth/IControlPad.hh | 12 +- .../imagine/bluetooth/PS3Controller.hh | 16 +- imagine/include/imagine/bluetooth/Wiimote.hh | 13 +- imagine/include/imagine/bluetooth/Zeemote.hh | 12 +- imagine/include/imagine/bluetooth/config.hh | 37 --- imagine/include/imagine/bluetooth/defs.hh | 83 +++++ imagine/include/imagine/bluetooth/sys.hh | 29 -- imagine/include/imagine/gfx/Renderer.hh | 3 +- imagine/include/imagine/gui/AlertView.hh | 28 +- imagine/include/imagine/gui/FSPicker.hh | 29 +- imagine/include/imagine/gui/MenuItem.hh | 36 +- imagine/include/imagine/gui/TableView.hh | 34 +- imagine/include/imagine/gui/viewDefs.hh | 49 +++ imagine/include/imagine/pixmap/PixelFormat.hh | 14 +- imagine/include/imagine/pixmap/Pixmap.hh | 4 +- .../imagine/util/container/RingBuffer.hh | 12 +- imagine/src/base/Base.mk | 1 - imagine/src/base/android/ALooperEventLoop.cc | 6 +- imagine/src/base/android/AndroidGLContext.cc | 4 +- imagine/src/base/android/Application.cc | 4 +- imagine/src/base/android/HardwareBuffer.cc | 3 +- imagine/src/base/android/compat.c | 59 +--- .../main/java/com/imagine/BaseActivity.java | 14 +- .../src/main/java/com/imagine/Bluetooth.java | 27 +- imagine/src/base/android/proguard/imagine.cfg | 2 +- imagine/src/base/common/EGLContextBase.cc | 4 +- imagine/src/base/common/Error.cc | 43 --- imagine/src/base/iphone/IOSGLContext.mm | 6 +- imagine/src/base/iphone/IOSScreen.mm | 5 + imagine/src/base/linux/FBDevFrameTimer.cc | 4 +- imagine/src/base/x11/XGLContextEGL.cc | 6 +- .../src/bluetooth/AndroidBluetoothAdapter.cc | 188 +++++------ imagine/src/bluetooth/BluetoothAdapter.cc | 58 ---- .../src/bluetooth/BluetoothInputDevScanner.cc | 67 ++-- .../src/bluetooth/BluezBluetoothAdapter.cc | 194 +++++------ .../src/bluetooth/BtstackBluetoothAdapter.cc | 182 +++++------ imagine/src/bluetooth/IControlPad.cc | 27 +- imagine/src/bluetooth/PS3Controller.cc | 39 ++- imagine/src/bluetooth/Wiimote.cc | 30 +- imagine/src/bluetooth/Zeemote.cc | 27 +- imagine/src/bluetooth/bluetooth.mk | 2 +- imagine/src/data-type/image/Android.cc | 2 +- imagine/src/fs/PosixFS.cc | 4 +- imagine/src/gfx/opengl/PixmapBufferTexture.cc | 17 +- imagine/src/gfx/opengl/Renderer.cc | 2 +- imagine/src/gui/AlertView.cc | 4 +- imagine/src/gui/FSPicker.cc | 7 +- imagine/src/gui/MenuItem.cc | 39 ++- imagine/src/gui/TableView.cc | 48 +-- imagine/src/io/PosixIO.cc | 2 +- 100 files changed, 1472 insertions(+), 1797 deletions(-) delete mode 100644 imagine/include/imagine/base/Error.hh delete mode 100644 imagine/include/imagine/bluetooth/BluetoothInputDevScanner.hh create mode 100644 imagine/include/imagine/bluetooth/BluetoothInputDevice.hh delete mode 100644 imagine/include/imagine/bluetooth/config.hh create mode 100644 imagine/include/imagine/bluetooth/defs.hh delete mode 100644 imagine/include/imagine/bluetooth/sys.hh delete mode 100644 imagine/src/base/common/Error.cc delete mode 100644 imagine/src/bluetooth/BluetoothAdapter.cc diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8e88eca5b..e68a290d7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,9 +44,9 @@ jobs: run: | mkdir imagine-sdk mkdir EX-Emulators - wget "https://dl.google.com/android/repository/android-ndk-r26c-linux.zip" - unzip android-ndk-r26c-linux.zip - echo "ANDROID_NDK_PATH=${{ github.workspace }}/android-ndk-r26c" >> $GITHUB_ENV + wget "https://dl.google.com/android/repository/android-ndk-r27-beta1-linux.zip" + unzip android-ndk-r27-beta1-linux.zip + echo "ANDROID_NDK_PATH=${{ github.workspace }}/android-ndk-r27-beta1" >> $GITHUB_ENV echo "EMUFRAMEWORK_PATH=${{ github.workspace }}/EmuFramework" >> $GITHUB_ENV echo "IMAGINE_PATH=${{ github.workspace }}/imagine" >> $GITHUB_ENV echo "IMAGINE_SDK_PATH=${{ github.workspace }}/imagine-sdk" >> $GITHUB_ENV diff --git a/EmuFramework/include/emuframework/Cheats.hh b/EmuFramework/include/emuframework/Cheats.hh index 8f0be2a41..c816915b5 100644 --- a/EmuFramework/include/emuframework/Cheats.hh +++ b/EmuFramework/include/emuframework/Cheats.hh @@ -42,7 +42,7 @@ protected: class BaseEditCheatListView : public TableView, public EmuAppHelper { public: - BaseEditCheatListView(ViewAttachParams attach, TableView::ItemsDelegate items, TableView::ItemDelegate item); + BaseEditCheatListView(ViewAttachParams attach, TableView::ItemSourceDelegate); void setOnCheatListChanged(RefreshCheatsDelegate del); protected: @@ -60,14 +60,13 @@ public: using EmuAppHelper>::app; BaseEditCheatView(UTF16Convertible auto &&viewName, ViewAttachParams attach, UTF16Convertible auto &&cheatName, - TableView::ItemsDelegate items, TableView::ItemDelegate item, TextMenuItem::SelectDelegate removed, + TableView::ItemSourceDelegate itemSrc, TextMenuItem::SelectDelegate removed, RefreshCheatsDelegate onCheatListChanged_): TableView { IG_forward(viewName), attach, - items, - item + itemSrc }, name { diff --git a/EmuFramework/include/emuframework/EmuApp.hh b/EmuFramework/include/emuframework/EmuApp.hh index 85af35d2e..b0034af27 100644 --- a/EmuFramework/include/emuframework/EmuApp.hh +++ b/EmuFramework/include/emuframework/EmuApp.hh @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -49,7 +50,6 @@ namespace IG { -class BluetoothAdapter; class FileIO; class BasicNavView; class YesNoAlertView; @@ -105,12 +105,6 @@ struct AssetDesc constexpr auto filename() const { return assetFilename[fileIdx()]; } }; -WISE_ENUM_CLASS((ImageChannel, uint8_t), - All, - Red, - Green, - Blue); - enum class AltSpeedMode { fast, slow @@ -124,11 +118,6 @@ WISE_ENUM_CLASS((CPUAffinityMode, uint8_t), Auto, Any, Manual ); -struct SystemKeyInputFlags -{ - bool allowTurboModifier{true}; -}; - constexpr float menuVideoBrightnessScale = .25f; class EmuApp : public IG::Application @@ -208,10 +197,9 @@ public: static void updateLegacySavePath(IG::ApplicationContext, CStringView path); auto screenshotDirectory() const { return system().userPath(userScreenshotPath); } std::unique_ptr makeCustomView(ViewAttachParams attach, ViewID id); - bool handleKeyInput(KeyInfo, const Input::Event &srcEvent); - bool handleAppActionKeyInput(InputAction, const Input::Event &srcEvent); - void handleSystemKeyInput(KeyInfo, Input::Action, uint32_t metaState = 0, SystemKeyInputFlags flags = {}); - void runTurboInputEvents(); + bool handleKeyInput(KeyInfo i, const Input::Event &srcEvent) { return inputManager.handleKeyInput(*this, i, srcEvent); } + bool handleAppActionKeyInput(InputAction a, const Input::Event &srcEvent) { return inputManager.handleAppActionKeyInput(*this, a, srcEvent); } + void handleSystemKeyInput(KeyInfo i, Input::Action a, uint32_t metaState = 0, SystemKeyInputFlags flags = {}) { inputManager.handleSystemKeyInput(*this, i, a, metaState, flags); } void resetInput(); void setRunSpeed(double speed); void saveSessionOptions(); @@ -247,7 +235,6 @@ public: FS::PathString makeNextScreenshotFilename(); bool mogaManagerIsActive() const { return bool(mogaManagerPtr); } void setMogaManagerActive(bool on, bool notify); - BluetoothAdapter *bluetoothAdapter(); void closeBluetoothConnections(); ViewAttachParams attachParams(); auto &customKeyConfigList() { return inputManager.customKeyConfigs; }; @@ -272,8 +259,7 @@ public: bool setWindowDrawableConfig(Gfx::DrawableConfig); Gfx::DrawableConfig windowDrawableConfig() const { return windowDrawableConf; } IG::PixelFormat windowPixelFormat() const; - void setRenderPixelFormat(std::optional); - IG::PixelFormat renderPixelFormat() const { return renderPixelFmt; } + void setRenderPixelFormat(IG::PixelFormat); bool setVideoAspectRatio(float val); float videoAspectRatio() const; float defaultVideoAspectRatio() const; @@ -284,10 +270,6 @@ public: void setContentRotation(IG::Rotation); void updateVideoContentRotation(); void updateContentRotation(); - float videoBrightness(ImageChannel) const; - const Gfx::Vec3 &videoBrightnessAsRGB() const { return videoBrightnessRGB; } - int videoBrightnessAsInt(ImageChannel ch) const { return videoBrightness(ch) * 100.f; } - void setVideoBrightness(float brightness, ImageChannel); Gfx::PresentMode effectivePresentMode() const { if(frameTimeSource == FrameTimeSource::Renderer) @@ -367,21 +349,21 @@ protected: EmuSystemTask emuSystemTask{*this}; mutable Gfx::Texture assetBuffImg[wise_enum::size]; int savedAdvancedFrames{}; - Gfx::Vec3 videoBrightnessRGB{1.f, 1.f, 1.f}; FS::PathString contentSearchPath_; [[no_unique_address]] IG::Data::PixmapReader pixmapReader; [[no_unique_address]] IG::Data::PixmapWriter pixmapWriter; [[no_unique_address]] PerformanceHintManager perfHintManager; [[no_unique_address]] PerformanceHintSession perfHintSession; - BluetoothAdapter *bta{}; ConditionalMember> mogaManagerPtr; Gfx::DrawableConfig windowDrawableConf; - IG::PixelFormat renderPixelFmt; ConditionalMember layoutBehindSystemUI{}; bool enableBlankFrameInsertion{}; public: + BluetoothAdapter bluetoothAdapter; RecentContent recentContent; std::string userScreenshotPath; + Property{.isValid = renderPixelFormatIsValid}> renderPixelFormat; ConditionalProperty cpuAffinityMask; Property{.defaultValue = 800, .isValid = isValidFastSpeed}> fastModeSpeed; @@ -410,7 +392,6 @@ public: Property{.defaultValue = PIXEL_NONE, .isValid = imageEffectPixelFormatIsValid}> imageEffectPixelFormat; Property{.defaultValue = 100, .isValid = optionMenuScaleIsValid}> menuScale; - Property{.defaultValue = 100, .isValid = optionContentScaleIsValid}> contentScale; ConditionalProperty showOnSecondScreen; Property textureBufferMode; Property renderPixelFormatOption() const; void applyRenderPixelFormat(); std::optional windowDrawablePixelFormatOption() const; std::optional windowDrawableColorSpaceOption() const; diff --git a/EmuFramework/include/emuframework/EmuInput.hh b/EmuFramework/include/emuframework/EmuInput.hh index 19bced99b..8a85a11a6 100644 --- a/EmuFramework/include/emuframework/EmuInput.hh +++ b/EmuFramework/include/emuframework/EmuInput.hh @@ -32,6 +32,7 @@ namespace EmuEx using namespace IG; class InputDeviceConfig; +struct InputAction; struct KeyCategory { @@ -153,6 +154,11 @@ struct InputDeviceSavedConfig bool matchesDevice(const Input::Device &dev) const; }; +struct SystemKeyInputFlags +{ + bool allowTurboModifier{true}; +}; + class InputManager { public: @@ -166,6 +172,9 @@ public: InputManager(ApplicationContext ctx): vController{ctx} {} + bool handleKeyInput(EmuApp&, KeyInfo, const Input::Event &srcEvent); + bool handleAppActionKeyInput(EmuApp&, InputAction, const Input::Event &srcEvent); + void handleSystemKeyInput(EmuApp&, KeyInfo, Input::Action, uint32_t metaState = 0, SystemKeyInputFlags flags = {}); void updateInputDevices(ApplicationContext); KeyConfig *customKeyConfig(std::string_view name, const Input::Device &) const; KeyConfigDesc keyConfig(std::string_view name, const Input::Device &) const; diff --git a/EmuFramework/include/emuframework/EmuOptions.hh b/EmuFramework/include/emuframework/EmuOptions.hh index 1e214c045..95f4634b2 100644 --- a/EmuFramework/include/emuframework/EmuOptions.hh +++ b/EmuFramework/include/emuframework/EmuOptions.hh @@ -155,6 +155,23 @@ constexpr bool isValidWithMinMax(const auto &v) return v >= min && v <= max; } +constexpr bool windowPixelFormatIsValid(const IG::PixelFormat &v) +{ + switch(v) + { + case IG::PIXEL_NONE: + case IG::PIXEL_RGB565: + case IG::PIXEL_RGBA8888: + return true; + default: return false; + } +} + +constexpr bool renderPixelFormatIsValid(const auto &v) +{ + return windowPixelFormatIsValid(v); +} + } namespace IG diff --git a/EmuFramework/include/emuframework/EmuSystem.hh b/EmuFramework/include/emuframework/EmuSystem.hh index c936604f3..4a474cd7b 100755 --- a/EmuFramework/include/emuframework/EmuSystem.hh +++ b/EmuFramework/include/emuframework/EmuSystem.hh @@ -26,7 +26,6 @@ #include #include #include -#include #include #include diff --git a/EmuFramework/include/emuframework/EmuVideo.hh b/EmuFramework/include/emuframework/EmuVideo.hh index e5e6a0540..74e7fb4a7 100644 --- a/EmuFramework/include/emuframework/EmuVideo.hh +++ b/EmuFramework/include/emuframework/EmuVideo.hh @@ -20,7 +20,6 @@ #include #include #include -#include namespace EmuEx { diff --git a/EmuFramework/include/emuframework/EmuVideoLayer.hh b/EmuFramework/include/emuframework/EmuVideoLayer.hh index eb20e42a0..99c541127 100644 --- a/EmuFramework/include/emuframework/EmuVideoLayer.hh +++ b/EmuFramework/include/emuframework/EmuVideoLayer.hh @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -30,6 +31,12 @@ class EmuVideo; class EmuSystem; class VController; +WISE_ENUM_CLASS((ImageChannel, uint8_t), + All, + Red, + Green, + Blue); + class EmuVideoLayer { public: @@ -48,15 +55,24 @@ public: void setEffectFormat(IG::PixelFormat); void setLinearFilter(bool on); bool usingLinearFilter() const { return useLinearFilter; } - void setBrightness(Gfx::Vec3); void onVideoFormatChanged(IG::PixelFormat effectFmt); Gfx::ColorSpace colorSpace() const { return colSpace; } bool srgbColorSpace() const { return colSpace == Gfx::ColorSpace::SRGB; } void setRotation(IG::Rotation); float evalAspectRatio(float aR); + float channelBrightness(ImageChannel) const; + int channelBrightnessAsInt(ImageChannel ch) const { return channelBrightness(ch) * 100.f; } + const Gfx::Vec3 &brightnessAsRGB() const { return brightnessUnscaled; } + void setBrightness(float brightness, ImageChannel); bool readConfig(MapIO &, unsigned key); void writeConfig(FileIO &) const; + void setBrightnessScale(float s) + { + brightnessScale = s; + updateBrightness(); + } + const IG::WindowRect &contentRect() const { return contentRect_; @@ -72,6 +88,8 @@ private: IG::WindowRect contentRect_; Gfx::Vec3 brightness{1.f, 1.f, 1.f}; Gfx::Vec3 brightnessSrgb{1.f, 1.f, 1.f}; + Gfx::Vec3 brightnessUnscaled{1.f, 1.f, 1.f}; + float brightnessScale; public: float landscapeAspectRatio; float portraitAspectRatio; @@ -82,7 +100,7 @@ private: ImageOverlayId userOverlayEffectId{}; Gfx::ColorSpace colSpace{}; public: - uint8_t scale{100}; + Property{.defaultValue = 100, .isValid = optionContentScaleIsValid}> scale; private: IG::Rotation rotation{}; bool useLinearFilter{true}; @@ -92,6 +110,7 @@ private: void buildEffectChain(); bool updateConvertColorSpaceEffect(); void updateSprite(); + void updateBrightness(); void logOutputFormat(); Gfx::Renderer &renderer(); Gfx::ColorSpace videoColorSpace(IG::PixelFormat videoFmt) const; diff --git a/EmuFramework/include/emuframework/InputManagerView.hh b/EmuFramework/include/emuframework/InputManagerView.hh index 2cbce98d9..259704183 100644 --- a/EmuFramework/include/emuframework/InputManagerView.hh +++ b/EmuFramework/include/emuframework/InputManagerView.hh @@ -83,13 +83,9 @@ private: ConditionalMember notifyDeviceChange; ConditionalMember bluetoothHeading; ConditionalMember keepBtActive; - #ifdef CONFIG_BLUETOOTH_SCAN_SECS - TextMenuItem btScanSecsItem[5]; - MultiChoiceMenuItem btScanSecs; - #endif - #ifdef CONFIG_BLUETOOTH_SCAN_CACHE_USAGE - BoolMenuItem btScanCache; - #endif + ConditionalMember btScanSecsItem[5]; + ConditionalMember btScanSecs; + ConditionalMember btScanCache; BoolMenuItem altGamepadConfirm; StaticArrayList item; EmuInputView *emuInputView{}; diff --git a/EmuFramework/include/emuframework/MainMenuView.hh b/EmuFramework/include/emuframework/MainMenuView.hh index 08a1d0307..fca9d9e4f 100644 --- a/EmuFramework/include/emuframework/MainMenuView.hh +++ b/EmuFramework/include/emuframework/MainMenuView.hh @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include namespace EmuEx @@ -52,9 +52,7 @@ protected: TextMenuItem benchmark; ConditionalMember scanWiimotes; ConditionalMember bluetoothDisconnect; - #ifdef CONFIG_BLUETOOTH_SERVER - TextMenuItem acceptPS3ControllerConnection; - #endif + ConditionalMember acceptPS3ControllerConnection; TextMenuItem about; TextMenuItem exitApp; StaticArrayList item; diff --git a/EmuFramework/include/emuframework/OutSizeTracker.hh b/EmuFramework/include/emuframework/OutSizeTracker.hh index d338f38bb..a0da913de 100644 --- a/EmuFramework/include/emuframework/OutSizeTracker.hh +++ b/EmuFramework/include/emuframework/OutSizeTracker.hh @@ -17,6 +17,7 @@ #include #include +#include namespace EmuEx { diff --git a/EmuFramework/include/emuframework/VideoImageEffect.hh b/EmuFramework/include/emuframework/VideoImageEffect.hh index 2c7550a52..9cf35e126 100644 --- a/EmuFramework/include/emuframework/VideoImageEffect.hh +++ b/EmuFramework/include/emuframework/VideoImageEffect.hh @@ -19,7 +19,6 @@ #include #include #include -#include namespace EmuEx { diff --git a/EmuFramework/include/emuframework/config.hh b/EmuFramework/include/emuframework/config.hh index 2cb6eef3f..e11c2317b 100644 --- a/EmuFramework/include/emuframework/config.hh +++ b/EmuFramework/include/emuframework/config.hh @@ -16,7 +16,7 @@ along with EmuFramework. If not, see */ #include -#include +#include #ifdef ENV_NOTE #define PLATFORM_INFO_STR ENV_NOTE " (" CONFIG_ARCH_STR ")" diff --git a/EmuFramework/src/ConfigFile.cc b/EmuFramework/src/ConfigFile.cc index db464b522..e3b1ef8a6 100644 --- a/EmuFramework/src/ConfigFile.cc +++ b/EmuFramework/src/ConfigFile.cc @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include namespace EmuEx @@ -28,22 +28,6 @@ namespace EmuEx constexpr SystemLogger log{"ConfigFile"}; -constexpr bool windowPixelFormatIsValid(uint8_t val) -{ - switch(val) - { - case IG::PIXEL_RGB565: - case IG::PIXEL_RGBA8888: - return true; - default: return false; - } -} - -constexpr bool renderPixelFormatIsValid(IG::PixelFormat val) -{ - return windowPixelFormatIsValid(val); -} - constexpr bool colorSpaceIsValid(Gfx::ColorSpace val) { return val == Gfx::ColorSpace::SRGB; @@ -60,7 +44,6 @@ void EmuApp::saveConfigFile(FileIO &io) recentContent.writeConfig(io); writeOptionValueIfNotDefault(io, imageEffectPixelFormat); writeOptionValueIfNotDefault(io, menuScale); - writeOptionValueIfNotDefault(io, contentScale); writeOptionValueIfNotDefault(io, fontSize); writeOptionValueIfNotDefault(io, showsBluetoothScan); writeOptionValueIfNotDefault(io, showsNotificationIcon); @@ -81,7 +64,7 @@ void EmuApp::saveConfigFile(FileIO &io) writeOptionValue(io, CFGKEY_AUDIO_SOLO_MIX, audio.manager.soloMixOption()); writeOptionValue(io, CFGKEY_WINDOW_PIXEL_FORMAT, windowDrawablePixelFormatOption()); writeOptionValue(io, CFGKEY_VIDEO_COLOR_SPACE, windowDrawableColorSpaceOption()); - writeOptionValue(io, CFGKEY_RENDER_PIXEL_FORMAT, renderPixelFormatOption()); + writeOptionValueIfNotDefault(io, renderPixelFormat); writeOptionValueIfNotDefault(io, textureBufferMode); writeOptionValueIfNotDefault(io, showOnSecondScreen); writeOptionValueIfNotDefault(io, showHiddenFilesInPicker); @@ -113,12 +96,8 @@ void EmuApp::saveConfigFile(FileIO &io) if(overrideScreenFrameRate) writeOptionValue(io, CFGKEY_OVERRIDE_SCREEN_FRAME_RATE, overrideScreenFrameRate); writeOptionValueIfNotDefault(io, allowBlankFrameInsertion); - if(videoBrightnessRGB != Gfx::Vec3{1.f, 1.f, 1.f}) - writeOptionValue(io, CFGKEY_VIDEO_BRIGHTNESS, videoBrightnessRGB); - #ifdef CONFIG_BLUETOOTH_SCAN_CACHE_USAGE - if(!BluetoothAdapter::scanCacheUsage()) + if(Config::Bluetooth::scanCache && !bluetoothAdapter.useScanCache) writeOptionValue(io, CFGKEY_BLUETOOTH_SCAN_CACHE, false); - #endif writeOptionValueIfNotDefault(io, cpuAffinityMask); writeOptionValueIfNotDefault(io, cpuAffinityMode); writeOptionValueIfNotDefault(io, presentMode); @@ -196,13 +175,10 @@ EmuApp::ConfigParams EmuApp::loadConfigFile(IG::ApplicationContext ctx) case CFGKEY_FONT_Y_SIZE: return readOptionValue(io, fontSize); case CFGKEY_GAME_ORIENTATION: return readOptionValue(io, emuOrientation); case CFGKEY_MENU_ORIENTATION: return readOptionValue(io, menuOrientation); - case CFGKEY_CONTENT_SCALE: return readOptionValue(io, contentScale); case CFGKEY_MENU_SCALE: return readOptionValue(io, menuScale); case CFGKEY_SHOW_ON_2ND_SCREEN: return readOptionValue(io, showOnSecondScreen); case CFGKEY_IMAGE_EFFECT_PIXEL_FORMAT: return readOptionValue(io, imageEffectPixelFormat); - case CFGKEY_RENDER_PIXEL_FORMAT: - setRenderPixelFormat(readOptionValue(io, renderPixelFormatIsValid)); - return true; + case CFGKEY_RENDER_PIXEL_FORMAT: return EmuSystem::canRenderRGBA8888 ? readOptionValue(io, renderPixelFormat) : false; case CFGKEY_RECENT_CONTENT: return recentContent.readLegacyConfig(io, system()); case CFGKEY_SWAPPED_GAMEPAD_CONFIM: setSwappedConfirmKeys(readOptionValue(io)); @@ -227,13 +203,10 @@ EmuApp::ConfigParams EmuApp::loadConfigFile(IG::ApplicationContext ctx) case CFGKEY_HIDE_OS_NAV: return readOptionValue(io, hidesOSNav); case CFGKEY_SUSTAINED_PERFORMANCE_MODE: return appContext().hasSustainedPerformanceMode() && readOptionValue(io, useSustainedPerformanceMode); - #ifdef CONFIG_INPUT_BLUETOOTH - case CFGKEY_KEEP_BLUETOOTH_ACTIVE: return readOptionValue(io, keepBluetoothActive); - case CFGKEY_SHOW_BLUETOOTH_SCAN: return readOptionValue(io, showsBluetoothScan); - #ifdef CONFIG_BLUETOOTH_SCAN_CACHE_USAGE - case CFGKEY_BLUETOOTH_SCAN_CACHE: return readOptionValue(io, [](auto on){BluetoothAdapter::setScanCacheUsage(on);}); - #endif - #endif + case CFGKEY_KEEP_BLUETOOTH_ACTIVE: return Config::Input::BLUETOOTH && readOptionValue(io, keepBluetoothActive); + case CFGKEY_SHOW_BLUETOOTH_SCAN: return Config::Input::BLUETOOTH && readOptionValue(io, showsBluetoothScan); + case CFGKEY_BLUETOOTH_SCAN_CACHE: return Config::Bluetooth::scanCache + && readOptionValue(io, [this](auto on){ bluetoothAdapter.useScanCache = on; }); case CFGKEY_CPU_AFFINITY_MASK: return readOptionValue(io, cpuAffinityMask); case CFGKEY_CPU_AFFINITY_MODE: return readOptionValue(io, cpuAffinityMode); case CFGKEY_RENDERER_PRESENT_MODE: return readOptionValue(io, presentMode); @@ -257,7 +230,6 @@ EmuApp::ConfigParams EmuApp::loadConfigFile(IG::ApplicationContext ctx) case CFGKEY_VIDEO_PORTRAIT_ASPECT_RATIO: return readOptionValue(io, videoLayer.portraitAspectRatio, isValidAspectRatio); case CFGKEY_VIDEO_LANDSCAPE_OFFSET: return readOptionValue(io, videoLayer.landscapeOffset, [](auto v){return v >= -4096 && v <= 4096;}); case CFGKEY_VIDEO_PORTRAIT_OFFSET: return readOptionValue(io, videoLayer.portraitOffset, [](auto v){return v >= -4096 && v <= 4096;}); - case CFGKEY_VIDEO_BRIGHTNESS: return readOptionValue(io, videoBrightnessRGB); case CFGKEY_INPUT_KEY_CONFIGS_V2: return inputManager.readCustomKeyConfig(io); case CFGKEY_INPUT_DEVICE_CONFIGS: return inputManager.readSavedInputDevices(io); } diff --git a/EmuFramework/src/EmuApp.cc b/EmuFramework/src/EmuApp.cc index 1774a3a5e..f95372bd4 100644 --- a/EmuFramework/src/EmuApp.cc +++ b/EmuFramework/src/EmuApp.cc @@ -30,7 +30,6 @@ #include #include #include "gui/AutosaveSlotView.hh" -#include "gui/ResetAlertView.hh" #include "InputDeviceData.hh" #include "WindowData.hh" #include "configFile.hh" @@ -50,7 +49,7 @@ #include #include #include -#include +#include #include #include @@ -100,7 +99,8 @@ EmuApp::EmuApp(ApplicationInitParams initParams, ApplicationContext &ctx): pixmapReader{ctx}, pixmapWriter{ctx}, perfHintManager{ctx.performanceHintManager()}, - layoutBehindSystemUI{ctx.hasTranslucentSysUI()} + layoutBehindSystemUI{ctx.hasTranslucentSysUI()}, + bluetoothAdapter{ctx} { if(ctx.registerInstance(initParams)) { @@ -309,26 +309,17 @@ IG::PixelFormat EmuApp::windowPixelFormat() const return appContext().defaultWindowPixelFormat(); } -void EmuApp::setRenderPixelFormat(std::optional fmtOpt) +void EmuApp::setRenderPixelFormat(IG::PixelFormat fmt) { - if(!fmtOpt) - return; - renderPixelFmt = *fmtOpt; + renderPixelFormat = fmt; applyRenderPixelFormat(); } -std::optional EmuApp::renderPixelFormatOption() const -{ - if(EmuSystem::canRenderRGBA8888 && renderPixelFmt) - return renderPixelFmt; - return {}; -} - void EmuApp::applyRenderPixelFormat() { if(!video.hasRendererTask()) return; - auto fmt = renderPixelFormat(); + auto fmt = renderPixelFormat.value(); if(!fmt) fmt = windowPixelFormat(); if(!EmuSystem::canRenderRGBA8888 && fmt != IG::PIXEL_RGB565) @@ -437,10 +428,8 @@ void EmuApp::mainInitCommon(IG::ApplicationInitParams initParams, IG::Applicatio audio.manager.endSession(); saveConfigFile(ctx); saveSystemOptions(); - #ifdef CONFIG_INPUT_BLUETOOTH - if(bta && (!backgrounded || (backgrounded && !keepBluetoothActive))) + if(!backgrounded || (backgrounded && !keepBluetoothActive)) closeBluetoothConnections(); - #endif onEvent(ctx, FreeCachesEvent{false}); return true; }); @@ -451,15 +440,7 @@ void EmuApp::mainInitCommon(IG::ApplicationInitParams initParams, IG::Applicatio [this, appConfig](IG::ApplicationContext ctx, IG::Window &win) { renderer.initMainTask(&win, windowDrawableConfig()); - if(textureBufferMode != Gfx::TextureBufferMode::DEFAULT) - { - auto mode = textureBufferMode; - if(renderer.makeValidTextureBufferMode(mode) != mode) - { - // reset to default if saved non-default mode isn't supported - textureBufferMode.reset(); - } - } + textureBufferMode = renderer.validateTextureBufferMode(textureBufferMode); viewManager.defaultFace = {renderer, fontManager.makeSystem(), fontSettings(win)}; viewManager.defaultBoldFace = {renderer, fontManager.makeBoldSystem(), fontSettings(win)}; ViewAttachParams viewAttach{viewManager, win, renderer.task()}; @@ -489,7 +470,6 @@ void EmuApp::mainInitCommon(IG::ApplicationInitParams initParams, IG::Applicatio videoLayer.setRendererTask(renderer.task()); applyRenderPixelFormat(); videoLayer.updateEffect(system(), videoEffectPixelFormat()); - videoLayer.scale = contentScale; system().onFrameUpdate = [this](FrameParams params) { emuSystemTask.updateFrameParams(params); @@ -718,7 +698,7 @@ bool EmuApp::advanceFrames(FrameParams frameParams, EmuSystemTask *taskPtr) if(taskPtr) taskPtr->framePending = true; } - runTurboInputEvents(); + inputManager.turboActions.update(*this); //log.debug("running {} frame(s), skip:{}", frameInfo.advanced, !videoPtr); runFrames({taskPtr}, videoPtr, audioPtr, frameInfo.advanced); if(!videoPtr) @@ -853,7 +833,7 @@ void EmuApp::startEmulation() { if(!viewController().isShowingEmulation()) return; - videoLayer.setBrightness(videoBrightnessRGB); + videoLayer.setBrightnessScale(1.f); video.setOnFrameFinished( [&, &viewController = viewController()](EmuVideo &) { @@ -875,7 +855,7 @@ void EmuApp::showUI(bool updateTopView) return; pauseEmulation(); configureAppForEmulation(false); - videoLayer.setBrightness(videoBrightnessRGB * menuVideoBrightnessScale); + videoLayer.setBrightnessScale(menuVideoBrightnessScale); viewController().showMenuView(updateTopView); } @@ -886,7 +866,7 @@ void EmuApp::pauseEmulation() video.setOnFrameFinished([](EmuVideo &){}); system().pause(*this); setRunSpeed(1.); - videoLayer.setBrightness(videoBrightnessRGB * pausedVideoBrightnessScale); + videoLayer.setBrightnessScale(pausedVideoBrightnessScale); emuWindow().setDrawEventPriority(); removeOnFrame(); } @@ -1171,228 +1151,6 @@ std::unique_ptr EmuApp::makeCloseContentView() }); } -bool EmuApp::handleKeyInput(KeyInfo keyInfo, const Input::Event &srcEvent) -{ - if(!keyInfo.flags.appCode) - { - handleSystemKeyInput(keyInfo, srcEvent.state(), srcEvent.metaKeyBits()); - } - else - { - for(auto c : keyInfo.codes) - { - if(handleAppActionKeyInput({c, keyInfo.flags, srcEvent.state(), srcEvent.metaKeyBits()}, srcEvent)) - return true; - } - } - return false; -} - -bool EmuApp::handleAppActionKeyInput(InputAction action, const Input::Event &srcEvent) -{ - bool isPushed = action.state == Input::Action::PUSHED; - assert(action.flags.appCode); - using enum AppKeyCode; - switch(AppKeyCode(action.code)) - { - case fastForward: - { - viewController().inputView.setAltSpeedMode(AltSpeedMode::fast, isPushed); - break; - } - case openContent: - { - if(!isPushed) - break; - log.info("show load game view from key event"); - viewController().popToRoot(); - viewController().pushAndShow(FilePicker::forLoading(attachParams(), srcEvent), srcEvent, false); - return true; - } - case closeContent: - { - if(!isPushed) - break; - viewController().pushAndShowModal(makeCloseContentView(), srcEvent, false); - return true; - } - case openSystemActions: - { - if(!isPushed) - break; - log.info("show system actions view from key event"); - showSystemActionsViewFromSystem(attachParams(), srcEvent); - return true; - } - case saveState: - { - if(!isPushed) - break; - static auto doSaveState = [](EmuApp &app, bool notify) - { - if(app.saveStateWithSlot(app.system().stateSlot()) && notify) - { - app.postMessage("State Saved"); - } - }; - if(shouldOverwriteExistingState()) - { - syncEmulationThread(); - doSaveState(*this, confirmOverwriteState); - } - else - { - viewController().pushAndShowModal(std::make_unique(attachParams(), "Really Overwrite State?", - YesNoAlertView::Delegates - { - .onYes = [this] - { - doSaveState(*this, false); - showEmulation(); - }, - .onNo = [this]{ showEmulation(); } - }), srcEvent, false); - } - return true; - } - case loadState: - { - if(!isPushed) - break; - syncEmulationThread(); - loadStateWithSlot(system().stateSlot()); - return true; - } - case decStateSlot: - { - if(!isPushed) - break; - system().decStateSlot(); - postMessage(1, false, std::format("State Slot: {}", system().stateSlotName())); - return true; - } - case incStateSlot: - { - if(!isPushed) - break; - system().incStateSlot(); - postMessage(1, false, std::format("State Slot: {}", system().stateSlotName())); - return true; - } - case takeScreenshot: - { - if(!isPushed) - break; - video.takeGameScreenshot(); - return true; - } - case toggleFastForward: - { - if(!isPushed) - break; - viewController().inputView.toggleAltSpeedMode(AltSpeedMode::fast); - break; - } - case openMenu: - { - if(!isPushed) - break; - log.info("show last view from key event"); - showLastViewFromSystem(attachParams(), srcEvent); - return true; - } - case turboModifier: - { - inputManager.turboModifierActive = isPushed; - if(!isPushed) - inputManager.turboActions = {}; - break; - } - case exitApp: - { - if(!isPushed) - break; - viewController().pushAndShowModal(std::make_unique(attachParams(), "Really Exit?", - YesNoAlertView::Delegates{.onYes = [this]{ appContext().exit(); }}), srcEvent, false); - break; - } - case slowMotion: - { - viewController().inputView.setAltSpeedMode(AltSpeedMode::slow, isPushed); - break; - } - case toggleSlowMotion: - { - if(!isPushed) - break; - viewController().inputView.toggleAltSpeedMode(AltSpeedMode::slow); - break; - } - case rewind: - { - if(!isPushed) - break; - if(rewindManager.maxStates) - rewindManager.rewindState(*this); - else - postMessage(3, false, "Please set rewind states in Options➔System"); - break; - } - case softReset: - { - if(!isPushed) - break; - syncEmulationThread(); - system().reset(*this, EmuSystem::ResetMode::SOFT); - break; - } - case hardReset: - { - if(!isPushed) - break; - syncEmulationThread(); - system().reset(*this, EmuSystem::ResetMode::HARD); - break; - } - case resetMenu: - { - if(!isPushed) - break; - viewController().pushAndShowModal(resetAlertView(attachParams(), *this), srcEvent, false); - break; - } - } - return false; -} - -void EmuApp::handleSystemKeyInput(KeyInfo keyInfo, Input::Action act, uint32_t metaState, SystemKeyInputFlags flags) -{ - if(flags.allowTurboModifier && inputManager.turboModifierActive && std::ranges::all_of(keyInfo.codes, allowsTurboModifier)) - keyInfo.flags.turbo = 1; - if(keyInfo.flags.toggle) - { - inputManager.toggleInput.updateEvent(*this, keyInfo, act); - } - else if(keyInfo.flags.turbo) - { - inputManager.turboActions.updateEvent(*this, keyInfo, act); - } - else - { - defaultVController().updateSystemKeys(keyInfo, act == Input::Action::PUSHED); - for(auto code : keyInfo.codes) - { - system().handleInputAction(this, {code, keyInfo.flags, act, metaState}); - } - } -} - -void EmuApp::runTurboInputEvents() -{ - assert(system().hasContent()); - inputManager.turboActions.update(*this); -} - void EmuApp::resetInput() { inputManager.turboModifierActive = false; @@ -1832,36 +1590,6 @@ void EmuApp::removeOnFrame() viewController().emuWindow().removeOnFrame(system().onFrameUpdate, frameTimeSource); } -static auto &videoBrightnessVal(ImageChannel ch, auto &videoBrightnessRGB) -{ - switch(ch) - { - case ImageChannel::All: break; - case ImageChannel::Red: return videoBrightnessRGB.r; - case ImageChannel::Green: return videoBrightnessRGB.g; - case ImageChannel::Blue: return videoBrightnessRGB.b; - } - bug_unreachable("invalid ImageChannel"); -} - -float EmuApp::videoBrightness(ImageChannel ch) const -{ - return videoBrightnessVal(ch, videoBrightnessRGB); -} - -void EmuApp::setVideoBrightness(float brightness, ImageChannel ch) -{ - if(ch == ImageChannel::All) - { - videoBrightnessRGB.r = videoBrightnessRGB.g = videoBrightnessRGB.b = brightness; - } - else - { - videoBrightnessVal(ch, videoBrightnessRGB) = brightness; - } - videoLayer.setBrightness(videoBrightnessRGB * menuVideoBrightnessScale); -} - bool EmuApp::setAltSpeed(AltSpeedMode mode, int16_t speed) { if(mode == AltSpeedMode::slow) @@ -1937,20 +1665,9 @@ std::unique_ptr EmuApp::makeView(ViewAttachParams attach, ViewID id) } } -BluetoothAdapter *EmuApp::bluetoothAdapter() -{ - if(bta) - { - return bta; - } - log.info("initializing Bluetooth"); - bta = BluetoothAdapter::defaultAdapter(appContext()); - return bta; -} - void EmuApp::closeBluetoothConnections() { - Bluetooth::closeBT(std::exchange(bta, {})); + Bluetooth::closeBT(bluetoothAdapter); } void EmuApp::reportFrameWorkTime() diff --git a/EmuFramework/src/EmuInput.cc b/EmuFramework/src/EmuInput.cc index 995c1f671..efccb50f0 100644 --- a/EmuFramework/src/EmuInput.cc +++ b/EmuFramework/src/EmuInput.cc @@ -17,7 +17,9 @@ #include #include #include +#include #include "InputDeviceData.hh" +#include "gui/ResetAlertView.hh" #include #include @@ -26,6 +28,224 @@ namespace EmuEx constexpr SystemLogger log{"InputManager"}; +bool InputManager::handleKeyInput(EmuApp& app, KeyInfo keyInfo, const Input::Event &srcEvent) +{ + if(!keyInfo.flags.appCode) + { + handleSystemKeyInput(app, keyInfo, srcEvent.state(), srcEvent.metaKeyBits()); + } + else + { + for(auto c : keyInfo.codes) + { + if(handleAppActionKeyInput(app, {c, keyInfo.flags, srcEvent.state(), srcEvent.metaKeyBits()}, srcEvent)) + return true; + } + } + return false; +} + +bool InputManager::handleAppActionKeyInput(EmuApp& app, InputAction action, const Input::Event &srcEvent) +{ + bool isPushed = action.state == Input::Action::PUSHED; + auto& viewController = app.viewController(); + auto& system = app.system(); + assert(action.flags.appCode); + using enum AppKeyCode; + switch(AppKeyCode(action.code)) + { + case fastForward: + { + viewController.inputView.setAltSpeedMode(AltSpeedMode::fast, isPushed); + break; + } + case openContent: + { + if(!isPushed) + break; + log.info("show load game view from key event"); + viewController.popToRoot(); + viewController.pushAndShow(FilePicker::forLoading(app.attachParams(), srcEvent), srcEvent, false); + return true; + } + case closeContent: + { + if(!isPushed) + break; + viewController.pushAndShowModal(app.makeCloseContentView(), srcEvent, false); + return true; + } + case openSystemActions: + { + if(!isPushed) + break; + log.info("show system actions view from key event"); + app.showSystemActionsViewFromSystem(app.attachParams(), srcEvent); + return true; + } + case saveState: + { + if(!isPushed) + break; + static auto doSaveState = [](EmuApp &app, bool notify) + { + if(app.saveStateWithSlot(app.system().stateSlot()) && notify) + { + app.postMessage("State Saved"); + } + }; + if(app.shouldOverwriteExistingState()) + { + app.syncEmulationThread(); + doSaveState(app, app.confirmOverwriteState); + } + else + { + viewController.pushAndShowModal(std::make_unique(app.attachParams(), "Really Overwrite State?", + YesNoAlertView::Delegates + { + .onYes = [&app] + { + doSaveState(app, false); + app.showEmulation(); + }, + .onNo = [&app]{ app.showEmulation(); } + }), srcEvent, false); + } + return true; + } + case loadState: + { + if(!isPushed) + break; + app.syncEmulationThread(); + app.loadStateWithSlot(system.stateSlot()); + return true; + } + case decStateSlot: + { + if(!isPushed) + break; + system.decStateSlot(); + app.postMessage(1, false, std::format("State Slot: {}", system.stateSlotName())); + return true; + } + case incStateSlot: + { + if(!isPushed) + break; + system.incStateSlot(); + app.postMessage(1, false, std::format("State Slot: {}", system.stateSlotName())); + return true; + } + case takeScreenshot: + { + if(!isPushed) + break; + app.video.takeGameScreenshot(); + return true; + } + case toggleFastForward: + { + if(!isPushed) + break; + viewController.inputView.toggleAltSpeedMode(AltSpeedMode::fast); + break; + } + case openMenu: + { + if(!isPushed) + break; + log.info("show last view from key event"); + app.showLastViewFromSystem(app.attachParams(), srcEvent); + return true; + } + case turboModifier: + { + turboModifierActive = isPushed; + if(!isPushed) + turboActions = {}; + break; + } + case exitApp: + { + if(!isPushed) + break; + viewController.pushAndShowModal(std::make_unique(app.attachParams(), "Really Exit?", + YesNoAlertView::Delegates{.onYes = [&app]{ app.appContext().exit(); }}), srcEvent, false); + break; + } + case slowMotion: + { + viewController.inputView.setAltSpeedMode(AltSpeedMode::slow, isPushed); + break; + } + case toggleSlowMotion: + { + if(!isPushed) + break; + viewController.inputView.toggleAltSpeedMode(AltSpeedMode::slow); + break; + } + case rewind: + { + if(!isPushed) + break; + if(app.rewindManager.maxStates) + app.rewindManager.rewindState(app); + else + app.postMessage(3, false, "Please set rewind states in Options➔System"); + break; + } + case softReset: + { + if(!isPushed) + break; + app.syncEmulationThread(); + system.reset(app, EmuSystem::ResetMode::SOFT); + break; + } + case hardReset: + { + if(!isPushed) + break; + app.syncEmulationThread(); + system.reset(app, EmuSystem::ResetMode::HARD); + break; + } + case resetMenu: + { + if(!isPushed) + break; + viewController.pushAndShowModal(resetAlertView(app.attachParams(), app), srcEvent, false); + break; + } + } + return false; +} + +void InputManager::handleSystemKeyInput(EmuApp& app, KeyInfo keyInfo, Input::Action act, uint32_t metaState, SystemKeyInputFlags flags) +{ + if(flags.allowTurboModifier && turboModifierActive && std::ranges::all_of(keyInfo.codes, app.allowsTurboModifier)) + keyInfo.flags.turbo = 1; + if(keyInfo.flags.toggle) + { + toggleInput.updateEvent(app, keyInfo, act); + } + else if(keyInfo.flags.turbo) + { + turboActions.updateEvent(app, keyInfo, act); + } + else + { + app.defaultVController().updateSystemKeys(keyInfo, act == Input::Action::PUSHED); + for(auto code : keyInfo.codes) + { + app.system().handleInputAction(&app, {code, keyInfo.flags, act, metaState}); + } + } +} + void InputManager::updateInputDevices(ApplicationContext ctx) { for(auto &devPtr : ctx.inputDevices()) diff --git a/EmuFramework/src/EmuOptions.cc b/EmuFramework/src/EmuOptions.cc index efdd4e5c0..5287a5473 100644 --- a/EmuFramework/src/EmuOptions.cc +++ b/EmuFramework/src/EmuOptions.cc @@ -53,7 +53,7 @@ void EmuApp::initOptions(IG::ApplicationContext ctx) { showsNotificationIcon.resetDefault(false); } - else + if(androidSdk >= 17) { showsBluetoothScan.resetDefault(false); } @@ -87,10 +87,9 @@ IG::PixelFormat EmuApp::videoEffectPixelFormat() const bool EmuApp::setContentScale(uint8_t val) { - if(!contentScale.set(val)) + if(!videoLayer.scale.set(val)) return false; - log.info("set content scale:{}", contentScale.value()); - videoLayer.scale = val; + log.info("set content scale:{}", val); viewController().placeEmuViews(); viewController().postDrawToEmuWindows(); return true; diff --git a/EmuFramework/src/EmuVideo.cc b/EmuFramework/src/EmuVideo.cc index 8de2c76a6..60d23a932 100644 --- a/EmuFramework/src/EmuVideo.cc +++ b/EmuFramework/src/EmuVideo.cc @@ -271,7 +271,7 @@ void EmuVideo::setOnFormatChanged(FormatChangedDelegate del) void EmuVideo::setTextureBufferMode(EmuSystem &sys, Gfx::TextureBufferMode mode) { - mode = renderer().makeValidTextureBufferMode(mode); + mode = renderer().evalTextureBufferMode(mode); if(bufferMode == mode) return; bufferMode = mode; diff --git a/EmuFramework/src/EmuVideoLayer.cc b/EmuFramework/src/EmuVideoLayer.cc index 8795aacf2..5cd293d1d 100644 --- a/EmuFramework/src/EmuVideoLayer.cc +++ b/EmuFramework/src/EmuVideoLayer.cc @@ -268,10 +268,10 @@ void EmuVideoLayer::setLinearFilter(bool on) video.setSampler(samplerConfig()); } -void EmuVideoLayer::setBrightness(Gfx::Vec3 b) +void EmuVideoLayer::updateBrightness() { - brightness = b; - brightnessSrgb = glm::convertSRGBToLinear(b); + brightness = brightnessUnscaled * brightnessScale; + brightnessSrgb = glm::convertSRGBToLinear(brightness); } void EmuVideoLayer::onVideoFormatChanged(IG::PixelFormat effectFmt) @@ -388,11 +388,43 @@ Gfx::ColorSpace EmuVideoLayer::videoColorSpace(IG::PixelFormat videoFmt) const Gfx::TextureSamplerConfig EmuVideoLayer::samplerConfig() const { return EmuVideo::samplerConfigForLinearFilter(useLinearFilter); } +static auto &channelBrightnessVal(ImageChannel ch, auto &brightnessUnscaled) +{ + switch(ch) + { + case ImageChannel::All: break; + case ImageChannel::Red: return brightnessUnscaled.r; + case ImageChannel::Green: return brightnessUnscaled.g; + case ImageChannel::Blue: return brightnessUnscaled.b; + } + bug_unreachable("invalid ImageChannel"); +} + +float EmuVideoLayer::channelBrightness(ImageChannel ch) const +{ + return channelBrightnessVal(ch, brightnessUnscaled); +} + +void EmuVideoLayer::setBrightness(float brightness, ImageChannel ch) +{ + if(ch == ImageChannel::All) + { + brightnessUnscaled.r = brightnessUnscaled.g = brightnessUnscaled.b = brightness; + } + else + { + channelBrightnessVal(ch, brightnessUnscaled) = brightness; + } + updateBrightness(); +} + bool EmuVideoLayer::readConfig(MapIO &io, unsigned key) { switch(key) { default: return false; + case CFGKEY_CONTENT_SCALE: return readOptionValue(io, scale); + case CFGKEY_VIDEO_BRIGHTNESS: return readOptionValue(io, brightnessUnscaled); case CFGKEY_GAME_IMG_FILTER: return readOptionValue(io, useLinearFilter); case CFGKEY_IMAGE_EFFECT: return readOptionValue(io, userEffectId, [](auto m){return m <= lastEnum;}); case CFGKEY_OVERLAY_EFFECT: return readOptionValue(io, userOverlayEffectId, [](auto m){return m <= lastEnum;}); @@ -402,6 +434,9 @@ bool EmuVideoLayer::readConfig(MapIO &io, unsigned key) void EmuVideoLayer::writeConfig(FileIO &io) const { + writeOptionValueIfNotDefault(io, scale); + if(brightnessUnscaled != Gfx::Vec3{1.f, 1.f, 1.f}) + writeOptionValue(io, CFGKEY_VIDEO_BRIGHTNESS, brightnessUnscaled); writeOptionValueIfNotDefault(io, CFGKEY_GAME_IMG_FILTER, useLinearFilter, true); writeOptionValueIfNotDefault(io, CFGKEY_IMAGE_EFFECT, userEffectId, ImageEffectId{}); writeOptionValueIfNotDefault(io, CFGKEY_OVERLAY_EFFECT, userOverlayEffectId, ImageOverlayId{}); diff --git a/EmuFramework/src/gui/ButtonConfigView.cc b/EmuFramework/src/gui/ButtonConfigView.cc index a6ece36bf..2662db5b1 100644 --- a/EmuFramework/src/gui/ButtonConfigView.cc +++ b/EmuFramework/src/gui/ButtonConfigView.cc @@ -48,18 +48,21 @@ ButtonConfigView::ButtonConfigView(ViewAttachParams attach, InputManagerView &ro { cat_.name, attach, - [this](const TableView &) + [this](ItemMessage msg) -> ItemReply { - return resetItemsSize + cat.keys.size(); - }, - [this](const TableView &, size_t idx) -> MenuItem& - { - if(idx == 0) - return resetDefaults; - else if(idx == 1) - return reset; - else - return btn[idx - resetItemsSize]; + return visit(overloaded + { + [&](const ItemsMessage &m) -> ItemReply { return resetItemsSize + cat.keys.size(); }, + [&](const GetItemMessage &m) -> ItemReply + { + if(m.idx == 0) + return &resetDefaults; + else if(m.idx == 1) + return &reset; + else + return &btn[m.idx - resetItemsSize]; + }, + }, msg); } }, rootIMView{rootIMView_}, diff --git a/EmuFramework/src/gui/Cheats.cc b/EmuFramework/src/gui/Cheats.cc index 52d6ac239..0fa3de9f6 100644 --- a/EmuFramework/src/gui/Cheats.cc +++ b/EmuFramework/src/gui/Cheats.cc @@ -25,16 +25,19 @@ BaseCheatsView::BaseCheatsView(ViewAttachParams attach): { "Cheats", attach, - [this](const TableView &) + [this](ItemMessage msg) -> ItemReply { - return 1 + cheat.size(); - }, - [this](const TableView &, size_t idx) -> MenuItem& - { - if(idx == 0) - return edit; - else - return cheat[idx - 1]; + return visit(overloaded + { + [&](const ItemsMessage &m) -> ItemReply { return 1 + cheat.size(); }, + [&](const GetItemMessage &m) -> ItemReply + { + if(m.idx == 0) + return &edit; + else + return &cheat[m.idx - 1]; + }, + }, msg); } }, edit @@ -55,13 +58,12 @@ BaseCheatsView::BaseCheatsView(ViewAttachParams attach): } } {} -BaseEditCheatListView::BaseEditCheatListView(ViewAttachParams attach, TableView::ItemsDelegate items, TableView::ItemDelegate item): +BaseEditCheatListView::BaseEditCheatListView(ViewAttachParams attach, TableView::ItemSourceDelegate itemSrc): TableView { "Edit Cheats", attach, - items, - item + itemSrc } {} void BaseEditCheatListView::setOnCheatListChanged(RefreshCheatsDelegate del) diff --git a/EmuFramework/src/gui/InputManagerView.cc b/EmuFramework/src/gui/InputManagerView.cc index ca6dd4739..ca4d85ba0 100644 --- a/EmuFramework/src/gui/InputManagerView.cc +++ b/EmuFramework/src/gui/InputManagerView.cc @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include @@ -283,14 +283,6 @@ void InputManagerView::pushAndShowDeviceView(const Input::Device &dev, const Inp pushAndShow(makeViewWithName(inputDevData(dev).displayName, *this, dev, inputManager), e); } -#ifdef CONFIG_BLUETOOTH_SCAN_SECS -static void setBTScanSecs(int secs) -{ - BluetoothAdapter::scanSecs = secs; - log.info("set bluetooth scan time {}", BluetoothAdapter::scanSecs); -} -#endif - InputManagerOptionsView::InputManagerOptionsView(ViewAttachParams attach, EmuInputView *emuInputView_): TableView{"General Input Options", attach, item}, mogaInputSystem @@ -330,74 +322,33 @@ InputManagerOptionsView::InputManagerOptionsView(ViewAttachParams attach, EmuInp app().keepBluetoothActive = item.flipBoolValue(*this); } }, - #ifdef CONFIG_BLUETOOTH_SCAN_SECS btScanSecsItem { - { - "2secs", attach, - [this]() - { - setBTScanSecs(2); - } - }, - { - "4secs", attach, - [this]() - { - setBTScanSecs(4); - } - }, - { - "6secs", attach, - [this]() - { - setBTScanSecs(6); - } - }, - { - "8secs", attach, - [this]() - { - setBTScanSecs(8); - } - }, - { - "10secs", attach, - [this]() - { - setBTScanSecs(10); - } - } + {"2secs", attach, MenuItem::Config{.id = 2}}, + {"4secs", attach, MenuItem::Config{.id = 4}}, + {"6secs", attach, MenuItem::Config{.id = 6}}, + {"8secs", attach, MenuItem::Config{.id = 8}}, + {"10secs", attach, MenuItem::Config{.id = 10}} }, btScanSecs { "Scan Time", attach, - []() + MenuId{app().bluetoothAdapter.scanSecs}, + btScanSecsItem, + MultiChoiceMenuItem::Config { - switch(BluetoothAdapter::scanSecs) - { - default: return 0; - case 2: return 0; - case 4: return 1; - case 6: return 2; - case 8: return 3; - case 10: return 4; - } - }(), - btScanSecsItem + .defaultItemOnSelect = [this](TextMenuItem &item) { app().bluetoothAdapter.scanSecs = item.id; } + } }, - #endif - #ifdef CONFIG_BLUETOOTH_SCAN_CACHE_USAGE btScanCache { "Cache Scan Results", attach, - BluetoothAdapter::scanCacheUsage(), + app().bluetoothAdapter.useScanCache, [this](BoolMenuItem &item) { - BluetoothAdapter::setScanCacheUsage(item.flipBoolValue(*this)); + app().bluetoothAdapter.useScanCache = item.flipBoolValue(*this); } }, - #endif altGamepadConfirm { "Swap Confirm/Cancel Keys", attach, @@ -431,12 +382,14 @@ InputManagerOptionsView::InputManagerOptionsView(ViewAttachParams attach, EmuInp { item.emplace_back(&keepBtActive); } - #ifdef CONFIG_BLUETOOTH_SCAN_SECS - item.emplace_back(&btScanSecs); - #endif - #ifdef CONFIG_BLUETOOTH_SCAN_CACHE_USAGE - item.emplace_back(&btScanCache); - #endif + if(used(btScanSecs)) + { + item.emplace_back(&btScanSecs); + } + if(used(btScanCache)) + { + item.emplace_back(&btScanCache); + } } } diff --git a/EmuFramework/src/gui/MainMenuView.cc b/EmuFramework/src/gui/MainMenuView.cc index 752974bf6..bfe6294ac 100644 --- a/EmuFramework/src/gui/MainMenuView.cc +++ b/EmuFramework/src/gui/MainMenuView.cc @@ -28,8 +28,7 @@ #include #include #include -#include -#include +#include #include #include @@ -47,7 +46,7 @@ class OptionCategoryView : public TableView, public EmuAppHelper static void handledFailedBTAdapterInit(ViewT &view, ViewAttachParams attach, const Input::Event &e) @@ -141,10 +140,11 @@ MainMenuView::MainMenuView(ViewAttachParams attach, bool customMenu): "Scan for Wiimotes/iCP/JS1", attach, [this](const Input::Event &e) { - if(app().bluetoothAdapter()) + app().bluetoothAdapter.openDefault(); + if(app().bluetoothAdapter.isOpen()) { - if(Bluetooth::scanForDevices(appContext(), *app().bluetoothAdapter(), - [this](BluetoothAdapter &, unsigned status, int arg) + if(Bluetooth::scanForDevices(appContext(), app().bluetoothAdapter, + [this](BluetoothAdapter &, BluetoothScanState status, int arg) { onScanStatus(app(), status, arg); })) @@ -176,21 +176,21 @@ MainMenuView::MainMenuView(ViewAttachParams attach, bool customMenu): } } }, - #ifdef CONFIG_BLUETOOTH_SERVER acceptPS3ControllerConnection { "Scan for PS3 Controller", attach, [this](const Input::Event &e) { - if(app().bluetoothAdapter()) + app().bluetoothAdapter.openDefault(); + if(app().bluetoothAdapter.isOpen()) { app().postMessage(4, "Prepare to push the PS button"); - auto startedScan = Bluetooth::listenForDevices(appContext(), *app().bluetoothAdapter(), - [this](BluetoothAdapter &bta, unsigned status, int arg) + auto startedScan = Bluetooth::listenForDevices(appContext(), app().bluetoothAdapter, + [this](BluetoothAdapter &bta, BluetoothScanState status, int arg) { switch(status) { - case BluetoothAdapter::INIT_FAILED: + case BluetoothScanState::InitFailed: { app().postErrorMessage(Config::envIsLinux ? 8 : 2, Config::envIsLinux ? @@ -198,7 +198,7 @@ MainMenuView::MainMenuView(ViewAttachParams attach, bool customMenu): "Bluetooth setup failed"); break; } - case BluetoothAdapter::SCAN_COMPLETE: + case BluetoothScanState::Complete: { app().postMessage(4, "Push the PS button on your controller\n(see website for pairing help)"); break; @@ -218,7 +218,6 @@ MainMenuView::MainMenuView(ViewAttachParams attach, bool customMenu): postDraw(); } }, - #endif about { "About", attach, @@ -242,11 +241,11 @@ MainMenuView::MainMenuView(ViewAttachParams attach, bool customMenu): } } -static void onScanStatus(EmuApp &app, unsigned status, int arg) +static void onScanStatus(EmuApp &app, BluetoothScanState status, int arg) { switch(status) { - case BluetoothAdapter::INIT_FAILED: + case BluetoothScanState::InitFailed: { if(Config::envIsIOS) { @@ -254,33 +253,33 @@ static void onScanStatus(EmuApp &app, unsigned status, int arg) } break; } - case BluetoothAdapter::SCAN_FAILED: + case BluetoothScanState::Failed: { app.postErrorMessage("Scan failed"); break; } - case BluetoothAdapter::SCAN_NO_DEVS: + case BluetoothScanState::NoDevs: { app.postMessage("No devices found"); break; } - case BluetoothAdapter::SCAN_PROCESSING: + case BluetoothScanState::Processing: { app.postMessage(2, 0, std::format("Checking {} device(s)...", arg)); break; } - case BluetoothAdapter::SCAN_NAME_FAILED: + case BluetoothScanState::NameFailed: { app.postErrorMessage("Failed reading a device name"); break; } - case BluetoothAdapter::SCAN_COMPLETE: + case BluetoothScanState::Complete: { int devs = Bluetooth::pendingDevs(); if(devs) { app.postMessage(2, 0, std::format("Connecting to {} device(s)...", devs)); - Bluetooth::connectPendingDevs(app.bluetoothAdapter()); + Bluetooth::connectPendingDevs(app.bluetoothAdapter); } else { @@ -288,7 +287,8 @@ static void onScanStatus(EmuApp &app, unsigned status, int arg) } break; } - /*case BluetoothAdapter::SOCKET_OPEN_FAILED: + case BluetoothScanState::Cancelled: break; + /*case BluetoothScanState::SocketOpenFailed: { app.postErrorMessage("Failed opening a Bluetooth connection"); }*/ @@ -323,9 +323,8 @@ void MainMenuView::loadStandardItems() if(used(scanWiimotes) && app().showsBluetoothScan) { item.emplace_back(&scanWiimotes); - #ifdef CONFIG_BLUETOOTH_SERVER - item.emplace_back(&acceptPS3ControllerConnection); - #endif + if(used(acceptPS3ControllerConnection)) + item.emplace_back(&acceptPS3ControllerConnection); item.emplace_back(&bluetoothDisconnect); } item.emplace_back(&benchmark); @@ -345,8 +344,14 @@ OptionCategoryView::OptionCategoryView(ViewAttachParams attach): { "Options", attach, - [this](const TableView &) { return EmuApp::hasGooglePlayStoreFeatures() ? std::size(subConfig) : std::size(subConfig)-1; }, - [this](const TableView &, size_t idx) -> MenuItem& { return subConfig[idx]; } + [this](ItemMessage msg) -> ItemReply + { + return visit(overloaded + { + [&](const ItemsMessage &m) -> ItemReply { return EmuApp::hasGooglePlayStoreFeatures() ? std::size(subConfig) : std::size(subConfig)-1; }, + [&](const GetItemMessage &m) -> ItemReply { return &subConfig[m.idx]; }, + }, msg); + } }, subConfig { diff --git a/EmuFramework/src/gui/PlaceVideoView.cc b/EmuFramework/src/gui/PlaceVideoView.cc index e001df3f3..8a9fce77f 100644 --- a/EmuFramework/src/gui/PlaceVideoView.cc +++ b/EmuFramework/src/gui/PlaceVideoView.cc @@ -30,13 +30,13 @@ PlaceVideoView::PlaceVideoView(ViewAttachParams attach, EmuVideoLayer &layer, VC quads{attach.rendererTask, {.size = 4}} { app().applyOSNavStyle(appContext(), true); - layer.setBrightness(app().videoBrightnessAsRGB()); + layer.setBrightnessScale(1.f); } PlaceVideoView::~PlaceVideoView() { app().applyOSNavStyle(appContext(), false); - layer.setBrightness(app().videoBrightnessAsRGB() * menuVideoBrightnessScale); + layer.setBrightnessScale(menuVideoBrightnessScale); } void PlaceVideoView::place() diff --git a/EmuFramework/src/gui/RecentContentView.cc b/EmuFramework/src/gui/RecentContentView.cc index 4f15688b0..b486b2af9 100644 --- a/EmuFramework/src/gui/RecentContentView.cc +++ b/EmuFramework/src/gui/RecentContentView.cc @@ -26,13 +26,13 @@ RecentContentView::RecentContentView(ViewAttachParams attach, RecentContent &rec TableView { "Recent Content", attach, - [this](const TableView &) + [this](TableView::ItemMessage msg) { - return 1 + recentItems.size(); - }, - [this](const TableView &, size_t idx) -> MenuItem& - { - return idx < recentItems.size() ? recentItems[idx] : clear; + return visit(overloaded + { + [&](const ItemsMessage &m) -> ItemReply { return 1 + recentItems.size(); }, + [&](const GetItemMessage &m) -> ItemReply { return m.idx < recentItems.size() ? &recentItems[m.idx] : &clear; }, + }, msg); } }, clear diff --git a/EmuFramework/src/gui/VideoOptionView.cc b/EmuFramework/src/gui/VideoOptionView.cc index a439e994e..6713c4ab3 100644 --- a/EmuFramework/src/gui/VideoOptionView.cc +++ b/EmuFramework/src/gui/VideoOptionView.cc @@ -38,7 +38,7 @@ static const char *autoWindowPixelFormatStr(IG::ApplicationContext ctx) constexpr uint16_t pack(Gfx::DrawableConfig c) { - return to_underlying(c.pixelFormat.id()) | to_underlying(c.colorSpace) << sizeof(c.colorSpace) * 8; + return to_underlying(c.pixelFormat.id) | to_underlying(c.colorSpace) << sizeof(c.colorSpace) * 8; } constexpr Gfx::DrawableConfig unpackDrawableConfig(uint16_t c) @@ -57,7 +57,7 @@ VideoOptionView::VideoOptionView(ViewAttachParams attach, EmuVideoLayer &videoLa items.emplace_back("Auto (Set optimal mode)", attach, [this](View &view) { app().textureBufferMode = Gfx::TextureBufferMode::DEFAULT; - auto defaultMode = renderer().makeValidTextureBufferMode(); + auto defaultMode = renderer().evalTextureBufferMode(); emuVideo().setTextureBufferMode(system(), defaultMode); textureBufferMode.setSelected(MenuId{defaultMode}); view.dismiss(); @@ -77,7 +77,7 @@ VideoOptionView::VideoOptionView(ViewAttachParams attach, EmuVideoLayer &videoLa textureBufferMode { "GPU Copy Mode", attach, - MenuId{renderer().makeValidTextureBufferMode(app().textureBufferMode)}, + MenuId{renderer().evalTextureBufferMode(app().textureBufferMode)}, textureBufferModeItem }, aspectRatioItem @@ -169,14 +169,14 @@ VideoOptionView::VideoOptionView(ViewAttachParams attach, EmuVideoLayer &videoLa contentScale { "Content Scale", attach, - MenuId{app().contentScale}, + MenuId{videoLayer_.scale}, contentScaleItems, { .onSetDisplayString = [this](auto idx, Gfx::Text &t) { - if(app().contentScale <= 200) + if(videoLayer.scale <= 200) { - t.resetString(std::format("{}%", app().contentScale.value())); + t.resetString(std::format("{}%", videoLayer.scale.value())); return true; } return false; @@ -444,7 +444,7 @@ VideoOptionView::VideoOptionView(ViewAttachParams attach, EmuVideoLayer &videoLa renderPixelFormat { "Render Color Format", attach, - MenuId{app().renderPixelFormat().id()}, + MenuId{app().renderPixelFormat.value().id}, renderPixelFormatItem, { .onSetDisplayString = [this](auto idx, Gfx::Text &t) @@ -464,7 +464,7 @@ VideoOptionView::VideoOptionView(ViewAttachParams attach, EmuVideoLayer &videoLa { "Default", attach, [this](View &v) { - app().setVideoBrightness(1.f, ImageChannel::All); + videoLayer.setBrightness(1.f, ImageChannel::All); setAllColorLevelsSelected(MenuId{100}); v.dismiss(); } @@ -473,17 +473,17 @@ VideoOptionView::VideoOptionView(ViewAttachParams attach, EmuVideoLayer &videoLa }, redItem { - {"Default", attach, [this](){ app().setVideoBrightness(1.f, ImageChannel::Red); }, {.id = 100}}, + {"Default", attach, [this](){ videoLayer.setBrightness(1.f, ImageChannel::Red); }, {.id = 100}}, {"Custom Value", attach, setVideoBrightnessCustomDel(ImageChannel::Red), {.id = defaultMenuId}}, }, greenItem { - {"Default", attach, [this](){ app().setVideoBrightness(1.f, ImageChannel::Green); }, {.id = 100}}, + {"Default", attach, [this](){ videoLayer.setBrightness(1.f, ImageChannel::Green); }, {.id = 100}}, {"Custom Value", attach, setVideoBrightnessCustomDel(ImageChannel::Green), {.id = defaultMenuId}}, }, blueItem { - {"Default", attach, [this](){ app().setVideoBrightness(1.f, ImageChannel::Blue); }, {.id = 100}}, + {"Default", attach, [this](){ videoLayer.setBrightness(1.f, ImageChannel::Blue); }, {.id = 100}}, {"Custom Value", attach, setVideoBrightnessCustomDel(ImageChannel::Blue), {.id = defaultMenuId}}, }, brightness @@ -497,12 +497,12 @@ VideoOptionView::VideoOptionView(ViewAttachParams attach, EmuVideoLayer &videoLa red { "Red", attach, - MenuId{app().videoBrightnessAsInt(ImageChannel::Red)}, + MenuId{videoLayer_.channelBrightnessAsInt(ImageChannel::Red)}, redItem, { .onSetDisplayString = [this](auto idx, Gfx::Text &t) { - t.resetString(std::format("{}%", app().videoBrightnessAsInt(ImageChannel::Red))); + t.resetString(std::format("{}%", videoLayer.channelBrightnessAsInt(ImageChannel::Red))); return true; } }, @@ -510,12 +510,12 @@ VideoOptionView::VideoOptionView(ViewAttachParams attach, EmuVideoLayer &videoLa green { "Green", attach, - MenuId{app().videoBrightnessAsInt(ImageChannel::Green)}, + MenuId{videoLayer_.channelBrightnessAsInt(ImageChannel::Green)}, greenItem, { .onSetDisplayString = [this](auto idx, Gfx::Text &t) { - t.resetString(std::format("{}%", app().videoBrightnessAsInt(ImageChannel::Green))); + t.resetString(std::format("{}%", videoLayer.channelBrightnessAsInt(ImageChannel::Green))); return true; } }, @@ -523,12 +523,12 @@ VideoOptionView::VideoOptionView(ViewAttachParams attach, EmuVideoLayer &videoLa blue { "Blue", attach, - MenuId{app().videoBrightnessAsInt(ImageChannel::Blue)}, + MenuId{videoLayer_.channelBrightnessAsInt(ImageChannel::Blue)}, blueItem, { .onSetDisplayString = [this](auto idx, Gfx::Text &t) { - t.resetString(std::format("{}%", app().videoBrightnessAsInt(ImageChannel::Blue))); + t.resetString(std::format("{}%", videoLayer.channelBrightnessAsInt(ImageChannel::Blue))); return true; } }, @@ -588,7 +588,7 @@ TextMenuItem::SelectDelegate VideoOptionView::setVideoBrightnessCustomDel(ImageC pushAndShowNewCollectValueRangeInputView(attachParams(), e, "Input 0 to 200", "", [=, this](CollectTextInputView &, auto val) { - app().setVideoBrightness(val / 100.f, ch); + videoLayer.setBrightness(val / 100.f, ch); if(ch == ImageChannel::All) setAllColorLevelsSelected(MenuId{val}); else diff --git a/EmuFramework/src/shared/mednafen-emuex/MDFNUtils.hh b/EmuFramework/src/shared/mednafen-emuex/MDFNUtils.hh index f443bbb5b..d37fda9f2 100644 --- a/EmuFramework/src/shared/mednafen-emuex/MDFNUtils.hh +++ b/EmuFramework/src/shared/mednafen-emuex/MDFNUtils.hh @@ -52,13 +52,13 @@ inline Mednafen::MDFN_Surface toMDFNSurface(IG::MutablePixmapView pix) MDFN_PixelFormat fmt = [&]() { - switch(pix.format().id()) + switch(pix.format().id) { case IG::PIXEL_BGRA8888: return MDFN_PixelFormat::ARGB32_8888; case IG::PIXEL_RGBA8888: return MDFN_PixelFormat::ABGR32_8888; case IG::PIXEL_RGB565: return MDFN_PixelFormat::RGB16_565; default: - bug_unreachable("format id == %d", pix.format().id()); + bug_unreachable("format id == %d", pix.format().id); }; }(); return {pix.data(), uint32(pix.w()), uint32(pix.h()), uint32(pix.pitchPx()), fmt}; diff --git a/GBA.emu/src/main/Cheats.cc b/GBA.emu/src/main/Cheats.cc index 6c5130b20..3fa38d582 100644 --- a/GBA.emu/src/main/Cheats.cc +++ b/GBA.emu/src/main/Cheats.cc @@ -37,19 +37,7 @@ EmuEditCheatView::EmuEditCheatView(ViewAttachParams attach, int cheatIdx, Refres "Edit Code", attach, cheatsList[cheatIdx].desc, - [this](const TableView &) - { - return 3; - }, - [this](const TableView &, unsigned idx) -> MenuItem& - { - switch(idx) - { - case 0: return name; - case 1: return code; - default: return remove; - } - }, + items, [this](TextMenuItem &item, View &parent, Input::Event e) { cheatsDelete(gGba.cpu, idx, true); @@ -60,6 +48,7 @@ EmuEditCheatView::EmuEditCheatView(ViewAttachParams attach, int cheatIdx, Refres }, onCheatListChanged_ }, + items{&name, &code, &remove}, code { "Code", @@ -163,18 +152,21 @@ EmuEditCheatListView::EmuEditCheatListView(ViewAttachParams attach): BaseEditCheatListView { attach, - [this](const TableView &) + [this](ItemMessage msg) -> ItemReply { - return 2 + cheat.size(); - }, - [this](const TableView &, size_t idx) -> MenuItem& - { - switch(idx) + return visit(overloaded { - case 0: return addGS12CBCode; - case 1: return addGS3Code; - default: return cheat[idx - 2]; - } + [&](const ItemsMessage &m) -> ItemReply { return 2 + cheat.size(); }, + [&](const GetItemMessage &m) -> ItemReply + { + switch(m.idx) + { + case 0: return &addGS12CBCode; + case 1: return &addGS3Code; + default: return &cheat[m.idx - 2]; + } + }, + }, msg); } }, addGS12CBCode diff --git a/GBA.emu/src/main/EmuCheatViews.hh b/GBA.emu/src/main/EmuCheatViews.hh index 4d2fdb747..f84f7f82e 100644 --- a/GBA.emu/src/main/EmuCheatViews.hh +++ b/GBA.emu/src/main/EmuCheatViews.hh @@ -50,6 +50,7 @@ public: void renamed(std::string_view); private: + std::array items; DualTextMenuItem code{}; int idx = 0; }; diff --git a/GBC.emu/src/main/Cheats.cc b/GBC.emu/src/main/Cheats.cc index 0598560e2..a7ede80a3 100644 --- a/GBC.emu/src/main/Cheats.cc +++ b/GBC.emu/src/main/Cheats.cc @@ -147,19 +147,7 @@ EmuEditCheatView::EmuEditCheatView(ViewAttachParams attach, GbcCheat &cheat_, Re "Edit Code", attach, cheat_.name, - [this](const TableView &) - { - return 3; - }, - [this](const TableView &, unsigned idx) -> MenuItem& - { - switch(idx) - { - case 0: return name; - case 1: return ggCode; - default: return remove; - } - }, + items, [this](TextMenuItem &, View &, Input::Event) { IG::eraseFirst(cheatList, *cheat); @@ -171,6 +159,7 @@ EmuEditCheatView::EmuEditCheatView(ViewAttachParams attach, GbcCheat &cheat_, Re }, onCheatListChanged_ }, + items{&name, &ggCode, &remove}, ggCode { "Code", @@ -216,17 +205,20 @@ EmuEditCheatListView::EmuEditCheatListView(ViewAttachParams attach): BaseEditCheatListView { attach, - [this](const TableView &) + [this](ItemMessage msg) -> ItemReply { - return 1 + cheat.size(); - }, - [this](const TableView &, size_t idx) -> MenuItem& - { - switch(idx) + return visit(overloaded { - case 0: return addGGGS; - default: return cheat[idx - 1]; - } + [&](const ItemsMessage &m) -> ItemReply { return 1 + cheat.size(); }, + [&](const GetItemMessage &m) -> ItemReply + { + switch(m.idx) + { + case 0: return &addGGGS; + default: return &cheat[m.idx - 1]; + } + }, + }, msg); } }, addGGGS diff --git a/GBC.emu/src/main/EmuCheatViews.hh b/GBC.emu/src/main/EmuCheatViews.hh index e8e156d5d..6b1f38b91 100644 --- a/GBC.emu/src/main/EmuCheatViews.hh +++ b/GBC.emu/src/main/EmuCheatViews.hh @@ -49,6 +49,7 @@ public: void renamed(std::string_view); private: + std::array items; DualTextMenuItem ggCode{}; GbcCheat *cheat{}; }; diff --git a/MD.emu/src/main/Cheats.cc b/MD.emu/src/main/Cheats.cc index fafe05f98..92d181439 100644 --- a/MD.emu/src/main/Cheats.cc +++ b/MD.emu/src/main/Cheats.cc @@ -501,19 +501,7 @@ EmuEditCheatView::EmuEditCheatView(ViewAttachParams attach, MdCheat &cheat_, Ref "Edit Code", attach, cheat_.name, - [this](const TableView &) - { - return 3; - }, - [this](const TableView &, unsigned idx) -> MenuItem& - { - switch(idx) - { - case 0: return name; - case 1: return code; - default: return remove; - } - }, + items, [this](TextMenuItem &, View &, Input::Event) { IG::eraseFirst(cheatList, *cheat); @@ -525,6 +513,7 @@ EmuEditCheatView::EmuEditCheatView(ViewAttachParams attach, MdCheat &cheat_, Ref }, onCheatListChanged_ }, + items{&name, &code, &remove}, code { "Code", @@ -588,17 +577,20 @@ EmuEditCheatListView::EmuEditCheatListView(ViewAttachParams attach): BaseEditCheatListView { attach, - [this](const TableView &) + [this](ItemMessage msg) -> ItemReply { - return 1 + cheat.size(); - }, - [this](const TableView &, size_t idx) -> MenuItem& - { - switch(idx) + return visit(overloaded { - case 0: return addCode; - default: return cheat[idx - 1]; - } + [&](const ItemsMessage &m) -> ItemReply { return 1 + cheat.size(); }, + [&](const GetItemMessage &m) -> ItemReply + { + switch(m.idx) + { + case 0: return &addCode; + default: return &cheat[m.idx - 1]; + } + }, + }, msg); } }, addCode diff --git a/MD.emu/src/main/EmuCheatViews.hh b/MD.emu/src/main/EmuCheatViews.hh index 1bb4499f3..3a55e1440 100644 --- a/MD.emu/src/main/EmuCheatViews.hh +++ b/MD.emu/src/main/EmuCheatViews.hh @@ -53,6 +53,7 @@ public: void renamed(std::string_view); private: + std::array items; DualTextMenuItem code{}; MdCheat *cheat{}; }; diff --git a/MSX.emu/src/main/EmuMenuViews.cc b/MSX.emu/src/main/EmuMenuViews.cc index eb8badfb7..ead3d8cb5 100644 --- a/MSX.emu/src/main/EmuMenuViews.cc +++ b/MSX.emu/src/main/EmuMenuViews.cc @@ -506,14 +506,7 @@ class MsxIOControlView : public TableView, public MainAppHelper int - { - return item.size(); - }, - [this](const TableView &, int idx) -> MenuItem& - { - return *item[idx]; - } + item } { for(auto slot : iotaCount(2)) diff --git a/NEO.emu/src/main/EmuMenuViews.cc b/NEO.emu/src/main/EmuMenuViews.cc index ecc65bbea..5d5592694 100644 --- a/NEO.emu/src/main/EmuMenuViews.cc +++ b/NEO.emu/src/main/EmuMenuViews.cc @@ -598,24 +598,15 @@ class UnibiosSwitchesView : public TableView memory.memcard[3] = val; } + std::array items{®ion, &system}; + public: UnibiosSwitchesView(ViewAttachParams attach): TableView { "Unibios Switches", attach, - [this](const TableView &) - { - return 2; - }, - [this](const TableView &, unsigned idx) -> MenuItem& - { - switch(idx) - { - case 0: return region; - default: return system; - } - } + items } {} diff --git a/NES.emu/src/main/Cheats.cc b/NES.emu/src/main/Cheats.cc index 6f74a8054..97554ea83 100644 --- a/NES.emu/src/main/Cheats.cc +++ b/NES.emu/src/main/Cheats.cc @@ -53,32 +53,35 @@ EmuEditCheatView::EmuEditCheatView(ViewAttachParams attach, unsigned cheatIdx, R u"", attach, u"", - [this](const TableView &) + [this](ItemMessage msg) -> ItemReply { - return type ? 3 : 5; - }, - [this](const TableView &, int idx) -> MenuItem& - { - if(type) - { - switch(idx) - { - case 0: return name; - case 1: return ggCode; - default: return remove; - } - } - else + return visit(overloaded { - switch(idx) + [&](const ItemsMessage &m) -> ItemReply { return type ? 3uz : 5uz; }, + [&](const GetItemMessage &m) -> ItemReply { - case 0: return name; - case 1: return addr; - case 2: return value; - case 3: return comp; - default: return remove; - } - } + if(type) + { + switch(m.idx) + { + case 0: return &name; + case 1: return &ggCode; + default: return &remove; + } + } + else + { + switch(m.idx) + { + case 0: return &name; + case 1: return &addr; + case 2: return &value; + case 3: return ∁ + default: return &remove; + } + } + }, + }, msg); }, [this](TextMenuItem &, View &, Input::Event) { @@ -307,18 +310,21 @@ EmuEditCheatListView::EmuEditCheatListView(ViewAttachParams attach): BaseEditCheatListView { attach, - [this](const TableView &) + [this](ItemMessage msg) -> ItemReply { - return 2 + cheat.size(); - }, - [this](const TableView &, size_t idx) -> MenuItem& - { - switch(idx) + return visit(overloaded { - case 0: return addGG; - case 1: return addRAM; - default: return cheat[idx - 2]; - } + [&](const ItemsMessage &m) -> ItemReply { return 2 + cheat.size(); }, + [&](const GetItemMessage &m) -> ItemReply + { + switch(m.idx) + { + case 0: return &addGG; + case 1: return &addRAM; + default: return &cheat[m.idx - 2]; + } + }, + }, msg); } }, addGG diff --git a/NES.emu/src/main/EmuMenuViews.cc b/NES.emu/src/main/EmuMenuViews.cc index cafee5032..92bb66e39 100644 --- a/NES.emu/src/main/EmuMenuViews.cc +++ b/NES.emu/src/main/EmuMenuViews.cc @@ -325,8 +325,8 @@ class CustomVideoOptionView : public VideoOptionView, public MainAppHelper } }; + std::array items{&setSide[0], &setSide[1], &setSide[2], &setSide[3], &insertEject}; + public: FDSControlView(ViewAttachParams attach): TableView { "FDS Control", attach, - [this](const TableView &) - { - return 5; - }, - [this](const TableView &, unsigned idx) -> MenuItem& - { - switch(idx) - { - case 0: return setSide[0]; - case 1: return setSide[1]; - case 2: return setSide[2]; - case 3: return setSide[3]; - default: return insertEject; - } - } + items } { setSide[0].setActive(0 < FCEU_FDSSides()); diff --git a/Saturn.emu/src/ss/vdp2_render.cpp b/Saturn.emu/src/ss/vdp2_render.cpp index aacc0eb9c..f85608a03 100644 --- a/Saturn.emu/src/ss/vdp2_render.cpp +++ b/Saturn.emu/src/ss/vdp2_render.cpp @@ -3211,21 +3211,20 @@ struct WQ_Entry uint32 Arg32; }; -static IG::RingBuffer WQ; +static IG::RingBuffer WQ; static INLINE void WWQ(uint16 command, uint32 arg32 = 0, uint16 arg16 = 0) { - WQ.push({command, arg16, arg32}, IG::RingBufferRWFlags{.blocking = true, .notifyOnBlock = true}); + WQ.push({command, arg16, arg32}, {.blocking = true, .flushSize = 64}); } static int RThreadEntry(void* data) { - bool Running = true; RThreadId = IG::thisThreadId(); - while(MDFN_LIKELY(Running)) + auto nextCommand = []() { return WQ.pop({.blocking = true}); }; + for(WQ_Entry entry = nextCommand(); entry.Command != COMMAND_EXIT; entry = nextCommand()) { - WQ_Entry entry = WQ.pop(IG::RingBufferRWFlags{.blocking = true}); WQ_Entry* wqe = &entry; switch(wqe->Command) @@ -3251,10 +3250,6 @@ static int RThreadEntry(void* data) case COMMAND_SET_LEM: UserLayerEnableMask = wqe->Arg32; break; - - case COMMAND_EXIT: - Running = false; - break; } // // @@ -3262,7 +3257,7 @@ static int RThreadEntry(void* data) WQ.notifyRead(); } - + WQ.notifyRead(); return 0; } @@ -3379,7 +3374,6 @@ void VDP2REND_StartFrame(EmulateSpecStruct* espec_arg, const bool clock28m, cons void VDP2REND_EndFrame(void) { - WQ.notifyWrite(); WQ.waitForSize(0); if(NextOutLine < VisibleLines) @@ -3459,7 +3453,6 @@ void VDP2REND_Write16_DB(uint32 A, uint16 DB) void VDP2REND_StateAction(StateMem* sm, const unsigned load, const bool data_only, uint16 (&rr)[0x100], uint16 (&cr)[2048], uint16 (&vr)[262144]) { - WQ.notifyWrite(); WQ.waitForSize(0); // // diff --git a/Snes9x/src/main/Cheats.cc b/Snes9x/src/main/Cheats.cc index 023a731ce..23fd8389e 100644 --- a/Snes9x/src/main/Cheats.cc +++ b/Snes9x/src/main/Cheats.cc @@ -189,21 +189,7 @@ EmuEditCheatView::EmuEditCheatView(ViewAttachParams attach, int cheatIdx, Refres "Edit Address/Values", attach, cheatName(cheatIdx), - [this](const TableView &) - { - return 5; - }, - [this](const TableView &, unsigned idx) -> MenuItem& - { - switch(idx) - { - case 0: return name; - case 1: return addr; - case 2: return value; - case 3: return saved; - default: return remove; - } - }, + items, [this](TextMenuItem &, View &, Input::Event) { deleteCheat(idx); @@ -214,6 +200,7 @@ EmuEditCheatView::EmuEditCheatView(ViewAttachParams attach, int cheatIdx, Refres }, onCheatListChanged_ }, + items{&name, &addr, &value, &saved, &remove}, addr { "Address", @@ -392,17 +379,20 @@ EmuEditCheatListView::EmuEditCheatListView(ViewAttachParams attach): BaseEditCheatListView { attach, - [this](const TableView &) + [this](ItemMessage msg) -> ItemReply { - return 1 + cheat.size(); - }, - [this](const TableView &, size_t idx) -> MenuItem& - { - switch(idx) + return visit(overloaded { - case 0: return addCode; - default: return cheat[idx - 1]; - } + [&](const ItemsMessage &m) -> ItemReply { return 1 + cheat.size(); }, + [&](const GetItemMessage &m) -> ItemReply + { + switch(m.idx) + { + case 0: return &addCode; + default: return &cheat[m.idx - 1]; + } + }, + }, msg); } }, addCode diff --git a/Snes9x/src/main/EmuCheatViews.hh b/Snes9x/src/main/EmuCheatViews.hh index 4bcef8e85..ac628a63f 100644 --- a/Snes9x/src/main/EmuCheatViews.hh +++ b/Snes9x/src/main/EmuCheatViews.hh @@ -42,6 +42,7 @@ public: void renamed(std::string_view); private: + std::array items; DualTextMenuItem addr{}, value{}, saved{}; int idx = 0; IG::StaticString<6> addrStr{}; diff --git a/imagine/doc/INSTALL-Android b/imagine/doc/INSTALL-Android index 54a8aa9e3..ee5125051 100644 --- a/imagine/doc/INSTALL-Android +++ b/imagine/doc/INSTALL-Android @@ -18,9 +18,9 @@ android_arch : Optional, default set by app 2. Dependencies =============== -If needed, install Clang r487747c (LLVM 17 dev version) or higher from android.googlesource.com via the script bundle/android/toolchains/get-clang-prebuilt.sh. Note that NDK r26 includes a sufficiently up to date version so this is optional. +Install the SDK platform for Android 14.0 (API 34), Android NDK Revision 27 (tested on 27.0.11718014), and the Android Support Library from Android Studio's SDK tools menu -Install the SDK platform for Android 14.0 (API 34), Android NDK Revision 26 (tested on 26.1.10909125), and the Android Support Library from Android Studio's SDK tools menu +The script bundle/android/toolchains/get-clang-prebuilt.sh can download an alternate version of Clang from android.googlesource.com, but this isn't needed with the current NDK. 3. Building =========== diff --git a/imagine/include/imagine/base/BaseApplication.hh b/imagine/include/imagine/base/BaseApplication.hh index 1d770d5b9..47594be50 100644 --- a/imagine/include/imagine/base/BaseApplication.hh +++ b/imagine/include/imagine/base/BaseApplication.hh @@ -33,6 +33,8 @@ namespace IG { +enum class BluetoothSocketState: uint8_t; + enum class ActivityState : uint8_t { PAUSED, @@ -114,7 +116,7 @@ public: void setSwappedConfirmKeys(std::optional); uint8_t keyEventFlags() const; bool processICadeKey(const Input::KeyEvent &, Window &); - void bluetoothInputDeviceStatus(ApplicationContext, Input::Device &, int status); + void bluetoothInputDeviceStatus(ApplicationContext, Input::Device&, BluetoothSocketState); protected: struct CommandMessage diff --git a/imagine/include/imagine/base/Error.hh b/imagine/include/imagine/base/Error.hh deleted file mode 100644 index fb50de81b..000000000 --- a/imagine/include/imagine/base/Error.hh +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -/* This file is part of Imagine. - - Imagine 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. - - Imagine 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 Imagine. If not, see */ - -#include -#include - -namespace IG -{ - -class ErrorCode : public std::error_code -{ -public: - ErrorCode() = default; - ErrorCode(int code); -}; - -class Error final: public std::exception -{ -public: - ErrorCode code{}; - - Error() = default; - Error(ErrorCode code): code{code} {} - const char* what() const noexcept final { return ""; } -}; - -} diff --git a/imagine/include/imagine/base/android/gralloc.h b/imagine/include/imagine/base/android/gralloc.h index cc164b381..592061f6f 100644 --- a/imagine/include/imagine/base/android/gralloc.h +++ b/imagine/include/imagine/base/android/gralloc.h @@ -292,7 +292,8 @@ struct android_native_buffer_t { #ifdef __cplusplus constexpr android_native_buffer_t() : - common{.magic = ANDROID_NATIVE_BUFFER_MAGIC, .version = sizeof(android_native_buffer_t)}, + common{.magic = ANDROID_NATIVE_BUFFER_MAGIC, .version = sizeof(android_native_buffer_t), + .reserved{}, .incRef{}, .decRef{}}, width{0}, height{0}, stride{0}, format{0}, usage{0}, reserved{}, handle{}, reserved_proc{} {} #endif diff --git a/imagine/include/imagine/bluetooth/AndroidBluetoothAdapter.hh b/imagine/include/imagine/bluetooth/AndroidBluetoothAdapter.hh index ef18d8965..f1bae5ea2 100644 --- a/imagine/include/imagine/bluetooth/AndroidBluetoothAdapter.hh +++ b/imagine/include/imagine/bluetooth/AndroidBluetoothAdapter.hh @@ -15,76 +15,68 @@ You should have received a copy of the GNU General Public License along with Imagine. If not, see */ -#include -#include "BluetoothAdapter.hh" +#include #include +#include #include #include +#include namespace IG { struct SocketStatusMessage; -class ErrorCode; -class AndroidBluetoothAdapter : public BluetoothAdapter +class BluetoothPendingSocket {}; + +class AndroidBluetoothAdapter { public: - constexpr AndroidBluetoothAdapter() = default; - static AndroidBluetoothAdapter *defaultAdapter(ApplicationContext); - bool startScan(OnStatusDelegate onResult, OnScanDeviceClassDelegate onDeviceClass, OnScanDeviceNameDelegate onDeviceName) final; - void cancelScan() final; - void close() final; #ifdef CONFIG_BLUETOOTH_SERVER - void setL2capService(uint32_t psm, bool active) final; + void setL2capService(uint32_t psm, bool active); #endif - State state() final; - void setActiveState(bool on, OnStateChangeDelegate onStateChange) final; bool handleScanClass(uint32_t classInt); void handleScanName(JNIEnv *env, jstring name, jstring addr); - void handleScanStatus(int status); + void handleScanStatus(BluetoothScanState status); void handleTurnOnResult(bool success); void sendSocketStatusMessage(const SocketStatusMessage &msg); jobject openSocket(JNIEnv *env, const char *addrStr, int channel, bool isL2cap); -private: +protected: jobject adapter{}; - OnStateChangeDelegate turnOnD; + BTOnStateChangeDelegate turnOnD; int statusPipe[2]{-1, -1}; - bool scanCancelled = false; - - bool openDefault(ApplicationContext); + bool scanCancelled{}; + bool inDetect{}; }; -class AndroidBluetoothSocket final: public BluetoothSocket +using BluetoothAdapterImpl = AndroidBluetoothAdapter; + +class AndroidBluetoothSocket { public: - AndroidBluetoothSocket() {} + constexpr AndroidBluetoothSocket() = default; AndroidBluetoothSocket(ApplicationContext ctx):ctx{ctx} {} ~AndroidBluetoothSocket(); - IG::ErrorCode openL2cap(BluetoothAdapter &, BluetoothAddr, uint32_t psm) final; - IG::ErrorCode openRfcomm(BluetoothAdapter &, BluetoothAddr, uint32_t channel) final; - #ifdef CONFIG_BLUETOOTH_SERVER - IG::ErrorCode open(BluetoothAdapter &, BluetoothPendingSocket &socket) final; - #endif void close(); - IG::ErrorCode write(const void *data, size_t size) final; - void onStatusDelegateMessage(int arg); + void onStatusDelegateMessage(BluetoothAdapter&, BluetoothSocketState); -private: +protected: jobject socket{}, outStream{}; ApplicationContext ctx{}; std::binary_semaphore connectSem{0}; FDEventSource fdSrc{}; int nativeFd = -1; - uint32_t channel = 0; - bool isClosing = false; - bool isL2cap = false; - bool isConnecting = false; + uint32_t channel{}; + bool isClosing{}; + bool isL2cap{}; + bool isConnecting{}; BluetoothAddrString addrStr{}; - IG::ErrorCode openSocket(BluetoothAdapter &, BluetoothAddr, uint32_t channel, bool l2cap); + void openSocket(BluetoothAdapter&, BluetoothAddr, uint32_t channel, bool l2cap); bool readPendingData(int events); }; +using BluetoothSocketImpl = AndroidBluetoothSocket; + } diff --git a/imagine/include/imagine/bluetooth/BluetoothAdapter.hh b/imagine/include/imagine/bluetooth/BluetoothAdapter.hh index fb517e28a..6295b7227 100644 --- a/imagine/include/imagine/bluetooth/BluetoothAdapter.hh +++ b/imagine/include/imagine/bluetooth/BluetoothAdapter.hh @@ -15,123 +15,74 @@ You should have received a copy of the GNU General Public License along with Imagine. If not, see */ -#include -#include +#include + +#if defined CONFIG_BLUETOOTH_ANDROID +#include "AndroidBluetoothAdapter.hh" +#elif defined CONFIG_BLUETOOTH_BTSTACK +#include "BtstackBluetoothAdapter.hh" +#elif defined CONFIG_BLUETOOTH_BLUEZ +#include "BluezBluetoothAdapter.hh" +#endif + #include #include -#include -#include +#include +#include namespace IG { -class BluetoothPendingSocket; -class ErrorCode; - -using BluetoothAddrString = std::array; - -struct BluetoothAddr -{ - constexpr BluetoothAddr() = default; - constexpr BluetoothAddr(const uint8_t b[6]): b{b[0], b[1], b[2], b[3], b[4], b[5]} {} - - const uint8_t *data() const - { - return b.data(); - } - - uint8_t *data() - { - return b.data(); - } - - constexpr bool operator ==(BluetoothAddr const& rhs) const = default; - -private: - std::arrayb{}; -} __attribute__((packed)); - -class BluetoothAdapter +class BluetoothAdapter : public BluetoothAdapterImpl { public: - enum { INIT_FAILED = 1, SCAN_FAILED, SCAN_PROCESSING, SCAN_NO_DEVS, SCAN_NAME_FAILED, - SCAN_COMPLETE, SCAN_CANCELLED/*, SOCKET_OPEN_FAILED*/ }; - enum State { STATE_OFF, STATE_ON, STATE_TURNING_OFF, STATE_TURNING_ON, STATE_ERROR }; - bool inDetect = false; - #ifdef CONFIG_BLUETOOTH_SCAN_CACHE_USAGE - static bool useScanCache; - #endif - #ifdef CONFIG_BLUETOOTH_SCAN_SECS - static uint32_t scanSecs; - #endif - using OnStateChangeDelegate = DelegateFunc; - using OnStatusDelegate = DelegateFunc; - using OnScanDeviceClassDelegate = DelegateFunc devClass)>; - using OnScanDeviceNameDelegate = DelegateFunc; - using OnIncomingL2capConnectionDelegate = DelegateFunc; - - constexpr BluetoothAdapter() = default; - static BluetoothAdapter *defaultAdapter(ApplicationContext); - virtual bool startScan(OnStatusDelegate onResult, OnScanDeviceClassDelegate onDeviceClass, OnScanDeviceNameDelegate onDeviceName) = 0; - virtual void cancelScan() = 0; - #ifdef CONFIG_BLUETOOTH_SCAN_CACHE_USAGE - static void setScanCacheUsage(bool on) { useScanCache = on; } - static bool scanCacheUsage() { return useScanCache; } - #endif - virtual void close() = 0; - virtual State state() = 0; - virtual void setActiveState(bool on, OnStateChangeDelegate onStateChange) = 0; - const OnStatusDelegate &onScanStatus() { return onScanStatusD; } - ApplicationContext appContext() const; - void setAppContext(ApplicationContext); - - #ifdef CONFIG_BLUETOOTH_SERVER - OnIncomingL2capConnectionDelegate &onIncomingL2capConnection() { return onIncomingL2capConnectionD; } - virtual void setL2capService(uint32_t psm, bool active, OnStatusDelegate onResult) = 0; - //virtual bool l2capServiceRegistered(uint32_t psm); - #endif + using State = BluetoothState; + using ScanState = BluetoothScanState; + using OnStateChangeDelegate = BTOnStateChangeDelegate; + using OnStatusDelegate = BTOnStatusDelegate; + using OnScanDeviceClassDelegate = BTOnScanDeviceClassDelegate; + using OnScanDeviceNameDelegate = BTOnScanDeviceNameDelegate; + using OnIncomingL2capConnectionDelegate = BTOnIncomingL2capConnectionDelegate; + + OnStatusDelegate onScanStatus; + OnScanDeviceClassDelegate onScanDeviceClass; + OnScanDeviceNameDelegate onScanDeviceName; + ConditionalMember onIncomingL2capConnection; + ConditionalMember scanSecs{4}; + ConditionalMember useScanCache{true}; + + BluetoothAdapter(ApplicationContext ctx):ctx{ctx} {} + bool openDefault(); + bool isOpen() const; + bool startScan(OnStatusDelegate, OnScanDeviceClassDelegate, OnScanDeviceNameDelegate); + void cancelScan(); + bool isInScan() const; + void close(); + State state(); + void setActiveState(bool on, OnStateChangeDelegate); + void setL2capService(uint32_t psm, bool active, OnStatusDelegate); + void requestName(BluetoothPendingSocket&, OnScanDeviceNameDelegate); + ApplicationContext appContext() const { return ctx; } protected: - OnStatusDelegate onScanStatusD; - OnScanDeviceClassDelegate onScanDeviceClassD; - OnScanDeviceNameDelegate onScanDeviceNameD; - #ifdef CONFIG_BLUETOOTH_SERVER - OnIncomingL2capConnectionDelegate onIncomingL2capConnectionD; - #endif ApplicationContext ctx{}; }; -class BluetoothSocket +class BluetoothSocket : public BluetoothSocketImpl { public: - constexpr BluetoothSocket() = default; - virtual ~BluetoothSocket() = default; - virtual IG::ErrorCode openL2cap(BluetoothAdapter &, BluetoothAddr, uint32_t psm) = 0; - virtual IG::ErrorCode openRfcomm(BluetoothAdapter &, BluetoothAddr, uint32_t channel) = 0; - #ifdef CONFIG_BLUETOOTH_SERVER - virtual IG::ErrorCode open(BluetoothAdapter &, BluetoothPendingSocket &socket) = 0; - #endif - virtual IG::ErrorCode write(const void *data, size_t size) = 0; - typedef DelegateFunc OnDataDelegate; - OnDataDelegate &onData() { return onDataD; } - typedef DelegateFunc OnStatusDelegate; - OnStatusDelegate &onStatus() { return onStatusD; } - enum { STATUS_CONNECTING, STATUS_CONNECT_ERROR, STATUS_OPENED, STATUS_READ_ERROR, STATUS_CLOSED }; - enum { OPEN_USAGE_NONE = 0, OPEN_USAGE_READ_EVENTS }; - -protected: - OnDataDelegate onDataD{}; - OnStatusDelegate onStatusD{}; -}; - -class BluetoothInputDevice : public Input::BaseDevice -{ -public: - BluetoothInputDevice(ApplicationContext, Input::Map, Input::DeviceTypeFlags, const char *name); - virtual IG::ErrorCode open(BluetoothAdapter &, Input::Device &) = 0; - -protected: - ApplicationContext ctx; + using OnDataDelegate = DelegateFunc; + using OnStatusDelegate = DelegateFunc; + using State = BluetoothSocketState; + + OnDataDelegate onData; + OnStatusDelegate onStatus; + + using BluetoothSocketImpl::BluetoothSocketImpl; + std::system_error openL2cap(BluetoothAdapter&, BluetoothAddr, uint32_t psm); + std::system_error openRfcomm(BluetoothAdapter&, BluetoothAddr, uint32_t channel); + std::system_error open(BluetoothAdapter&, BluetoothPendingSocket& socket); + ssize_t write(const void* data, size_t size); }; } diff --git a/imagine/include/imagine/bluetooth/BluetoothInputDevScanner.hh b/imagine/include/imagine/bluetooth/BluetoothInputDevScanner.hh deleted file mode 100644 index 9ed03cb62..000000000 --- a/imagine/include/imagine/bluetooth/BluetoothInputDevScanner.hh +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include - -namespace IG -{ -class ApplicationContext; -} - -namespace IG::Bluetooth -{ - -bool scanForDevices(ApplicationContext, BluetoothAdapter &, BluetoothAdapter::OnStatusDelegate); -bool listenForDevices(ApplicationContext, BluetoothAdapter &bta, const BluetoothAdapter::OnStatusDelegate &onScanStatus); -void closeDevices(BluetoothAdapter *bta); -void closeBT(BluetoothAdapter *bta); -uint32_t devsConnected(ApplicationContext); -uint32_t pendingDevs(); -void connectPendingDevs(BluetoothAdapter *bta); - -} diff --git a/imagine/include/imagine/bluetooth/BluetoothInputDevice.hh b/imagine/include/imagine/bluetooth/BluetoothInputDevice.hh new file mode 100644 index 000000000..30e66ded8 --- /dev/null +++ b/imagine/include/imagine/bluetooth/BluetoothInputDevice.hh @@ -0,0 +1,50 @@ +#pragma once + +/* This file is part of Imagine. + + Imagine 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. + + Imagine 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 Imagine. If not, see */ + +#include +#include +#include + +namespace IG +{ + +class ApplicationContext; + +class BluetoothInputDevice : public Input::BaseDevice +{ +public: + BluetoothInputDevice(ApplicationContext, Input::Map, Input::DeviceTypeFlags, const char* name); + virtual bool open(BluetoothAdapter&, Input::Device&) = 0; + +protected: + ApplicationContext ctx; +}; + +} + +namespace IG::Bluetooth +{ + +bool scanForDevices(ApplicationContext, BluetoothAdapter&, BTOnStatusDelegate); +bool listenForDevices(ApplicationContext, BluetoothAdapter&, const BTOnStatusDelegate&); +void closeDevices(BluetoothAdapter&); +void closeBT(BluetoothAdapter&); +size_t devsConnected(ApplicationContext); +size_t pendingDevs(); +void connectPendingDevs(BluetoothAdapter&); + +} diff --git a/imagine/include/imagine/bluetooth/BluezBluetoothAdapter.hh b/imagine/include/imagine/bluetooth/BluezBluetoothAdapter.hh index cb8c74318..5c4bf247d 100644 --- a/imagine/include/imagine/bluetooth/BluezBluetoothAdapter.hh +++ b/imagine/include/imagine/bluetooth/BluezBluetoothAdapter.hh @@ -15,12 +15,12 @@ You should have received a copy of the GNU General Public License along with Imagine. If not, see */ -#include -#include "BluetoothAdapter.hh" +#include #include #include #include #include +#include #ifdef CONFIG_BLUETOOTH_SERVER #include #endif @@ -28,18 +28,16 @@ namespace IG { -class ErrorCode; - class BluetoothPendingSocket { public: int fd = -1; - sockaddr_l2 addr {}; + sockaddr_l2 addr{}; constexpr BluetoothPendingSocket() = default; void close(); uint32_t channel() { return addr.l2_psm; } - void requestName(BluetoothAdapter::OnScanDeviceNameDelegate onDeviceName); + void requestName(BluetoothAdapter&, BTOnScanDeviceNameDelegate); explicit operator bool() const { @@ -47,31 +45,18 @@ public: } }; -class BluezBluetoothAdapter : public BluetoothAdapter +class BluezBluetoothAdapter { -public: - BluezBluetoothAdapter() {} - static BluezBluetoothAdapter *defaultAdapter(ApplicationContext); - bool startScan(OnStatusDelegate onResult, OnScanDeviceClassDelegate onDeviceClass, OnScanDeviceNameDelegate onDeviceName) final; - void cancelScan() final; - void close() final; - #ifdef CONFIG_BLUETOOTH_SERVER - void setL2capService(uint32_t psm, bool active, OnStatusDelegate onResult) final; - //bool l2capServiceRegistered(uint32_t psm) final; - #endif - void requestName(BluetoothPendingSocket &pending, OnScanDeviceNameDelegate onDeviceName); - State state() final; - void setActiveState(bool on, OnStateChangeDelegate onStateChange) final; - -private: +protected: int devId = -1, socket = -1; Pipe statusPipe{"BluezBluetoothAdapter::statusPipe"}; - bool scanCancelled = false; + bool scanCancelled{}; + bool inDetect{}; #ifdef CONFIG_BLUETOOTH_SERVER struct L2CapServer { - L2CapServer() {} - L2CapServer(uint32_t psm, int fd): psm(psm), fd(fd) {} + constexpr L2CapServer() = default; + constexpr L2CapServer(uint32_t psm, int fd): psm(psm), fd(fd) {} uint32_t psm = 0; int fd = -1; FDEventSource connectSrc; @@ -79,30 +64,27 @@ private: StaticArrayList serverList; #endif - bool openDefault(); - IG::ErrorCode doScan(const OnScanDeviceClassDelegate &onDeviceClass, const OnScanDeviceNameDelegate &onDeviceName); - void sendBTScanStatusDelegate(uint8_t type, uint8_t arg); + bool doScan(const BTOnScanDeviceClassDelegate&, const BTOnScanDeviceNameDelegate&); + void sendBTScanStatusDelegate(BluetoothScanState, uint8_t arg); }; -class BluezBluetoothSocket final: public BluetoothSocket +using BluetoothAdapterImpl = BluezBluetoothAdapter; + +class BluezBluetoothSocket { public: - BluezBluetoothSocket() {} + constexpr BluezBluetoothSocket() = default; BluezBluetoothSocket(ApplicationContext) {} ~BluezBluetoothSocket(); - IG::ErrorCode openL2cap(BluetoothAdapter &, BluetoothAddr, uint32_t psm) final; - IG::ErrorCode openRfcomm(BluetoothAdapter &, BluetoothAddr, uint32_t channel) final; - #ifdef CONFIG_BLUETOOTH_SERVER - IG::ErrorCode open(BluetoothAdapter &, BluetoothPendingSocket &) final; - #endif void close(); - IG::ErrorCode write(const void *data, size_t size) final; bool readPendingData(int events); -private: +protected: FDEventSource fdSrc{}; int fd = -1; void setupFDEvents(int events); }; +using BluetoothSocketImpl = BluezBluetoothSocket; + } diff --git a/imagine/include/imagine/bluetooth/BtstackBluetoothAdapter.hh b/imagine/include/imagine/bluetooth/BtstackBluetoothAdapter.hh index f7f1b5b34..930ccfe68 100644 --- a/imagine/include/imagine/bluetooth/BtstackBluetoothAdapter.hh +++ b/imagine/include/imagine/bluetooth/BtstackBluetoothAdapter.hh @@ -15,41 +15,31 @@ You should have received a copy of the GNU General Public License along with Imagine. If not, see */ -#include -#include "BluetoothAdapter.hh" +#include +#include #import namespace IG { -class ErrorCode; - -class BtstackBluetoothAdapter : public BluetoothAdapter +class BtstackBluetoothAdapter { public: - constexpr BtstackBluetoothAdapter() = default; - static BtstackBluetoothAdapter *defaultAdapter(ApplicationContext); - bool startScan(OnStatusDelegate onResult, OnScanDeviceClassDelegate onDeviceClass, OnScanDeviceNameDelegate onDeviceName) final; - void cancelScan() final; - void close() final; - void setL2capService(uint32_t psm, bool active, OnStatusDelegate onResult) final; - State state() final; - void setActiveState(bool on, OnStateChangeDelegate onStateChange) final; - void requestName(BluetoothPendingSocket &pending, OnScanDeviceNameDelegate onDeviceName); void packetHandler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); static void processCommands(); -private: - uint32_t state_ = HCI_STATE_OFF, scanResponses = 0; - bool isOpen = false; +protected: + uint32_t state_ = HCI_STATE_OFF, scanResponses{}; static bool cmdActive; - OnStatusDelegate setL2capServiceOnResult; - OnStateChangeDelegate onStateChangeD; + bool inDetect{}; + BTOnStatusDelegate setL2capServiceOnResult; + BTOnStateChangeDelegate onStateChange; - IG::ErrorCode openDefault(); bool isInactive(); }; +using BluetoothAdapterImpl = BtstackBluetoothAdapter; + class BluetoothPendingSocket { public: @@ -63,7 +53,7 @@ public: type(type), addr(addr), ch(ch), localCh(localCh) {} void close(); uint32_t channel() { return ch; } - void requestName(BluetoothAdapter::OnScanDeviceNameDelegate onDeviceName); + void requestName(BluetoothAdapter&, BTOnScanDeviceNameDelegate); explicit operator bool() const { @@ -71,37 +61,33 @@ public: } }; -class BtstackBluetoothSocket final: public BluetoothSocket +class BtstackBluetoothSocket { public: constexpr BtstackBluetoothSocket() = default; - constexpr BtstackBluetoothSocket(ApplicationContext) {} + BtstackBluetoothSocket(ApplicationContext ctx) {} ~BtstackBluetoothSocket(); - IG::ErrorCode openL2cap(BluetoothAdapter &, BluetoothAddr addr, uint32_t psm) final; - IG::ErrorCode openRfcomm(BluetoothAdapter &, BluetoothAddr addr, uint32_t channel) final; - #ifdef CONFIG_BLUETOOTH_SERVER - IG::ErrorCode open(BluetoothAdapter &, BluetoothPendingSocket &pending) final; - #endif void close(); - IG::ErrorCode write(const void *data, size_t size) final; const void *pin(uint32_t &size); void setPin(const void *pin, uint32_t size); - static BtstackBluetoothSocket *findSocket(const bd_addr_t addr, uint16_t ch); - static BtstackBluetoothSocket *findSocket(const bd_addr_t addr); - static BtstackBluetoothSocket *findSocket(uint16_t localCh); + static BluetoothSocket *findSocket(const bd_addr_t addr, uint16_t ch); + static BluetoothSocket *findSocket(const bd_addr_t addr); + static BluetoothSocket *findSocket(uint16_t localCh); static void handleL2capChannelOpened(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); static void handleRfcommChannelOpened(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); -private: +protected: uint32_t type = 0; BluetoothAddr addr{}; uint16_t ch = 0; uint16_t localCh = 0; public: uint16_t handle = 0; -private: +protected: const void *pinCode = nullptr; uint32_t pinSize = 0; }; +using BluetoothSocketImpl = BtstackBluetoothSocket; + } diff --git a/imagine/include/imagine/bluetooth/IControlPad.hh b/imagine/include/imagine/bluetooth/IControlPad.hh index 0513368c2..b100ceacc 100644 --- a/imagine/include/imagine/bluetooth/IControlPad.hh +++ b/imagine/include/imagine/bluetooth/IControlPad.hh @@ -15,24 +15,22 @@ You should have received a copy of the GNU General Public License along with Imagine. If not, see */ -#include -#include +#include +#include #include namespace IG { -class ErrorCode; - struct IControlPad : public BluetoothInputDevice { public: static constexpr std::array btClass{0x00, 0x1F, 0x00}; IControlPad(ApplicationContext, BluetoothAddr); - ErrorCode open(BluetoothAdapter &, Input::Device &) final; + bool open(BluetoothAdapter &, Input::Device &) final; void close(); - uint32_t statusHandler(Input::Device &, BluetoothSocket &, uint32_t status); + uint32_t statusHandler(Input::Device &, BluetoothSocket &, BluetoothSocketState status); bool dataHandler(Input::Device &, const char *packet, size_t size); const char *keyName(Input::Key k) const; std::span motionAxes() { return axis; } @@ -46,7 +44,7 @@ private: FUNC_GP_REPORTS, }; static constexpr float axisScaler = 1./127.; - BluetoothSocketSys sock; + BluetoothSocket sock; char inputBuffer[6]{}; uint32_t inputBufferPos = 0; int function = 0; diff --git a/imagine/include/imagine/bluetooth/PS3Controller.hh b/imagine/include/imagine/bluetooth/PS3Controller.hh index d908bf611..592a0715c 100644 --- a/imagine/include/imagine/bluetooth/PS3Controller.hh +++ b/imagine/include/imagine/bluetooth/PS3Controller.hh @@ -15,25 +15,23 @@ You should have received a copy of the GNU General Public License along with Imagine. If not, see */ -#include -#include +#include +#include #include namespace IG { -class ErrorCode; - class PS3Controller : public BluetoothInputDevice { public: PS3Controller(ApplicationContext, BluetoothAddr); - IG::ErrorCode open(BluetoothAdapter &, Input::Device &) final; - IG::ErrorCode open1Ctl(BluetoothAdapter &adapter, BluetoothPendingSocket &pending, Input::Device &); - IG::ErrorCode open2Int(BluetoothAdapter &adapter, BluetoothPendingSocket &pending); + bool open(BluetoothAdapter &, Input::Device &) final; + bool open1Ctl(BluetoothAdapter &adapter, BluetoothPendingSocket &pending, Input::Device &); + bool open2Int(BluetoothAdapter &adapter, BluetoothPendingSocket &pending); void close(); bool dataHandler(Input::Device &, const char *data, size_t size); - uint32_t statusHandler(Input::Device &, BluetoothSocket &, uint32_t status); + uint32_t statusHandler(Input::Device &, BluetoothSocket &, BluetoothSocketState status); void setLEDs(uint32_t player); const char *keyName(Input::Key k) const; std::span motionAxes() { return axis; }; @@ -49,7 +47,7 @@ private: {Input::Map::PS3PAD, Input::AxisId::Z, axisScaler}, // Right X Axis {Input::Map::PS3PAD, Input::AxisId::RZ, axisScaler} // Right Y Axis }; - BluetoothSocketSys ctlSock, intSock; + BluetoothSocket ctlSock, intSock; BluetoothAddr addr; static uint8_t playerLEDs(uint32_t player); diff --git a/imagine/include/imagine/bluetooth/Wiimote.hh b/imagine/include/imagine/bluetooth/Wiimote.hh index 36d69c693..a0c9722de 100644 --- a/imagine/include/imagine/bluetooth/Wiimote.hh +++ b/imagine/include/imagine/bluetooth/Wiimote.hh @@ -15,15 +15,13 @@ You should have received a copy of the GNU General Public License along with Imagine. If not, see */ -#include -#include +#include +#include #include namespace IG { -class ErrorCode; - struct WiimoteExtDevice : public Input::BaseDevice { WiimoteExtDevice() {} @@ -41,9 +39,9 @@ public: Wiimote(ApplicationContext, BluetoothAddr); ~Wiimote(); - ErrorCode open(BluetoothAdapter &, Input::Device &) final; + bool open(BluetoothAdapter &, Input::Device &) final; bool dataHandler(Input::Device &, const char *data, size_t size); - uint32_t statusHandler(Input::Device &, BluetoothSocket &, uint32_t status); + uint32_t statusHandler(Input::Device &, BluetoothSocket &, BluetoothSocketState status); void requestStatus(); void setLEDs(uint8_t player); void sendDataMode(uint8_t mode); @@ -54,7 +52,8 @@ public: static bool isSupportedClass(std::array devClass); private: - BluetoothSocketSys ctlSock, intSock; + BluetoothAdapter *btaPtr; + BluetoothSocket ctlSock, intSock; int extension = EXT_NONE; uint32_t function = FUNC_NONE; Input::Axis axis[4]; diff --git a/imagine/include/imagine/bluetooth/Zeemote.hh b/imagine/include/imagine/bluetooth/Zeemote.hh index d59b695a0..9a16e6cb1 100644 --- a/imagine/include/imagine/bluetooth/Zeemote.hh +++ b/imagine/include/imagine/bluetooth/Zeemote.hh @@ -15,24 +15,22 @@ You should have received a copy of the GNU General Public License along with Imagine. If not, see */ -#include -#include +#include +#include #include namespace IG { -class ErrorCode; - struct Zeemote : public BluetoothInputDevice { public: static constexpr std::array btClass{0x84, 0x05, 0x00}; Zeemote(ApplicationContext ctx, BluetoothAddr addr); - ErrorCode open(BluetoothAdapter &, Input::Device &) final; + bool open(BluetoothAdapter &, Input::Device &) final; void close(); - uint32_t statusHandler(Input::Device &, BluetoothSocket &, uint32_t status); + uint32_t statusHandler(Input::Device&, BluetoothSocket&, BluetoothSocketState); bool dataHandler(Input::Device &, const char *packet, size_t size); const char *keyName(Input::Key k) const; std::span motionAxes() { return axis; }; @@ -40,7 +38,7 @@ public: private: static constexpr float axisScaler = 1./127.; - BluetoothSocketSys sock; + BluetoothSocket sock; uint8_t inputBuffer[46]{}; bool prevBtnPush[4]{}; uint32_t inputBufferPos = 0; diff --git a/imagine/include/imagine/bluetooth/config.hh b/imagine/include/imagine/bluetooth/config.hh deleted file mode 100644 index 51bccd234..000000000 --- a/imagine/include/imagine/bluetooth/config.hh +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -/* This file is part of Imagine. - - Imagine 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. - - Imagine 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 Imagine. If not, see */ - -#include - -#if defined __ANDROID__ -#define CONFIG_BLUETOOTH_ANDROID -#elif defined __APPLE__ && TARGET_OS_IPHONE -#define CONFIG_BLUETOOTH_BTSTACK -#elif defined __linux__ -#define CONFIG_BLUETOOTH_BLUEZ -#endif - -#if !defined CONFIG_BLUETOOTH_SERVER - #if defined CONFIG_BLUETOOTH_BLUEZ || defined CONFIG_BLUETOOTH_BTSTACK - #define CONFIG_BLUETOOTH_SERVER - #endif -#endif - -#if defined CONFIG_BLUETOOTH_BLUEZ || defined CONFIG_BLUETOOTH_BTSTACK -#define CONFIG_BLUETOOTH_SCAN_CACHE_USAGE -#define CONFIG_BLUETOOTH_SCAN_SECS -#endif diff --git a/imagine/include/imagine/bluetooth/defs.hh b/imagine/include/imagine/bluetooth/defs.hh new file mode 100644 index 000000000..3b4064593 --- /dev/null +++ b/imagine/include/imagine/bluetooth/defs.hh @@ -0,0 +1,83 @@ +#pragma once + +/* This file is part of Imagine. + + Imagine 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. + + Imagine 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 Imagine. If not, see */ + +#include +#include +#include +#include + +#if defined __ANDROID__ +#define CONFIG_BLUETOOTH_ANDROID +#elif defined __APPLE__ && TARGET_OS_IPHONE +#define CONFIG_BLUETOOTH_BTSTACK +#elif defined __linux__ +#define CONFIG_BLUETOOTH_BLUEZ +#endif + +namespace Config::Bluetooth +{ + +#if defined CONFIG_BLUETOOTH_BLUEZ || defined CONFIG_BLUETOOTH_BTSTACK +#define CONFIG_BLUETOOTH_SERVER +constexpr bool server = true; +#else +constexpr bool server = false; +#endif + +#if defined CONFIG_BLUETOOTH_BLUEZ || defined CONFIG_BLUETOOTH_BTSTACK +#define CONFIG_BLUETOOTH_SCAN_CACHE_USAGE +#define CONFIG_BLUETOOTH_SCAN_SECS +constexpr bool scanCache = true; +constexpr bool scanTime = true; +#else +constexpr bool scanCache = false; +constexpr bool scanTime = false; +#endif + +} + +namespace IG +{ + +class BluetoothAdapter; +class BluetoothSocket; +class BluetoothPendingSocket; +struct BluetoothAddr; + +enum class BluetoothState: uint8_t { Off, On, TurningOff, TurningOn, Error }; +enum class BluetoothScanState: uint8_t { InitFailed = 1, Failed, Processing, NoDevs, NameFailed, Complete, Cancelled /*, SocketOpenFailed*/ }; + +// socket +enum class BluetoothSocketState: uint8_t { Connecting, ConnectError, Opened, ReadError, Closed }; + +using BTOnStateChangeDelegate = DelegateFunc; +using BTOnStatusDelegate = DelegateFunc; +using BTOnScanDeviceClassDelegate = DelegateFunc devClass)>; +using BTOnScanDeviceNameDelegate = DelegateFunc; +using BTOnIncomingL2capConnectionDelegate = DelegateFunc; + +using BluetoothAddrString = std::array; + +struct BluetoothAddr : public std::array +{ + using BaseArray = std::array; + using BaseArray::BaseArray; + + constexpr BluetoothAddr(const uint8_t b[6]): std::array{b[0], b[1], b[2], b[3], b[4], b[5]} {} +}; + +} diff --git a/imagine/include/imagine/bluetooth/sys.hh b/imagine/include/imagine/bluetooth/sys.hh deleted file mode 100644 index bcd06800a..000000000 --- a/imagine/include/imagine/bluetooth/sys.hh +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -/* This file is part of Imagine. - - Imagine 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. - - Imagine 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 Imagine. If not, see */ - -#include - -#if defined CONFIG_BLUETOOTH_ANDROID -#include "AndroidBluetoothAdapter.hh" -namespace IG { using BluetoothSocketSys = AndroidBluetoothSocket; } -#elif defined CONFIG_BLUETOOTH_BTSTACK -#include "BtstackBluetoothAdapter.hh" -namespace IG { using BluetoothSocketSys = BtstackBluetoothSocket; } -#elif defined CONFIG_BLUETOOTH_BLUEZ -#include "BluezBluetoothAdapter.hh" -namespace IG { using BluetoothSocketSys = BluezBluetoothSocket; } -#endif diff --git a/imagine/include/imagine/gfx/Renderer.hh b/imagine/include/imagine/gfx/Renderer.hh index ebd558247..c1b736054 100755 --- a/imagine/include/imagine/gfx/Renderer.hh +++ b/imagine/include/imagine/gfx/Renderer.hh @@ -115,7 +115,8 @@ public: Texture makeTexture(Data::PixmapSource, TextureSamplerConfig samplerConf = {}, bool makeMipmaps = true); PixmapBufferTexture makePixmapBufferTexture(TextureConfig config, TextureBufferMode mode = {}, bool singleBuffer = false); std::vector textureBufferModes(); - TextureBufferMode makeValidTextureBufferMode(TextureBufferMode mode = {}); + TextureBufferMode evalTextureBufferMode(TextureBufferMode mode = {}); + TextureBufferMode validateTextureBufferMode(TextureBufferMode); TextureSampler makeTextureSampler(TextureSamplerConfig); // color space control diff --git a/imagine/include/imagine/gui/AlertView.hh b/imagine/include/imagine/gui/AlertView.hh index dd340d2d1..ff47d2807 100644 --- a/imagine/include/imagine/gui/AlertView.hh +++ b/imagine/include/imagine/gui/AlertView.hh @@ -22,6 +22,7 @@ #include #include #include +#include namespace IG { @@ -29,26 +30,16 @@ namespace IG class BaseAlertView : public View { public: - BaseAlertView(ViewAttachParams attach, UTF16Convertible auto &&label, TableView::ItemsDelegate items, TableView::ItemDelegate item): + BaseAlertView(ViewAttachParams attach, UTF16Convertible auto &&label, TableView::ItemSourceDelegate items): View{attach}, bgQuads{attach.rendererTask, {.size = 2}}, text{attach.rendererTask, IG_forward(label), &defaultFace()}, menu { attach, - items, - item + items } { init(); } - BaseAlertView(ViewAttachParams attach, UTF16Convertible auto &&label, IG::Container auto &item): - BaseAlertView - { - attach, - IG_forward(label), - [&item](const TableView &) { return std::size(item); }, - [&item](const TableView &, size_t idx) -> MenuItem& { return indirect(std::data(item)[idx]); } - } {} - void place() override; bool inputEvent(const Input::Event &) override; void prepareDraw() override; @@ -90,11 +81,12 @@ public: YesNoAlertView(ViewAttachParams attach, UTF16Convertible auto &&label, UTF16Convertible auto &&yesStr, UTF16Convertible auto &&noStr, Delegates delegates): - BaseAlertView(attach, IG_forward(label), - [](const TableView &) -> size_t { return 2; }, - [this](const TableView &, size_t idx) -> MenuItem& { return idx == 0 ? yes : no; }), - yes{IG_forward(yesStr), attach, delegates.onYes}, - no{IG_forward(noStr), attach,delegates.onNo} {} + BaseAlertView{attach, IG_forward(label), yesNo}, + yesNo + { + TextMenuItem{IG_forward(yesStr), attach, delegates.onYes}, + TextMenuItem{IG_forward(noStr), attach, delegates.onNo} + } {} YesNoAlertView(ViewAttachParams attach, UTF16Convertible auto &&label, Delegates delegates): YesNoAlertView{attach, IG_forward(label), u"Yes", u"No", delegates} {} @@ -103,7 +95,7 @@ public: void setOnNo(TextMenuItem::SelectDelegate del); protected: - TextMenuItem yes, no; + std::array yesNo; }; } diff --git a/imagine/include/imagine/gui/FSPicker.hh b/imagine/include/imagine/gui/FSPicker.hh index f296f44b7..aaf745fb9 100644 --- a/imagine/include/imagine/gui/FSPicker.hh +++ b/imagine/include/imagine/gui/FSPicker.hh @@ -47,6 +47,21 @@ public: using OnSelectPathDelegate = DelegateFunc; enum class Mode : uint8_t { FILE, FILE_IN_DIR, DIR }; + struct FileEntry + { + static constexpr auto isDirFlag = bit(0); + + std::string path; + TextMenuItem text; + + FileEntry(ViewAttachParams attach, auto &&path, UTF16Convertible auto &&name): + path{IG_forward(path)}, text{IG_forward(name), attach} {} + bool isDir() const { return text.flags.user & isDirFlag; } + TextMenuItem &menuItem() { return text; } + }; + + enum class DepthMode { increment, decrement, reset }; + FSPicker(ViewAttachParams attach, Gfx::TextureSpan backRes, Gfx::TextureSpan closeRes, FilterFunc filter = {}, Mode mode = Mode::FILE, Gfx::GlyphTextureSet *face = {}); void place() override; @@ -73,20 +88,6 @@ public: bool onDocumentPicked(const DocumentPickerEvent&) override; protected: - struct FileEntry - { - static constexpr auto isDirFlag = bit(0); - - std::string path; - TextMenuItem text; - - FileEntry(ViewAttachParams attach, auto &&path, UTF16Convertible auto &&name): - path{IG_forward(path)}, text{IG_forward(name), attach} {} - bool isDir() const { return text.flags.user & isDirFlag; } - }; - - enum class DepthMode { increment, decrement, reset }; - FilterFunc filter{}; ViewStack controller; OnChangePathDelegate onChangePath_; diff --git a/imagine/include/imagine/gui/MenuItem.hh b/imagine/include/imagine/gui/MenuItem.hh index 86d840da1..1258fdfc3 100644 --- a/imagine/include/imagine/gui/MenuItem.hh +++ b/imagine/include/imagine/gui/MenuItem.hh @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -310,9 +311,12 @@ protected: class MultiChoiceMenuItem : public BaseDualTextMenuItem { public: + struct ItemsMessage {const MultiChoiceMenuItem& item;}; + struct GetItemMessage {const MultiChoiceMenuItem& item; size_t idx;}; + using ItemMessage = std::variant; + using ItemReply = std::variant; + using ItemSourceDelegate = MenuItemSourceDelegate; using SelectDelegate = DelegateFunc; - using ItemsDelegate = DelegateFunc; - using ItemDelegate = DelegateFunc; using SetDisplayStringDelegate = DelegateFunc; struct SelectedInit @@ -340,7 +344,7 @@ public: MultiChoiceMenuItem() = default; MultiChoiceMenuItem(UTF16Convertible auto &&name, ViewAttachParams attach, - SelectedInit selected, ItemsDelegate items, ItemDelegate item, Config conf = Config::defaultConfig()): + SelectedInit selected, ItemSourceDelegate itemSrc, Config conf = Config::defaultConfig()): BaseDualTextMenuItem{IG_forward(name), UTF16String{}, attach, toBaseConfig(conf)}, onSelect { @@ -350,8 +354,7 @@ public: item.defaultOnSelect(view, e); } }, - items_{items}, - item_{item}, + itemSrc{itemSrc}, onSetDisplayString{conf.onSetDisplayString}, selected_{selected.isId ? idxOfId(MenuId{selected.val}) : selected.val} {} @@ -360,7 +363,7 @@ public: MultiChoiceMenuItem { IG_forward(name), attach, selected, - itemsDelegate(IG_forward(item)), itemDelegate(IG_forward(item)), conf + ItemSourceDelegate{IG_forward(item)}, conf } { if(conf.defaultItemOnSelect) @@ -378,6 +381,7 @@ public: void compile() override; int selected() const; size_t items() const; + TextMenuItem& item(size_t idx) { return item(itemSrc, idx); } bool setSelected(int idx, View &view); bool setSelected(int idx); bool setSelected(MenuId, View &view); @@ -391,28 +395,12 @@ public: int idxOfId(MenuId); protected: - ItemsDelegate items_; - ItemDelegate item_; + ItemSourceDelegate itemSrc; SetDisplayStringDelegate onSetDisplayString; int selected_{}; void setDisplayString(size_t idx); - - static constexpr ItemsDelegate itemsDelegate(Container auto &&item) - { - if constexpr(std::is_rvalue_reference_v) - return [size = std::size(item)](const MultiChoiceMenuItem &) { return size; }; - else - return [&item](const MultiChoiceMenuItem &) { return std::size(item); }; - } - - static constexpr ItemDelegate itemDelegate(Container auto &&item) - { - if constexpr(std::is_rvalue_reference_v) - return [item](const MultiChoiceMenuItem &, size_t idx) -> TextMenuItem& { return std::data(item)[idx]; }; - else - return [&item](const MultiChoiceMenuItem &, size_t idx) -> TextMenuItem& { return std::data(item)[idx]; }; - } + TextMenuItem& item(ItemSourceDelegate, size_t idx); }; } diff --git a/imagine/include/imagine/gui/TableView.hh b/imagine/include/imagine/gui/TableView.hh index 4468cbd1b..0abb0bff7 100644 --- a/imagine/include/imagine/gui/TableView.hh +++ b/imagine/include/imagine/gui/TableView.hh @@ -37,29 +37,20 @@ class MenuItem; class TableView : public ScrollView { public: - using ItemsDelegate = DelegateFunc; - using ItemDelegate = DelegateFunc; + struct ItemsMessage {const TableView& item;}; + struct GetItemMessage {const TableView& item; size_t idx;}; + using ItemMessage = std::variant; + using ItemReply = std::variant; + using ItemSourceDelegate = MenuItemSourceDelegate; using SelectElementDelegate = DelegateFunc; - TableView(UTF16Convertible auto &&name, ViewAttachParams attach, ItemsDelegate items, ItemDelegate item): - ScrollView{attach}, items{items}, item{item}, nameStr{IG_forward(name)}, + TableView(UTF16Convertible auto &&name, ViewAttachParams attach, ItemSourceDelegate itemSrc): + ScrollView{attach}, itemSrc{itemSrc}, nameStr{IG_forward(name)}, selectQuads{attach.rendererTask, {.size = 1}}, separatorQuads{attach.rendererTask, {.size = maxSeparators, .usageHint = Gfx::BufferUsageHint::streaming}} {} - TableView(ViewAttachParams attach, Container auto &item): - TableView{UTF16String{}, attach, item} {} - - TableView(UTF16Convertible auto &&name, ViewAttachParams attach, Container auto &item): - TableView - { - IG_forward(name), - attach, - [&item](const TableView &) { return std::size(item); }, - [&item](const TableView &, size_t idx) -> MenuItem& { return indirect(std::data(item)[idx]); } - } {} - - TableView(ViewAttachParams attach, ItemsDelegate items, ItemDelegate item): - TableView{UTF16String{}, attach, items, item} {} + TableView(ViewAttachParams attach, ItemSourceDelegate itemSrc): + TableView{UTF16String{}, attach, itemSrc} {} void prepareDraw() override; void draw(Gfx::RendererCommands &__restrict__) override; @@ -74,6 +65,7 @@ public: void onAddedToController(ViewController *, const Input::Event &) override; void setFocus(bool focused) override; void setOnSelectElement(SelectElementDelegate del); + MenuItem& item(size_t idx) { return item(itemSrc, idx); } size_t cells() const; WSize cellSize() const; void highlightCell(int idx); @@ -84,12 +76,11 @@ public: std::u16string_view name() const override; void resetName(UTF16Convertible auto &&name) { nameStr = IG_forward(name); } void resetName() { nameStr.clear(); } - void setItemsDelegate(ItemsDelegate items_ = [](const TableView &){ return 0; }) { items = items_; } + void resetItemSource(ItemSourceDelegate src = [](ItemMessage) -> ItemReply { return 0uz; }) { itemSrc = src; } protected: static constexpr size_t maxSeparators = 32; - ItemsDelegate items{}; - ItemDelegate item{}; + ItemSourceDelegate itemSrc; SelectElementDelegate selectElementDel{}; UTF16String nameStr{}; Gfx::IQuads selectQuads; @@ -110,6 +101,7 @@ protected: int prevSelectableElement(int start, int items); bool handleTableInput(const Input::Event &, bool &movedSelected); virtual void drawElement(Gfx::RendererCommands &__restrict__, size_t i, MenuItem &item, WRect rect, int xIndent) const; + MenuItem& item(ItemSourceDelegate, size_t idx); }; } diff --git a/imagine/include/imagine/gui/viewDefs.hh b/imagine/include/imagine/gui/viewDefs.hh index 6e301756b..dd99390b8 100644 --- a/imagine/include/imagine/gui/viewDefs.hh +++ b/imagine/include/imagine/gui/viewDefs.hh @@ -17,6 +17,9 @@ #include #include +#include +#include +#include namespace IG::ViewDefs { @@ -36,4 +39,50 @@ struct TableUIState int scrollOffset{}; }; +template +class MenuItemSourceDelegate : public DelegateFunc +{ +public: + using DelegateFuncBase = DelegateFunc; + using DelegateFuncBase::DelegateFuncBase; + + constexpr MenuItemSourceDelegate(Container auto&& items): + DelegateFuncBase{itemsDelegate(IG_forward(items))} {} + + static constexpr ItemReply handleItemMessage(const ItemMessage& msg, auto& items) + { + return visit(overloaded + { + [&](const ItemsMessage& m) -> ItemReply { return std::size(items); }, + [&](const GetItemMessage& m) -> ItemReply + { + auto itemPtr = &indirect(std::data(items)[m.idx]); + if constexpr(requires {itemPtr->menuItem();}) + return &itemPtr->menuItem(); + else + return itemPtr; + }, + }, msg); + }; + + template + static constexpr MenuItemSourceDelegate itemsDelegate(T&& items) + { + if constexpr(std::is_rvalue_reference_v) + { + return [items](ItemMessage msg) { return handleItemMessage(msg, items); }; + } + else + { + return [&items](ItemMessage msg) { return handleItemMessage(msg, items); }; + } + } + + template + static constexpr MenuItemSourceDelegate itemsDelegate(std::span items) + { + return [items](ItemMessage msg) { return handleItemMessage(msg, items); }; + } +}; + } diff --git a/imagine/include/imagine/pixmap/PixelFormat.hh b/imagine/include/imagine/pixmap/PixelFormat.hh index 52c91ec1a..bc48180a5 100755 --- a/imagine/include/imagine/pixmap/PixelFormat.hh +++ b/imagine/include/imagine/pixmap/PixelFormat.hh @@ -52,10 +52,11 @@ constexpr PixelDesc PIXEL_DESC_RGBA8888_NATIVE = PIXEL_DESC_RGBA8888.nativeOrder class PixelFormat { public: + PixelFormatID id{PIXEL_NONE}; + constexpr PixelFormat() = default; - constexpr PixelFormat(PixelFormatID id): id_{id} {} - constexpr PixelFormatID id() const { return id_; } - constexpr operator PixelFormatID() const { return id_; } + constexpr PixelFormat(PixelFormatID id): id{id} {} + constexpr operator PixelFormatID() const { return id; } constexpr int offsetBytes(int x, int y, int pitchBytes) const { return desc().offsetBytes(x, y, pitchBytes); } constexpr int pixelBytes(int pixels) const { return desc().pixelBytes(pixels); } constexpr int bytesPerPixel() const { return desc().bytesPerPixel(); } @@ -63,8 +64,8 @@ public: constexpr const char *name() const { return desc().name(); } constexpr bool isGrayscale() const { return desc().isGrayscale(); } constexpr bool isBGROrder() const { return desc().isBGROrder(); } - explicit constexpr operator bool() const { return (bool)id_; } - constexpr PixelDesc desc() const { return desc(id_); } + explicit constexpr operator bool() const { return (bool)id; } + constexpr PixelDesc desc() const { return desc(id); } static constexpr PixelDesc desc(PixelFormatID id) { @@ -84,9 +85,6 @@ public: } return PIXEL_DESC_NONE; } - -protected: - PixelFormatID id_ = PIXEL_NONE; }; constexpr PixelFormat PIXEL_FMT_NONE{PIXEL_NONE}; diff --git a/imagine/include/imagine/pixmap/Pixmap.hh b/imagine/include/imagine/pixmap/Pixmap.hh index 9f80bf913..c70c54ac1 100644 --- a/imagine/include/imagine/pixmap/Pixmap.hh +++ b/imagine/include/imagine/pixmap/Pixmap.hh @@ -189,8 +189,8 @@ public: write(pixmap); return; } - auto srcFormatID = pixmap.format().id(); - switch(format().id()) + auto srcFormatID = pixmap.format().id; + switch(format().id) { case PIXEL_RGBA8888: switch(srcFormatID) diff --git a/imagine/include/imagine/util/container/RingBuffer.hh b/imagine/include/imagine/util/container/RingBuffer.hh index 45064843d..e4d97e27e 100644 --- a/imagine/include/imagine/util/container/RingBuffer.hh +++ b/imagine/include/imagine/util/container/RingBuffer.hh @@ -39,7 +39,7 @@ struct RingBufferConf struct RingBufferRWFlags { bool blocking:1{}; - bool notifyOnBlock:1{}; + size_t flushSize{}; }; struct RingBufferIdxPair @@ -110,8 +110,6 @@ public: IdxPair idxs{.read = readIdx.load(std::memory_order_relaxed), .write = writeIdx.load(std::memory_order_acquire)}; if(flags.blocking && empty(idxs)) { - if(flags.notifyOnBlock) - notifyWrite(); writeIdx.wait(idxs.write, std::memory_order_acquire); idxs.write = writeIdx.load(std::memory_order_acquire); } @@ -162,8 +160,7 @@ public: IdxPair idxs{.read = readIdx.load(std::memory_order_acquire), .write = writeIdx.load(std::memory_order_relaxed)}; if(flags.blocking && !freeSpace(idxs)) { - if(flags.notifyOnBlock) - notifyRead(); + notifyWrite(); readIdx.wait(idxs.read, std::memory_order_acquire); idxs.read = readIdx.load(std::memory_order_acquire); } @@ -183,6 +180,8 @@ public: auto span = beginWrite(buff.size(), flags); copy_n(buff.data(), span.size(), span.data()); endWrite(span); + if(flags.flushSize && (size() + span.size() >= flags.flushSize)) + notifyWrite(); return span.size(); } @@ -223,6 +222,9 @@ public: void waitForSize(size_t s) { + if(size() == s) + return; + notifyWrite(); IdxPair idxs = loadIdxs(); while(size(idxs) != s) { diff --git a/imagine/src/base/Base.mk b/imagine/src/base/Base.mk index 39abf57d3..5442df6ad 100644 --- a/imagine/src/base/Base.mk +++ b/imagine/src/base/Base.mk @@ -4,7 +4,6 @@ inc_base_common := 1 SRC += base/common/ApplicationContext.cc \ base/common/Application.cc \ base/common/Base.cc \ - base/common/Error.cc \ base/common/Screen.cc \ base/common/Window.cc diff --git a/imagine/src/base/android/ALooperEventLoop.cc b/imagine/src/base/android/ALooperEventLoop.cc index c9e1dee82..6fe03cfb2 100644 --- a/imagine/src/base/android/ALooperEventLoop.cc +++ b/imagine/src/base/android/ALooperEventLoop.cc @@ -150,7 +150,11 @@ static const char *aLooperPollResultStr(int res) void EventLoop::run() { - int res = ALooper_pollAll(-1, nullptr, nullptr, nullptr); + int res; + do + { + res = ALooper_pollOnce(-1, nullptr, nullptr, nullptr); + } while(res == ALOOPER_POLL_CALLBACK); if(res != ALOOPER_POLL_WAKE) log.debug("ALooper_pollAll returned:{}", aLooperPollResultStr(res)); } diff --git a/imagine/src/base/android/AndroidGLContext.cc b/imagine/src/base/android/AndroidGLContext.cc index 37e11fbb7..7d4555595 100644 --- a/imagine/src/base/android/AndroidGLContext.cc +++ b/imagine/src/base/android/AndroidGLContext.cc @@ -62,12 +62,12 @@ NativeWindowFormat GLManager::nativeWindowFormat(ApplicationContext, GLBufferCon bool GLManager::hasBufferConfig(GLBufferConfigAttributes attrs) const { - switch(attrs.pixelFormat.id()) + switch(attrs.pixelFormat.id) { case PIXEL_NONE: case PIXEL_RGB565: case PIXEL_RGBA8888: return true; - default: bug_unreachable("format id == %d", attrs.pixelFormat.id()); + default: bug_unreachable("format id == %d", attrs.pixelFormat.id); } } diff --git a/imagine/src/base/android/Application.cc b/imagine/src/base/android/Application.cc index 746785c6e..14538bd12 100644 --- a/imagine/src/base/android/Application.cc +++ b/imagine/src/base/android/Application.cc @@ -145,7 +145,7 @@ UniqueFileDescriptor AndroidApplication::openFileUriFd(JNIEnv *env, jobject base if(openFlags.test) return -1; else - throw std::system_error{ENOENT, std::system_category(), uri}; + throw std::system_error{ENOENT, std::generic_category(), uri}; } log.info("opened fd:{} file URI:{}", fd, uri); return fd; @@ -208,7 +208,7 @@ bool AndroidApplication::forEachInDirectoryUri(JNIEnv *env, jobject baseActivity if(flags.test) return false; else - throw std::system_error{ENOENT, std::system_category(), uri}; + throw std::system_error{ENOENT, std::generic_category(), uri}; } return true; } diff --git a/imagine/src/base/android/HardwareBuffer.cc b/imagine/src/base/android/HardwareBuffer.cc index 753ce3196..fc30f3cae 100644 --- a/imagine/src/base/android/HardwareBuffer.cc +++ b/imagine/src/base/android/HardwareBuffer.cc @@ -60,7 +60,8 @@ HardwareBuffer::HardwareBuffer(uint32_t w, uint32_t h, uint32_t format, uint32_t HardwareBuffer() { assumeExpr(AHardwareBuffer_allocate); - AHardwareBuffer_Desc hardwareDesc{.width = w, .height = h, .layers = 1, .format = format, .usage = usage}; + AHardwareBuffer_Desc hardwareDesc{.width = w, .height = h, .layers = 1, .format = format, .usage = usage, + .stride{}, .rfu0{}, .rfu1{}}; AHardwareBuffer *newBuff; if(AHardwareBuffer_allocate(&hardwareDesc, &newBuff) != 0) [[unlikely]] { diff --git a/imagine/src/base/android/compat.c b/imagine/src/base/android/compat.c index 454793d26..9ade066eb 100644 --- a/imagine/src/base/android/compat.c +++ b/imagine/src/base/android/compat.c @@ -5,10 +5,13 @@ #include #include #include +#include // Implementation of missing libc functions when compiling with newer NDK headers // and an old minimum SDK level, mostly from Bionic +int fstat64(int fd, struct stat64* buf) { return fstat(fd, (struct stat*)buf); } + void android_set_abort_message(const char* __msg) {} size_t __ctype_get_mb_cur_max(void) { return 1; } @@ -37,54 +40,6 @@ int grantpt(int __fd __attribute((unused))) { return 0; /* devpts does this all for us! */ } -int isalnum_l(int c, locale_t loc) { - return isalnum(c); -} - -int isalpha_l(int c, locale_t loc) { - return isalpha(c); -} - -int isblank_l(int c, locale_t loc) { - return isblank(c); -} - -int iscntrl_l(int c, locale_t loc) { - return iscntrl(c); -} - -int isdigit_l(int c, locale_t loc) { - return isdigit(c); -} - -int isgraph_l(int c, locale_t loc) { - return isgraph(c); -} - -int islower_l(int c, locale_t loc) { - return islower(c); -} - -int isprint_l(int c, locale_t loc) { - return isprint(c); -} - -int ispunct_l(int c, locale_t loc) { - return ispunct(c); -} - -int isspace_l(int c, locale_t loc) { - return isspace(c); -} - -int isupper_l(int c, locale_t loc) { - return isupper(c); -} - -int isxdigit_l(int c, locale_t loc) { - return isxdigit(c); -} - int iswalnum_l(wint_t c, locale_t loc) { return iswalnum(c); } @@ -133,14 +88,6 @@ int iswxdigit_l(wint_t c, locale_t loc) { return iswxdigit(c); } -int toupper_l(int c, locale_t loc) { - return toupper(c); -} - -int tolower_l(int c, locale_t loc) { - return tolower(c); -} - wint_t towupper_l(wint_t c, locale_t loc) { return towupper(c); } diff --git a/imagine/src/base/android/imagine-v9/src/main/java/com/imagine/BaseActivity.java b/imagine/src/base/android/imagine-v9/src/main/java/com/imagine/BaseActivity.java index 94314c581..2904ba879 100644 --- a/imagine/src/base/android/imagine-v9/src/main/java/com/imagine/BaseActivity.java +++ b/imagine/src/base/android/imagine-v9/src/main/java/com/imagine/BaseActivity.java @@ -282,15 +282,15 @@ void removeNotification() NotificationHelper.removeNotification(this); } - static native void onBTScanStatus(int result); - static native boolean onScanDeviceClass(int btClass); - static native void onScanDeviceName(String name, String addr); - static native void onBTOn(boolean success); + static native void onBTScanStatus(long nativeBta, int result); + static native boolean onScanDeviceClass(long nativeBta, int btClass); + static native void onScanDeviceName(long nativeBta, String name, String addr); + static native void onBTOn(long nativeBta, boolean success); - BluetoothAdapter btDefaultAdapter() + BluetoothAdapter btDefaultAdapter(long nativeBta) { //Log.i(logTag, "btDefaultAdapter()"); - return Bluetooth.defaultAdapter(); + return Bluetooth.defaultAdapter(nativeBta); } int btStartScan(BluetoothAdapter adapter) @@ -325,7 +325,7 @@ void btTurnOn() { if(requestCode == REQUEST_BT_ON) { - onBTOn(resultCode == RESULT_OK); + onBTOn(Bluetooth.nativeBta, resultCode == RESULT_OK); } else if(android.os.Build.VERSION.SDK_INT >= 21 && requestCode == REQUEST_OPEN_DOCUMENT_TREE && resultCode == RESULT_OK && intent != null && activityResultNativeUserData != 0) diff --git a/imagine/src/base/android/imagine-v9/src/main/java/com/imagine/Bluetooth.java b/imagine/src/base/android/imagine-v9/src/main/java/com/imagine/Bluetooth.java index 404ba712b..905ad888d 100644 --- a/imagine/src/base/android/imagine-v9/src/main/java/com/imagine/Bluetooth.java +++ b/imagine/src/base/android/imagine-v9/src/main/java/com/imagine/Bluetooth.java @@ -30,8 +30,9 @@ final class Bluetooth private static ArrayList devs = null; private static final Constructor l2capInsecureSocketConstructor = Util.getConstructor(BluetoothSocket.class, new Class[] {int.class, int.class, boolean.class, boolean.class, BluetoothDevice.class, int.class, ParcelUuid.class}); private static final Method createInsecureRfcommSocket = Util.getMethod(BluetoothDevice.class, "createInsecureRfcommSocket", new Class[] { int.class }); + static long nativeBta = 0; - static BluetoothAdapter defaultAdapter() + static BluetoothAdapter defaultAdapter(long nativeObj) { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if(adapter == null) @@ -39,6 +40,7 @@ static BluetoothAdapter defaultAdapter() //Log.i(logTag, "no bluetooth adapter found"); return null; } + nativeBta = nativeObj; return adapter; } @@ -47,11 +49,18 @@ static boolean startScan(Activity act, BluetoothAdapter adapter) if(devs == null) devs = new ArrayList(); devs.clear(); - if(adapter.isDiscovering()) - adapter.cancelDiscovery(); - act.registerReceiver(onDiscoveryFinished, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)); - act.registerReceiver(onDeviceFound, new IntentFilter(BluetoothDevice.ACTION_FOUND)); - return adapter.startDiscovery(); + try + { + if(adapter.isDiscovering()) + adapter.cancelDiscovery(); + act.registerReceiver(onDiscoveryFinished, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)); + act.registerReceiver(onDeviceFound, new IntentFilter(BluetoothDevice.ACTION_FOUND)); + return adapter.startDiscovery(); + } + catch(Exception e) + { + return false; + } } static void cancelScan(Activity act, BluetoothAdapter adapter) @@ -129,7 +138,7 @@ static BluetoothSocket openSocket(BluetoothAdapter adapter, String address, int context.unregisterReceiver(onDiscoveryFinished); context.unregisterReceiver(onDeviceFound); devs.clear(); - BaseActivity.onBTScanStatus(1); + BaseActivity.onBTScanStatus(nativeBta, 1); } }; @@ -154,9 +163,9 @@ static BluetoothSocket openSocket(BluetoothAdapter adapter, String address, int } } devs.add(found); - if(BaseActivity.onScanDeviceClass(found.getBluetoothClass().getDeviceClass())) + if(BaseActivity.onScanDeviceClass(nativeBta, found.getBluetoothClass().getDeviceClass())) { - BaseActivity.onScanDeviceName(found.getName(), found.getAddress()); + BaseActivity.onScanDeviceName(nativeBta, found.getName(), found.getAddress()); } } } diff --git a/imagine/src/base/android/proguard/imagine.cfg b/imagine/src/base/android/proguard/imagine.cfg index 2bb0743f5..bb26b9960 100644 --- a/imagine/src/base/android/proguard/imagine.cfg +++ b/imagine/src/base/android/proguard/imagine.cfg @@ -5,7 +5,7 @@ void addNotification(java.lang.String, java.lang.String, java.lang.String); void removeNotification(); com.imagine.TextEntry newTextEntry(java.lang.String, java.lang.String, int, int, int, int, int, long); - android.bluetooth.BluetoothAdapter btDefaultAdapter(); + android.bluetooth.BluetoothAdapter btDefaultAdapter(long); int btStartScan(android.bluetooth.BluetoothAdapter); void btCancelScan(android.bluetooth.BluetoothAdapter); android.bluetooth.BluetoothSocket btOpenSocket(android.bluetooth.BluetoothAdapter, java.lang.String, int, boolean); diff --git a/imagine/src/base/common/EGLContextBase.cc b/imagine/src/base/common/EGLContextBase.cc index 3bd8f0902..79ca2c0ab 100644 --- a/imagine/src/base/common/EGLContextBase.cc +++ b/imagine/src/base/common/EGLContextBase.cc @@ -55,10 +55,10 @@ static EGLAttrList glConfigAttrsToEGLAttrs(int renderableType, GLBufferConfigAtt // don't accept slow configs list.push_back(EGL_CONFIG_CAVEAT); list.push_back(EGL_NONE); - switch(attr.pixelFormat.id()) + switch(attr.pixelFormat.id) { default: - bug_unreachable("format id == %d", attr.pixelFormat.id()); + bug_unreachable("format id == %d", attr.pixelFormat.id); case PIXEL_NONE: break; // don't set any color bits case PIXEL_RGB565: diff --git a/imagine/src/base/common/Error.cc b/imagine/src/base/common/Error.cc deleted file mode 100644 index 0febf789f..000000000 --- a/imagine/src/base/common/Error.cc +++ /dev/null @@ -1,43 +0,0 @@ -/* This file is part of Imagine. - - Imagine 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. - - Imagine 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 Imagine. If not, see */ - -#include -#include - -namespace IG -{ - -class GeneralErrorCategory final : public std::error_category -{ -public: - [[nodiscard]] - const char* name() const noexcept final - { - return "general"; - } - - [[nodiscard]] - std::string message(int condition) const final - { - // TODO - return {}; - } -}; - -static GeneralErrorCategory generalErrorCategory{}; - -ErrorCode::ErrorCode(int code): std::error_code{code, generalErrorCategory} {} - -} diff --git a/imagine/src/base/iphone/IOSGLContext.mm b/imagine/src/base/iphone/IOSGLContext.mm index 3b5f60c18..ba52a6071 100644 --- a/imagine/src/base/iphone/IOSGLContext.mm +++ b/imagine/src/base/iphone/IOSGLContext.mm @@ -199,12 +199,12 @@ static EAGLRenderingAPI majorVersionToAPI(int version) bool GLManager::hasBufferConfig(GLBufferConfigAttributes attrs) const { - switch(attrs.pixelFormat.id()) + switch(attrs.pixelFormat.id) { case PIXEL_NONE: case PIXEL_RGB565: case PIXEL_RGBA8888: return true; - default: bug_unreachable("format id == %d", attrs.pixelFormat.id()); + default: bug_unreachable("format id == %d", attrs.pixelFormat.id); } } @@ -235,6 +235,8 @@ static EAGLRenderingAPI majorVersionToAPI(int version) return {}; } +bool GLManager::hasPresentationTime() const { return false; } + bool GLBufferConfig::maySupportGLES(GLDisplay, int majorVersion) const { return majorVersion >= 1 && majorVersion <= 3; diff --git a/imagine/src/base/iphone/IOSScreen.mm b/imagine/src/base/iphone/IOSScreen.mm index 2381a3641..9a0357cd9 100644 --- a/imagine/src/base/iphone/IOSScreen.mm +++ b/imagine/src/base/iphone/IOSScreen.mm @@ -175,4 +175,9 @@ - (void)onFrame:(CADisplayLink *)displayLink return {&frameRate_, 1}; } +void Screen::setVariableFrameTime(bool useVariableTime) +{ + // TODO +} + } diff --git a/imagine/src/base/linux/FBDevFrameTimer.cc b/imagine/src/base/linux/FBDevFrameTimer.cc index c93ebbfe6..2ffabceef 100644 --- a/imagine/src/base/linux/FBDevFrameTimer.cc +++ b/imagine/src/base/linux/FBDevFrameTimer.cc @@ -41,7 +41,7 @@ FBDevFrameTimer::FBDevFrameTimer(Screen &screen, EventLoop loop) auto fbdev = openDevice(); if(fbdev == -1) { - logErr("error opening device:%s", std::system_category().message(errno).c_str()); + logErr("error opening device:%s", std::generic_category().message(errno).c_str()); return; } int fd = eventfd(0, 0); @@ -124,7 +124,7 @@ bool FBDevFrameTimer::testSupport() auto fbdev = openDevice(); if(fbdev == -1) { - logErr("error opening device:%s", std::system_category().message(errno).c_str()); + logErr("error opening device:%s", std::generic_category().message(errno).c_str()); return false; } // test ioctl FBIO_WAITFORVSYNC diff --git a/imagine/src/base/x11/XGLContextEGL.cc b/imagine/src/base/x11/XGLContextEGL.cc index a7fca3a26..59e643fb1 100644 --- a/imagine/src/base/x11/XGLContextEGL.cc +++ b/imagine/src/base/x11/XGLContextEGL.cc @@ -117,7 +117,7 @@ NativeWindowFormat GLManager::nativeWindowFormat(ApplicationContext ctx, GLBuffe bool GLManager::hasBufferConfig(GLBufferConfigAttributes attrs) const { - if(attrs.pixelFormat.id() == PIXEL_NONE) + if(attrs.pixelFormat.id == PIXEL_NONE) return true; auto dpy = display(); auto configOpt = chooseConfig(dpy, 0, attrs, false); @@ -131,7 +131,7 @@ bool GLManager::hasBufferConfig(GLBufferConfigAttributes attrs) const eglGetConfigAttrib(dpy, config, attr, &val); return val; }; - switch(attrs.pixelFormat.id()) + switch(attrs.pixelFormat.id) { case PIXEL_RGB565: return eglConfigInt(dpy, *configOpt, EGL_BUFFER_SIZE) == 16 && @@ -139,7 +139,7 @@ bool GLManager::hasBufferConfig(GLBufferConfigAttributes attrs) const case PIXEL_RGBA8888: return eglConfigInt(dpy, *configOpt, EGL_BUFFER_SIZE) >= 24 && eglConfigInt(dpy, *configOpt, EGL_RED_SIZE) == 8; - default: bug_unreachable("format id == %d", attrs.pixelFormat.id()); + default: bug_unreachable("format id == %d", attrs.pixelFormat.id); } } diff --git a/imagine/src/bluetooth/AndroidBluetoothAdapter.cc b/imagine/src/bluetooth/AndroidBluetoothAdapter.cc index 66ddb8960..8489c8ab9 100644 --- a/imagine/src/bluetooth/AndroidBluetoothAdapter.cc +++ b/imagine/src/bluetooth/AndroidBluetoothAdapter.cc @@ -14,9 +14,8 @@ along with Imagine. If not, see */ #define LOGTAG "AndroidBT" -#include +#include #include -#include #include #include #include @@ -29,10 +28,11 @@ namespace IG struct SocketStatusMessage { + BluetoothSocket *socket{}; + BluetoothSocketState type{}; + constexpr SocketStatusMessage() {} - constexpr SocketStatusMessage(AndroidBluetoothSocket &socket, uint8_t type): socket(&socket), type(type) {} - AndroidBluetoothSocket *socket = nullptr; - uint8_t type = 0; + constexpr SocketStatusMessage(BluetoothSocket &socket, BluetoothSocketState type): socket(&socket), type(type) {} }; // From Android source header abort_socket.h @@ -42,13 +42,11 @@ struct asocket int abort_fd[2]; /* pipe used to abort */ }; -static AndroidBluetoothAdapter defaultAndroidAdapter; - static JNI::InstMethod jStartScan; static JNI::InstMethod jInRead; static JNI::InstMethod jGetFd; static JNI::InstMethod jState; -static JNI::InstMethod jDefaultAdapter; +static JNI::InstMethod jDefaultAdapter; static JNI::InstMethod jOpenSocket; static JNI::InstMethod jBtSocketInputStream, jBtSocketOutputStream; static JNI::InstMethod jBtSocketClose; @@ -65,29 +63,33 @@ void AndroidBluetoothAdapter::sendSocketStatusMessage(const SocketStatusMessage } } -static void JNICALL btScanStatus(JNIEnv* env, jobject thiz, jint res) +static void JNICALL btScanStatus(JNIEnv* env, jobject thiz, jlong btaAddr, jint res) { - defaultAndroidAdapter.handleScanStatus(res); + auto &bta = *reinterpret_cast(btaAddr); + bta.handleScanStatus(BluetoothScanState(res)); } -void AndroidBluetoothAdapter::handleScanStatus(int result) +void AndroidBluetoothAdapter::handleScanStatus(BluetoothScanState result) { + auto &bta = static_cast(*this); assert(inDetect); logMsg("scan complete"); if(scanCancelled) - onScanStatusD(*this, BluetoothAdapter::SCAN_CANCELLED, 0); + bta.onScanStatus(bta, BluetoothScanState::Cancelled, 0); else - onScanStatusD(*this, BluetoothAdapter::SCAN_COMPLETE, 0); + bta.onScanStatus(bta, BluetoothScanState::Complete, 0); inDetect = 0; } -static jboolean JNICALL scanDeviceClass(JNIEnv* env, jobject thiz, jint classInt) +static jboolean JNICALL scanDeviceClass(JNIEnv* env, jobject thiz, jlong btaAddr, jint classInt) { - return defaultAndroidAdapter.handleScanClass(classInt); + auto &bta = *reinterpret_cast(btaAddr); + return bta.handleScanClass(classInt); } bool AndroidBluetoothAdapter::handleScanClass(uint32_t classInt) { + auto &bta = static_cast(*this); if(scanCancelled) { logMsg("scan canceled while handling device class"); @@ -98,7 +100,7 @@ bool AndroidBluetoothAdapter::handleScanClass(uint32_t classInt) classByte[2] = classInt >> 16; classByte[1] = (classInt >> 8) & 0xff; classByte[0] = classInt & 0xff; - if(!onScanDeviceClassD(*this, classByte)) + if(!bta.onScanDeviceClass(bta, classByte)) { logMsg("skipping device due to class %X:%X:%X", classByte[0], classByte[1], classByte[2]); return 0; @@ -106,13 +108,15 @@ bool AndroidBluetoothAdapter::handleScanClass(uint32_t classInt) return 1; } -static void JNICALL scanDeviceName(JNIEnv* env, jobject thiz, jstring name, jstring addr) +static void JNICALL scanDeviceName(JNIEnv* env, jobject thiz, jlong btaAddr, jstring name, jstring addr) { - defaultAndroidAdapter.handleScanName(env, name, addr); + auto &bta = *reinterpret_cast(btaAddr); + bta.handleScanName(env, name, addr); } void AndroidBluetoothAdapter::handleScanName(JNIEnv* env, jstring name, jstring addr) { + auto &bta = static_cast(*this); if(scanCancelled) { logMsg("scan canceled while handling device name"); @@ -126,39 +130,39 @@ void AndroidBluetoothAdapter::handleScanName(JNIEnv* env, jstring name, jstring env->ReleaseStringUTFChars(addr, addrStr); } logMsg("got name %s", nameStr); - onScanDeviceNameD(*this, nameStr, addrByte); + bta.onScanDeviceName(bta, nameStr, addrByte); env->ReleaseStringUTFChars(name, nameStr); } -static void JNICALL turnOnResult(JNIEnv* env, jobject thiz, jboolean success) +static void JNICALL turnOnResult(JNIEnv* env, jobject thiz, jlong btaAddr, jboolean success) { - defaultAndroidAdapter.handleTurnOnResult(success); + auto &bta = *reinterpret_cast(btaAddr); + bta.handleTurnOnResult(success); } void AndroidBluetoothAdapter::handleTurnOnResult(bool success) { + auto &bta = static_cast(*this); logMsg("bluetooth power on result: %d", int(success)); if(turnOnD) { - turnOnD(*this, success ? STATE_ON : STATE_ERROR); + turnOnD(bta, success ? BluetoothState::On : BluetoothState::Error); turnOnD = {}; } } -bool AndroidBluetoothAdapter::openDefault(ApplicationContext ctx) +bool BluetoothAdapter::openDefault() { - if(adapter) + if(isOpen()) return true; - setAppContext(ctx); - // setup JNI auto env = ctx.mainThreadJniEnv(); if(!jDefaultAdapter) { logMsg("JNI setup"); auto baseActivityCls = (jclass)env->GetObjectClass(ctx.baseActivityObject()); - jDefaultAdapter = {env, baseActivityCls, "btDefaultAdapter", "()Landroid/bluetooth/BluetoothAdapter;"}; + jDefaultAdapter = {env, baseActivityCls, "btDefaultAdapter", "(J)Landroid/bluetooth/BluetoothAdapter;"}; jStartScan = {env, baseActivityCls, "btStartScan", "(Landroid/bluetooth/BluetoothAdapter;)I"}; jCancelScan = {env, baseActivityCls, "btCancelScan", "(Landroid/bluetooth/BluetoothAdapter;)V"}; jOpenSocket = {env, baseActivityCls, "btOpenSocket", "(Landroid/bluetooth/BluetoothAdapter;Ljava/lang/String;IZ)Landroid/bluetooth/BluetoothSocket;"}; @@ -208,17 +212,17 @@ bool AndroidBluetoothAdapter::openDefault(ApplicationContext ctx) static JNINativeMethod activityMethods[] = { - {"onBTScanStatus", "(I)V", (void *)&btScanStatus}, - {"onScanDeviceClass", "(I)Z", (void *)&scanDeviceClass}, - {"onScanDeviceName", "(Ljava/lang/String;Ljava/lang/String;)V", (void *)&scanDeviceName}, - {"onBTOn", "(Z)V", (void *)&turnOnResult} + {"onBTScanStatus", "(JI)V", (void *)&btScanStatus}, + {"onScanDeviceClass", "(JI)Z", (void *)&scanDeviceClass}, + {"onScanDeviceName", "(JLjava/lang/String;Ljava/lang/String;)V", (void *)&scanDeviceName}, + {"onBTOn", "(JZ)V", (void *)&turnOnResult} }; env->RegisterNatives(baseActivityCls, activityMethods, std::size(activityMethods)); } logMsg("opening default BT adapter"); - adapter = jDefaultAdapter(env, ctx.baseActivityObject()); + adapter = jDefaultAdapter(env, ctx.baseActivityObject(), (jlong)this); if(!adapter) { logErr("error opening adapter"); @@ -226,7 +230,6 @@ bool AndroidBluetoothAdapter::openDefault(ApplicationContext ctx) } adapter = env->NewGlobalRef(adapter); assert(adapter); - { int ret = pipe(statusPipe); assert(ret == 0); @@ -244,22 +247,25 @@ bool AndroidBluetoothAdapter::openDefault(ApplicationContext ctx) return 1; } logMsg("got bluetooth socket status delegate message"); - msg.socket->onStatusDelegateMessage(msg.type); + auto &bta = *static_cast(data); + msg.socket->onStatusDelegateMessage(bta, msg.type); } return 1; - }, nullptr); + }, this); assert(ret == 1); } return true; } -void AndroidBluetoothAdapter::cancelScan() +bool BluetoothAdapter::isOpen() const { return adapter; } + +void BluetoothAdapter::cancelScan() { scanCancelled = 1; jCancelScan(appContext().mainThreadJniEnv(), appContext().baseActivityObject(), adapter); } -void AndroidBluetoothAdapter::close() +void BluetoothAdapter::close() { if(inDetect) { @@ -268,15 +274,7 @@ void AndroidBluetoothAdapter::close() } } -AndroidBluetoothAdapter *AndroidBluetoothAdapter::defaultAdapter(ApplicationContext ctx) -{ - if(defaultAndroidAdapter.openDefault(ctx)) - return &defaultAndroidAdapter; - else - return nullptr; -} - -bool AndroidBluetoothAdapter::startScan(OnStatusDelegate onResult, OnScanDeviceClassDelegate onDeviceClass, OnScanDeviceNameDelegate onDeviceName) +bool BluetoothAdapter::startScan(OnStatusDelegate onResult, OnScanDeviceClassDelegate onDeviceClass, OnScanDeviceNameDelegate onDeviceName) { if(!inDetect) { @@ -284,11 +282,11 @@ bool AndroidBluetoothAdapter::startScan(OnStatusDelegate onResult, OnScanDeviceC auto doScan = [this](BluetoothAdapter &, State newState) { - if(newState != STATE_ON) + if(newState != BluetoothState::On) { logMsg("failed to turn on bluetooth"); inDetect = 0; - onScanStatusD(*this, SCAN_FAILED, 0); + onScanStatus(*this, BluetoothScanState::Failed, 0); return; } @@ -297,22 +295,22 @@ bool AndroidBluetoothAdapter::startScan(OnStatusDelegate onResult, OnScanDeviceC { inDetect = 0; logMsg("failed to start scan"); - onScanStatusD(*this, SCAN_FAILED, 0); + onScanStatus(*this, BluetoothScanState::Failed, 0); } }; scanCancelled = 0; inDetect = 1; - onScanStatusD = onResult; - onScanDeviceClassD = onDeviceClass; - onScanDeviceNameD = onDeviceName; - if(state() != STATE_ON) + onScanStatus = onResult; + onScanDeviceClass = onDeviceClass; + onScanDeviceName = onDeviceName; + if(state() != BluetoothState::On) { setActiveState(true, doScan); } else { - doScan(*this, STATE_ON); + doScan(*this, BluetoothState::On); } return 1; } @@ -323,54 +321,59 @@ bool AndroidBluetoothAdapter::startScan(OnStatusDelegate onResult, OnScanDeviceC } } -BluetoothAdapter::State AndroidBluetoothAdapter::state() +BluetoothAdapter::State BluetoothAdapter::state() { auto currState = jState(appContext().mainThreadJniEnv(), appContext().baseActivityObject(), adapter); switch(currState) { - case 10: return STATE_OFF; - case 12: return STATE_ON; - case 13: return STATE_TURNING_OFF; - case 11: return STATE_TURNING_ON; + case 10: return BluetoothState::Off; + case 12: return BluetoothState::On; + case 13: return BluetoothState::TurningOff; + case 11: return BluetoothState::TurningOn; } logMsg("unknown state: %d", currState); - return STATE_OFF; + return BluetoothState::Off; } -void AndroidBluetoothAdapter::setActiveState(bool on, OnStateChangeDelegate onStateChange) +void BluetoothAdapter::setActiveState(bool on, OnStateChangeDelegate onStateChange) { + auto &bta = static_cast(*this); if(on) { auto currState = state(); - if(currState != STATE_ON) + if(currState != BluetoothState::On) { logMsg("radio is off, requesting activation"); turnOnD = onStateChange; - jTurnOn(appContext().mainThreadJniEnv(), appContext().baseActivityObject()); + jTurnOn(bta.appContext().mainThreadJniEnv(), appContext().baseActivityObject()); } else { - onStateChange(*this, STATE_ON); + onStateChange(*this, BluetoothState::On); } } else { - onStateChange(*this, STATE_ERROR); + onStateChange(*this, BluetoothState::Error); } } jobject AndroidBluetoothAdapter::openSocket(JNIEnv *env, const char *addrStr, int channel, bool isL2cap) { - return jOpenSocket(env, appContext().baseActivityObject(), adapter, env->NewStringUTF(addrStr), channel, isL2cap ? 1 : 0); + auto &bta = static_cast(*this); + return jOpenSocket(env, bta.appContext().baseActivityObject(), adapter, env->NewStringUTF(addrStr), channel, isL2cap ? 1 : 0); } +bool BluetoothAdapter::isInScan() const { return inDetect; } + bool AndroidBluetoothSocket::readPendingData(int events) { + auto &sock = static_cast(*this); if(events & POLLEV_ERR) { logMsg("socket %d disconnected", nativeFd); - onStatusD(*this, STATUS_READ_ERROR); + sock.onStatus(sock, BluetoothSocketState::ReadError); return false; } else if(events & POLLEV_IN) @@ -383,26 +386,27 @@ bool AndroidBluetoothSocket::readPendingData(int events) if(len <= 0) [[unlikely]] { logMsg("error %d reading packet from socket %d", len == -1 ? errno : 0, nativeFd); - onStatusD(*this, STATUS_READ_ERROR); + sock.onStatus(sock, BluetoothSocketState::ReadError); return false; } //logMsg("read %d bytes from socket %d", len, nativeFd); - if(!onDataD(buff, len)) + if(!sock.onData(buff, len)) break; // socket was closed } } return true; } -void AndroidBluetoothSocket::onStatusDelegateMessage(int status) +void AndroidBluetoothSocket::onStatusDelegateMessage(BluetoothAdapter& bta, BluetoothSocketState status) { - if(status != STATUS_OPENED) + auto &sock = static_cast(*this); + if(status != BluetoothSocketState::Opened) { // error - onStatusD(*this, status); + sock.onStatus(sock, status); return; } - if(onStatusD(*this, STATUS_OPENED) == OPEN_USAGE_READ_EVENTS) + if(sock.onStatus(sock, BluetoothSocketState::Opened) == 1) { if(nativeFd != -1) { @@ -416,10 +420,11 @@ void AndroidBluetoothSocket::onStatusDelegateMessage(int status) { logMsg("starting read thread"); IG::makeDetachedThread( - [this]() + [this, &bta]() { if(Config::DEBUG_BUILD) logMsg("in read thread %d", gettid()); + auto &sock = static_cast(*this); JNIEnv *env = ctx.thisThreadJniEnv(); if(!env) { @@ -438,7 +443,7 @@ void AndroidBluetoothSocket::onStatusDelegateMessage(int status) { if(events & POLLEV_ERR) return 0; - auto &socket = *((AndroidBluetoothSocket*)data); + auto &socket = *((BluetoothSocket*)data); while(fd_bytesReadable(fd)) { uint16_t size; @@ -455,7 +460,7 @@ void AndroidBluetoothSocket::onStatusDelegateMessage(int status) logErr("error reading BT socket data header in pipe, returned %d", ret); return 1; } - socket.onData()(&data[0], size); + socket.onData(&data[0], size); } return 1; }, this); @@ -483,7 +488,7 @@ void AndroidBluetoothSocket::onStatusDelegateMessage(int status) logMsg("error reading packet from input stream %p", jInput); env->ExceptionClear(); if(!isClosing) - defaultAndroidAdapter.sendSocketStatusMessage({*this, STATUS_READ_ERROR}); + bta.sendSocketStatusMessage({sock, BluetoothSocketState::ReadError}); break; } if(::write(dataPipe[1], &len, sizeof(len)) != sizeof(len)) @@ -548,16 +553,17 @@ static int nativeFdForSocket(JNIEnv *env, jobject btSocket) return -1; } -IG::ErrorCode AndroidBluetoothSocket::openSocket(BluetoothAdapter &adapter, BluetoothAddr bdaddr, uint32_t channel, bool l2cap) +void AndroidBluetoothSocket::openSocket(BluetoothAdapter &adapter, BluetoothAddr bdaddr, uint32_t channel, bool l2cap) { addrStr = ba2str(bdaddr); this->channel = channel; isL2cap = l2cap; isConnecting = true; IG::makeDetachedThread( - [this, &adapter = *static_cast(&adapter)]() + [this, &adapter]() { logMsg("in connect thread %d", gettid()); + auto &sock = static_cast(*this); JNIEnv *env = ctx.thisThreadJniEnv(); if(!env) { @@ -584,27 +590,28 @@ IG::ErrorCode AndroidBluetoothSocket::openSocket(BluetoothAdapter &adapter, Blue logMsg("opened output stream %p", outStream); outStream = env->NewGlobalRef(outStream); } - defaultAndroidAdapter.sendSocketStatusMessage({*this, STATUS_OPENED}); + adapter.sendSocketStatusMessage({sock, BluetoothSocketState::Opened}); } else - defaultAndroidAdapter.sendSocketStatusMessage({*this, STATUS_CONNECT_ERROR}); + adapter.sendSocketStatusMessage({sock, BluetoothSocketState::ConnectError}); connectSem.release(); isConnecting = false; } ); - return {}; } -IG::ErrorCode AndroidBluetoothSocket::openRfcomm(BluetoothAdapter &adapter, BluetoothAddr bdaddr, uint32_t channel) +std::system_error BluetoothSocket::openRfcomm(BluetoothAdapter &adapter, BluetoothAddr bdaddr, uint32_t channel) { logMsg("opening RFCOMM channel %d", channel); - return openSocket(adapter, bdaddr, channel, 0); + openSocket(adapter, bdaddr, channel, 0); + return std::error_code{}; } -IG::ErrorCode AndroidBluetoothSocket::openL2cap(BluetoothAdapter &adapter, BluetoothAddr bdaddr, uint32_t psm) +std::system_error BluetoothSocket::openL2cap(BluetoothAdapter &adapter, BluetoothAddr bdaddr, uint32_t psm) { logMsg("opening L2CAP psm %d", psm); - return openSocket(adapter, bdaddr, psm, 1); + openSocket(adapter, bdaddr, psm, 1); + return std::error_code{}; } AndroidBluetoothSocket::~AndroidBluetoothSocket() @@ -636,15 +643,12 @@ void AndroidBluetoothSocket::close() } } -IG::ErrorCode AndroidBluetoothSocket::write(const void *data, size_t size) +ssize_t BluetoothSocket::write(const void *data, size_t size) { logMsg("writing %zd bytes", size); if(nativeFd != -1) { - if(fd_writeAll(nativeFd, data, size) != (ssize_t)size) - { - return {EIO}; - } + return fd_writeAll(nativeFd, data, size); } else { @@ -653,8 +657,8 @@ IG::ErrorCode AndroidBluetoothSocket::write(const void *data, size_t size) env->SetByteArrayRegion(jData, 0, size, (jbyte *)data); jOutWrite(env, outStream, jData, 0, size); env->DeleteLocalRef(jData); + return size; } - return {}; } } diff --git a/imagine/src/bluetooth/BluetoothAdapter.cc b/imagine/src/bluetooth/BluetoothAdapter.cc deleted file mode 100644 index af2b158b7..000000000 --- a/imagine/src/bluetooth/BluetoothAdapter.cc +++ /dev/null @@ -1,58 +0,0 @@ -/* This file is part of Imagine. - - Imagine 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. - - Imagine 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 Imagine. If not, see */ - -#include -#include - -namespace IG -{ - -#ifdef CONFIG_BLUETOOTH_SCAN_CACHE_USAGE -bool BluetoothAdapter::useScanCache = 1; -#endif -#ifdef CONFIG_BLUETOOTH_SCAN_SECS -uint32_t BluetoothAdapter::scanSecs = 4; -#endif - -BluetoothAdapter *BluetoothAdapter::defaultAdapter(ApplicationContext ctx) -{ - #if defined CONFIG_BLUETOOTH_ANDROID - return AndroidBluetoothAdapter::defaultAdapter(ctx); - #elif defined CONFIG_BLUETOOTH_BTSTACK - return BtstackBluetoothAdapter::defaultAdapter(ctx); - #elif defined CONFIG_BLUETOOTH_BLUEZ - return BluezBluetoothAdapter::defaultAdapter(ctx); - #else - #error "no Bluetooth back-ends are selected" - #endif -} - -ApplicationContext BluetoothAdapter::appContext() const -{ - return ctx; -} - -void BluetoothAdapter::setAppContext(ApplicationContext ctx_) -{ - ctx = ctx_; -} - -BluetoothInputDevice::BluetoothInputDevice(ApplicationContext ctx, Input::Map map, - Input::DeviceTypeFlags typeFlags, const char *name): - BaseDevice{0, map, typeFlags, name}, - ctx{ctx} -{} - -} diff --git a/imagine/src/bluetooth/BluetoothInputDevScanner.cc b/imagine/src/bluetooth/BluetoothInputDevScanner.cc index 165e6dfb2..4bdf0dd7b 100644 --- a/imagine/src/bluetooth/BluetoothInputDevScanner.cc +++ b/imagine/src/bluetooth/BluetoothInputDevScanner.cc @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #ifdef CONFIG_BLUETOOTH_SERVER @@ -56,12 +55,12 @@ static void removePendingDevs() #ifdef CONFIG_BLUETOOTH_SERVER bool listenForDevices(ApplicationContext ctx, BluetoothAdapter &bta, const BluetoothAdapter::OnStatusDelegate &onScanStatus) { - if(bta.inDetect || hidServiceActive) + if(bta.isInScan() || hidServiceActive) { return false; } onServerStatus = onScanStatus; - bta.onIncomingL2capConnection() = + bta.onIncomingL2capConnection = [ctx](BluetoothAdapter &bta, BluetoothPendingSocket &pending) { if(!pending) @@ -79,12 +78,12 @@ bool listenForDevices(ApplicationContext ctx, BluetoothAdapter &bta, const Bluet { logMsg("request for PSM 0x11"); pendingSocket = pending; - pending.requestName( + pending.requestName(bta, [ctx](BluetoothAdapter &bta, const char *name, BluetoothAddr addr) { if(!name) { - onServerStatus(bta, BluetoothAdapter::SCAN_NAME_FAILED, 0); + onServerStatus(bta, BluetoothScanState::NameFailed, 0); pendingSocket = {}; return; } @@ -117,28 +116,28 @@ bool listenForDevices(ApplicationContext ctx, BluetoothAdapter &bta, const Bluet logMsg("registering HID PSMs"); hidServiceActive = true; bta.setL2capService(0x13, true, - [](BluetoothAdapter &bta, uint32_t success, int arg) + [](BluetoothAdapter &bta, BluetoothScanState state, int arg) { - if(!success) + if(state <= BluetoothScanState::Failed) { hidServiceActive = false; - onServerStatus(bta, BluetoothAdapter::INIT_FAILED, 0); + onServerStatus(bta, BluetoothScanState::InitFailed, 0); return; } logMsg("INT PSM registered"); // now register the 2nd PSM bta.setL2capService(0x11, true, - [](BluetoothAdapter &bta, uint32_t success, int arg) + [](BluetoothAdapter &bta, BluetoothScanState state, int arg) { - if(!success) + if(state <= BluetoothScanState::Failed) { bta.setL2capService(0x13, false, {}); hidServiceActive = false; - onServerStatus(bta, BluetoothAdapter::INIT_FAILED, 0); + onServerStatus(bta, BluetoothScanState::InitFailed, 0); return; } logMsg("CTL PSM registered"); - onServerStatus(bta, BluetoothAdapter::SCAN_COMPLETE, 0); + onServerStatus(bta, BluetoothScanState::Complete, 0); // both PSMs are registered unregisterHIDServiceCallback.runIn(IG::Seconds{8}, {}, [&bta]() @@ -159,7 +158,7 @@ bool listenForDevices(ApplicationContext ctx, BluetoothAdapter &bta, const Bluet bool scanForDevices(ApplicationContext ctx, BluetoothAdapter &bta, BluetoothAdapter::OnStatusDelegate onScanStatus) { - if(!bta.inDetect && !hidServiceActive) + if(!bta.isInScan() && !hidServiceActive) { removePendingDevs(); return bta.startScan(onScanStatus, @@ -172,7 +171,7 @@ bool scanForDevices(ApplicationContext ctx, BluetoothAdapter &bta, BluetoothAdap { if(!name) { - onScanStatus(bta, BluetoothAdapter::SCAN_NAME_FAILED, 0); + onScanStatus(bta, BluetoothScanState::NameFailed, 0); return; } if(strstr(name, "Nintendo RVL-CNT-01")) @@ -193,12 +192,12 @@ bool scanForDevices(ApplicationContext ctx, BluetoothAdapter &bta, BluetoothAdap return 0; } -void closeDevices(BluetoothAdapter *bta) +void closeDevices(BluetoothAdapter& bta) { - if(!bta) + if(!bta.isOpen()) return; // Bluetooth was never used logMsg("closing all BT input devs"); - auto ctx = bta->appContext(); + auto ctx = bta.appContext(); auto &app = ctx.application(); app.removeInputDevices(ctx, Input::Map::WIIMOTE); app.removeInputDevices(ctx, Input::Map::WII_CC); @@ -209,36 +208,36 @@ void closeDevices(BluetoothAdapter *bta) #endif } -uint32_t pendingDevs() +size_t pendingDevs() { return btInputDevPendingList.size(); } -void connectPendingDevs(BluetoothAdapter *bta) +void connectPendingDevs(BluetoothAdapter& bta) { logMsg("connecting to %d devices", (int)btInputDevPendingList.size()); for(auto &e : btInputDevPendingList) { visit([&](auto &btDev) { - if constexpr(requires {btDev.open(*bta, *e);}) - btDev.open(*bta, *e); + if constexpr(requires {btDev.open(bta, *e);}) + btDev.open(bta, *e); }, *e); } } -void closeBT(BluetoothAdapter *bta) +void closeBT(BluetoothAdapter& bta) { - if(!bta) + if(!bta.isOpen()) return; // Bluetooth was never used - if(bta->inDetect) + if(bta.isInScan()) { logMsg("keeping BT active due to scan"); return; } removePendingDevs(); closeDevices(bta); - bta->close(); + bta.close(); } static bool isBluetoothDeviceInputMap(Input::Map map) @@ -258,7 +257,7 @@ static bool isBluetoothDeviceInputMap(Input::Map map) } } -uint32_t devsConnected(ApplicationContext ctx) +size_t devsConnected(ApplicationContext ctx) { auto &devs = ctx.inputDevices(); return std::count_if(devs.begin(), devs.end(), [](auto &devPtr){ return isBluetoothDeviceInputMap(devPtr->map()); }); @@ -269,15 +268,20 @@ uint32_t devsConnected(ApplicationContext ctx) namespace IG { -void BaseApplication::bluetoothInputDeviceStatus(ApplicationContext ctx, Input::Device &dev, int status) +BluetoothInputDevice::BluetoothInputDevice(ApplicationContext ctx, Input::Map map, + Input::DeviceTypeFlags typeFlags, const char *name): + BaseDevice{0, map, typeFlags, name}, + ctx{ctx} {} + +void BaseApplication::bluetoothInputDeviceStatus(ApplicationContext ctx, Input::Device &dev, BluetoothSocketState status) { using namespace Bluetooth; switch(status) { - case BluetoothSocket::STATUS_CONNECT_ERROR: + case BluetoothSocketState::ConnectError: std::erase_if(btInputDevPendingList, [&](auto &devPtr){ return devPtr.get() == &dev; }); break; - case BluetoothSocket::STATUS_OPENED: + case BluetoothSocketState::Opened: { logMsg("back %p, param %p", btInputDevPendingList.back().get(), &dev); auto devPtr = IG::moveOutIf(btInputDevPendingList, [&](auto &devPtr){ return devPtr.get() == &dev; }); @@ -288,10 +292,11 @@ void BaseApplication::bluetoothInputDeviceStatus(ApplicationContext ctx, Input:: } break; } - case BluetoothSocket::STATUS_READ_ERROR: - case BluetoothSocket::STATUS_CLOSED: + case BluetoothSocketState::ReadError: + case BluetoothSocketState::Closed: removeInputDevice(ctx, dev, true); break; + case BluetoothSocketState::Connecting: break; } } diff --git a/imagine/src/bluetooth/BluezBluetoothAdapter.cc b/imagine/src/bluetooth/BluezBluetoothAdapter.cc index 00f5fb6b9..153698bec 100644 --- a/imagine/src/bluetooth/BluezBluetoothAdapter.cc +++ b/imagine/src/bluetooth/BluezBluetoothAdapter.cc @@ -14,9 +14,8 @@ along with Imagine. If not, see */ #define LOGTAG "Bluez" -#include +#include #include -#include #include #include #include @@ -39,12 +38,11 @@ namespace IG struct ScanStatusMessage { - uint8_t type, arg; + BluetoothScanState type; + uint8_t arg; }; -static BluezBluetoothAdapter defaultBluezAdapter; - -void BluezBluetoothAdapter::sendBTScanStatusDelegate(uint8_t type, uint8_t arg = 0) +void BluezBluetoothAdapter::sendBTScanStatusDelegate(BluetoothScanState type, uint8_t arg = 0) { ScanStatusMessage msg {type, arg}; statusPipe.sink().put(msg); @@ -55,56 +53,57 @@ void BluezBluetoothAdapter::sendBTScanStatusDelegate(uint8_t type, uint8_t arg = } // TODO: allow providing specific EventLoop to handle events -bool BluezBluetoothAdapter::openDefault() +bool BluetoothAdapter::openDefault() { - if(socket > 0) - return 1; + if(isOpen()) + return true; logMsg("opening default BT adapter"); #ifdef __ANDROID__ if(bluez_dl() != 0) - return 0; + return; #endif devId = hci_get_route(0); if(devId < 0) { logMsg("no routes, errno: %d, %s", errno, strerror(errno)); - return 0; + return false; } socket = hci_open_dev(devId); if(socket < 0) { logMsg("error opening socket"); - return 0; + return false; } - - { - statusPipe.attach( - [this](auto &io) + statusPipe.attach( + [this](auto &io) + { + while(statusPipe.hasData()) { - while(statusPipe.hasData()) + auto &bta = static_cast(*this); + auto msg = io.template getExpected(); + if(!msg.has_value()) { - auto msg = io.template get(); - if(!msg.type) - { - logErr("error reading BT socket status message in pipe"); - return true; - } - logMsg("got bluetooth adapter status delegate message"); - onScanStatus()(*this, msg.type, msg.arg); + logErr("error reading BT socket status message in pipe"); + return true; } - return true; - }); - } - - return 1; + logMsg("got bluetooth adapter status delegate message"); + bta.onScanStatus(bta, msg->type, msg->arg); + } + return true; + }); + return true; } -void BluezBluetoothAdapter::cancelScan() +bool BluetoothAdapter::isOpen() const { return socket != -1; } + +void BluetoothAdapter::cancelScan() { scanCancelled = 1; } -void BluezBluetoothAdapter::close() +bool BluetoothAdapter::isInScan() const { return inDetect; } + +void BluetoothAdapter::close() { if(inDetect) { @@ -120,49 +119,41 @@ void BluezBluetoothAdapter::close() statusPipe.detach(); } -BluezBluetoothAdapter *BluezBluetoothAdapter::defaultAdapter(ApplicationContext ctx) -{ - defaultBluezAdapter.setAppContext(ctx); - if(defaultBluezAdapter.openDefault()) - return &defaultBluezAdapter; - else - return nullptr; -} - -IG::ErrorCode BluezBluetoothAdapter::doScan(const OnScanDeviceClassDelegate &onDeviceClass, const OnScanDeviceNameDelegate &onDeviceName) +bool BluezBluetoothAdapter::doScan(const BTOnScanDeviceClassDelegate &onDeviceClass, const BTOnScanDeviceNameDelegate &onDeviceName) { - logMsg("starting Bluetooth scan, cache %d", BluetoothAdapter::useScanCache); + auto &bta = static_cast(*this); + logMsg("starting Bluetooth scan, cache %d", bta.useScanCache); int devices = 0, maxDevices = 10; inquiry_info *deviceInfo = 0; - devices = hci_inquiry(devId, scanSecs, maxDevices, 0, &deviceInfo, - BluetoothAdapter::useScanCache ? 0 : IREQ_CACHE_FLUSH); + devices = hci_inquiry(devId, bta.scanSecs, maxDevices, 0, &deviceInfo, + bta.useScanCache ? 0 : IREQ_CACHE_FLUSH); if(devices == -1) { logMsg("inquiry failed"); if(deviceInfo) free(deviceInfo); - sendBTScanStatusDelegate(SCAN_FAILED); - return {EINVAL}; + sendBTScanStatusDelegate(BluetoothScanState::Failed); + return false; } if(scanCancelled) { logMsg("cancelled scan after hci_inquiry"); - sendBTScanStatusDelegate(SCAN_CANCELLED); - return {}; + sendBTScanStatusDelegate(BluetoothScanState::Cancelled); + return true; } logMsg("%d devices", devices); if(devices == 0) { - sendBTScanStatusDelegate(SCAN_NO_DEVS); + sendBTScanStatusDelegate(BluetoothScanState::NoDevs); if(deviceInfo) free(deviceInfo); - return {}; + return true; } else - sendBTScanStatusDelegate(SCAN_PROCESSING, devices); + sendBTScanStatusDelegate(BluetoothScanState::Processing, devices); for(auto i : iotaCount(devices)) { - if(!onDeviceClass(*this, std::to_array(deviceInfo[i].dev_class))) + if(!onDeviceClass(bta, std::to_array(deviceInfo[i].dev_class))) { logMsg("skipping device due to class %X:%X:%X", deviceInfo[i].dev_class[0], deviceInfo[i].dev_class[1], deviceInfo[i].dev_class[2]); continue; @@ -171,46 +162,46 @@ IG::ErrorCode BluezBluetoothAdapter::doScan(const OnScanDeviceClassDelegate &onD if(scanCancelled) { logMsg("cancelled scan in hci_read_remote_name loop"); - sendBTScanStatusDelegate(SCAN_CANCELLED); - return {}; + sendBTScanStatusDelegate(BluetoothScanState::Cancelled); + return true; } char name[248]; if(hci_read_remote_name(socket, &deviceInfo[i].bdaddr, sizeof(name), name, 0) < 0) { logMsg("error reading device name"); - sendBTScanStatusDelegate(SCAN_NAME_FAILED); + sendBTScanStatusDelegate(BluetoothScanState::NameFailed); continue; } logMsg("device name: %s", name); - onDeviceName(*this, name, deviceInfo[i].bdaddr.b); + onDeviceName(bta, name, deviceInfo[i].bdaddr.b); } if(deviceInfo) free(deviceInfo); if(scanCancelled) { logMsg("canceled scan after hci_read_remote_name loop"); - sendBTScanStatusDelegate(SCAN_CANCELLED); - return {}; + sendBTScanStatusDelegate(BluetoothScanState::Cancelled); + return true; } - sendBTScanStatusDelegate(SCAN_COMPLETE); - return {}; + sendBTScanStatusDelegate(BluetoothScanState::Complete); + return true; } -bool BluezBluetoothAdapter::startScan(OnStatusDelegate onResult, OnScanDeviceClassDelegate onDeviceClass, OnScanDeviceNameDelegate onDeviceName) +bool BluetoothAdapter::startScan(OnStatusDelegate onResult, OnScanDeviceClassDelegate onDeviceClass, OnScanDeviceNameDelegate onDeviceName) { if(!inDetect) { scanCancelled = 0; inDetect = 1; - onScanStatusD = onResult; - onScanDeviceClassD = onDeviceClass; - onScanDeviceNameD = onDeviceName; + onScanStatus = onResult; + onScanDeviceClass = onDeviceClass; + onScanDeviceName = onDeviceName; IG::makeDetachedThread( [this]() { - doScan(onScanDeviceClassD, onScanDeviceNameD); + doScan(onScanDeviceClass, onScanDeviceName); inDetect = 0; }); return 1; @@ -222,34 +213,34 @@ bool BluezBluetoothAdapter::startScan(OnStatusDelegate onResult, OnScanDeviceCla } } -void BluezBluetoothAdapter::requestName(BluetoothPendingSocket &pending, OnScanDeviceNameDelegate onDeviceName) +void BluetoothAdapter::requestName(BluetoothPendingSocket& pending, BTOnScanDeviceNameDelegate onDeviceName) { + auto &bta = static_cast(*this); char name[248]; auto &baddr = pending.addr.l2_bdaddr.b; - if(hci_read_remote_name(defaultBluezAdapter.socket, &pending.addr.l2_bdaddr, sizeof(name), name, 0) < 0) + if(hci_read_remote_name(bta.socket, &pending.addr.l2_bdaddr, sizeof(name), name, 0) < 0) { logErr("error reading device name"); - onDeviceName(*this, nullptr, baddr); + onDeviceName(bta, nullptr, baddr); return; } logMsg("device name: %s", name); - onDeviceName(*this, name, baddr); + onDeviceName(bta, name, baddr); } -BluetoothAdapter::State BluezBluetoothAdapter::state() +BluetoothAdapter::State BluetoothAdapter::state() { // TODO - return STATE_ON; + return BluetoothState::On; } -void BluezBluetoothAdapter::setActiveState(bool on, OnStateChangeDelegate onStateChange) +void BluetoothAdapter::setActiveState(bool on, OnStateChangeDelegate onStateChange) { // TODO - onStateChange(*this, on ? STATE_ON : STATE_OFF); + onStateChange(*this, on ? BluetoothState::On : BluetoothState::Off); } -#ifdef CONFIG_BLUETOOTH_SERVER -void BluezBluetoothAdapter::setL2capService(uint32_t psm, bool active, OnStatusDelegate onResult) +void BluetoothAdapter::setL2capService(uint32_t psm, bool active, OnStatusDelegate onResult) { if(!active) { @@ -267,7 +258,7 @@ void BluezBluetoothAdapter::setL2capService(uint32_t psm, bool active, OnStatusD if(serverList.isFull()) { logErr("too many l2cap services registered"); - onResult(*this, 0, 0); + onResult(*this, BluetoothScanState::Failed, 0); return; } logMsg("registering l2cap service for PSM 0x%X", psm); @@ -279,7 +270,7 @@ void BluezBluetoothAdapter::setL2capService(uint32_t psm, bool active, OnStatusD if(serverFd == -1) { logErr("error creating L2CAP socket with PSM %d", psm); - onResult(*this, 0, 0); + onResult(*this, BluetoothScanState::Failed, 0); return; } @@ -288,7 +279,7 @@ void BluezBluetoothAdapter::setL2capService(uint32_t psm, bool active, OnStatusD { logErr("error in bind()"); ::close(serverFd); - onResult(*this, 0, 0); + onResult(*this, BluetoothScanState::Failed, 0); return; } @@ -307,7 +298,7 @@ void BluezBluetoothAdapter::setL2capService(uint32_t psm, bool active, OnStatusD { logErr("error in listen()"); ::close(serverFd); - onResult(*this, 0, 0); + onResult(*this, BluetoothScanState::Failed, 0); return; } @@ -323,16 +314,15 @@ void BluezBluetoothAdapter::setL2capService(uint32_t psm, bool active, OnStatusD { logErr("failed accepting connection"); BluetoothPendingSocket error; - onIncomingL2capConnectionD(*this, error); + onIncomingL2capConnection(*this, error); return true; } logMsg("for PSM 0x%X, fd %d", pending.addr.l2_psm, pending.fd); - onIncomingL2capConnectionD(*this, pending); + onIncomingL2capConnection(*this, pending); return true; }}; //addPollEvent(serverFd, serverList.back().onConnect, POLLEV_IN); - onResult(*this, 1, 0); - return; + onResult(*this, BluetoothScanState::Complete, 0); } /*bool BluezBluetoothAdapter::l2capServiceRegistered(uint32_t psm) @@ -346,25 +336,24 @@ void BluezBluetoothAdapter::setL2capService(uint32_t psm, bool active, OnStatusD } return false; }*/ -#endif void BluetoothPendingSocket::close() { ::close(fd); } -void BluetoothPendingSocket::requestName(BluetoothAdapter::OnScanDeviceNameDelegate onDeviceName) +void BluetoothPendingSocket::requestName(BluetoothAdapter& bta, BluetoothAdapter::OnScanDeviceNameDelegate onDeviceName) { - defaultBluezAdapter.requestName(*this, onDeviceName); + bta.requestName(*this, onDeviceName); } -IG::ErrorCode BluezBluetoothSocket::open(BluetoothAdapter &, BluetoothPendingSocket &pending) +std::system_error BluetoothSocket::open(BluetoothAdapter&, BluetoothPendingSocket& pending) { assert(pending); logMsg("accepting connection from fd %d", pending.fd); fd = pending.fd; pending = {}; - if(onStatusD(*this, STATUS_OPENED) == OPEN_USAGE_READ_EVENTS) + if(onStatus(*this, BluetoothSocketState::Opened) == 1) setupFDEvents(POLLEV_IN); //addPollEvent(fd, pollEvDel, POLLEV_IN); return {}; @@ -372,6 +361,7 @@ IG::ErrorCode BluezBluetoothSocket::open(BluetoothAdapter &, BluetoothPendingSoc bool BluezBluetoothSocket::readPendingData(int events) { + auto &sock = static_cast(*this); if(events & POLLEV_ERR) { logMsg("poll error with events %X", events); @@ -384,13 +374,13 @@ bool BluezBluetoothSocket::readPendingData(int events) getsockopt(fd, SOL_SOCKET, SO_ERROR, &opt, &optLen); logMsg("got so_error %d", opt); } - onStatusD(*this, STATUS_CONNECT_ERROR); + sock.onStatus(sock, BluetoothSocketState::ConnectError); //defaultBluezAdapter.onScanStatus()(defaultBluezAdapter, BluetoothAdapter::SOCKET_OPEN_FAILED, 0); } else { logMsg("socket %d disconnected", fd); - onStatusD(*this, STATUS_READ_ERROR); + sock.onStatus(sock, BluetoothSocketState::ReadError); } return false; } @@ -405,18 +395,18 @@ bool BluezBluetoothSocket::readPendingData(int events) if(len <= 0) [[unlikely]] { logMsg("error %d reading packet from socket %d", len == -1 ? errno : 0, fd); - onStatusD(*this, STATUS_READ_ERROR); + sock.onStatus(sock, BluetoothSocketState::ReadError); return false; } //logMsg("read %d bytes from socket %d", len, fd); - if(!onDataD(buff, len)) + if(!sock.onData(buff, len)) break; // socket was closed } } else if(events & POLLEV_OUT) { logMsg("finished opening socket %d", fd); - if(onStatusD(*this, STATUS_OPENED) == OPEN_USAGE_READ_EVENTS) + if(sock.onStatus(sock, BluetoothSocketState::Opened) == 1) fdSrc.setEvents(POLLEV_IN); else fdSrc.detach(); @@ -435,7 +425,7 @@ void BluezBluetoothSocket::setupFDEvents(int events) POLLEV_OUT}; } -IG::ErrorCode BluezBluetoothSocket::openRfcomm(BluetoothAdapter &, BluetoothAddr bdaddr, uint32_t channel) +std::system_error BluetoothSocket::openRfcomm(BluetoothAdapter &, BluetoothAddr bdaddr, uint32_t channel) { struct sockaddr_rc addr{}; addr.rc_family = AF_BLUETOOTH; @@ -447,7 +437,7 @@ IG::ErrorCode BluezBluetoothSocket::openRfcomm(BluetoothAdapter &, BluetoothAddr { logMsg("error creating RFCOMM socket with channel %d", channel); //onStatus.invoke(*this, STATUS_ERROR); - return {EIO}; + return std::system_error{errno, std::generic_category()}; } fd_setNonblock(fd, 1); if(connect(fd, (struct sockaddr *)&addr, sizeof addr) == -1) @@ -463,7 +453,7 @@ IG::ErrorCode BluezBluetoothSocket::openRfcomm(BluetoothAdapter &, BluetoothAddr return {}; } -IG::ErrorCode BluezBluetoothSocket::openL2cap(BluetoothAdapter &, BluetoothAddr bdaddr, uint32_t psm) +std::system_error BluetoothSocket::openL2cap(BluetoothAdapter &, BluetoothAddr bdaddr, uint32_t psm) { struct sockaddr_l2 addr{}; addr.l2_family = AF_BLUETOOTH; @@ -474,7 +464,7 @@ IG::ErrorCode BluezBluetoothSocket::openL2cap(BluetoothAdapter &, BluetoothAddr if(fd == -1) { logMsg("error creating L2CAP socket with PSM %d", psm); - return {EIO}; + return std::system_error{errno, std::generic_category()}; } fd_setNonblock(fd, 1); @@ -513,14 +503,10 @@ void BluezBluetoothSocket::close() } } -IG::ErrorCode BluezBluetoothSocket::write(const void *data, size_t size) +ssize_t BluetoothSocket::write(const void *data, size_t size) { assert(fd >= 0); - if(fd_writeAll(fd, data, size) != (ssize_t)size) - { - return {EIO}; - } - return {}; + return fd_writeAll(fd, data, size); } } diff --git a/imagine/src/bluetooth/BtstackBluetoothAdapter.cc b/imagine/src/bluetooth/BtstackBluetoothAdapter.cc index e3876691b..b03d2e750 100644 --- a/imagine/src/bluetooth/BtstackBluetoothAdapter.cc +++ b/imagine/src/bluetooth/BtstackBluetoothAdapter.cc @@ -14,8 +14,7 @@ along with Imagine. If not, see */ #define LOGTAG "BTstack" -#include -#include +#include #include #include #include @@ -25,7 +24,6 @@ namespace IG { -static BtstackBluetoothAdapter defaultBtstackAdapter; static int writeAuthEnable = -1; static bool inL2capSocketOpenHandler = 0; @@ -234,16 +232,18 @@ static void sprintBTAddr(char *addrStr, bd_addr_t &addr) static StaticArrayList scanDevList; static StaticArrayList incomingDevList; -static StaticArrayList socketList; +static StaticArrayList socketList; +static BluetoothAdapter *btaPtr{}; static void btHandler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { - defaultBtstackAdapter.packetHandler(packet_type, channel, packet, size); + btaPtr->packetHandler(packet_type, channel, packet, size); } void BtstackBluetoothAdapter::packetHandler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { //logMsg("got packet type: %s", btstackPacketTypeToString(packet_type)); + auto &bta = static_cast(*this); switch (packet_type) { case L2CAP_DATA_PACKET: @@ -258,7 +258,7 @@ void BtstackBluetoothAdapter::packetHandler(uint8_t packet_type, uint16_t channe logErr("can't find socket"); return; } - sock->onData()((char*)packet, size); + sock->onData((char*)packet, size); //debugPrintL2CAPPacket(channel, packet, size); break; } @@ -273,10 +273,10 @@ void BtstackBluetoothAdapter::packetHandler(uint8_t packet_type, uint16_t channe logMsg("got BTSTACK_EVENT_STATE: %d", state_); if(state_ == HCI_STATE_WORKING) { - if(onStateChangeD) + if(bta.onStateChange) { - onStateChangeD(*this, STATE_ON); - onStateChangeD = {}; + bta.onStateChange(bta, BluetoothState::On); + bta.onStateChange = {}; } //printAddrs(); //BtstackBluetoothAdapter::processCommands(); @@ -285,7 +285,7 @@ void BtstackBluetoothAdapter::packetHandler(uint8_t packet_type, uint16_t channe { if(inDetect) { - onScanStatusD(*this, SCAN_FAILED, 0); + bta.onScanStatus(bta, BluetoothScanState::Failed, 0); inDetect = 0; cmdActive = 0; } @@ -297,17 +297,17 @@ void BtstackBluetoothAdapter::packetHandler(uint8_t packet_type, uint16_t channe { if(inDetect) { - //onStatus.invoke(SCAN_FAILED); + //onStatus.invoke(BluetoothScanState::Failed); inDetect = 0; cmdActive = 0; } state_ = HCI_STATE_OFF; //logMsg("Bluetooth not accessible, Make sure you have turned off Bluetooth in the System Settings."); - //onScanStatusD(INIT_FAILED, 0); - if(onStateChangeD) + //onScanStatus(INIT_FAILED, 0); + if(bta.onStateChange) { - onStateChangeD(*this, STATE_ERROR); - onStateChangeD = {}; + bta.onStateChange(bta, BluetoothState::Error); + bta.onStateChange = {}; } break; } @@ -366,7 +366,7 @@ void BtstackBluetoothAdapter::packetHandler(uint8_t packet_type, uint16_t channe break; logMsg("disconnection while l2cap open in progress"); cmdActive = 0; - sock->onStatus()(*sock, BluetoothSocket::STATUS_CONNECT_ERROR); + sock->onStatus(*sock, BluetoothSocketState::ConnectError); /*if(defaultBtstackAdapter.onScanStatus()) defaultBtstackAdapter.onScanStatus()(defaultBtstackAdapter, BluetoothAdapter::SOCKET_OPEN_FAILED, 0);*/ } @@ -436,7 +436,7 @@ void BtstackBluetoothAdapter::packetHandler(uint8_t packet_type, uint16_t channe auto devClassPtr = &packet[3 + responses*(6+1+1+1) + i*3]; std::array devClass{devClassPtr[0], devClassPtr[1], devClassPtr[2]}; - if(!onScanDeviceClassD(*this, devClass)) + if(!bta.onScanDeviceClass(bta, devClass)) { logMsg("skipping device #%d due to class %X:%X:%X", i, devClass[0], devClass[1], devClass[2]); continue; @@ -471,19 +471,19 @@ void BtstackBluetoothAdapter::packetHandler(uint8_t packet_type, uint16_t channe { e.requestName(); } - onScanStatusD(*this, SCAN_PROCESSING, scanDevList.size()); + bta.onScanStatus(bta, BluetoothScanState::Processing, scanDevList.size()); } else { inDetect = 0; if(!scanResponses) { - onScanStatusD(*this, SCAN_NO_DEVS, 0); + bta.onScanStatus(bta, BluetoothScanState::NoDevs, 0); } else { logMsg("no name requests needed, scan complete"); - onScanStatusD(*this, SCAN_COMPLETE, 0); + bta.onScanStatus(bta, BluetoothScanState::Complete, 0); } } BtstackBluetoothAdapter::processCommands(); @@ -492,7 +492,7 @@ void BtstackBluetoothAdapter::packetHandler(uint8_t packet_type, uint16_t channe } case BTSTACK_EVENT_REMOTE_NAME_CACHED: - if(!BluetoothAdapter::useScanCache) + if(!bta.useScanCache) { logMsg("ignoring cached name"); break; @@ -513,11 +513,11 @@ void BtstackBluetoothAdapter::packetHandler(uint8_t packet_type, uint16_t channe packet[9+255] = 0; char* name = (char*)&packet[9]; logMsg("Name: '%s', Addr: %s, cached: %d", name, bd_addr_to_str(addr), cached); - onScanDeviceNameD(*this, name, addr); + bta.onScanDeviceName(bta, name, addr); } else { - onScanDeviceNameD(*this, nullptr, addr); + bta.onScanDeviceName(bta, nullptr, addr); logMsg("Failed to get name: page timeout"); } @@ -526,8 +526,8 @@ void BtstackBluetoothAdapter::packetHandler(uint8_t packet_type, uint16_t channe { cmdActive = 0; scanDevList.clear(); - if(onScanStatusD) - onScanStatusD(*this, SCAN_CANCELLED, 0); + if(bta.onScanStatus) + bta.onScanStatus(bta, BluetoothScanState::Cancelled, 0); BtstackBluetoothAdapter::processCommands(); break; } @@ -539,7 +539,7 @@ void BtstackBluetoothAdapter::packetHandler(uint8_t packet_type, uint16_t channe { logMsg("finished name requests, scan complete"); inDetect = 0; - onScanStatusD(*this, SCAN_COMPLETE, 0); + bta.onScanStatus(bta, BluetoothScanState::Complete, 0); } BtstackBluetoothAdapter::processCommands(); break; @@ -626,7 +626,7 @@ void BtstackBluetoothAdapter::packetHandler(uint8_t packet_type, uint16_t channe logMsg("socket already removed from list"); return; } - sock->onStatus()(*sock, BluetoothSocket::STATUS_READ_ERROR); + sock->onStatus(*sock, BluetoothSocketState::ReadError); break; } @@ -639,11 +639,11 @@ void BtstackBluetoothAdapter::packetHandler(uint8_t packet_type, uint16_t channe if(status && status != L2CAP_SERVICE_ALREADY_REGISTERED) { logErr("error %d registering psm %d", status, psm); - onResult(*this, 0, 0); + onResult(bta, BluetoothScanState::InitFailed, 0); break; } logMsg("registered l2cap service for psm 0x%X", psm); - onResult(*this, 1, 0); + onResult(bta, BluetoothScanState::Complete, 0); BtstackBluetoothAdapter::processCommands(); break; } @@ -655,7 +655,7 @@ void BtstackBluetoothAdapter::packetHandler(uint8_t packet_type, uint16_t channe bd_addr_t addr; bt_flip_addr(addr, &packet[2]); BluetoothPendingSocket pending {0, addr, psm, sourceCid}; - onIncomingL2capConnectionD(*this, pending); + bta.onIncomingL2capConnection(bta, pending); break; } @@ -675,7 +675,7 @@ void BtstackBluetoothAdapter::packetHandler(uint8_t packet_type, uint16_t channe //logMsg("end packet"); } -void BtstackBluetoothAdapter::setL2capService(uint32_t psm, bool on, OnStatusDelegate onResult) +void BluetoothAdapter::setL2capService(uint32_t psm, bool on, OnStatusDelegate onResult) { if(on) { @@ -688,10 +688,10 @@ void BtstackBluetoothAdapter::setL2capService(uint32_t psm, bool on, OnStatusDel setActiveState(true, [this](BluetoothAdapter &, State newState) { - if(newState != STATE_ON) + if(newState != BluetoothState::On) { auto onResult = std::exchange(setL2capServiceOnResult, {}); - onResult(*this, 0, 0); + onResult(*this, BluetoothScanState::InitFailed, 0); return; } BtstackBluetoothAdapter::processCommands(); @@ -708,28 +708,28 @@ void BtstackBluetoothAdapter::setL2capService(uint32_t psm, bool on, OnStatusDel } } -BluetoothAdapter::State BtstackBluetoothAdapter::state() +BluetoothAdapter::State BluetoothAdapter::state() { switch(state_) { case HCI_STATE_OFF: case HCI_STATE_SLEEPING: - return STATE_OFF; + return BluetoothState::Off; case HCI_STATE_INITIALIZING: - return STATE_TURNING_ON; + return BluetoothState::TurningOn; case HCI_STATE_WORKING: - return STATE_ON; + return BluetoothState::On; case HCI_STATE_HALTING: case HCI_STATE_FALLING_ASLEEP: - return STATE_TURNING_OFF; + return BluetoothState::TurningOff; } logWarn("unknown bluetooth state: %d", state_); - return STATE_OFF; + return BluetoothState::Off; } -void BtstackBluetoothAdapter::setActiveState(bool on, OnStateChangeDelegate onStateChange) +void BluetoothAdapter::setActiveState(bool on, OnStateChangeDelegate onStateChange) { - if(onStateChangeD) + if(onStateChange) { logErr("state change already in progress"); return; @@ -739,31 +739,31 @@ void BtstackBluetoothAdapter::setActiveState(bool on, OnStateChangeDelegate onSt if(isInactive()) { logMsg("powering on Bluetooth"); - onStateChangeD = onStateChange; + this->onStateChange = onStateChange; bt_send_cmd(&btstack_set_power_mode, HCI_POWER_ON); } else if(onStateChange) // already on { logMsg("Bluetooth is already on"); - onStateChange(*this, STATE_ON); + onStateChange(*this, BluetoothState::On); } } else { // TODO bug_unreachable("TODO"); - onStateChange(*this, STATE_ERROR); + onStateChange(*this, BluetoothState::Error); } } -bool BtstackBluetoothAdapter::startScan(OnStatusDelegate onResult, OnScanDeviceClassDelegate onDeviceClass, OnScanDeviceNameDelegate onDeviceName) +bool BluetoothAdapter::startScan(OnStatusDelegate onResult, OnScanDeviceClassDelegate onDeviceClass, OnScanDeviceNameDelegate onDeviceName) { if(!inDetect) { inDetect = 1; - onScanStatusD = onResult; - onScanDeviceClassD = onDeviceClass; - onScanDeviceNameD = onDeviceName; + onScanStatus = onResult; + onScanDeviceClass = onDeviceClass; + onScanDeviceName = onDeviceName; pendingCmdList.emplace_back(BtstackCmd::inquiry(scanSecs)); logMsg("starting inquiry"); if(isInactive()) @@ -771,9 +771,9 @@ bool BtstackBluetoothAdapter::startScan(OnStatusDelegate onResult, OnScanDeviceC setActiveState(true, [this](BluetoothAdapter &, State newState) { - if(newState != STATE_ON) + if(newState != BluetoothState::On) { - onScanStatusD(*this, INIT_FAILED, 0); + onScanStatus(*this, BluetoothScanState::InitFailed, 0); return; } BtstackBluetoothAdapter::processCommands(); @@ -791,16 +791,18 @@ bool BtstackBluetoothAdapter::startScan(OnStatusDelegate onResult, OnScanDeviceC } } +bool BluetoothAdapter::isInScan() const { return inDetect; } + bool BtstackBluetoothAdapter::isInactive() { return state_ != HCI_STATE_INITIALIZING && state_ != HCI_STATE_WORKING; } -IG::ErrorCode BtstackBluetoothAdapter::openDefault() +bool BluetoothAdapter::openDefault() { - if(isOpen) + if(isOpen()) { - return {}; + return true; } logMsg("opening BT adapter"); @@ -815,15 +817,20 @@ IG::ErrorCode BtstackBluetoothAdapter::openDefault() if(bt_open()) { logWarn("Failed to open connection to BTdaemon"); - return {EINVAL}; + return false; } + btaPtr = this; bt_register_packet_handler(btHandler); - isOpen = 1; logMsg("BTStack init"); - return {}; + return true; +} + +bool BluetoothAdapter::isOpen() const +{ + return btaPtr; } -void BtstackBluetoothAdapter::cancelScan() +void BluetoothAdapter::cancelScan() { if(inDetect) { @@ -833,7 +840,7 @@ void BtstackBluetoothAdapter::cancelScan() { logMsg("cancelling scan from queue"); inDetect = 0; - onScanStatusD(*this, SCAN_CANCELLED, 0); + onScanStatus(*this, BluetoothScanState::Cancelled, 0); } else { @@ -846,38 +853,29 @@ void BtstackBluetoothAdapter::cancelScan() } } -void BtstackBluetoothAdapter::close() +void BluetoothAdapter::close() { - if(isOpen) + if(isOpen()) { logMsg("closing BTstack"); cancelScan(); bt_close(); - isOpen = 0; state_ = HCI_STATE_OFF; + btaPtr = {}; } } -BtstackBluetoothAdapter *BtstackBluetoothAdapter::defaultAdapter(ApplicationContext ctx) -{ - defaultBtstackAdapter.setAppContext(ctx); - if(defaultBtstackAdapter.openDefault()) - return nullptr; - else - return &defaultBtstackAdapter; -} - -void BtstackBluetoothAdapter::requestName(BluetoothPendingSocket &pending, OnScanDeviceNameDelegate onDeviceName) +void BluetoothAdapter::requestName(BluetoothPendingSocket &pending, OnScanDeviceNameDelegate onDeviceName) { - onScanDeviceNameD = onDeviceName; + onScanDeviceName = onDeviceName; pendingCmdList.emplace_back(BtstackCmd::remoteNameRequest(pending.addr, 0, 0)); BtstackBluetoothAdapter::processCommands(); } -void BluetoothPendingSocket::requestName(BluetoothAdapter::OnScanDeviceNameDelegate onDeviceName) +void BluetoothPendingSocket::requestName(BluetoothAdapter& bta, BTOnScanDeviceNameDelegate onDeviceName) { assert(ch); - defaultBtstackAdapter.requestName(*this, onDeviceName); + bta.requestName(*this, onDeviceName); } void BluetoothPendingSocket::close() @@ -888,13 +886,13 @@ void BluetoothPendingSocket::close() ch = 0; } -IG::ErrorCode BtstackBluetoothSocket::openRfcomm(BluetoothAdapter &, BluetoothAddr addr, uint32_t channel) +std::system_error BluetoothSocket::openRfcomm(BluetoothAdapter &, BluetoothAddr addr, uint32_t channel) { type = 1; if(socketList.isFull()) { logMsg("no space left in socket list"); - return {ENOSPC}; + return {ENOSPC, std::generic_category()}; } socketList.emplace_back(this); logMsg("creating RFCOMM channel %d socket", channel); @@ -903,16 +901,16 @@ IG::ErrorCode BtstackBluetoothSocket::openRfcomm(BluetoothAdapter &, BluetoothAd this->addr = addr; ch = channel; BtstackBluetoothAdapter::processCommands(); - return {}; + return std::error_code{}; } -IG::ErrorCode BtstackBluetoothSocket::openL2cap(BluetoothAdapter &, BluetoothAddr addr, uint32_t psm) +std::system_error BluetoothSocket::openL2cap(BluetoothAdapter &, BluetoothAddr addr, uint32_t psm) { type = 0; if(socketList.isFull()) { logMsg("no space left in socket list"); - return {ENOSPC}; + return {ENOSPC, std::generic_category()}; } socketList.emplace_back(this); if(inL2capSocketOpenHandler) @@ -928,10 +926,10 @@ IG::ErrorCode BtstackBluetoothSocket::openL2cap(BluetoothAdapter &, BluetoothAdd this->addr = addr; ch = psm; BtstackBluetoothAdapter::processCommands(); - return {}; + return std::error_code{}; } -IG::ErrorCode BtstackBluetoothSocket::open(BluetoothAdapter &, BluetoothPendingSocket &pending) +std::system_error BluetoothSocket::open(BluetoothAdapter &, BluetoothPendingSocket &pending) { assert(pending); addr = pending.addr; @@ -941,13 +939,13 @@ IG::ErrorCode BtstackBluetoothSocket::open(BluetoothAdapter &, BluetoothPendingS if(socketList.isFull()) { logMsg("no space left in socket list"); - return {ENOSPC}; + return {ENOSPC, std::generic_category()}; } socketList.emplace_back(this); pendingCmdList.emplace_back(BtstackCmd::l2capAcceptConnection(localCh)); pending = {}; BtstackBluetoothAdapter::processCommands(); - return {}; + return std::error_code{}; } static bool btAddrIsEqual(BluetoothAddr addr1, const bd_addr_t addr2) @@ -955,7 +953,7 @@ static bool btAddrIsEqual(BluetoothAddr addr1, const bd_addr_t addr2) return memcmp(addr1.data(), addr2, 6) == 0; } -BtstackBluetoothSocket *BtstackBluetoothSocket::findSocket(uint16_t localCh) +BluetoothSocket* BtstackBluetoothSocket::findSocket(uint16_t localCh) { for(auto e : socketList) { @@ -967,7 +965,7 @@ BtstackBluetoothSocket *BtstackBluetoothSocket::findSocket(uint16_t localCh) return nullptr; } -BtstackBluetoothSocket *BtstackBluetoothSocket::findSocket(const bd_addr_t addr, uint16_t ch) +BluetoothSocket* BtstackBluetoothSocket::findSocket(const bd_addr_t addr, uint16_t ch) { for(auto e : socketList) { @@ -979,7 +977,7 @@ BtstackBluetoothSocket *BtstackBluetoothSocket::findSocket(const bd_addr_t addr, return nullptr; } -BtstackBluetoothSocket *BtstackBluetoothSocket::findSocket(const bd_addr_t addr) +BluetoothSocket* BtstackBluetoothSocket::findSocket(const bd_addr_t addr) { for(auto e : socketList) { @@ -1022,7 +1020,7 @@ void BtstackBluetoothSocket::handleRfcommChannelOpened(uint8_t packet_type, uint logMsg("rfcomm ch %d, handle %d", rfcommCh, handle); sock->localCh = rfcommCh; sock->handle = handle; - if(sock->onStatus()(*sock, STATUS_OPENED) == OPEN_USAGE_READ_EVENTS) + if(sock->onStatus(*sock, BluetoothSocketState::Opened) == 1) { } @@ -1030,7 +1028,7 @@ void BtstackBluetoothSocket::handleRfcommChannelOpened(uint8_t packet_type, uint else { logMsg("failed. status code %u\n", packet[2]); - sock->onStatus()(*sock, STATUS_CONNECT_ERROR); + sock->onStatus(*sock, BluetoothSocketState::ConnectError); /*if(defaultBtstackAdapter.onScanStatus()) defaultBtstackAdapter.onScanStatus()(defaultBtstackAdapter, BluetoothAdapter::SOCKET_OPEN_FAILED, 0);*/ } @@ -1056,7 +1054,7 @@ void BtstackBluetoothSocket::handleL2capChannelOpened(uint8_t packet_type, uint1 sock->localCh = sourceCid; sock->handle = handle; inL2capSocketOpenHandler = 1; - if(sock->onStatus()(*sock, STATUS_OPENED) == OPEN_USAGE_READ_EVENTS) + if(sock->onStatus(*sock, BluetoothSocketState::Opened) == 1) { } @@ -1065,7 +1063,7 @@ void BtstackBluetoothSocket::handleL2capChannelOpened(uint8_t packet_type, uint1 else { logMsg("failed. status code %u\n", packet[2]); - sock->onStatus()(*sock, STATUS_CONNECT_ERROR); + sock->onStatus(*sock, BluetoothSocketState::ConnectError); /*if(defaultBtstackAdapter.onScanStatus()) defaultBtstackAdapter.onScanStatus()(defaultBtstackAdapter, BluetoothAdapter::SOCKET_OPEN_FAILED, 0);*/ } @@ -1092,14 +1090,14 @@ void BtstackBluetoothSocket::close() IG::eraseFirst(socketList, this); } -IG::ErrorCode BtstackBluetoothSocket::write(const void *data, size_t size) +ssize_t BluetoothSocket::write(const void *data, size_t size) { assert(localCh); if(type) bt_send_rfcomm(localCh, (uint8_t*)data, size); else bt_send_l2cap(localCh, (uint8_t*)data, size); - return {}; + return size; } } diff --git a/imagine/src/bluetooth/IControlPad.cc b/imagine/src/bluetooth/IControlPad.cc index c99851519..3856a22cd 100644 --- a/imagine/src/bluetooth/IControlPad.cc +++ b/imagine/src/bluetooth/IControlPad.cc @@ -16,7 +16,6 @@ #define LOGTAG "ICP" #include #include -#include #include #include #include @@ -116,26 +115,26 @@ const char *IControlPad::keyName(Key k) const return icpButtonName(k); } -IG::ErrorCode IControlPad::open(BluetoothAdapter &adapter, Input::Device &dev) +bool IControlPad::open(BluetoothAdapter &adapter, Input::Device &dev) { logMsg("connecting to iCP"); - sock.onData() = + sock.onData = [&dev](const char *packet, size_t size) { return getAs(dev).dataHandler(dev, packet, size); }; - sock.onStatus() = - [&dev](BluetoothSocket &sock, uint32_t status) + sock.onStatus = + [&dev](BluetoothSocket &sock, BluetoothSocketState status) { return getAs(dev).statusHandler(dev, sock, status); }; if(auto err = sock.openRfcomm(adapter, addr, 1); - err) + err.code()) { logErr("error opening socket"); - return err; + return false; } - return {}; + return true; } void IControlPad::close() @@ -143,22 +142,22 @@ void IControlPad::close() sock.close(); } -uint32_t IControlPad::statusHandler(Input::Device &dev, BluetoothSocket &sock, uint32_t status) +uint32_t IControlPad::statusHandler(Input::Device &dev, BluetoothSocket &sock, BluetoothSocketState status) { - if(status == BluetoothSocket::STATUS_OPENED) + if(status == BluetoothSocketState::Opened) { logMsg("iCP opened successfully"); ctx.application().bluetoothInputDeviceStatus(ctx, dev, status); sock.write(setLEDPulseInverse, sizeof setLEDPulseInverse); function = FUNC_SET_LED_MODE; - return BluetoothSocket::OPEN_USAGE_READ_EVENTS; + return 1; } - else if(status == BluetoothSocket::STATUS_CONNECT_ERROR) + else if(status == BluetoothSocketState::ConnectError) { logErr("iCP connection error"); ctx.application().bluetoothInputDeviceStatus(ctx, dev, status); } - else if(status == BluetoothSocket::STATUS_READ_ERROR) + else if(status == BluetoothSocketState::ReadError) { logErr("iCP read error, disconnecting"); ctx.application().bluetoothInputDeviceStatus(ctx, dev, status); @@ -178,7 +177,7 @@ bool IControlPad::dataHandler(Input::Device &dev, const char *packetPtr, size_t if(packet[size-bytesLeft] != RESP_OKAY) { logErr("error: iCP didn't respond with OK"); - ctx.application().bluetoothInputDeviceStatus(ctx, dev, BluetoothSocket::STATUS_READ_ERROR); + ctx.application().bluetoothInputDeviceStatus(ctx, dev, BluetoothSocketState::ReadError); return 0; } logMsg("got OK reply"); diff --git a/imagine/src/bluetooth/PS3Controller.cc b/imagine/src/bluetooth/PS3Controller.cc index 22a3b8964..e9872a8f0 100644 --- a/imagine/src/bluetooth/PS3Controller.cc +++ b/imagine/src/bluetooth/PS3Controller.cc @@ -16,7 +16,6 @@ #define LOGTAG "PS3Ctrl" #include #include -#include #include #include #include @@ -122,65 +121,65 @@ const char *PS3Controller::keyName(Input::Key k) const return ps3ButtonName(k); } -IG::ErrorCode PS3Controller::open(BluetoothAdapter &adapter, Input::Device &dev) +bool PS3Controller::open(BluetoothAdapter &adapter, Input::Device &dev) { - return {ENOTSUP}; + return false; } -IG::ErrorCode PS3Controller::open1Ctl(BluetoothAdapter &adapter, BluetoothPendingSocket &pending, Input::Device &dev) +bool PS3Controller::open1Ctl(BluetoothAdapter &adapter, BluetoothPendingSocket &pending, Input::Device &dev) { - ctlSock.onData() = intSock.onData() = + ctlSock.onData = intSock.onData = [&dev](const char *packet, size_t size) { return getAs(dev).dataHandler(dev, packet, size); }; - ctlSock.onStatus() = intSock.onStatus() = - [&dev](BluetoothSocket &sock, uint32_t status) + ctlSock.onStatus = intSock.onStatus = + [&dev](BluetoothSocket &sock, BluetoothSocketState status) { return getAs(dev).statusHandler(dev, sock, status); }; logMsg("accepting PS3 control channel"); if(auto err = ctlSock.open(adapter, pending); - err) + err.code()) { logErr("error opening control socket"); - return err; + return false; } - return {}; + return true; } -IG::ErrorCode PS3Controller::open2Int(BluetoothAdapter &adapter, BluetoothPendingSocket &pending) +bool PS3Controller::open2Int(BluetoothAdapter &adapter, BluetoothPendingSocket &pending) { logMsg("accepting PS3 interrupt channel"); if(auto err = intSock.open(adapter, pending); - err) + err.code()) { logErr("error opening interrupt socket"); - return err; + return false; } - return {}; + return true; } -uint32_t PS3Controller::statusHandler(Input::Device &dev, BluetoothSocket &sock, uint32_t status) +uint32_t PS3Controller::statusHandler(Input::Device &dev, BluetoothSocket &sock, BluetoothSocketState status) { - if(status == BluetoothSocket::STATUS_OPENED && &sock == (BluetoothSocket*)&ctlSock) + if(status == BluetoothSocketState::Opened && &sock == (BluetoothSocket*)&ctlSock) { logMsg("opened PS3 control socket, waiting for interrupt socket"); return 0; // don't add ctlSock to event loop } - else if(status == BluetoothSocket::STATUS_OPENED && &sock == (BluetoothSocket*)&intSock) + else if(status == BluetoothSocketState::Opened && &sock == (BluetoothSocket*)&intSock) { logMsg("PS3 controller opened successfully"); ctx.application().bluetoothInputDeviceStatus(ctx, dev, status); sendFeatureReport(); - return BluetoothSocket::OPEN_USAGE_READ_EVENTS; + return 1; } - else if(status == BluetoothSocket::STATUS_CONNECT_ERROR) + else if(status == BluetoothSocketState::ConnectError) { logErr("PS3 controller connection error"); ctx.application().bluetoothInputDeviceStatus(ctx, dev, status); } - else if(status == BluetoothSocket::STATUS_READ_ERROR) + else if(status == BluetoothSocketState::ReadError) { logErr("PS3 controller read error, disconnecting"); ctx.application().bluetoothInputDeviceStatus(ctx, dev, status); diff --git a/imagine/src/bluetooth/Wiimote.cc b/imagine/src/bluetooth/Wiimote.cc index f979d5478..40bd19ae3 100644 --- a/imagine/src/bluetooth/Wiimote.cc +++ b/imagine/src/bluetooth/Wiimote.cc @@ -16,7 +16,6 @@ #define LOGTAG "Wiimote" #include #include -#include #include #include #include @@ -186,49 +185,50 @@ const char *WiimoteExtDevice::keyName(Input::Key k) const return wiiKeyName(k, map_); } -IG::ErrorCode Wiimote::open(BluetoothAdapter &adapter, Input::Device &dev) +bool Wiimote::open(BluetoothAdapter& adapter, Input::Device& dev) { logMsg("opening Wiimote"); - ctlSock.onData() = intSock.onData() = + btaPtr = &adapter; + ctlSock.onData = intSock.onData = [&dev](const char *packet, size_t size) { return getAs(dev).dataHandler(dev, packet, size); }; - ctlSock.onStatus() = intSock.onStatus() = - [&dev](BluetoothSocket &sock, uint32_t status) + ctlSock.onStatus = intSock.onStatus = + [&dev](BluetoothSocket &sock, BluetoothSocketState status) { return getAs(dev).statusHandler(dev, sock, status); }; - if(ctlSock.openL2cap(adapter, addr, 17)) + if(ctlSock.openL2cap(adapter, addr, 17).code()) { logErr("error opening control socket"); - return {EIO}; + return false; } - return {}; + return true; } -uint32_t Wiimote::statusHandler(Input::Device &dev, BluetoothSocket &sock, uint32_t status) +uint32_t Wiimote::statusHandler(Input::Device &dev, BluetoothSocket &sock, BluetoothSocketState status) { - if(status == BluetoothSocket::STATUS_OPENED && &sock == (BluetoothSocket*)&ctlSock) + if(status == BluetoothSocketState::Opened && &sock == (BluetoothSocket*)&ctlSock) { logMsg("opened Wiimote control socket, opening interrupt socket"); - intSock.openL2cap(*BluetoothAdapter::defaultAdapter(ctx), addr, 19); + intSock.openL2cap(*btaPtr, addr, 19); return 0; // don't add ctlSock to event loop } - else if(status == BluetoothSocket::STATUS_OPENED && &sock == (BluetoothSocket*)&intSock) + else if(status == BluetoothSocketState::Opened && &sock == (BluetoothSocket*)&intSock) { logMsg("Wiimote opened successfully"); ctx.application().bluetoothInputDeviceStatus(ctx, dev, status); setLEDs(enumId_); requestStatus(); - return BluetoothSocket::OPEN_USAGE_READ_EVENTS; + return 1; } - else if(status == BluetoothSocket::STATUS_CONNECT_ERROR) + else if(status == BluetoothSocketState::ConnectError) { logErr("Wiimote connection error"); ctx.application().bluetoothInputDeviceStatus(ctx, dev, status); } - else if(status == BluetoothSocket::STATUS_READ_ERROR) + else if(status == BluetoothSocketState::ReadError) { logErr("Wiimote read error"); ctx.application().bluetoothInputDeviceStatus(ctx, dev, status); diff --git a/imagine/src/bluetooth/Zeemote.cc b/imagine/src/bluetooth/Zeemote.cc index 5cc73a708..8aed3e847 100644 --- a/imagine/src/bluetooth/Zeemote.cc +++ b/imagine/src/bluetooth/Zeemote.cc @@ -16,7 +16,6 @@ #define LOGTAG "Zeemote" #include #include -#include #include #include #include @@ -62,16 +61,16 @@ const char *Zeemote::keyName(Input::Key k) const return zeemoteButtonName(k); } -IG::ErrorCode Zeemote::open(BluetoothAdapter &adapter, Input::Device &dev) +bool Zeemote::open(BluetoothAdapter& adapter, Input::Device& dev) { logMsg("connecting to Zeemote"); - sock.onData() = + sock.onData = [&dev](const char *packet, size_t size) { return getAs(dev).dataHandler(dev, packet, size); }; - sock.onStatus() = - [&dev](BluetoothSocket &sock, uint32_t status) + sock.onStatus = + [&dev](BluetoothSocket &sock, BluetoothSocketState status) { return getAs(dev).statusHandler(dev, sock, status); }; @@ -79,12 +78,12 @@ IG::ErrorCode Zeemote::open(BluetoothAdapter &adapter, Input::Device &dev) sock.setPin("0000", 4); #endif if(auto err = sock.openRfcomm(adapter, addr, 1); - err) + err.code()) { logErr("error opening socket"); - return err; + return false; } - return {}; + return true; } void Zeemote::close() @@ -92,20 +91,20 @@ void Zeemote::close() sock.close(); } -uint32_t Zeemote::statusHandler(Input::Device &dev, BluetoothSocket &sock, uint32_t status) +uint32_t Zeemote::statusHandler(Input::Device &dev, BluetoothSocket &sock, BluetoothSocketState status) { - if(status == BluetoothSocket::STATUS_OPENED) + if(status == BluetoothSocketState::Opened) { logMsg("Zeemote opened successfully"); ctx.application().bluetoothInputDeviceStatus(ctx, dev, status); - return BluetoothSocket::OPEN_USAGE_READ_EVENTS; + return 1; } - else if(status == BluetoothSocket::STATUS_CONNECT_ERROR) + else if(status == BluetoothSocketState::ConnectError) { logErr("Zeemote connection error"); ctx.application().bluetoothInputDeviceStatus(ctx, dev, status); } - else if(status == BluetoothSocket::STATUS_READ_ERROR) + else if(status == BluetoothSocketState::ReadError) { logErr("Zeemote read error, disconnecting"); ctx.application().bluetoothInputDeviceStatus(ctx, dev, status); @@ -131,7 +130,7 @@ bool Zeemote::dataHandler(Input::Device &dev, const char *packet, size_t size) if(packetSize > sizeof(inputBuffer)) { logErr("can't handle packet, closing Zeemote"); - ctx.application().bluetoothInputDeviceStatus(ctx, dev, BluetoothSocket::STATUS_READ_ERROR); + ctx.application().bluetoothInputDeviceStatus(ctx, dev, BluetoothSocketState::ReadError); return 0; } diff --git a/imagine/src/bluetooth/bluetooth.mk b/imagine/src/bluetooth/bluetooth.mk index ca20e9799..7ae1ddb61 100644 --- a/imagine/src/bluetooth/bluetooth.mk +++ b/imagine/src/bluetooth/bluetooth.mk @@ -1,7 +1,7 @@ ifndef inc_bluetooth inc_bluetooth := 1 -SRC += bluetooth/BluetoothAdapter.cc bluetooth/BluetoothInputDevScanner.cc \ +SRC += bluetooth/BluetoothInputDevScanner.cc \ bluetooth/Wiimote.cc bluetooth/IControlPad.cc bluetooth/Zeemote.cc ifeq ($(ENV), linux) diff --git a/imagine/src/data-type/image/Android.cc b/imagine/src/data-type/image/Android.cc index 46f21dcad..de0875daa 100755 --- a/imagine/src/data-type/image/Android.cc +++ b/imagine/src/data-type/image/Android.cc @@ -115,7 +115,7 @@ BitmapWriter::BitmapWriter(ApplicationContext ctx): bool PixmapWriter::writeToFile(PixmapView pix, const char *path) const { auto env = app().thisThreadJniEnv(); - auto aFormat = pix.format().id() == PIXEL_RGB565 ? ANDROID_BITMAP_FORMAT_RGB_565 : ANDROID_BITMAP_FORMAT_RGBA_8888; + auto aFormat = pix.format().id == PIXEL_RGB565 ? ANDROID_BITMAP_FORMAT_RGB_565 : ANDROID_BITMAP_FORMAT_RGBA_8888; auto bitmap = jMakeBitmap(env, baseActivity, pix.w(), pix.h(), aFormat); if(!bitmap) { diff --git a/imagine/src/fs/PosixFS.cc b/imagine/src/fs/PosixFS.cc index 4ea04471b..871b9454f 100644 --- a/imagine/src/fs/PosixFS.cc +++ b/imagine/src/fs/PosixFS.cc @@ -59,7 +59,7 @@ DirectoryStream::DirectoryStream(CStringView path, DirOpenFlags flags): if(flags.test) return; else - throw std::system_error{errno, std::system_category(), path}; + throw std::system_error{errno, std::generic_category(), path}; } logMsg("opened directory:%s", path.data()); basePath = path; @@ -321,7 +321,7 @@ bool create_directory(CStringView path) { if(Config::DEBUG_BUILD) logErr("mkdir(%s) error:%s", path.data(), strerror(err)); - throw std::system_error(err, std::system_category(), path); + throw std::system_error(err, std::generic_category(), path); } } logMsg("made directory:%s", path.data()); diff --git a/imagine/src/gfx/opengl/PixmapBufferTexture.cc b/imagine/src/gfx/opengl/PixmapBufferTexture.cc index b07cce582..2b806a759 100644 --- a/imagine/src/gfx/opengl/PixmapBufferTexture.cc +++ b/imagine/src/gfx/opengl/PixmapBufferTexture.cc @@ -64,7 +64,7 @@ namespace IG::Gfx PixmapBufferTexture::PixmapBufferTexture(RendererTask &r, TextureConfig config, TextureBufferMode mode, bool singleBuffer) { - mode = r.renderer().makeValidTextureBufferMode(mode); + mode = r.renderer().evalTextureBufferMode(mode); try { if(mode == TextureBufferMode::SYSTEM_MEMORY) @@ -429,7 +429,7 @@ std::vector Renderer::textureBufferModes() return methodDesc; } -TextureBufferMode Renderer::makeValidTextureBufferMode(TextureBufferMode mode) +TextureBufferMode Renderer::evalTextureBufferMode(TextureBufferMode mode) { switch(mode) { @@ -448,14 +448,21 @@ TextureBufferMode Renderer::makeValidTextureBufferMode(TextureBufferMode mode) case TextureBufferMode::SYSTEM_MEMORY: return TextureBufferMode::SYSTEM_MEMORY; case TextureBufferMode::PBO: - return hasPersistentBufferMapping(*this) ? TextureBufferMode::PBO : makeValidTextureBufferMode(); + return hasPersistentBufferMapping(*this) ? TextureBufferMode::PBO : evalTextureBufferMode(); #ifdef __ANDROID__ case TextureBufferMode::ANDROID_HARDWARE_BUFFER: - return hasHardwareBuffer(*this) ? TextureBufferMode::ANDROID_HARDWARE_BUFFER : makeValidTextureBufferMode(); + return hasHardwareBuffer(*this) ? TextureBufferMode::ANDROID_HARDWARE_BUFFER : evalTextureBufferMode(); case TextureBufferMode::ANDROID_SURFACE_TEXTURE: - return hasSurfaceTexture(*this) ? TextureBufferMode::ANDROID_SURFACE_TEXTURE : makeValidTextureBufferMode(); + return hasSurfaceTexture(*this) ? TextureBufferMode::ANDROID_SURFACE_TEXTURE : evalTextureBufferMode(); #endif } } +TextureBufferMode Renderer::validateTextureBufferMode(TextureBufferMode mode) +{ + if(mode == Gfx::TextureBufferMode::DEFAULT || evalTextureBufferMode(mode) == mode) + return mode; + return Gfx::TextureBufferMode::DEFAULT; +} + } diff --git a/imagine/src/gfx/opengl/Renderer.cc b/imagine/src/gfx/opengl/Renderer.cc index 785cb7756..1d33999c4 100755 --- a/imagine/src/gfx/opengl/Renderer.cc +++ b/imagine/src/gfx/opengl/Renderer.cc @@ -355,7 +355,7 @@ ColorSpace Renderer::supportedColorSpace(PixelFormat fmt, ColorSpace wantedColor { case ColorSpace::LINEAR: return ColorSpace::LINEAR; case ColorSpace::SRGB: - switch(fmt.id()) + switch(fmt.id) { case PIXEL_FMT_RGBA8888: case PIXEL_FMT_BGRA8888: diff --git a/imagine/src/gui/AlertView.cc b/imagine/src/gui/AlertView.cc index 7f2fee4b0..0a78ce2dc 100644 --- a/imagine/src/gui/AlertView.cc +++ b/imagine/src/gui/AlertView.cc @@ -105,12 +105,12 @@ void BaseAlertView::onAddedToController(ViewController *c, const Input::Event &e void YesNoAlertView::setOnYes(TextMenuItem::SelectDelegate del) { - yes.onSelect = del; + yesNo[0].onSelect = del; } void YesNoAlertView::setOnNo(TextMenuItem::SelectDelegate del) { - no.onSelect = del; + yesNo[1].onSelect = del; } } diff --git a/imagine/src/gui/FSPicker.cc b/imagine/src/gui/FSPicker.cc index 572cab3f1..d8ca5a29e 100644 --- a/imagine/src/gui/FSPicker.cc +++ b/imagine/src/gui/FSPicker.cc @@ -72,8 +72,7 @@ FSPicker::FSPicker(ViewAttachParams attach, Gfx::TextureSpan backRes, Gfx::Textu pushFileLocationsView(e); }); controller.setNavView(std::move(nav)); - controller.push(makeView([](const TableView &) { return 0; }, - [&d = dir](const TableView &, size_t idx) -> MenuItem& { return d[idx].text; })); + controller.push(makeView(dir)); controller.navView()->showLeftBtn(true); dir.reserve(16); // start with some initial capacity to avoid small reallocations } @@ -468,10 +467,10 @@ void FSPicker::startDirectoryListThread(CStringView path) return; } dir.clear(); - fileTableView().setItemsDelegate(); + fileTableView().resetItemSource(); dirListEvent.setCallback([this]() { - fileTableView().setItemsDelegate([&d = dir](const TableView &) { return d.size(); }); + fileTableView().resetItemSource(dir); place(); fileTableView().restoreUIState(std::exchange(newFileUIState, {})); postDraw(); diff --git a/imagine/src/gui/MenuItem.cc b/imagine/src/gui/MenuItem.cc index c00a586f7..cfa22ea8b 100644 --- a/imagine/src/gui/MenuItem.cc +++ b/imagine/src/gui/MenuItem.cc @@ -179,8 +179,8 @@ class MenuItemTableView : public TableView int activeItem; MultiChoiceMenuItem &src; - MenuItemTableView(UTF16Convertible auto &&name, ViewAttachParams attach, int active, ItemsDelegate items, ItemDelegate item, MultiChoiceMenuItem &src): - TableView{IG_forward(name), attach, items, item}, + MenuItemTableView(UTF16Convertible auto &&name, ViewAttachParams attach, int active, ItemSourceDelegate itemSrc, MultiChoiceMenuItem &src): + TableView{IG_forward(name), attach, itemSrc}, activeItem{active}, src{src} { @@ -228,7 +228,12 @@ int MultiChoiceMenuItem::selected() const size_t MultiChoiceMenuItem::items() const { - return items_(*this); + return getAs(itemSrc(ItemsMessage{*this})); +} + +TextMenuItem& MultiChoiceMenuItem::item(ItemSourceDelegate src, size_t idx) +{ + return *getAs(src(GetItemMessage{*this, idx})); } bool MultiChoiceMenuItem::setSelected(int idx, View &view) @@ -263,9 +268,9 @@ void MultiChoiceMenuItem::setDisplayString(size_t idx) { return; } - else if(idx < items_(*this)) + else if(idx < items()) { - t2.resetString(std::u16string{item_(*this, idx).text().stringView()}); + t2.resetString(std::u16string{item(idx).text().stringView()}); } else { @@ -298,14 +303,14 @@ std::unique_ptr MultiChoiceMenuItem::makeTableView(ViewAttachParams a ( std::u16string{t.stringView()}, attach, - selected_ < (int)items_(*this) ? selected_ : -1, - [this](const TableView &) + selected_ < (ssize_t)items() ? selected_ : -1, + [this](TableView::ItemMessage msg) -> TableView::ItemReply { - return items_(*this); - }, - [this](const TableView &, size_t idx) -> MenuItem& - { - return item_(*this, idx); + return visit(overloaded + { + [&](const TableView::ItemsMessage &m) -> TableView::ItemReply { return items(); }, + [&](const TableView::GetItemMessage &m) -> TableView::ItemReply { return &item(m.idx); }, + }, msg); }, *this ); @@ -323,17 +328,17 @@ void MultiChoiceMenuItem::updateDisplayString() int MultiChoiceMenuItem::idxOfId(MenuId id) { - auto items = items_(*this); - auto item = item_; + auto count = items(); + auto src = itemSrc; MenuId lastId{}; - for(auto i : iotaCount(items)) + for(auto i : iotaCount(count)) { - lastId = item(*this, i).id; + lastId = item(src, i).id; if(lastId == id) return (int)i; } if(lastId == defaultMenuId) // special case to simplify uses where the last menu item represents a custom value - return items - 1; + return count - 1; else return -1; } diff --git a/imagine/src/gui/TableView.cc b/imagine/src/gui/TableView.cc index f86286a90..6acbbadf4 100755 --- a/imagine/src/gui/TableView.cc +++ b/imagine/src/gui/TableView.cc @@ -33,7 +33,7 @@ constexpr SystemLogger log{"TableView"}; size_t TableView::cells() const { - return items(*this); + return getAs(itemSrc(ItemsMessage{*this})); } WSize TableView::cellSize() const @@ -43,7 +43,7 @@ WSize TableView::cellSize() const void TableView::highlightCell(int idx) { - auto cells_ = items(*this); + auto cells_ = cells(); if(!cells_) return; if(idx >= 0) @@ -60,17 +60,19 @@ void TableView::setAlign(_2DOrigin align) void TableView::prepareDraw() { - for(auto i : iotaCount(items(*this))) + auto src = itemSrc; + for(auto i : iotaCount(cells())) { - item(*this, i).prepareDraw(); + item(src, i).prepareDraw(); } } void TableView::draw(Gfx::RendererCommands &__restrict__ cmds) { - ssize_t cells_ = items(*this); + ssize_t cells_ = cells(); if(!cells_) return; + auto src = itemSrc; using namespace IG::Gfx; auto visibleRect = viewRect() + WindowRect{{}, {0, displayRect().y2 - viewRect().y2}}; cmds.setClipRect(renderer().makeClipRect(window(), visibleRect)); @@ -119,7 +121,7 @@ void TableView::draw(Gfx::RendererCommands &__restrict__ cmds) { int ySize = regularYSize; auto color = regularColor; - if(!elementIsSelectable(item(*this, i - 1))) + if(!elementIsSelectable(item(src, i - 1))) { ySize = headingYSize; color = headingColor; @@ -161,7 +163,7 @@ void TableView::draw(Gfx::RendererCommands &__restrict__ cmds) for(ssize_t i = startYCell; i < endYCell; i++) { auto rect = IG::makeWindowRectRel({x, y}, {viewRect().xSize(), yCellSize}); - drawElement(cmds, i, item(*this, i), rect, xIndent); + drawElement(cmds, i, item(src, i), rect, xIndent); y += yCellSize; } cmds.setClipTest(false); @@ -169,15 +171,16 @@ void TableView::draw(Gfx::RendererCommands &__restrict__ cmds) void TableView::place() { - auto cells_ = items(*this); + auto cells_ = cells(); + auto src = itemSrc; for(auto i : iotaCount(cells_)) { //log.debug("compile item:{}", i); - item(*this, i).compile(); + item(src, i).compile(); } if(cells_) { - setYCellSize(IG::makeEvenRoundedUp(item(*this, 0).ySize()*2)); + setYCellSize(IG::makeEvenRoundedUp(item(0).ySize()*2)); visibleCells = IG::divRoundUp(displayRect().ySize(), yCellSize) + 1; scrollToFocusRect(); selectQuads.write(0, {.bounds = WRect{{}, {viewRect().xSize(), yCellSize-1}}.as()}); @@ -200,10 +203,10 @@ void TableView::onAddedToController(ViewController *, const Input::Event &e) { if(e.keyEvent()) { - auto cells = items(*this); - if(!cells) + auto cells_ = cells(); + if(!cells_) return; - selected = nextSelectableElement(0, cells); + selected = nextSelectableElement(0, cells_); } } @@ -272,7 +275,7 @@ void TableView::clearSelection() void TableView::setYCellSize(int s) { yCellSize = s; - ScrollView::setContentSize({viewRect().xSize(), (int)items(*this) * s}); + ScrollView::setContentSize({viewRect().xSize(), (int)cells() * s}); } IG::WindowRect TableView::focusRect() @@ -286,9 +289,10 @@ IG::WindowRect TableView::focusRect() int TableView::nextSelectableElement(int start, int items) { int elem = wrapMinMax(start, 0, items); + auto src = itemSrc; for(auto i : iotaCount(items)) { - if(elementIsSelectable(item(*this, elem))) + if(elementIsSelectable(item(src, elem))) { return elem; } @@ -300,9 +304,10 @@ int TableView::nextSelectableElement(int start, int items) int TableView::prevSelectableElement(int start, int items) { int elem = wrapMinMax(start, 0, items); + auto src = itemSrc; for(auto i : iotaCount(items)) { - if(elementIsSelectable(item(*this, elem))) + if(elementIsSelectable(item(src, elem))) { return elem; } @@ -313,7 +318,7 @@ int TableView::prevSelectableElement(int start, int items) bool TableView::handleTableInput(const Input::Event &e, bool &movedSelected) { - ssize_t cells_ = items(*this); + ssize_t cells_ = cells(); return visit(overloaded { [&](const Input::KeyEvent &keyEv) @@ -414,7 +419,7 @@ bool TableView::handleTableInput(const Input::Event &e, bool &movedSelected) { //log.debug("entry:{} pushed", selected); selectedIsActivated = true; - onSelectElement(e, selected, item(*this, selected)); + onSelectElement(e, selected, item(selected)); } return true; } @@ -457,7 +462,7 @@ bool TableView::handleTableInput(const Input::Event &e, bool &movedSelected) //logMsg("pushed outside of item bounds"); return false; } - auto &it = item(*this, i); + auto &it = item(i); if(motionEv.pushed()) { //log.info("input pushed on cell:{}", i); @@ -518,6 +523,11 @@ TableUIState TableView::saveUIState() const return {.highlightedCell = highlightedCell(), .scrollOffset = scrollOffset()}; } +MenuItem& TableView::item(ItemSourceDelegate src, size_t idx) +{ + return *getAs(src(GetItemMessage{*this, idx})); +} + void TableView::restoreUIState(TableUIState state) { if(state.highlightedCell != -1) diff --git a/imagine/src/io/PosixIO.cc b/imagine/src/io/PosixIO.cc index 24d9a30e2..68beaeb0f 100755 --- a/imagine/src/io/PosixIO.cc +++ b/imagine/src/io/PosixIO.cc @@ -96,7 +96,7 @@ PosixIO::PosixIO(CStringView path, OpenFlags openFlags) if(openFlags.test) return; else - throw std::system_error{errno, std::system_category(), path}; + throw std::system_error{errno, std::generic_category(), path}; } if constexpr(Config::DEBUG_BUILD) log.info("opened ({}) fd:{} @ {}", flagsString(openFlags), fd(), path);