diff --git a/EmuFramework/build.mk b/EmuFramework/build.mk index c87bc875a..71027dfb2 100644 --- a/EmuFramework/build.mk +++ b/EmuFramework/build.mk @@ -17,6 +17,7 @@ InputDeviceData.cc \ KeyConfig.cc \ OutputTimingManager.cc \ pathUtils.cc \ +ToggleInput.cc \ TurboInput.cc \ VideoImageEffect.cc \ VideoImageOverlay.cc \ diff --git a/EmuFramework/include/emuframework/EmuApp.hh b/EmuFramework/include/emuframework/EmuApp.hh index 94b64dc0c..64bf8896d 100644 --- a/EmuFramework/include/emuframework/EmuApp.hh +++ b/EmuFramework/include/emuframework/EmuApp.hh @@ -226,7 +226,6 @@ public: bool handleKeyInput(KeyInfo, const Input::Event &srcEvent); bool handleAppActionKeyInput(InputAction, const Input::Event &srcEvent); void handleSystemKeyInput(KeyInfo, Input::Action, uint32_t metaState = 0); - void handleSystemKeyInput(InputAction); void runTurboInputEvents(); void resetInput(); void setRunSpeed(double speed); diff --git a/EmuFramework/include/emuframework/EmuInput.hh b/EmuFramework/include/emuframework/EmuInput.hh index d49f55bfe..6aa26e481 100644 --- a/EmuFramework/include/emuframework/EmuInput.hh +++ b/EmuFramework/include/emuframework/EmuInput.hh @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -145,6 +146,7 @@ public: std::vector> customKeyConfigs; std::vector> savedInputDevs; TurboInput turboActions; + ToggleInput toggleInput; DelegateFunc onUpdateDevices; bool turboModifierActive{}; diff --git a/EmuFramework/include/emuframework/EmuSystem.hh b/EmuFramework/include/emuframework/EmuSystem.hh index a53a7716c..5a1bc1d4f 100755 --- a/EmuFramework/include/emuframework/EmuSystem.hh +++ b/EmuFramework/include/emuframework/EmuSystem.hh @@ -93,6 +93,7 @@ struct InputAction uint32_t metaState{}; constexpr bool isPushed() const { return state == Input::Action::PUSHED; } + constexpr operator KeyInfo() const { return {code, flags}; } }; enum class InputComponent : uint8_t diff --git a/EmuFramework/include/emuframework/ToggleInput.hh b/EmuFramework/include/emuframework/ToggleInput.hh new file mode 100644 index 000000000..32de57c22 --- /dev/null +++ b/EmuFramework/include/emuframework/ToggleInput.hh @@ -0,0 +1,34 @@ +#pragma once + +/* This file is part of EmuFramework. + + 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 EmuFramework. If not, see */ + +#include +#include + +namespace EmuEx +{ + +class EmuApp; + +struct ToggleInput +{ + StaticArrayList keys; + + constexpr ToggleInput() = default; + void updateEvent(EmuApp &, KeyInfo, Input::Action); +}; + +} diff --git a/EmuFramework/include/emuframework/TurboInput.hh b/EmuFramework/include/emuframework/TurboInput.hh index 7aed94009..b95f4d0b1 100644 --- a/EmuFramework/include/emuframework/TurboInput.hh +++ b/EmuFramework/include/emuframework/TurboInput.hh @@ -25,7 +25,7 @@ class EmuApp; struct TurboInput { - IG::StaticArrayList keys; + StaticArrayList keys; int clock{}; constexpr TurboInput() = default; diff --git a/EmuFramework/include/emuframework/VController.hh b/EmuFramework/include/emuframework/VController.hh index 366054adf..bf609c9ab 100755 --- a/EmuFramework/include/emuframework/VController.hh +++ b/EmuFramework/include/emuframework/VController.hh @@ -87,7 +87,8 @@ public: config{.keys{keys[0], keys[1], keys[2], keys[3]}} {} constexpr VControllerDPad(const Config &config): config{config} {} void setImage(Gfx::TextureSpan); - void draw(Gfx::RendererCommands &__restrict__) const; + void drawButtons(Gfx::RendererCommands &__restrict__) const; + void drawBounds(Gfx::RendererCommands &__restrict__) const; void setShowBounds(Gfx::Renderer &r, bool on); bool showBounds() const { return config.visualizeBounds; } std::array getInput(WPt c) const; @@ -191,6 +192,12 @@ public: bool overlaps(WPt windowPos) const { return enabled && realBounds().overlaps(windowPos); } void setAlpha(float alpha); + void updateColor(Gfx::Color c, float alpha) + { + color = c; + setAlpha(alpha); + } + protected: Gfx::LitSprite spr; WRect bounds_{}; @@ -244,7 +251,8 @@ public: WRect realBounds() const { return bounds() + paddingRect(); } int rows() const; std::array findButtonIndices(WPt windowPos) const; - void draw(Gfx::RendererCommands &__restrict__) const; + void drawButtons(Gfx::RendererCommands &__restrict__) const; + void drawBounds(Gfx::RendererCommands &__restrict__) const; std::string name(const EmuApp &) const; void updateMeasurements(const Window &win); void transposeKeysForPlayer(const EmuApp &, int player); @@ -273,8 +281,6 @@ protected: int spacingPixels{}; int16_t btnStagger{}; int16_t btnRowShift{}; - - void drawButtons(Gfx::RendererCommands &__restrict__) const; public: LayoutConfig layout{}; }; @@ -305,7 +311,7 @@ public: auto bounds() const { return bounds_; } WRect realBounds() const { return bounds(); } int rows() const; - void draw(Gfx::RendererCommands &__restrict__) const; + void drawButtons(Gfx::RendererCommands &__restrict__) const; std::string name(const EmuApp &) const; void setAlpha(float alpha) { for(auto &b : buttons) { b.setAlpha(alpha); } } @@ -362,11 +368,30 @@ public: return state == VControllerState::SHOWN || (showHidden && state != VControllerState::OFF); } - void draw(Gfx::RendererCommands &__restrict__ cmds, bool showHidden) const + void drawButtons(Gfx::RendererCommands &__restrict__ cmds, bool showHidden) const { if(!shouldDraw(state, showHidden)) return; - visit([&](auto &e){ e.draw(cmds); }, *this); + visit([&](auto &e){ e.drawButtons(cmds); }, *this); + } + + void drawBounds(Gfx::RendererCommands &__restrict__ cmds, bool showHidden) const + { + if(!shouldDraw(state, showHidden)) + return; + visit([&](auto &e) + { + if constexpr(requires {e.drawBounds(cmds);}) + { + e.drawBounds(cmds); + } + }, *this); + } + + void draw(Gfx::RendererCommands &__restrict__ cmds, bool showHidden) const + { + drawBounds(cmds, showHidden); + drawButtons(cmds, showHidden); } void place(WRect viewBounds, WRect windowBounds, int layoutIdx) @@ -495,7 +520,9 @@ public: int yMMSizeToPixel(const Window &win, float mm) const; void setInputPlayer(int8_t player); auto inputPlayer() const { return inputPlayer_; } + bool keyIsEnabled(KeyInfo) const; void setDisabledInputKeys(std::span keys); + void updateEnabledButtons(VControllerButtonGroup &) const; void updateKeyboardMapping(); void updateTextures(); void resetInput(); @@ -569,6 +596,7 @@ private: VControllerKeyboard kb{}; std::vector gpElements{}; std::vector uiElements{}; + std::span disabledKeys{}; float alphaF{}; Input::DragTracker> dragTracker{}; int16_t defaultButtonSize{}; diff --git a/EmuFramework/include/emuframework/inputDefs.hh b/EmuFramework/include/emuframework/inputDefs.hh index 99ac52d8e..88c577683 100644 --- a/EmuFramework/include/emuframework/inputDefs.hh +++ b/EmuFramework/include/emuframework/inputDefs.hh @@ -32,7 +32,8 @@ struct KeyFlags uint8_t appCode:1{}, turbo:1{}, - unused:2{}, + toggle:1{}, + unused:1{}, deviceId:4{}; constexpr bool operator==(const KeyFlags &) const = default; diff --git a/EmuFramework/src/EmuApp.cc b/EmuFramework/src/EmuApp.cc index ffb824b0f..ad5e7ee61 100644 --- a/EmuFramework/src/EmuApp.cc +++ b/EmuFramework/src/EmuApp.cc @@ -55,6 +55,7 @@ namespace EmuEx { +constexpr SystemLogger log{"App"}; static EmuApp *gAppPtr{}; [[gnu::weak]] bool EmuApp::hasIcon = true; [[gnu::weak]] bool EmuApp::needsGlobalInstance = false; @@ -160,7 +161,7 @@ EmuApp::EmuApp(ApplicationInitParams initParams, ApplicationContext &ctx): { [&](InterProcessMessageEvent &e) { - logMsg("got IPC path:%s", e.filename.data()); + log.info("got IPC path:{}", e.filename); system().setInitialLoadPath(e.filename); }, [](auto &) {} @@ -223,7 +224,7 @@ Gfx::TextureSpan EmuApp::asset(AssetDesc desc) const } catch(...) { - logErr("error loading asset:%s", desc.filename()); + log.error("error loading asset:{}", desc.filename()); } } return {&res, desc.texBounds}; @@ -315,7 +316,7 @@ static const char *parseCommandArgs(IG::CommandArgs arg) return nullptr; } auto launchPath = arg.v[1]; - logMsg("starting content from command line:%s", launchPath); + log.info("starting content from command line:{}", launchPath); return launchPath; } @@ -378,7 +379,7 @@ void EmuApp::applyRenderPixelFormat() fmt = windowPixelFormat(); if(!EmuSystem::canRenderRGBA8888 && fmt != IG::PIXEL_RGB565) { - logMsg("Using RGB565 render format since emulated system can't render RGBA8888"); + log.info("Using RGB565 render format since emulated system can't render RGBA8888"); fmt = IG::PIXEL_RGB565; } emuVideoLayer.setFormat(system(), fmt, videoEffectPixelFormat(), windowDrawableConf.colorSpace); @@ -391,7 +392,7 @@ void EmuApp::renderSystemFramebuffer(EmuVideo &video) video.clear(); return; } - logMsg("updating video with current framebuffer content"); + log.info("updating video with current framebuffer content"); system().renderFramebuffer(video); } @@ -410,7 +411,7 @@ void EmuApp::updateLegacySavePath(IG::ApplicationContext ctx, CStringView path) auto oldSaveSubDirs = subDirectoryStrings(ctx, path); if(oldSaveSubDirs.empty()) { - logMsg("no legacy save folders in:%s", path.data()); + log.info("no legacy save folders in:{}", path); return; } flattenSubDirectories(ctx, oldSaveSubDirs, path); @@ -537,7 +538,7 @@ void EmuApp::mainInitCommon(IG::ApplicationInitParams initParams, IG::Applicatio { windowFrameTimeSource = WindowFrameTimeSource::SCREEN; } - logMsg("timestamp source:%s", windowFrameTimeSource == WindowFrameTimeSource::RENDERER ? "renderer" : "screen"); + log.info("timestamp source:{}", windowFrameTimeSource == WindowFrameTimeSource::RENDERER ? "renderer" : "screen"); winData.viewController.placeElements(); winData.viewController.pushAndShowMainMenu(viewAttach, emuVideoLayer, emuAudio); configureSecondaryScreens(); @@ -608,7 +609,7 @@ void EmuApp::mainInitCommon(IG::ApplicationInitParams initParams, IG::Applicatio } else { - //logDMsg("previous async frame not ready yet"); + //log.debug("previous async frame not ready yet"); doIfUsed(frameTimeStats, [&](auto &stats) { stats.missedFrameCallbacks++; }); } win.setDrawEventPriority(Window::drawEventPriorityLocked); @@ -655,7 +656,7 @@ void EmuApp::mainInitCommon(IG::ApplicationInitParams initParams, IG::Applicatio }, [&](DragDropEvent &e) { - logMsg("got DnD: %s", e.filename.data()); + log.info("got DnD:{}", e.filename); handleOpenFileCommand(e.filename); return true; }, @@ -677,20 +678,20 @@ void EmuApp::mainInitCommon(IG::ApplicationInitParams initParams, IG::Applicatio { [&](InterProcessMessageEvent &e) { - logMsg("got IPC path:%s", e.filename.data()); + log.info("got IPC path:{}", e.filename); handleOpenFileCommand(e.filename); }, [&](ScreenChangeEvent &e) { if(e.change == ScreenChange::added) { - logMsg("screen added"); + log.info("screen added"); if(showOnSecondScreenOption() && ctx.screens().size() > 1) setEmuViewOnExtraWindow(true, e.screen); } else if(e.change == ScreenChange::removed) { - logMsg("screen removed"); + log.info("screen removed"); if(hasExtraWindow(appContext()) && *extraWindowScreen(appContext()) == e.screen) setEmuViewOnExtraWindow(false, e.screen); } @@ -702,7 +703,7 @@ void EmuApp::mainInitCommon(IG::ApplicationInitParams initParams, IG::Applicatio { auto targetTime = targetFrameTime(e.screen); perfHintSession.updateTargetWorkTime(targetTime); - logMsg("updated performance hint session with target time:%lldns", (long long)targetTime.count()); + log.info("updated performance hint session with target time:{}", targetTime); } syncEmulationThread(); configFrameTime(); @@ -711,12 +712,12 @@ void EmuApp::mainInitCommon(IG::ApplicationInitParams initParams, IG::Applicatio }, [&](Input::DevicesEnumeratedEvent &) { - logMsg("input devs enumerated"); + log.info("input devs enumerated"); inputManager.updateInputDevices(ctx); }, [&](Input::DeviceChangeEvent &e) { - logMsg("got input dev change"); + log.info("got input dev change"); inputManager.updateInputDevices(ctx); if(notifyOnInputDeviceChange && (e.change == Input::DeviceChange::added || e.change == Input::DeviceChange::removed)) { @@ -757,7 +758,7 @@ void EmuApp::mainInitCommon(IG::ApplicationInitParams initParams, IG::Applicatio viewController().prepareDraw(); if(viewController().isShowingEmulation() && focused && system().isPaused()) { - logMsg("resuming emulation due to app resume"); + log.info("resuming emulation due to app resume"); viewController().inputView.resetInput(); startEmulation(); } @@ -821,7 +822,7 @@ void EmuApp::launchSystem(const Input::Event &e) app.autosaveManager_.load(mode); if(!app.system().hasContent()) { - logErr("system was closed while trying to load autosave"); + log.error("system was closed while trying to load autosave"); return; } app.showEmulation(); @@ -870,7 +871,7 @@ void EmuApp::handleOpenFileCommand(CStringView path) } if(!IG::isUri(path) && FS::status(path).type() == FS::file_type::directory) { - logMsg("changing to dir %s from external command", path.data()); + log.info("changing to dir {} from external command", path); showUI(false); viewController().popToRoot(); setContentSearchPath(path); @@ -880,7 +881,7 @@ void EmuApp::handleOpenFileCommand(CStringView path) false); return; } - logMsg("opening file %s from external command", path.data()); + log.info("opening file {} from external command", path); showUI(); viewController().popToRoot(); onSelectFileFromPicker({}, path, name, Input::KeyEvent{}, {}, attachParams()); @@ -888,11 +889,11 @@ void EmuApp::handleOpenFileCommand(CStringView path) void EmuApp::runBenchmarkOneShot(EmuVideo &emuVideo) { - logMsg("starting benchmark"); + log.info("starting benchmark"); auto time = system().benchmark(emuVideo); autosaveManager_.resetSlot(noAutosaveName); closeSystem(); - logMsg("done in: %f", duration_cast(time).count()); + log.info("done in:{}", duration_cast(time)); postMessage(2, 0, std::format("{:.2f} fps", 180. / time.count())); } @@ -1007,7 +1008,7 @@ void EmuApp::reloadSystem(EmuSystemCreateParams params) } catch(...) { - logErr("Error reloading system"); + log.error("Error reloading system"); system().clearGamePaths(); } } @@ -1061,7 +1062,7 @@ void EmuApp::createSystemWithMedia(IO io, CStringView path, std::string_view dis IG::makeDetachedThread( [this, io{std::move(io)}, pathStr = FS::PathString{path}, nameStr = FS::FileString{displayName}, &msgPort, params]() mutable { - logMsg("starting loader thread"); + log.info("starting loader thread"); try { system().createWithMedia(std::move(io), pathStr, nameStr, params, @@ -1073,7 +1074,7 @@ void EmuApp::createSystemWithMedia(IO io, CStringView path, std::string_view dis return true; }); msgPort.send({EmuSystem::LoadProgress::OK, 0, 0, 0}); - logMsg("loader thread finished"); + log.info("loader thread finished"); } catch(std::exception &err) { @@ -1082,11 +1083,11 @@ void EmuApp::createSystemWithMedia(IO io, CStringView path, std::string_view dis auto len = errStr.size(); if(len > 1024) { - logWarn("truncating long error size:%zu", len); + log.warn("truncating long error size:{}", len); len = 1024; } msgPort.sendWithExtraData({EmuSystem::LoadProgress::FAILED, 0, 0, int(len)}, std::span{errStr.data(), len}); - logErr("loader thread failed"); + log.error("loader thread failed"); return; } }); @@ -1118,7 +1119,7 @@ bool EmuApp::saveState(CStringView path) return false; } syncEmulationThread(); - logMsg("saving state %s", path.data()); + log.info("saving state {}", path); try { system().saveState(path); @@ -1143,7 +1144,7 @@ bool EmuApp::loadState(CStringView path) postErrorMessage("System not running"); return false; } - logMsg("loading state %s", path.data()); + log.info("loading state {}", path); syncEmulationThread(); try { @@ -1227,7 +1228,7 @@ bool EmuApp::handleAppActionKeyInput(InputAction action, const Input::Event &src { if(!isPushed) break; - logMsg("show load game view from key event"); + log.info("show load game view from key event"); viewController().popToRoot(); viewController().pushAndShow(FilePicker::forLoading(attachParams(), srcEvent), srcEvent, false); return true; @@ -1236,7 +1237,7 @@ bool EmuApp::handleAppActionKeyInput(InputAction action, const Input::Event &src { if(!isPushed) break; - logMsg("show system actions view from key event"); + log.info("show system actions view from key event"); showSystemActionsViewFromSystem(attachParams(), srcEvent); return true; } @@ -1313,7 +1314,7 @@ bool EmuApp::handleAppActionKeyInput(InputAction action, const Input::Event &src { if(!isPushed) break; - logMsg("show last view from key event"); + log.info("show last view from key event"); showLastViewFromSystem(attachParams(), srcEvent); return true; } @@ -1350,24 +1351,23 @@ bool EmuApp::handleAppActionKeyInput(InputAction action, const Input::Event &src void EmuApp::handleSystemKeyInput(KeyInfo keyInfo, Input::Action act, uint32_t metaState) { - for(auto code : keyInfo.codes) + if(inputManager.turboModifierActive && std::ranges::all_of(keyInfo.codes, allowsTurboModifier)) + keyInfo.flags.turbo = 1; + if(keyInfo.flags.toggle) { - handleSystemKeyInput({code, keyInfo.flags, act, metaState}); + inputManager.toggleInput.updateEvent(*this, keyInfo, act); } - defaultVController().updateSystemKeys(keyInfo, act == Input::Action::PUSHED); -} - -void EmuApp::handleSystemKeyInput(InputAction action) -{ - if(inputManager.turboModifierActive && allowsTurboModifier(action.code)) - action.flags.turbo = 1; - if(action.flags.turbo) + else if(keyInfo.flags.turbo) { - inputManager.turboActions.updateEvent(*this, action.code, action.state); + inputManager.turboActions.updateEvent(*this, keyInfo, act); } else { - system().handleInputAction(this, action); + for(auto code : keyInfo.codes) + { + system().handleInputAction(this, {code, keyInfo.flags, act, metaState}); + } + defaultVController().updateSystemKeys(keyInfo, act == Input::Action::PUSHED); } } @@ -1429,16 +1429,16 @@ void EmuApp::saveSessionOptions() // delete file if only header was written configFile = {}; ctx.removeFileUri(configFilePath); - logMsg("deleted empty session config file:%s", configFilePath.data()); + log.info("deleted empty session config file:{}", configFilePath); } else { - logMsg("wrote session config file:%s", configFilePath.data()); + log.info("wrote session config file:{}", configFilePath); } } catch(...) { - logMsg("error creating session config file:%s", configFilePath.data()); + log.info("error creating session config file:{}", configFilePath); } } @@ -1455,7 +1455,7 @@ void EmuApp::loadSessionOptions() { if(!system().readConfig(ConfigType::SESSION, io, key, size)) { - logMsg("skipping unknown key %u", (unsigned)key); + log.info("skipping unknown key {}", key); } } } @@ -1475,7 +1475,7 @@ void EmuApp::loadSystemOptions() { if(!system().readConfig(ConfigType::CORE, io, key, size)) { - logMsg("skipping unknown system config key:%u", (unsigned)key); + log.info("skipping unknown system config key:{}", key); } }); } @@ -1495,12 +1495,12 @@ void EmuApp::saveSystemOptions() // delete file if only header was written configFile = {}; FS::remove(configFilePath); - logMsg("deleted empty system config file"); + log.info("deleted empty system config file"); } } catch(...) { - logErr("error writing system config file"); + log.error("error writing system config file"); } } @@ -1562,7 +1562,7 @@ bool EmuApp::skipForwardFrames(EmuSystemTaskContext taskCtx, int frames) skipFrames(taskCtx, 1, nullptr); if(!system().shouldFastForward()) { - logMsg("skip-forward ended early after %u frame(s)", i); + log.info("skip-forward ended early after {} frame(s)", i); return false; } } @@ -1605,17 +1605,17 @@ void EmuApp::addRecentContent(std::string_view fullPath, std::string_view name) { if(fullPath.empty()) return; - logMsg("adding %s @ %s to recent list, current size: %zu", name.data(), fullPath.data(), recentContentList.size()); + log.info("adding {} @ {} to recent list, current size:{}", name, fullPath, recentContentList.size()); RecentContentInfo recent{FS::PathString{fullPath}, FS::FileString{name}}; IG::eraseFirst(recentContentList, recent); // remove existing entry so it's added to the front if(recentContentList.isFull()) // list full recentContentList.pop_back(); recentContentList.insert(recentContentList.begin(), recent); - /*logMsg("list contents:"); + /*log.info("list contents:"); for(auto &e : recentContentList) { - logMsg("path: %s name: %s", e.path.data(), e.name.data()); + log.info("path: {} name: {}", e.path, e.name); }*/ } @@ -1655,7 +1655,7 @@ void EmuApp::setIntendedFrameRate(Window &win, FrameTimeConfig config) if(!overrideScreenFrameRate) { config.rate *= config.refreshMultiplier; - logMsg("Multiplied intended frame rate to:%.2f", config.rate); + log.info("Multiplied intended frame rate to:{:g}", config.rate); } } return win.setIntendedFrameRate(overrideScreenFrameRate ? FrameRate(overrideScreenFrameRate) : config.rate); @@ -1667,13 +1667,13 @@ void EmuApp::onFocusChange(bool in) { if(in && system().isPaused()) { - logMsg("resuming emulation due to window focus"); + log.info("resuming emulation due to window focus"); viewController().inputView.resetInput(); startEmulation(); } else if(pauseUnfocusedOption() && !system().isPaused() && !allWindowsAreFocused()) { - logMsg("pausing emulation with all windows unfocused"); + log.info("pausing emulation with all windows unfocused"); pauseEmulation(); viewController().postDrawToEmuWindows(); } @@ -1690,7 +1690,7 @@ void EmuApp::setEmuViewOnExtraWindow(bool on, IG::Screen &screen) auto ctx = appContext(); if(on && !hasExtraWindow(ctx)) { - logMsg("setting emu view on extra window"); + log.info("setting emu view on extra window"); IG::WindowConfig winConf{ .title = ctx.applicationName }; winConf.setScreen(screen); winConf.setFormat(windowDrawableConfig().pixelFormat); @@ -1736,7 +1736,7 @@ void EmuApp::setEmuViewOnExtraWindow(bool on, IG::Screen &screen) }, [&](DragDropEvent &e) { - logMsg("got DnD: %s", e.filename.data()); + log.info("got DnD:{}", e.filename); handleOpenFileCommand(e.filename); return true; }, @@ -1754,7 +1754,7 @@ void EmuApp::setEmuViewOnExtraWindow(bool on, IG::Screen &screen) [&](DismissEvent &e) { system().resetFrameTime(); - logMsg("setting emu view on main window"); + log.info("setting emu view on main window"); viewController().moveEmuViewToWindow(appContext().mainWindow()); viewController().movePopupToWindow(appContext().mainWindow()); viewController().placeEmuViews(); @@ -1777,7 +1777,7 @@ void EmuApp::setEmuViewOnExtraWindow(bool on, IG::Screen &screen) }); if(!extraWin) { - logErr("error creating extra window"); + log.error("error creating extra window"); return; } } @@ -1834,7 +1834,7 @@ void EmuApp::addOnFrameDelayed() { // delay before adding onFrame handler to let timestamps stabilize auto delay = viewController().emuWindowScreen()->frameRate() / 4; - //logMsg("delaying onFrame handler by %d frames", onFrameHandlerDelay); + //log.info("delaying onFrame handler by {} frames", onFrameHandlerDelay); addOnFrameDelegate(onFrameDelayed(delay)); } @@ -1908,22 +1908,21 @@ void EmuApp::applyCPUAffinity(bool active) auto targetTime = targetFrameTime(emuScreen()); perfHintSession = perfHintManager.session(frameThreadGroup, targetTime); if(perfHintSession) - logMsg("made performance hint session with target time:%lldns (%lld - %lld)", - (long long)targetTime.count(), (long long)emuScreen().frameTime().count(), - (long long)emuScreen().presentationDeadline().count()); + log.info("made performance hint session with target time:{} ({} - {})", + targetTime, emuScreen().frameTime(), emuScreen().presentationDeadline()); else - logErr("error making performance hint session"); + log.error("error making performance hint session"); } else { perfHintSession = {}; - logMsg("closed performance hint session"); + log.info("closed performance hint session"); } return; } auto mask = active ? (cpuAffinityMode == CPUAffinityMode::Auto ? appContext().performanceCPUMask() : CPUMask(cpuAffinityMask)) : 0; - logMsg("applying CPU affinity mask 0x%X", (unsigned)mask); + log.info("applying CPU affinity mask {:X}", mask); setThreadCPUAffinityMask(frameThreadGroup, mask); } @@ -1964,7 +1963,7 @@ BluetoothAdapter *EmuApp::bluetoothAdapter() { return bta; } - logMsg("initializing Bluetooth"); + log.info("initializing Bluetooth"); bta = BluetoothAdapter::defaultAdapter(appContext()); return bta; } diff --git a/EmuFramework/src/EmuInput.cc b/EmuFramework/src/EmuInput.cc index dbc73feb4..bb612a1a3 100644 --- a/EmuFramework/src/EmuInput.cc +++ b/EmuFramework/src/EmuInput.cc @@ -244,8 +244,12 @@ std::string InputManager::toString(KeyInfo k) const s += " + "; s += toString(c, k.flags); } - if(k.flags.turbo) + if(k.flags.turbo && k.flags.toggle) + s += " (Turbo Toggle)"; + else if(k.flags.turbo) s += " (Turbo)"; + else if(k.flags.toggle) + s += " (Toggle)"; return s; } @@ -293,7 +297,7 @@ const KeyCategory *InputManager::categoryOfKeyCode(KeyInfo key) const return &appKeyCategory; for(const auto &cat : EmuApp::keyCategories()) { - if(contains(cat.keys, key)) + if(containsIf(cat.keys, [&](auto &k) { return k.codes == key.codes; })) return &cat; } return {}; diff --git a/EmuFramework/src/ToggleInput.cc b/EmuFramework/src/ToggleInput.cc new file mode 100644 index 000000000..ed72e1a94 --- /dev/null +++ b/EmuFramework/src/ToggleInput.cc @@ -0,0 +1,45 @@ +/* This file is part of EmuFramework. + + 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 EmuFramework. If not, see */ + +#include +#include +#include + +namespace EmuEx +{ + +constexpr SystemLogger log{"ToggleInput"}; + +void ToggleInput::updateEvent(EmuApp &app, KeyInfo key, Input::Action act) +{ + if(act != Input::Action::PUSHED) + return; + key.flags.toggle = 0; + if(!contains(keys, key)) + { + if(keys.tryPushBack(key)) + { + log.info("added event action {}", key.codes[0]); + app.handleSystemKeyInput(key, Input::Action::PUSHED); + } + } + else if(erase(keys, key)) + { + log.info("removed event action {}", key.codes[0]); + app.handleSystemKeyInput(key, Input::Action::RELEASED); + } +} + +} diff --git a/EmuFramework/src/TurboInput.cc b/EmuFramework/src/TurboInput.cc index d6e57b881..d2b6c321d 100644 --- a/EmuFramework/src/TurboInput.cc +++ b/EmuFramework/src/TurboInput.cc @@ -15,10 +15,13 @@ #include #include +#include namespace EmuEx { +constexpr SystemLogger log{"TurboInput"}; + void TurboInput::addEvent(KeyInfo key) { key.flags.turbo = 0; @@ -26,7 +29,7 @@ void TurboInput::addEvent(KeyInfo key) clock = 0; // Reset the clock so new turbo event takes effect next frame if(keys.tryPushBack(key)) { - logMsg("added turbo event action %d", key.codes[0]); + log.info("added event action {}", key.codes[0]); } } @@ -35,7 +38,7 @@ void TurboInput::removeEvent(KeyInfo key) key.flags.turbo = 0; if(erase(keys, key)) { - logMsg("removed turbo event action %d", key.codes[0]); + log.info("removed event action {}", key.codes[0]); } } @@ -60,12 +63,10 @@ void TurboInput::update(EmuApp &app) { if(clock == 0) { - //logMsg("turbo push for player %d, action %d", e.player, e.action); app.handleSystemKeyInput(k, Input::Action::PUSHED); } else if(clock == turboFrames/2) { - //logMsg("turbo release for player %d, action %d", e.player, e.action); app.handleSystemKeyInput(k, Input::Action::RELEASED); } } diff --git a/EmuFramework/src/gui/EmuInputView.cc b/EmuFramework/src/gui/EmuInputView.cc index 8034525b9..bf2092462 100644 --- a/EmuFramework/src/gui/EmuInputView.cc +++ b/EmuFramework/src/gui/EmuInputView.cc @@ -145,9 +145,9 @@ void EmuInputView::setSystemGestureExclusion(bool on) { auto rectsSize = vController->deviceElements().size(); WRect rects[rectsSize]; - for(int i = 0; const auto &e : vController->deviceElements()) + for(auto &&[i, e] : enumerate(vController->deviceElements())) { - rects[i++] = e.realBounds(); + rects[i] = e.realBounds(); } window().setSystemGestureExclusionRects({rects, rectsSize}); } diff --git a/EmuFramework/src/gui/TouchConfigView.cc b/EmuFramework/src/gui/TouchConfigView.cc index 8f18c0cbd..060013c5d 100644 --- a/EmuFramework/src/gui/TouchConfigView.cc +++ b/EmuFramework/src/gui/TouchConfigView.cc @@ -310,7 +310,10 @@ class ButtonElementConfigView : public TableView, public EmuAppHelper item{&key, &remove}; + std::vector item; + + void reloadItems() + { + item.clear(); + item.emplace_back(&key); + if(!btn.key.flags.appCode) + { + item.emplace_back(&turbo); + item.emplace_back(&toggle); + } + item.emplace_back(&remove); + } }; class ButtonGroupElementConfigView : public TableView, public EmuAppHelper diff --git a/EmuFramework/src/vcontrols/VController.cc b/EmuFramework/src/vcontrols/VController.cc index 9ac51122c..2c1c2cc0f 100644 --- a/EmuFramework/src/vcontrols/VController.cc +++ b/EmuFramework/src/vcontrols/VController.cc @@ -13,7 +13,6 @@ You should have received a copy of the GNU General Public License along with EmuFramework. If not, see */ -#define LOGTAG "VController" #include #include #include @@ -30,11 +29,12 @@ namespace EmuEx { -static constexpr uint8_t DEFAULT_ALPHA = 255. * .5; +constexpr SystemLogger log{"VController"}; +constexpr uint8_t defaultAlpha = 255. * .5; VController::VController(IG::ApplicationContext ctx): appCtx{ctx}, - alphaF{DEFAULT_ALPHA / 255.}, + alphaF{defaultAlpha / 255.}, defaultButtonSize { #ifdef CONFIG_BASE_IOS @@ -44,7 +44,7 @@ VController::VController(IG::ApplicationContext ctx): #endif }, btnSize{defaultButtonSize}, - alpha{DEFAULT_ALPHA} + alpha{defaultAlpha} { // set dummy buttons to indicate uninitialized state gpElements.emplace_back(std::in_place_type); @@ -157,11 +157,11 @@ void VController::updateAltSpeedModeInput(AltSpeedMode mode, bool on) { if(b.key.codes[0] == KeyCode(AppKeyCode::fastForward) || b.key.codes[0] == KeyCode(AppKeyCode::toggleFastForward)) { - b.color = on && mode == AltSpeedMode::fast ? Gfx::Color{Gfx::ColorName::RED} : Gfx::Color{}; + b.updateColor(on && mode == AltSpeedMode::fast ? Gfx::Color{Gfx::ColorName::RED} : Gfx::Color{}, alphaF); } else if(b.key.codes[0] == KeyCode(AppKeyCode::slowMotion) || b.key.codes[0] == KeyCode(AppKeyCode::toggleSlowMotion)) { - b.color = on && mode == AltSpeedMode::slow ? Gfx::Color{Gfx::ColorName::RED} : Gfx::Color{}; + b.updateColor(on && mode == AltSpeedMode::slow ? Gfx::Color{Gfx::ColorName::RED} : Gfx::Color{}, alphaF); } } } @@ -190,7 +190,7 @@ void VController::place() void VController::toggleKeyboard() { - logMsg("toggling keyboard"); + log.info("toggling keyboard"); resetInput(); kbMode ^= true; system().onVKeyboardShown(kb, kbMode); @@ -236,14 +236,14 @@ KeyInfo VController::keyboardKeyFromPointer(const Input::MotionEvent &e) { if(!e.pushed()) return {}; - logMsg("dismiss kb"); + log.info("dismiss kb"); toggleKeyboard(); } else if(kb.translateInput(kbIdx) == CHANGE_KEYBOARD_MODE) { if(!e.pushed()) return {}; - logMsg("switch kb mode"); + log.info("switch kb mode"); kb.cycleMode(system(), renderer()); resetInput(); } @@ -292,7 +292,7 @@ bool VController::pointerInputEvent(const Input::MotionEvent &e, IG::WindowRect { if(vBtn && !contains(currElements, vBtn)) { - //logMsg("releasing %d", vBtn[0]); + //log.info("releasing {}", vBtn[0]); app().handleSystemKeyInput(vBtn, Input::Action::RELEASED); } } @@ -301,7 +301,7 @@ bool VController::pointerInputEvent(const Input::MotionEvent &e, IG::WindowRect { if(vBtn && !contains(prevElements, vBtn)) { - //logMsg("pushing %d", vBtn[0]); + //log.info("pushing {}", vBtn[0]); app().handleSystemKeyInput(vBtn, Input::Action::PUSHED); if(vibrateOnTouchInput()) { @@ -337,7 +337,7 @@ bool VController::pointerInputEvent(const Input::MotionEvent &e, IG::WindowRect if(!elementsArePushed && !gamepadControlsVisible() && shouldShowOnTouchInput() && !isInKeyboardMode() && e.isTouch() && e.pushed()) [[unlikely]] { - logMsg("turning on on-screen controls from touch input"); + log.info("turning on on-screen controls from touch input"); setGamepadControlsVisible(true); app().viewController().placeEmuViews(); } @@ -363,18 +363,18 @@ void VController::draw(Gfx::RendererCommands &__restrict__ cmds, bool showHidden } else if(gamepadIsVisible || showHidden) { - for(const auto &e : gpElements) + auto elementIsEnabled = [&](const VControllerElement &e) { - if(e.buttonGroup() && gamepadDisabledFlags & VController::GAMEPAD_BUTTONS_BIT) - continue; - if(e.dPad() && gamepadDisabledFlags & VController::GAMEPAD_DPAD_BIT) - continue; - e.draw(cmds, showHidden); - } + return !((e.buttonGroup() && gamepadDisabledFlags & VController::GAMEPAD_BUTTONS_BIT) || + (e.dPad() && gamepadDisabledFlags & VController::GAMEPAD_DPAD_BIT)); + }; + auto activeElements = gpElements | std::views::filter(elementIsEnabled); + for(const auto &e : activeElements) { e.drawBounds(cmds, showHidden); } + for(const auto &e : activeElements) { e.drawButtons(cmds, showHidden); } } for(auto &e : uiElements) { - e.draw(cmds, showHidden); + e.drawButtons(cmds, showHidden); } } @@ -392,25 +392,35 @@ void VController::setInputPlayer(int8_t player) } } -void VController::setDisabledInputKeys(std::span disabledKeys) +bool VController::keyIsEnabled(KeyInfo k) const { + if(!disabledKeys.size()) + return true; + return !contains(disabledKeys, k.codes[0]) + && !contains(disabledKeys, k.codes[1]) + && !contains(disabledKeys, k.codes[2]); +} + +void VController::setDisabledInputKeys(std::span disabledKeys_) +{ + disabledKeys = disabledKeys_; for(auto &e : gpElements) { visit(overloaded { - [&](VControllerButtonGroup &grp) - { - for(auto &btn : grp.buttons) - btn.enabled = !contains(disabledKeys, btn.key.codes[0]) - && !contains(disabledKeys, btn.key.codes[1]) - && !contains(disabledKeys, btn.key.codes[2]); - }, + [&](VControllerButtonGroup &grp) { updateEnabledButtons(grp); }, [](auto &e){} }, e); } place(); } +void VController::updateEnabledButtons(VControllerButtonGroup &grp) const +{ + for(auto &btn : grp.buttons) + btn.enabled = keyIsEnabled(btn.key); +} + void VController::updateKeyboardMapping() { kb.updateKeyboardMapping(system()); @@ -518,7 +528,7 @@ void VController::setPhysicalControlsPresent(bool present) { if(present != physicalControlsPresent) { - logMsg("Physical controls present:%s", present ? "y" : "n"); + log.info("Physical controls present:{}", present ? "y" : "n"); } physicalControlsPresent = present; updateAutoOnScreenControlVisible(); @@ -530,13 +540,13 @@ bool VController::updateAutoOnScreenControlVisible() { if(gamepadIsVisible && physicalControlsPresent) { - logMsg("auto-turning off on-screen controls"); + log.info("auto-turning off on-screen controls"); gamepadIsVisible = false; return true; } else if(!gamepadIsVisible && !physicalControlsPresent) { - logMsg("auto-turning on on-screen controls"); + log.info("auto-turning on on-screen controls"); gamepadIsVisible = true; return true; } @@ -591,7 +601,7 @@ static bool readVControllerElement(EmuApp &app, MapIO &io, std::vector); @@ -635,7 +645,7 @@ bool VController::readConfig(EmuApp &app, MapIO &io, unsigned key, size_t size) auto emuDeviceId = io.get(); // reserved for future use auto configId = io.get(); // reserved for future use auto elements = io.get(); - logMsg("read emu device button data (%zu bytes) with %u element(s)", size, elements); + log.info("read emu device button data ({} bytes) with {} element(s)", size, elements); for(auto i : iotaCount(elements)) { if(!readVControllerElement(app, io, gpElements, false)) @@ -648,7 +658,7 @@ bool VController::readConfig(EmuApp &app, MapIO &io, unsigned key, size_t size) uiElements.clear(); auto configId = io.get(); // reserved for future use auto elements = io.get(); - logMsg("read UI button data (%zu bytes) with %u element(s)", size, elements); + log.info("read UI button data ({} bytes) with {} element(s)", size, elements); for(auto i : iotaCount(elements)) { if(!readVControllerElement(app, io, uiElements, true)) @@ -723,7 +733,7 @@ void VController::writeDeviceButtonsConfig(FileIO &io) const auto bytes = configDataSizeBytes(gpElements, false); if(bytes > std::numeric_limits::max()) { - logErr("device button data bytes:%zu too large, skipped writing to config", bytes); + log.error("device button data bytes:{} too large, skipped writing to config", bytes); return; } io.put(uint16_t(bytes)); @@ -731,7 +741,7 @@ void VController::writeDeviceButtonsConfig(FileIO &io) const io.put(int8_t(0)); io.put(int8_t(0)); io.put(uint8_t(std::min(gpElements.size(), 255zu))); - logMsg("wrote emu device button data (%zu bytes) with %zu element(s)", bytes, gpElements.size()); + log.info("wrote emu device button data ({} bytes) with {} element(s)", bytes, gpElements.size()); for(const auto &e : gpElements) { writeToConfig(e, io); } } @@ -740,20 +750,20 @@ void VController::writeUIButtonsConfig(FileIO &io) const auto bytes = configDataSizeBytes(uiElements, true); if(bytes > std::numeric_limits::max()) { - logErr("UI button data bytes:%zu too large, skipped writing to config", bytes); + log.error("UI button data bytes:{} too large, skipped writing to config", bytes); return; } io.put(uint16_t(bytes)); io.put(uint16_t(CFGKEY_VCONTROLLER_UI_BUTTONS_V2)); io.put(int8_t(0)); io.put(uint8_t(std::min(uiElements.size(), 255zu))); - logMsg("wrote UI button data (%zu bytes) with %zu element(s)", bytes, uiElements.size()); + log.info("wrote UI button data ({} bytes) with {} element(s)", bytes, uiElements.size()); for(const auto &e : uiElements) { writeToConfig(e, io); } } void VController::writeConfig(FileIO &io) const { - if(buttonAlpha() != DEFAULT_ALPHA) + if(buttonAlpha() != defaultAlpha) writeOptionValue(io, CFGKEY_TOUCH_CONTROL_ALPHA, buttonAlpha()); if(buttonSize() != defaultButtonSize) writeOptionValue(io, CFGKEY_TOUCH_CONTROL_SIZE, buttonSize()); @@ -776,7 +786,11 @@ void VController::configure(IG::Window &win, Gfx::Renderer &renderer, const Gfx: setRenderer(renderer); setFace(face); for(auto &e : uiElements) { update(e); }; - for(auto &e : gpElements) { update(e); }; + for(auto &e : gpElements) + { + update(e); + if(e.dPad()) e.dPad()->updateBoundingAreaGfx(renderer); + }; if(uiElements.size() && uiElements[0].layoutPos[0].pos.x == -1) resetUIGroups(); if(gpElements.size() && gpElements[0].layoutPos[0].pos.x == -1) @@ -787,7 +801,7 @@ void VController::configure(IG::Window &win, Gfx::Renderer &renderer, const Gfx: void VController::resetEmulatedDevicePositions(std::vector &gpElements) const { auto &win = window(); - logMsg("resetting emulated device controls to default positions"); + log.info("resetting emulated device controls to default positions"); const int shortSidePadding = xMMSizeToPixel(win, 1); const int longSidePadding = xMMSizeToPixel(win, 3); const int yBottomPadding = xMMSizeToPixel(win, 3); @@ -829,7 +843,7 @@ void VController::resetEmulatedDevicePositions() { resetEmulatedDevicePositions( void VController::resetEmulatedDeviceGroups() { - logMsg("setting default emu device button groups"); + log.info("setting default emu device button groups"); gpElements = defaultEmulatedDeviceGroups(); } @@ -849,7 +863,7 @@ std::vector VController::defaultEmulatedDeviceGroups() const void VController::resetUIPositions(std::vector &uiElements) const { auto &win = window(); - logMsg("resetting UI controls to default positions"); + log.info("resetting UI controls to default positions"); const auto sidePadding = xMMSizeToPixel(win, 2); const int yTop = win.contentBounds().y; for(int leftY{yTop}, rightY{yTop}; auto &e : uiElements) @@ -869,7 +883,7 @@ void VController::resetUIPositions() { resetUIPositions(uiElements); } void VController::resetUIGroups() { - logMsg("setting default UI button groups"); + log.info("setting default UI button groups"); uiElements = defaultUIGroups(); } @@ -949,14 +963,17 @@ VControllerElement &VController::add(std::vector &elems, Inp return elems.emplace_back(std::in_place_type, std::span{c.keyCodes.data(), 4}); case InputComponent::button: case InputComponent::trigger: - return elems.emplace_back(std::in_place_type, c.keyCodes, c.layoutOrigin, rowSize(c)); + { + auto &e = elems.emplace_back(std::in_place_type, c.keyCodes, c.layoutOrigin, rowSize(c)); + auto &grp = *e.buttonGroup(); + updateEnabledButtons(grp); + if(to_underlying(c.flags & InputComponentFlagsMask::staggeredLayout)) + grp.setStaggerType(5); + return e; + } } bug_unreachable("invalid InputComponent"); }(); - if(to_underlying(c.flags & InputComponentFlagsMask::staggeredLayout) && elem.buttonGroup()) - { - elem.buttonGroup()->setStaggerType(5); - } if(hasWindow()) { auto layoutPos = VControllerLayoutPosition::fromPixelPos(layoutBounds().center(), elem.bounds().size(), window().bounds()); @@ -995,6 +1012,12 @@ void VController::updateSystemKeys(KeyInfo key, bool isPushed) { if(!highlightPushedButtons) return; + auto stripFlags = [](KeyInfo k) + { + k.flags.turbo = 0; + k.flags.toggle = 0; + return k; + }; for(auto &e : gpElements) { visit(overloaded @@ -1003,7 +1026,7 @@ void VController::updateSystemKeys(KeyInfo key, bool isPushed) { for(auto &btn : grp.buttons) { - if(btn.key == key) + if(stripFlags(btn.key) == key) { btn.isHighlighted = isPushed; btn.setAlpha(alphaF); @@ -1015,7 +1038,7 @@ void VController::updateSystemKeys(KeyInfo key, bool isPushed) bool didUpdate{}; for(auto &&[i, k] : enumerate(dpad.config.keys)) { - if(k == key) + if(stripFlags(k) == key) { dpad.isHighlighted[i] = isPushed; didUpdate = true; diff --git a/EmuFramework/src/vcontrols/VControllerButton.cc b/EmuFramework/src/vcontrols/VControllerButton.cc index 8bc373d8d..eb00b885c 100644 --- a/EmuFramework/src/vcontrols/VControllerButton.cc +++ b/EmuFramework/src/vcontrols/VControllerButton.cc @@ -70,8 +70,12 @@ void VControllerButton::setAlpha(float alpha) } else { - if(key.flags.turbo) + if(key.flags.turbo && key.flags.toggle) + spriteColor = Gfx::Color{alpha * 2.f, alpha * 2.f, alpha, alpha}.multiplyRGB(brightness); + else if(key.flags.turbo) spriteColor = Gfx::Color{alpha * 2.f, alpha, alpha, alpha}.multiplyRGB(brightness); + else if(key.flags.toggle) + spriteColor = Gfx::Color{alpha, alpha * 2.f, alpha, alpha}.multiplyRGB(brightness); else spriteColor = Gfx::Color{alpha}.multiplyRGB(brightness); } diff --git a/EmuFramework/src/vcontrols/VControllerButtonGroup.cc b/EmuFramework/src/vcontrols/VControllerButtonGroup.cc index e3dae5b8c..0a70e51a6 100644 --- a/EmuFramework/src/vcontrols/VControllerButtonGroup.cc +++ b/EmuFramework/src/vcontrols/VControllerButtonGroup.cc @@ -21,7 +21,6 @@ #include #include #include -#include namespace EmuEx { @@ -191,30 +190,27 @@ std::array VControllerButtonGroup::findButtonIndices(WPt windowPos) return btnOut; } -void VControllerButtonGroup::draw(Gfx::RendererCommands &__restrict__ cmds) const +void VControllerButtonGroup::drawButtons(Gfx::RendererCommands &__restrict__ cmds) const { - auto &basicEffect = cmds.basicEffect(); - if(layout.showBoundingArea) + cmds.basicEffect().enableTexture(cmds); + for(auto &b : buttons) { - basicEffect.disableTexture(cmds); - for(const auto &b : buttons) - { - if(!b.enabled) - continue; - b.drawBounds(cmds); - } + if(!b.enabled) + continue; + b.drawSprite(cmds); } - drawButtons(cmds); } -void VControllerButtonGroup::drawButtons(Gfx::RendererCommands &__restrict__ cmds) const +void VControllerButtonGroup::drawBounds(Gfx::RendererCommands &__restrict__ cmds) const { - cmds.basicEffect().enableTexture(cmds); - for(auto &b : buttons) + if(!layout.showBoundingArea) + return; + cmds.basicEffect().disableTexture(cmds); + for(const auto &b : buttons) { if(!b.enabled) continue; - b.drawSprite(cmds); + b.drawBounds(cmds); } } @@ -312,7 +308,7 @@ int VControllerUIButtonGroup::rows() const return divRoundUp(buttonsToLayout(buttons), layout.rowItems); } -void VControllerUIButtonGroup::draw(Gfx::RendererCommands &__restrict__ cmds) const +void VControllerUIButtonGroup::drawButtons(Gfx::RendererCommands &__restrict__ cmds) const { cmds.basicEffect().enableTexture(cmds); for(auto &b : buttons) diff --git a/EmuFramework/src/vcontrols/VControllerDPad.cc b/EmuFramework/src/vcontrols/VControllerDPad.cc index 079336923..3ea2bf9e6 100644 --- a/EmuFramework/src/vcontrols/VControllerDPad.cc +++ b/EmuFramework/src/vcontrols/VControllerDPad.cc @@ -13,7 +13,6 @@ You should have received a copy of the GNU General Public License along with EmuFramework. If not, see */ -#define LOGTAG "VControllerGamepad" #include #include #include @@ -28,6 +27,8 @@ namespace EmuEx { +constexpr SystemLogger log{"VControllerGamepad"}; + void VControllerDPad::setImage(Gfx::TextureSpan img) { spr.set(img); @@ -35,25 +36,24 @@ void VControllerDPad::setImage(Gfx::TextureSpan img) void VControllerDPad::updateBoundingAreaGfx(Gfx::Renderer &r) { - if(config.visualizeBounds && padArea.xSize()) - { - MemPixmap mapMemPix{{padArea.size(), PIXEL_FMT_RGB565}}; - auto mapPix = mapMemPix.view(); - auto pixels = mapPix.mdspan(); - for(auto y : iotaCount(pixels.extent(0))) - for(auto x : iotaCount(pixels.extent(1))) - { - auto input = getInput({padArea.xPos(LT2DO) + int(x), padArea.yPos(LT2DO) + int(y)}); - //logMsg("got input %d", input); - pixels[y, x] = input == std::array{} ? PIXEL_DESC_RGB565.build(1., 0., 0.) - : (input[0] && input[1]) ? PIXEL_DESC_RGB565.build(0., 1., 0.) - : PIXEL_DESC_RGB565.build(1., 1., 1.); - } - mapImg = r.makeTexture({mapPix.desc(), View::imageSamplerConfig}); - mapImg.write(0, mapPix, {}); - mapSpr.set(mapImg); - mapSpr.setPos(padArea); - } + if(!config.visualizeBounds || !padArea.xSize()) + return; + MemPixmap mapMemPix{{padArea.size(), PIXEL_FMT_RGB565}}; + auto mapPix = mapMemPix.view(); + auto pixels = mapPix.mdspan(); + for(auto y : iotaCount(pixels.extent(0))) + for(auto x : iotaCount(pixels.extent(1))) + { + auto input = getInput({padArea.xPos(LT2DO) + int(x), padArea.yPos(LT2DO) + int(y)}); + //log.info("got input {}", input); + pixels[y, x] = input == std::array{} ? PIXEL_DESC_RGB565.build(1., 0., 0.) + : (input[0] && input[1]) ? PIXEL_DESC_RGB565.build(0., 1., 0.) + : PIXEL_DESC_RGB565.build(1., 1., 1.); + } + mapImg = r.makeTexture({mapPix.desc(), View::imageSamplerConfig}); + mapImg.write(0, mapPix, {}); + mapSpr.set(mapImg); + mapSpr.setPos(padArea); } static bool isValidDeadzone(int val) { return val >= 100 && val <= 300; } @@ -78,7 +78,7 @@ bool VControllerDPad::setDiagonalSensitivity(Gfx::Renderer &r, float newDiagonal return false; if(config.diagonalSensitivity == newDiagonalSensitivity) return true; - logMsg("set diagonal sensitivity: %f", (double)newDiagonalSensitivity); + log.info("set diagonal sensitivity: {}", (double)newDiagonalSensitivity); config.diagonalSensitivity = newDiagonalSensitivity; updateBoundingAreaGfx(r); return true; @@ -86,17 +86,14 @@ bool VControllerDPad::setDiagonalSensitivity(Gfx::Renderer &r, float newDiagonal void VControllerDPad::setSize(Gfx::Renderer &r, int sizeInPixels) { - //logMsg("set dpad pixel size: %d", sizeInPixels); + //log.info("set dpad pixel size: {}", sizeInPixels); btnSizePixels = sizeInPixels; auto rect = makeWindowRectRel({0, 0}, {btnSizePixels, btnSizePixels}); bool changedSize = rect.xSize() != padBaseArea.xSize(); padBaseArea = rect; padArea = {{}, {int(padBaseArea.xSize() * 1.5f), int(padBaseArea.xSize() * 1.5f)}}; - if(config.visualizeBounds) - { - if(changedSize) - updateBoundingAreaGfx(r); - } + if(changedSize) + updateBoundingAreaGfx(r); } void VControllerDPad::setPos(WPt pos, WRect viewBounds) @@ -104,7 +101,7 @@ void VControllerDPad::setPos(WPt pos, WRect viewBounds) padBaseArea.setPos(pos, C2DO); padBaseArea.fitIn(viewBounds); spr.setPos(padBaseArea); - //logMsg("set dpad pos %d:%d:%d:%d, %f:%f:%f:%f", padBaseArea.x, padBaseArea.y, padBaseArea.x2, padBaseArea.y2, + //log.info("set dpad pos {}:{}:{}:{}, {}:{}:{}:{}", padBaseArea.x, padBaseArea.y, padBaseArea.x2, padBaseArea.y2, // (double)padBase.x, (double)padBase.y, (double)padBase.x2, (double)padBase.y2); padArea.setPos(padBaseArea.pos(C2DO), C2DO); if(config.visualizeBounds) @@ -120,12 +117,8 @@ void VControllerDPad::setShowBounds(Gfx::Renderer &r, bool on) config.visualizeBounds = on; if(!on) { - if(mapSpr.hasTexture()) - { - logMsg("deallocating bounding box display resources"); - mapSpr = {}; - mapImg = {}; - } + log.info("deallocating bounding box display resources"); + mapImg = {}; } else { @@ -146,14 +139,18 @@ void VControllerDPad::transposeKeysForPlayer(const EmuApp &app, int player) } } -void VControllerDPad::draw(Gfx::RendererCommands &__restrict__ cmds) const +void VControllerDPad::drawButtons(Gfx::RendererCommands &__restrict__ cmds) const { cmds.basicEffect().enableTexture(cmds); spr.draw(cmds); - if(config.visualizeBounds) - { - mapSpr.draw(cmds); - } +} + +void VControllerDPad::drawBounds(Gfx::RendererCommands &__restrict__ cmds) const +{ + cmds.basicEffect().enableTexture(cmds); + if(!config.visualizeBounds) + return; + mapSpr.draw(cmds); } std::array VControllerDPad::getInput(WPt c) const @@ -206,10 +203,7 @@ void VControllerDPad::setAlpha(float alpha) if(isHighlighted[0] || isHighlighted[1]) colors[2] = colors[2].multiplyRGB(2.f); for(auto &&[i, vtx] : enumerate(spr)) { vtx.color = colors[i]; } - if(config.visualizeBounds) - { - for(auto &&[i, vtx] : enumerate(mapSpr)) { vtx.color = colors[i]; } - } + for(auto &&[i, vtx] : enumerate(mapSpr)) { vtx.color = colors[i]; } } } diff --git a/imagine/doc/INSTALL b/imagine/doc/INSTALL index f328c7ca9..bb047e4e2 100644 --- a/imagine/doc/INSTALL +++ b/imagine/doc/INSTALL @@ -5,7 +5,7 @@ Building the Imagine SDK and applications is currently supported on Linux and Ma You need the following basic tools to start building Imagine. For platform-specific info, see the INSTALL-* file for that port. -Linux: GCC 13/Clang 16 or newer, GNU Make, pkgconfig (install from your distro's package manager) +Linux: GCC 13/Clang 16 or newer, mold linker, GNU Make, pkgconfig (install from your distro's package manager) MacOS X: Xcode 12 or newer (user must provide a Clang 16 or newer toolchain), MacPorts versions of coreutils, libtool, pkgconfig and gnutar (http://www.macports.org) diff --git a/imagine/make/linux-gcc.mk b/imagine/make/linux-gcc.mk index d0f2884b2..a549c07f2 100644 --- a/imagine/make/linux-gcc.mk +++ b/imagine/make/linux-gcc.mk @@ -28,4 +28,4 @@ ifndef PROFILE OPTIMIZE_LDFLAGS = -s endif LDLIBS += -lm -LDFLAGS_SYSTEM += -fuse-ld=gold -Wl,-O3,--gc-sections,--as-needed,--compress-debug-sections=$(COMPRESS_DEBUG_SECTIONS),--icf=all +LDFLAGS_SYSTEM += -fuse-ld=mold -Wl,-O3,--gc-sections,--as-needed,--compress-debug-sections=$(COMPRESS_DEBUG_SECTIONS),--icf=all