mirror of
https://github.com/hyprwm/Hyprland.git
synced 2026-08-18 02:51:23 +00:00
protocols/bell: play a sound by default (#15502)
--------- Co-authored-by: thompty <tatthompson44@gmail.com>
This commit is contained in:
1
.github/actions/setup_base/action.yml
vendored
1
.github/actions/setup_base/action.yml
vendored
@ -29,6 +29,7 @@ runs:
|
||||
hyprcursor \
|
||||
jq \
|
||||
libc++ \
|
||||
libcanberra \
|
||||
libdisplay-info \
|
||||
libdrm \
|
||||
libei \
|
||||
|
||||
@ -285,6 +285,7 @@ pkg_check_modules(deps
|
||||
re2
|
||||
muparser
|
||||
lcms2
|
||||
libcanberra
|
||||
)
|
||||
|
||||
pkg_search_module(LUA REQUIRED IMPORTED_TARGET GLOBAL lua55 lua5.5 lua-55 lua-5.5 lua>=5.5 lua<5.6)
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
hyprwayland-scanner,
|
||||
hyprwire,
|
||||
lcms2,
|
||||
libcanberra,
|
||||
libGL,
|
||||
libdrm,
|
||||
libei,
|
||||
@ -190,6 +191,7 @@ customStdenv.mkDerivation (finalAttrs: {
|
||||
hyprutils
|
||||
hyprwire
|
||||
lcms2
|
||||
libcanberra
|
||||
libdrm
|
||||
libgbm
|
||||
libGL
|
||||
|
||||
@ -114,6 +114,8 @@ CLuaEventHandler::CLuaEventHandler(lua_State* L) : m_lua(L) {
|
||||
CLuaWorkspace::push(L, ws);
|
||||
});
|
||||
}));
|
||||
m_listeners.push_back(
|
||||
bus()->m_events.window.bell.listen([this](PHLWINDOW w, Event::SCallbackInfo&) { dispatch("window.bell", 1, [&](lua_State* L) { CLuaWindow::push(L, w); }); }));
|
||||
|
||||
m_listeners.push_back(bus()->m_events.layer.opened.listen([this](PHLLS ls) { dispatch("layer.opened", 1, [&](lua_State* L) { CLuaLayerSurface::push(L, ls); }); }));
|
||||
m_listeners.push_back(bus()->m_events.layer.closed.listen([this](PHLLS ls) { dispatch("layer.closed", 1, [&](lua_State* L) { CLuaLayerSurface::push(L, ls); }); }));
|
||||
@ -278,6 +280,7 @@ std::unordered_set<std::string> CLuaEventHandler::knownEvents() {
|
||||
"window.fullscreen",
|
||||
"window.update_rules",
|
||||
"window.move_to_workspace",
|
||||
"window.bell",
|
||||
"layer.opened",
|
||||
"layer.closed",
|
||||
"monitor.added",
|
||||
|
||||
@ -525,6 +525,7 @@ std::vector<SP<IValue>> Values::getConfigValues() {
|
||||
MS<Bool>("misc:screencopy_force_8b", "forces 8 bit screencopy", true),
|
||||
MS<Bool>("misc:disable_scale_notification", "disables notification popup when a monitor fails to set a suitable scale", false),
|
||||
MS<Bool>("misc:size_limits_tiled", "whether to apply minsize and maxsize rules to tiled windows", false),
|
||||
MS<String>("misc:bell_sound", "path to custom wav/ogg system bell. `none` or an empty string mute it. `default` uses the system's current one.", "default"),
|
||||
MS<Int>("misc:new_float_force_onscreen", "whether new floating windows must be placed fully/partially on-screen", 2),
|
||||
MS<Int>("misc:float_force_onscreen", "whether existing floating windows must remain fully/partially on-screen", 0),
|
||||
|
||||
|
||||
@ -90,6 +90,7 @@ namespace Event {
|
||||
Event<PHLWINDOW> floating;
|
||||
Event<PHLWINDOW> updateRules;
|
||||
Event<PHLWINDOW, PHLWORKSPACE> moveToWorkspace;
|
||||
Cancellable<PHLWINDOW> bell;
|
||||
} window;
|
||||
|
||||
struct {
|
||||
|
||||
72
src/helpers/BellSound.cpp
Normal file
72
src/helpers/BellSound.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
#include "BellSound.hpp"
|
||||
#include <canberra.h>
|
||||
#include "../config/ConfigValue.hpp"
|
||||
#include "../config/ConfigManager.hpp"
|
||||
#include "./MiscFunctions.hpp"
|
||||
#include "../event/EventBus.hpp"
|
||||
#include "../debug/log/Logger.hpp"
|
||||
|
||||
CBellSound::CBellSound() {
|
||||
onNewConfig();
|
||||
m_configListener = Event::bus()->m_events.config.reloaded.listen([this] { onNewConfig(); });
|
||||
}
|
||||
|
||||
CBellSound::~CBellSound() {
|
||||
if (m_context) {
|
||||
ca_context_destroy(m_context);
|
||||
ca_proplist_destroy(m_sound);
|
||||
}
|
||||
}
|
||||
|
||||
void CBellSound::onNewConfig() {
|
||||
const auto VALUE = *CConfigValue<std::string>("misc:bell_sound");
|
||||
|
||||
if (VALUE == "default") {
|
||||
m_muted = false;
|
||||
initializeSoundContext();
|
||||
ca_proplist_sets(m_sound, CA_PROP_EVENT_ID, "bell-window-system");
|
||||
} else if (VALUE.empty() || VALUE == "none")
|
||||
m_muted = true;
|
||||
else {
|
||||
m_muted = false;
|
||||
initializeSoundContext();
|
||||
|
||||
const auto RESOLVEDPATH = absolutePath(VALUE, Config::mgr()->getMainConfigPath());
|
||||
|
||||
if (std::filesystem::exists(RESOLVEDPATH)) {
|
||||
ca_proplist_sets(m_sound, CA_PROP_MEDIA_FILENAME, RESOLVEDPATH.c_str());
|
||||
ca_proplist_set(m_sound, CA_PROP_EVENT_ID, nullptr, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
ca_proplist_sets(m_sound, CA_PROP_EVENT_ID, "bell-window-system");
|
||||
Log::logger->log(Log::WARN, "bell: resolved custom sound path '{}' doesn't exist, falling back to default", RESOLVEDPATH);
|
||||
}
|
||||
}
|
||||
|
||||
void CBellSound::initializeSoundContext() {
|
||||
if (m_context)
|
||||
return;
|
||||
|
||||
int result = ca_context_create(&m_context) || ca_proplist_create(&m_sound);
|
||||
if UNLIKELY (result != CA_SUCCESS)
|
||||
Log::logger->log(Log::ERR, "bell: failed to create canberra context, '{}'", ca_strerror(result));
|
||||
|
||||
ca_context_change_props(m_context, CA_PROP_APPLICATION_NAME, "Hyprland", CA_PROP_MEDIA_NAME, "System Bell", CA_PROP_EVENT_DESCRIPTION, "Wayland system bell",
|
||||
CA_PROP_MEDIA_ROLE, "event", CA_PROP_MEDIA_ICON_NAME, "preferences-system-notifications", CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", nullptr);
|
||||
}
|
||||
|
||||
void CBellSound::play() {
|
||||
static CBellSound instance;
|
||||
|
||||
if (instance.m_muted)
|
||||
return;
|
||||
|
||||
int result = ca_context_play_full(instance.m_context, 0, instance.m_sound, nullptr, nullptr);
|
||||
if UNLIKELY (result != CA_SUCCESS) {
|
||||
if (result == CA_ERROR_CORRUPT)
|
||||
Log::logger->log(Log::WARN, "bell: sound is not a wav/ogg file");
|
||||
else
|
||||
Log::logger->log(Log::WARN, "bell: failed to play sound, '{}'", ca_strerror(result));
|
||||
}
|
||||
}
|
||||
24
src/helpers/BellSound.hpp
Normal file
24
src/helpers/BellSound.hpp
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <hyprutils/signal/Listener.hpp>
|
||||
|
||||
struct ca_context;
|
||||
struct ca_proplist;
|
||||
|
||||
class CBellSound {
|
||||
public:
|
||||
static void play();
|
||||
|
||||
private:
|
||||
CBellSound();
|
||||
~CBellSound();
|
||||
|
||||
void onNewConfig();
|
||||
void initializeSoundContext();
|
||||
|
||||
bool m_muted = false;
|
||||
ca_proplist* m_sound = nullptr;
|
||||
ca_context* m_context = nullptr;
|
||||
|
||||
Hyprutils::Signal::CHyprSignalListener m_configListener;
|
||||
};
|
||||
@ -1,52 +1,29 @@
|
||||
#include "XDGBell.hpp"
|
||||
#include "core/Compositor.hpp"
|
||||
#include "../desktop/view/Window.hpp"
|
||||
#include "../helpers/BellSound.hpp"
|
||||
#include "./core/Compositor.hpp"
|
||||
#include "../desktop/state/ViewState.hpp"
|
||||
#include "../desktop/state/ViewQuery.hpp"
|
||||
#include "../event/EventBus.hpp"
|
||||
#include "../ipc/s2/S2.hpp"
|
||||
#include "../Compositor.hpp"
|
||||
#include <format>
|
||||
|
||||
CXDGSystemBellManagerResource::CXDGSystemBellManagerResource(UP<CXdgSystemBellV1>&& resource) : m_resource(std::move(resource)) {
|
||||
if UNLIKELY (!good())
|
||||
return;
|
||||
|
||||
m_resource->setDestroy([this](CXdgSystemBellV1* r) { PROTO::xdgBell->destroyResource(this); });
|
||||
m_resource->setOnDestroy([this](CXdgSystemBellV1* r) { PROTO::xdgBell->destroyResource(this); });
|
||||
m_resource->setDestroy([this](CXdgSystemBellV1*) { PROTO::xdgBell->destroyResource(this); });
|
||||
m_resource->setOnDestroy([this](CXdgSystemBellV1*) { PROTO::xdgBell->destroyResource(this); });
|
||||
|
||||
m_resource->setRing([](CXdgSystemBellV1* r, wl_resource* surface) {
|
||||
if (!surface) {
|
||||
IPC::Socket2::sock()->postEvent({
|
||||
.event = "bell",
|
||||
.data = "",
|
||||
});
|
||||
m_resource->setRing([](CXdgSystemBellV1*, wl_resource* surface) {
|
||||
const auto WINDOW = Desktop::viewState()->query().surface(CWLSurfaceResource::fromResource(surface)).type(Desktop::View::VIEW_TYPE_WINDOW).runWindow();
|
||||
|
||||
Event::SCallbackInfo info;
|
||||
Event::bus()->m_events.window.bell.emit(WINDOW, info);
|
||||
if (info.cancelled)
|
||||
return;
|
||||
}
|
||||
|
||||
const auto SURFACE = CWLSurfaceResource::fromResource(surface);
|
||||
|
||||
if (!SURFACE) {
|
||||
IPC::Socket2::sock()->postEvent({
|
||||
.event = "bell",
|
||||
.data = "",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& w : Desktop::windowState()->windows()) {
|
||||
if (!w->m_isMapped || w->m_isX11 || !w->m_xdgSurface || !w->wlSurface())
|
||||
continue;
|
||||
|
||||
if (w->wlSurface()->resource() == SURFACE) {
|
||||
IPC::Socket2::sock()->postEvent({
|
||||
.event = "bell",
|
||||
.data = std::format("{:x}", rc<uintptr_t>(w.get())),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
IPC::Socket2::sock()->postEvent({
|
||||
.event = "bell",
|
||||
.data = "",
|
||||
});
|
||||
IPC::Socket2::sock()->postEvent({.event = "bell", .data = WINDOW ? std::format("{:x}", rc<uintptr_t>(WINDOW.get())) : ""});
|
||||
CBellSound::play();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user