From 3d2899fb6e76dedde00bc47b708bde5b511c3fe0 Mon Sep 17 00:00:00 2001 From: toorux Date: Wed, 7 Oct 2026 14:32:57 +0800 Subject: [PATCH] Use quiet Steam keyboard clicks for UI sound feedback --- docs/user-guide/terminal-and-files.md | 2 +- docs/zh-CN/user-guide/terminal-and-files.md | 2 +- native/ui_feedback.h | 7 ++++--- tests/test_ui_feedback.py | 3 ++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/user-guide/terminal-and-files.md b/docs/user-guide/terminal-and-files.md index 06050d7..ccc4f95 100644 --- a/docs/user-guide/terminal-and-files.md +++ b/docs/user-guide/terminal-and-files.md @@ -12,7 +12,7 @@ Plugin contains installed plugins, the plugin library, sources and notification ## Interaction feedback -In **Settings → General → Interaction feedback**, Haptic feedback is enabled by default; Sound feedback is disabled by default. Both preferences are saved across restarts. Haptic feedback controls laser hover and launcher gaze-focus vibration across Framely and plugin pages; it does not change APK game rumble. Sound feedback uses Steam’s installed navigation and activation sounds for focus and clicks. It requires the local Steam sound files and audio runtime; no sound files are bundled or downloaded. +In **Settings → General → Interaction feedback**, Haptic feedback is enabled by default; Sound feedback is disabled by default. Both preferences are saved across restarts. Haptic feedback controls laser hover and launcher gaze-focus vibration across Framely and plugin pages; it does not change APK game rumble. Sound feedback uses Steam’s installed keyboard sound at reduced volume for focus and clicks. It requires the local Steam sound files and audio runtime; no sound files are bundled or downloaded. ## Terminal diff --git a/docs/zh-CN/user-guide/terminal-and-files.md b/docs/zh-CN/user-guide/terminal-and-files.md index 4d4a7a4..46a66bb 100644 --- a/docs/zh-CN/user-guide/terminal-and-files.md +++ b/docs/zh-CN/user-guide/terminal-and-files.md @@ -12,7 +12,7 @@ ## 交互反馈 -在 **设置 → 通用 → 交互反馈** 中,振动反馈默认开启,提示音反馈默认关闭,修改后会保存。振动开关统一控制 Framely 和插件页面的激光悬停、启动台眼动聚焦振动,不影响 APK 游戏振动。提示音开关使用 Steam 本机的导航和点击音效,需要本机 Steam 音效文件与音频运行库,不打包或下载这些音效。 +在 **设置 → 通用 → 交互反馈** 中,振动反馈默认开启,提示音反馈默认关闭,修改后会保存。振动开关统一控制 Framely 和插件页面的激光悬停、启动台眼动聚焦振动,不影响 APK 游戏振动。提示音开关使用 Steam 本机键盘音效,以较低音量反馈聚焦和点击,需要本机 Steam 音效文件与音频运行库,不打包或下载这些音效。 ## 终端 diff --git a/native/ui_feedback.h b/native/ui_feedback.h index a391489..9090565 100644 --- a/native/ui_feedback.h +++ b/native/ui_feedback.h @@ -20,6 +20,7 @@ class SteamUiSounds { bool (*resume)(void*)=nullptr; bool (*put)(void*,const void*,int)=nullptr; bool (*clear)(void*)=nullptr; + bool (*gain)(void*,float)=nullptr; void (*destroy)(void*)=nullptr; void (*release)(void*)=nullptr; template bool bind(T& fn,const char* name){fn=reinterpret_cast(dlsym(library,name));return fn!=nullptr;} @@ -36,13 +37,13 @@ class SteamUiSounds { if(root.empty())return false; for(const auto* name:{"steamrtarm64/libSDL3.so.0","steamrt64/libSDL3.so.0"}){library=dlopen((root+"/"+name).c_str(),RTLD_NOW|RTLD_LOCAL);if(library)break;} if(!library)return false; - if(!(bind(init,"SDL_InitSubSystem")&&bind(quit,"SDL_QuitSubSystem")&&bind(load,"SDL_LoadWAV")&&bind(open,"SDL_OpenAudioDeviceStream")&&bind(resume,"SDL_ResumeAudioStreamDevice")&&bind(put,"SDL_PutAudioStreamData")&&bind(clear,"SDL_ClearAudioStream")&&bind(destroy,"SDL_DestroyAudioStream")&&bind(release,"SDL_free"))){shutdown();return false;} + if(!(bind(init,"SDL_InitSubSystem")&&bind(quit,"SDL_QuitSubSystem")&&bind(load,"SDL_LoadWAV")&&bind(open,"SDL_OpenAudioDeviceStream")&&bind(resume,"SDL_ResumeAudioStreamDevice")&&bind(put,"SDL_PutAudioStreamData")&&bind(clear,"SDL_ClearAudioStream")&&bind(gain,"SDL_SetAudioStreamGain")&&bind(destroy,"SDL_DestroyAudioStream")&&bind(release,"SDL_free"))){shutdown();return false;} if(!init(0x10)){shutdown();return false;}initialized=true; - const char* names[]={"deck_ui_navigation.wav","deck_ui_default_activation.wav"}; + const char* names[]={"deck_ui_typing.wav","deck_ui_typing.wav"}; for(int i=0;i<2;++i){Spec spec{};auto& clip=clips[i]; if(!load((root+"/steamui/sounds/"+names[i]).c_str(),&spec,&clip.data,&clip.size)||!clip.size||clip.size>1024*1024){shutdown();return false;} clip.stream=open(0xffffffffu,&spec,nullptr,nullptr); - if(!clip.stream||!resume(clip.stream)){shutdown();return false;} + if(!clip.stream||!gain(clip.stream,i==0?.30f:.45f)||!resume(clip.stream)){shutdown();return false;} } return true; } diff --git a/tests/test_ui_feedback.py b/tests/test_ui_feedback.py index f98c527..f4d1d95 100644 --- a/tests/test_ui_feedback.py +++ b/tests/test_ui_feedback.py @@ -14,8 +14,9 @@ static int loads=0, writes=0, streams=0, cleared=0; extern "C" { bool SDL_InitSubSystem(uint32_t flags){assert(flags==0x10);return true;} void SDL_QuitSubSystem(uint32_t flags){assert(flags==0x10);assert(streams==0);} -bool SDL_LoadWAV(const char* path,void* spec,uint8_t** data,uint32_t* len){assert(strstr(path,"/steamui/sounds/deck_ui_"));++loads;*data=(uint8_t*)malloc(4);*len=4;return true;} +bool SDL_LoadWAV(const char* path,void* spec,uint8_t** data,uint32_t* len){assert(strstr(path,"/steamui/sounds/deck_ui_typing.wav"));++loads;*data=(uint8_t*)malloc(4);*len=4;return true;} void* SDL_OpenAudioDeviceStream(uint32_t id,const void*,void* callback,void* userdata){assert(id==0xffffffffu&&!callback&&!userdata);++streams;return malloc(1);} +bool SDL_SetAudioStreamGain(void*,float gain){assert(gain==.30f||gain==.45f);return true;} bool SDL_ResumeAudioStreamDevice(void*){return true;} bool SDL_PutAudioStreamData(void*,const void*,int len){assert(loads==2&&len==4);++writes;return true;} bool SDL_ClearAudioStream(void*){++cleared;return true;}