/* NeXT/Open/GNUstep / MacOSX communication module. -*- coding: utf-8 -*-
-Copyright (C) 1989, 1993-1994, 2005-2006, 2008-2015 Free Software
+Copyright (C) 1989, 1993-1994, 2005-2006, 2008-2016 Free Software
Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs 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.
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
return NULL;
}
-static void
-ns_timeout (int usecs)
-/* --------------------------------------------------------------------------
- Blocking timer utility used by ns_ring_bell
- -------------------------------------------------------------------------- */
+
+void
+ns_init_locale (void)
+/* OS X doesn't set any environment variables for the locale when run
+ from the GUI. Get the locale from the OS and set LANG. */
{
- struct timespec wakeup = timespec_add (current_timespec (),
- make_timespec (0, usecs * 1000));
+ NSLocale *locale = [NSLocale currentLocale];
- /* Keep waiting until past the time wakeup. */
- while (1)
+ NSTRACE ("ns_init_locale");
+
+ @try
{
- struct timespec timeout, now = current_timespec ();
- if (timespec_cmp (wakeup, now) <= 0)
- break;
- timeout = timespec_sub (wakeup, now);
+ /* It seems OS X should probably use UTF-8 everywhere.
+ 'localeIdentifier' does not specify the encoding, and I can't
+ find any way to get the OS to tell us which encoding to use,
+ so hard-code '.UTF-8'. */
+ NSString *localeID = [NSString stringWithFormat:@"%@.UTF-8",
+ [locale localeIdentifier]];
- /* Try to wait that long--but we might wake up sooner. */
- pselect (0, NULL, NULL, NULL, &timeout, NULL);
+ /* Set LANG to locale, but not if LANG is already set. */
+ setenv("LANG", [localeID UTF8String], 0);
+ }
+ @catch (NSException *e)
+ {
+ NSLog (@"Locale detection failed: %@: %@", [e name], [e reason]);
}
}
}
-/* True, if the menu bar should be hidden. */
-
static BOOL
ns_menu_bar_should_be_hidden (void)
+/* True, if the menu bar should be hidden. */
{
return !NILP (ns_auto_hide_menu_bar)
&& [NSApp respondsToSelector:@selector(setPresentationOptions:)];
}
-static CGFloat
-ns_menu_bar_height (NSScreen *screen)
-/* The height of the menu bar, if visible. */
+struct EmacsMargins
{
- // NSTRACE ("ns_menu_bar_height");
+ CGFloat top;
+ CGFloat bottom;
+ CGFloat left;
+ CGFloat right;
+};
+
+
+static struct EmacsMargins
+ns_screen_margins (NSScreen *screen)
+/* The parts of SCREEN used by the operating system. */
+{
+ NSTRACE ("ns_screen_margins");
+
+ struct EmacsMargins margins;
- CGFloat res;
+ NSRect screenFrame = [screen frame];
+ NSRect screenVisibleFrame = [screen visibleFrame];
+ /* Sometimes, visibleFrame isn't up-to-date with respect to a hidden
+ menu bar, check this explicitly. */
if (ns_menu_bar_should_be_hidden())
{
- res = 0;
+ margins.top = 0;
}
else
{
- NSRect screenFrame = [screen frame];
- NSRect screenVisibleFrame = [screen visibleFrame];
-
CGFloat frameTop = screenFrame.origin.y + screenFrame.size.height;
CGFloat visibleFrameTop = (screenVisibleFrame.origin.y
+ screenVisibleFrame.size.height);
- res = frameTop - visibleFrameTop;
+ margins.top = frameTop - visibleFrameTop;
+ }
+
+ {
+ CGFloat frameRight = screenFrame.origin.x + screenFrame.size.width;
+ CGFloat visibleFrameRight = (screenVisibleFrame.origin.x
+ + screenVisibleFrame.size.width);
+ margins.right = frameRight - visibleFrameRight;
+ }
+
+ margins.bottom = screenVisibleFrame.origin.y - screenFrame.origin.y;
+ margins.left = screenVisibleFrame.origin.x - screenFrame.origin.x;
+
+ NSTRACE_MSG ("left:%g right:%g top:%g bottom:%g",
+ margins.left,
+ margins.right,
+ margins.top,
+ margins.bottom);
+
+ return margins;
+}
+
+
+/* A screen margin between 1 and DOCK_IGNORE_LIMIT (inclusive) is
+ assumed to contain a hidden dock. OS X currently use 4 pixels for
+ this, however, to be future compatible, a larger value is used. */
+#define DOCK_IGNORE_LIMIT 6
+
+static struct EmacsMargins
+ns_screen_margins_ignoring_hidden_dock (NSScreen *screen)
+/* The parts of SCREEN used by the operating system, excluding the parts
+reserved for an hidden dock. */
+{
+ NSTRACE ("ns_screen_margins_ignoring_hidden_dock");
+
+ struct EmacsMargins margins = ns_screen_margins(screen);
+ /* OS X (currently) reserved 4 pixels along the edge where a hidden
+ dock is located. Unfortunately, it's not possible to find the
+ location and information about if the dock is hidden. Instead,
+ it is assumed that if the margin of an edge is less than
+ DOCK_IGNORE_LIMIT, it contains a hidden dock. */
+ if (margins.left <= DOCK_IGNORE_LIMIT)
+ {
+ margins.left = 0;
+ }
+ if (margins.right <= DOCK_IGNORE_LIMIT)
+ {
+ margins.right = 0;
}
+ if (margins.top <= DOCK_IGNORE_LIMIT)
+ {
+ margins.top = 0;
+ }
+ /* Note: This doesn't occur in current versions of OS X, but
+ included for completeness and future compatibility. */
+ if (margins.bottom <= DOCK_IGNORE_LIMIT)
+ {
+ margins.bottom = 0;
+ }
+
+ NSTRACE_MSG ("left:%g right:%g top:%g bottom:%g",
+ margins.left,
+ margins.right,
+ margins.top,
+ margins.bottom);
+
+ return margins;
+}
+
+
+static CGFloat
+ns_menu_bar_height (NSScreen *screen)
+/* The height of the menu bar, if visible.
+
+ Note: Don't use this when fullscreen is enabled -- the screen
+ sometimes includes, sometimes excludes the menu bar area. */
+{
+ struct EmacsMargins margins = ns_screen_margins(screen);
- // NSTRACE_MSG (NSTRACE_FMT_RETURN "%.0f", res);
+ CGFloat res = margins.top;
+
+ NSTRACE ("ns_menu_bar_height " NSTRACE_FMT_RETURN " %.0f", res);
return res;
}
// Result: Menu bar visible, frame placed immediately below the menu.
//
-static NSRect constrain_frame_rect(NSRect frameRect)
+static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
{
NSTRACE ("constrain_frame_rect(" NSTRACE_FMT_RECT ")",
NSTRACE_ARG_RECT (frameRect));
{
multiscreenRect = NSUnionRect (multiscreenRect, scrRect);
- menu_bar_height = max(menu_bar_height, ns_menu_bar_height (s));
+ if (!isFullscreen)
+ {
+ CGFloat screen_menu_bar_height = ns_menu_bar_height (s);
+ menu_bar_height = max(menu_bar_height, screen_menu_bar_height);
+ }
}
}
if (![view isFullscreen])
{
[[view window]
- setFrame:constrain_frame_rect([[view window] frame])
+ setFrame:constrain_frame_rect([[view window] frame], false)
display:NO];
}
}
}
+/* ==========================================================================
+
+ 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;
+
static void
ns_ring_bell (struct frame *f)
/* --------------------------------------------------------------------------
NSTRACE ("ns_ring_bell");
if (visible_bell)
{
- NSAutoreleasePool *pool;
struct frame *frame = SELECTED_FRAME ();
NSView *view;
+ if (bell_view == nil)
+ {
+ bell_view = [[EmacsBell alloc] init];
+ [bell_view retain];
+ }
+
block_input ();
- pool = [[NSAutoreleasePool alloc] init];
view = FRAME_NS_VIEW (frame);
if (view != nil)
{
- NSRect r, surr;
- NSPoint dim = NSMakePoint (128, 128);
-
- r = [view bounds];
- r.origin.x += (r.size.width - dim.x) / 2;
- r.origin.y += (r.size.height - dim.y) / 2;
- r.size.width = dim.x;
- r.size.height = dim.y;
- surr = NSInsetRect (r, -2, -2);
- ns_focus (frame, &surr, 1);
- [[view window] cacheImageInRect: [view convertRect: surr toView:nil]];
- [ns_lookup_indexed_color (NS_FACE_FOREGROUND
- (FRAME_DEFAULT_FACE (frame)), frame) set];
- NSRectFill (r);
- [[view window] flushWindow];
- ns_timeout (150000);
- [[view window] restoreCachedImage];
- [[view window] flushWindow];
- ns_unfocus (frame);
+ [bell_view show:view];
}
- [pool release];
+
unblock_input ();
}
else
}
}
+
+static void hide_bell ()
+/* --------------------------------------------------------------------------
+ Ensure the bell is hidden.
+ -------------------------------------------------------------------------- */
+{
+ NSTRACE ("hide_bell");
+
+ if (bell_view != nil)
+ {
+ [bell_view remove];
+ }
+}
+
+
/* ==========================================================================
Frame / window manager related functions
[[view window] orderOut: NSApp];
[[view window] setFrame: t display: NO];
}
+
+ /* Processing input while Emacs is being minimized can cause a
+ crash, so block it for the duration. */
+ block_input();
[[view window] miniaturize: NSApp];
+ unblock_input();
}
/* Free X resources of frame F. */
NSRect wr = [window frame];
int tb = FRAME_EXTERNAL_TOOL_BAR (f);
int pixelwidth, pixelheight;
- int rows, cols;
int orig_height = wr.size.height;
NSTRACE ("x_set_window_size");
{
pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
- cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, pixelwidth);
- rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, pixelheight);
}
else
{
pixelwidth = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, width);
pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, height);
- cols = width;
- rows = height;
}
/* If we have a toolbar, take its height into account. */
static void
ns_copy_bits (struct frame *f, NSRect src, NSRect dest)
{
+ NSTRACE ("ns_copy_bits");
+
if (FRAME_NS_VIEW (f))
{
+ hide_bell(); // Ensure the bell image isn't scrolled.
+
ns_focus (f, &dest, 1);
[FRAME_NS_VIEW (f) scrollRect: src
by: NSMakeSize (dest.origin.x - src.origin.x,
[img setXBMColor: bm_color];
}
+#ifdef NS_IMPL_COCOA
// Note: For periodic images, the full image height is "h + hd".
// By using the height h, a suitable part of the image is used.
NSRect fromRect = NSMakeRect(0, 0, p->wd, p->h);
NSTRACE_RECT ("fromRect", fromRect);
-#ifdef NS_IMPL_COCOA
[img drawInRect: r
fromRect: fromRect
operation: NSCompositeSourceOver
if (oldr != rows || oldc != cols || neww != oldw || newh != oldh)
{
NSView *view = FRAME_NS_VIEW (emacsframe);
- NSWindow *win = [view window];
change_frame_size (emacsframe,
FRAME_PIXEL_TO_TEXT_WIDTH (emacsframe, neww),
NSString *name;
NSTRACE ("[EmacsView initFrameFromEmacs:]");
- NSTRACE_MSG ("cols:%d lines:%d\n", f->text_cols, f->text_lines);
+ NSTRACE_MSG ("cols:%d lines:%d", f->text_cols, f->text_lines);
windowClosing = NO;
processingCompose = NO;
- (void)windowDidExitFullScreen /* provided for direct calls */
{
- NSTRACE ("[EamcsView windowDidExitFullScreen]");
+ NSTRACE ("[EmacsView windowDidExitFullScreen]");
if (!FRAME_LIVE_P (emacsframe))
{
NSTRACE_MSG ("Ignored (frame dead)");
- (BOOL)isFullscreen
{
- NSTRACE ("[EmacsView isFullscreen]");
+ BOOL res;
- if (! fs_is_native) return nonfs_window != nil;
+ if (! fs_is_native)
+ {
+ res = (nonfs_window != nil);
+ }
+ else
+ {
#ifdef HAVE_NATIVE_FS
- return ([[self window] styleMask] & NSFullScreenWindowMask) != 0;
+ res = (([[self window] styleMask] & NSFullScreenWindowMask) != 0);
#else
- return NO;
+ res = NO;
#endif
+ }
+
+ NSTRACE ("[EmacsView isFullscreen] " NSTRACE_FMT_RETURN " %d",
+ (int) res);
+
+ return res;
}
#ifdef HAVE_NATIVE_FS
#endif
#endif
- return constrain_frame_rect(frameRect);
+ return constrain_frame_rect(frameRect,
+ [(EmacsView *)[self delegate] isFullscreen]);
}
- (void)zoom:(id)sender
{
- struct frame * f = SELECTED_FRAME ();
-
NSTRACE ("[EmacsWindow zoom:]");
ns_update_auto_hide_menu_bar();
// the menu-bar.
[super zoom:sender];
-#elsif 0
+#elif 0
// Native zoom done using the standard zoom animation, plus an
- // explicit resize to cover the full screen.
+ // explicit resize to cover the full screen, except the menu-bar and
+ // dock, if present.
[super zoom:sender];
// After the native zoom, resize the resulting frame to fill the
NSTRACE_FSTYPE ("fullscreenState", fs_state);
NSRect sr = [screen frame];
+ struct EmacsMargins margins
+ = ns_screen_margins_ignoring_hidden_dock(screen);
+
NSRect wr = [self frame];
NSTRACE_RECT ("Rect after zoom", wr);
if (fs_state == FULLSCREEN_MAXIMIZED
|| fs_state == FULLSCREEN_HEIGHT)
{
- newWr.origin.x = 0;
- newWr.size.height = sr.size.height - ns_menu_bar_height(screen);
+ newWr.origin.y = sr.origin.y + margins.bottom;
+ newWr.size.height = sr.size.height - margins.top - margins.bottom;
}
if (fs_state == FULLSCREEN_MAXIMIZED
|| fs_state == FULLSCREEN_WIDTH)
{
- newWr.origin.y = 0;
- newWr.size.width = sr.size.width;
+ newWr.origin.x = sr.origin.x + margins.left;
+ newWr.size.width = sr.size.width - margins.right - margins.left;
}
if (newWr.size.width != wr.size.width
}
}
#else
- // Non-native zoom which is done instantaneously. The resulting frame
- // covers the entire screen, except the menu-bar, if present.
+ // Non-native zoom which is done instantaneously. The resulting
+ // frame covers the entire screen, except the menu-bar and dock, if
+ // present.
NSScreen * screen = [self screen];
if (screen != nil)
{
NSRect sr = [screen frame];
- sr.size.height -= ns_menu_bar_height (screen);
+ struct EmacsMargins margins
+ = ns_screen_margins_ignoring_hidden_dock(screen);
+
+ sr.size.height -= (margins.top + margins.bottom);
+ sr.size.width -= (margins.left + margins.right);
+ sr.origin.x += margins.left;
+ sr.origin.y += margins.bottom;
sr = [[self delegate] windowWillUseStandardFrame:self
defaultFrame:sr];