+/* ==========================================================================
+
+ Visible bell and beep.
+
+ ========================================================================== */
+
+
+// This bell implementation shows the visual bell image asynchronously
+// from the rest of Emacs. This is done by adding a NSView to the
+// superview of the Emacs window and removing it using a timer.
+//
+// Unfortunately, some Emacs operations, like scrolling, is done using
+// low-level primitives that copy the content of the window, including
+// the bell image. To some extent, this is handled by removing the
+// image prior to scrolling and marking that the window is in need for
+// redisplay.
+//
+// To test this code, make sure that there is no artifacts of the bell
+// image in the following situations. Use a non-empty buffer (like the
+// tutorial) to ensure that a scroll is performed:
+//
+// * Single-window: C-g C-v
+//
+// * Side-by-windows: C-x 3 C-g C-v
+//
+// * Windows above each other: C-x 2 C-g C-v
+
+@interface EmacsBell : NSImageView
+{
+ // Number of currently active bell:s.
+ unsigned int nestCount;
+ NSView * mView;
+ bool isAttached;
+}
+- (void)show:(NSView *)view;
+- (void)hide;
+- (void)remove;
+@end
+
+@implementation EmacsBell
+
+- (id)init;
+{
+ NSTRACE ("[EmacsBell init]");
+ if ((self = [super init]))
+ {
+ nestCount = 0;
+ isAttached = false;
+#ifdef NS_IMPL_GNUSTEP
+ // GNUstep doesn't provide named images. This was reported in
+ // 2011, see https://savannah.gnu.org/bugs/?33396
+ //
+ // As a drop in replacement, a semitransparent gray square is used.
+ self.image = [[NSImage alloc] initWithSize:NSMakeSize(32 * 5, 32 * 5)];
+ [self.image lockFocus];
+ [[NSColor colorForEmacsRed:0.5 green:0.5 blue:0.5 alpha:0.5] set];
+ NSRectFill(NSMakeRect(0, 0, 32, 32));
+ [self.image unlockFocus];
+#else
+ self.image = [NSImage imageNamed:NSImageNameCaution];
+ [self.image setSize:NSMakeSize(self.image.size.width * 5,
+ self.image.size.height * 5)];
+#endif
+ }
+ return self;
+}
+
+- (void)show:(NSView *)view
+{
+ NSTRACE ("[EmacsBell show:]");
+ NSTRACE_MSG ("nestCount: %u", nestCount);
+
+ // Show the image, unless it's already shown.
+ if (nestCount == 0)
+ {
+ NSRect rect = [view bounds];
+ NSPoint pos;
+ pos.x = rect.origin.x + (rect.size.width - self.image.size.width )/2;
+ pos.y = rect.origin.y + (rect.size.height - self.image.size.height)/2;
+
+ [self setFrameOrigin:pos];
+ [self setFrameSize:self.image.size];
+
+ isAttached = true;
+ mView = view;
+ [[[view window] contentView] addSubview:self
+ positioned:NSWindowAbove
+ relativeTo:nil];
+ }
+
+ ++nestCount;
+
+ [self performSelector:@selector(hide) withObject:self afterDelay:0.5];
+}
+
+
+- (void)hide
+{
+ // Note: Trace output from this method isn't shown, reason unknown.
+ // NSTRACE ("[EmacsBell hide]");
+
+ if (nestCount > 0)
+ --nestCount;
+
+ // Remove the image once the last bell became inactive.
+ if (nestCount == 0)
+ {
+ [self remove];
+ }
+}
+
+
+-(void)remove
+{
+ NSTRACE ("[EmacsBell remove]");
+ if (isAttached)
+ {
+ NSTRACE_MSG ("removeFromSuperview");
+ [self removeFromSuperview];
+ mView.needsDisplay = YES;
+ isAttached = false;
+ }
+}
+
+@end
+
+
+static EmacsBell * bell_view = nil;
+