Detailed changes
@@ -7601,6 +7601,7 @@ dependencies = [
"media",
"metal",
"objc",
+ "objc2-app-kit",
"parking_lot",
"pathfinder_geometry",
"raw-window-handle",
@@ -11211,6 +11212,16 @@ dependencies = [
"objc2-encode",
]
+[[package]]
+name = "objc2-app-kit"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc"
+dependencies = [
+ "objc2",
+ "objc2-foundation",
+]
+
[[package]]
name = "objc2-audio-toolbox"
version = "0.3.2"
@@ -604,6 +604,7 @@ nbformat = "1.2.0"
nix = "0.29"
num-format = "0.4.4"
objc = "0.2"
+objc2-app-kit = { version = "0.3", default-features = false, features = [ "NSGraphics" ] }
objc2-foundation = { version = "=0.3.2", default-features = false, features = [
"NSArray",
"NSAttributedString",
@@ -821,6 +822,7 @@ features = [
"Win32_System_Com",
"Win32_System_Com_StructuredStorage",
"Win32_System_Console",
+ "Win32_System_Diagnostics_Debug",
"Win32_System_DataExchange",
"Win32_System_IO",
"Win32_System_LibraryLoader",
@@ -85,14 +85,24 @@ impl TextInput {
fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
if self.selected_range.is_empty() {
- self.select_to(self.previous_boundary(self.cursor_offset()), cx)
+ let prev = self.previous_boundary(self.cursor_offset());
+ if self.cursor_offset() == prev {
+ window.play_system_bell();
+ return;
+ }
+ self.select_to(prev, cx)
}
self.replace_text_in_range(None, "", window, cx)
}
fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
if self.selected_range.is_empty() {
- self.select_to(self.next_boundary(self.cursor_offset()), cx)
+ let next = self.next_boundary(self.cursor_offset());
+ if self.cursor_offset() == next {
+ window.play_system_bell();
+ return;
+ }
+ self.select_to(next, cx)
}
self.replace_text_in_range(None, "", window, cx)
}
@@ -689,6 +689,8 @@ pub trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
fn update_ime_position(&self, _bounds: Bounds<Pixels>);
+ fn play_system_bell(&self) {}
+
#[cfg(any(test, feature = "test-support"))]
fn as_test(&mut self) -> Option<&mut TestWindow> {
None
@@ -5024,6 +5024,12 @@ impl Window {
.set_tabbing_identifier(tabbing_identifier)
}
+ /// Request the OS to play an alert sound. On some platforms this is associated
+ /// with the window, for others it's just a simple global function call.
+ pub fn play_system_bell(&self) {
+ self.platform_window.play_system_bell()
+ }
+
/// Toggles the inspector mode on this window.
#[cfg(any(feature = "inspector", debug_assertions))]
pub fn toggle_inspector(&mut self, cx: &mut App) {
@@ -58,6 +58,7 @@ use wayland_protocols::xdg::decoration::zv1::client::{
zxdg_decoration_manager_v1, zxdg_toplevel_decoration_v1,
};
use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};
+use wayland_protocols::xdg::system_bell::v1::client::xdg_system_bell_v1;
use wayland_protocols::{
wp::cursor_shape::v1::client::{wp_cursor_shape_device_v1, wp_cursor_shape_manager_v1},
xdg::dialog::v1::client::xdg_wm_dialog_v1::{self, XdgWmDialogV1},
@@ -129,6 +130,7 @@ pub struct Globals {
pub text_input_manager: Option<zwp_text_input_manager_v3::ZwpTextInputManagerV3>,
pub gesture_manager: Option<zwp_pointer_gestures_v1::ZwpPointerGesturesV1>,
pub dialog: Option<xdg_wm_dialog_v1::XdgWmDialogV1>,
+ pub system_bell: Option<xdg_system_bell_v1::XdgSystemBellV1>,
pub executor: ForegroundExecutor,
}
@@ -170,6 +172,7 @@ impl Globals {
text_input_manager: globals.bind(&qh, 1..=1, ()).ok(),
gesture_manager: globals.bind(&qh, 1..=3, ()).ok(),
dialog: globals.bind(&qh, dialog_v..=dialog_v, ()).ok(),
+ system_bell: globals.bind(&qh, 1..=1, ()).ok(),
executor,
qh,
}
@@ -1069,6 +1072,7 @@ impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for WaylandClientStat
}
delegate_noop!(WaylandClientStatePtr: ignore xdg_activation_v1::XdgActivationV1);
+delegate_noop!(WaylandClientStatePtr: ignore xdg_system_bell_v1::XdgSystemBellV1);
delegate_noop!(WaylandClientStatePtr: ignore wl_compositor::WlCompositor);
delegate_noop!(WaylandClientStatePtr: ignore wp_cursor_shape_device_v1::WpCursorShapeDeviceV1);
delegate_noop!(WaylandClientStatePtr: ignore wp_cursor_shape_manager_v1::WpCursorShapeManagerV1);
@@ -1479,6 +1479,18 @@ impl PlatformWindow for WaylandWindow {
fn gpu_specs(&self) -> Option<GpuSpecs> {
self.borrow().renderer.gpu_specs().into()
}
+
+ fn play_system_bell(&self) {
+ let state = self.borrow();
+ let surface = if state.surface_state.toplevel().is_some() {
+ Some(&state.surface)
+ } else {
+ None
+ };
+ if let Some(bell) = state.globals.system_bell.as_ref() {
+ bell.ring(surface);
+ }
+ }
}
fn update_window(mut state: RefMut<WaylandWindowState>) {
@@ -1846,4 +1846,9 @@ impl PlatformWindow for X11Window {
fn gpu_specs(&self) -> Option<GpuSpecs> {
self.0.state.borrow().renderer.gpu_specs().into()
}
+
+ fn play_system_bell(&self) {
+ // Volume 0% means don't increase or decrease from system volume
+ let _ = self.0.xcb.bell(0);
+ }
}
@@ -48,6 +48,7 @@ mach2.workspace = true
media.workspace = true
metal.workspace = true
objc.workspace = true
+objc2-app-kit.workspace = true
parking_lot.workspace = true
pathfinder_geometry = "0.5"
raw-window-handle = "0.6"
@@ -49,6 +49,7 @@ use objc::{
runtime::{BOOL, Class, NO, Object, Protocol, Sel, YES},
sel, sel_impl,
};
+use objc2_app_kit::NSBeep;
use parking_lot::Mutex;
use raw_window_handle as rwh;
use smallvec::SmallVec;
@@ -1676,6 +1677,10 @@ impl PlatformWindow for MacWindow {
}
}
+ fn play_system_bell(&self) {
+ unsafe { NSBeep() }
+ }
+
#[cfg(any(test, feature = "test-support"))]
fn render_to_image(&self, scene: &gpui::Scene) -> Result<RgbaImage> {
let mut this = self.0.lock();
@@ -20,7 +20,9 @@ use windows::{
Foundation::*,
Graphics::Dwm::*,
Graphics::Gdi::*,
- System::{Com::*, LibraryLoader::*, Ole::*, SystemServices::*},
+ System::{
+ Com::*, Diagnostics::Debug::MessageBeep, LibraryLoader::*, Ole::*, SystemServices::*,
+ },
UI::{Controls::*, HiDpi::*, Input::KeyboardAndMouse::*, Shell::*, WindowsAndMessaging::*},
},
core::*,
@@ -950,6 +952,11 @@ impl PlatformWindow for WindowsWindow {
self.0.update_ime_position(self.0.hwnd, caret_position);
}
+
+ fn play_system_bell(&self) {
+ // MB_OK: The sound specified as the Windows Default Beep sound.
+ let _ = unsafe { MessageBeep(MB_OK) };
+ }
}
#[implement(IDropTarget)]