nsterm.m 295 KB
Newer Older
1
/* NeXT/Open/GNUstep / macOS communication module.      -*- coding: utf-8 -*-
2

Paul Eggert's avatar
Paul Eggert committed
3
Copyright (C) 1989, 1993-1994, 2005-2006, 2008-2020 Free Software
4
Foundation, Inc.
5 6 7

This file is part of GNU Emacs.

8
GNU Emacs is free software: you can redistribute it and/or modify
9
it under the terms of the GNU General Public License as published by
10 11
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
12 13 14 15 16 17 18

GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
19
along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
20

21
/*
22 23 24
Originally by Carl Edman
Updated by Christian Limpach (chris@nice.ch)
OpenStep/Rhapsody port by Scott Bender (sbender@harmony-ds.com)
25
macOS/Aqua port by Christophe de Dinechin (descubes@earthlink.net)
26 27 28
GNUstep port and post-20 update by Adrian Robert (arobert@cogsci.ucsd.edu)
*/

29
/* This should be the first include, as it may set up #defines affecting
30
   interpretation of even the system includes.  */
Dan Nicolaescu's avatar
Dan Nicolaescu committed
31
#include <config.h>
32

33
#include <fcntl.h>
34
#include <math.h>
35
#include <pthread.h>
36 37
#include <sys/types.h>
#include <time.h>
38
#include <signal.h>
39
#include <unistd.h>
40
#include <stdbool.h>
41 42

#include <c-ctype.h>
43
#include <c-strcase.h>
44
#include <ftoastr.h>
45 46 47 48 49 50 51 52 53 54 55 56 57

#include "lisp.h"
#include "blockinput.h"
#include "sysselect.h"
#include "nsterm.h"
#include "systime.h"
#include "character.h"
#include "fontset.h"
#include "composite.h"
#include "ccl.h"

#include "termhooks.h"
#include "termchar.h"
58
#include "menu.h"
59 60
#include "window.h"
#include "keyboard.h"
61
#include "buffer.h"
62
#include "font.h"
Daniel Colascione's avatar
Daniel Colascione committed
63
#include "pdumper.h"
64

65 66 67 68
#ifdef NS_IMPL_GNUSTEP
#include "process.h"
#endif

69 70
#ifdef NS_IMPL_COCOA
#include "macfont.h"
71
#include <Carbon/Carbon.h>
72 73
#endif

74 75 76 77
static EmacsMenu *dockMenu;
#ifdef NS_IMPL_COCOA
static EmacsMenu *mainMenu;
#endif
78

79 80 81 82 83 84
/* ==========================================================================

   NSTRACE, Trace support.

   ========================================================================== */

85
#if NSTRACE_ENABLED
86 87

/* The following use "volatile" since they can be accessed from
88
   parallel threads.  */
89 90 91 92 93 94 95 96
volatile int nstrace_num = 0;
volatile int nstrace_depth = 0;

/* When 0, no trace is emitted.  This is used by NSTRACE_WHEN and
   NSTRACE_UNLESS to silence functions called.

   TODO: This should really be a thread-local variable, to avoid that
   a function with disabled trace thread silence trace output in
97
   another.  However, in practice this seldom is a problem.  */
98
volatile int nstrace_enabled_global = 1;
99

100
/* Called when nstrace_enabled goes out of scope.  */
101 102 103 104 105 106 107 108 109
void nstrace_leave(int * pointer_to_nstrace_enabled)
{
  if (*pointer_to_nstrace_enabled)
    {
      --nstrace_depth;
    }
}


110
/* Called when nstrace_saved_enabled_global goes out of scope.  */
111
void nstrace_restore_global_trace_state(int * pointer_to_saved_enabled_global)
112
{
113 114
  nstrace_enabled_global = *pointer_to_saved_enabled_global;
}
115

116 117 118

char const * nstrace_fullscreen_type_name (int fs_type)
{
119 120
  switch (fs_type)
    {
121 122 123 124 125 126 127
    case -1:                   return "-1";
    case FULLSCREEN_NONE:      return "FULLSCREEN_NONE";
    case FULLSCREEN_WIDTH:     return "FULLSCREEN_WIDTH";
    case FULLSCREEN_HEIGHT:    return "FULLSCREEN_HEIGHT";
    case FULLSCREEN_BOTH:      return "FULLSCREEN_BOTH";
    case FULLSCREEN_MAXIMIZED: return "FULLSCREEN_MAXIMIZED";
    default:                   return "FULLSCREEN_?????";
128 129
    }
}
130 131
#endif

132

133 134 135 136 137 138 139 140 141
/* ==========================================================================

   NSColor, EmacsColor category.

   ========================================================================== */
@implementation NSColor (EmacsColor)
+ (NSColor *)colorForEmacsRed:(CGFloat)red green:(CGFloat)green
                         blue:(CGFloat)blue alpha:(CGFloat)alpha
{
142 143 144 145 146 147
#if defined (NS_IMPL_COCOA) \
  && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
  if (ns_use_srgb_colorspace
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
      && [NSColor respondsToSelector:
                    @selector(colorWithSRGBRed:green:blue:alpha:)]
148
#endif
149 150 151 152 153
      )
    return [NSColor colorWithSRGBRed: red
                               green: green
                                blue: blue
                               alpha: alpha];
154 155 156 157 158 159 160 161 162
#endif
  return [NSColor colorWithCalibratedRed: red
                                   green: green
                                    blue: blue
                                   alpha: alpha];
}

- (NSColor *)colorUsingDefaultColorSpace
{
163 164 165
  /* FIXME: We're checking for colorWithSRGBRed here so this will only
     work in the same place as in the method above.  It should really
     be a check whether we're on macOS 10.7 or above.  */
166 167 168
#if defined (NS_IMPL_COCOA) \
  && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
169 170
  if ([NSColor respondsToSelector:
                 @selector(colorWithSRGBRed:green:blue:alpha:)])
171
#endif
172 173 174 175 176 177 178 179
    {
      if (ns_use_srgb_colorspace)
        return [self colorUsingColorSpace: [NSColorSpace sRGBColorSpace]];
      else
        return [self colorUsingColorSpace: [NSColorSpace deviceRGBColorSpace]];
    }
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
  else
180
#endif
181 182
#endif /* NS_IMPL_COCOA && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070  */
#if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 1070
183
  return [self colorUsingColorSpaceName: NSCalibratedRGBColorSpace];
184
#endif
185 186 187 188
}

@end

189 190 191 192 193 194 195 196
/* ==========================================================================

    Local declarations

   ========================================================================== */

/* Convert a symbol indexed with an NSxxx value to a value as defined
   in keyboard.c (lispy_function_key). I hope this is a correct way
197
   of doing things...  */
198 199 200 201 202 203 204 205 206 207 208 209 210
static unsigned convert_ns_to_X_keysym[] =
{
  NSHomeFunctionKey,            0x50,
  NSLeftArrowFunctionKey,       0x51,
  NSUpArrowFunctionKey,         0x52,
  NSRightArrowFunctionKey,      0x53,
  NSDownArrowFunctionKey,       0x54,
  NSPageUpFunctionKey,          0x55,
  NSPageDownFunctionKey,        0x56,
  NSEndFunctionKey,             0x57,
  NSBeginFunctionKey,           0x58,
  NSSelectFunctionKey,          0x60,
  NSPrintFunctionKey,           0x61,
211
  NSClearLineFunctionKey,       0x0B,
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
  NSExecuteFunctionKey,         0x62,
  NSInsertFunctionKey,          0x63,
  NSUndoFunctionKey,            0x65,
  NSRedoFunctionKey,            0x66,
  NSMenuFunctionKey,            0x67,
  NSFindFunctionKey,            0x68,
  NSHelpFunctionKey,            0x6A,
  NSBreakFunctionKey,           0x6B,

  NSF1FunctionKey,              0xBE,
  NSF2FunctionKey,              0xBF,
  NSF3FunctionKey,              0xC0,
  NSF4FunctionKey,              0xC1,
  NSF5FunctionKey,              0xC2,
  NSF6FunctionKey,              0xC3,
  NSF7FunctionKey,              0xC4,
  NSF8FunctionKey,              0xC5,
  NSF9FunctionKey,              0xC6,
  NSF10FunctionKey,             0xC7,
  NSF11FunctionKey,             0xC8,
  NSF12FunctionKey,             0xC9,
  NSF13FunctionKey,             0xCA,
  NSF14FunctionKey,             0xCB,
  NSF15FunctionKey,             0xCC,
236 237 238 239 240 241 242 243 244
  NSF16FunctionKey,             0xCD,
  NSF17FunctionKey,             0xCE,
  NSF18FunctionKey,             0xCF,
  NSF19FunctionKey,             0xD0,
  NSF20FunctionKey,             0xD1,
  NSF21FunctionKey,             0xD2,
  NSF22FunctionKey,             0xD3,
  NSF23FunctionKey,             0xD4,
  NSF24FunctionKey,             0xD5,
245

246 247 248
  NSBackspaceCharacter,         0x08,  /* 8: Not on some KBs.  */
  NSDeleteCharacter,            0xFF,  /* 127: Big 'delete' key upper right.  */
  NSDeleteFunctionKey,          0x9F,  /* 63272: Del forw key off main array.  */
249 250 251 252 253 254 255

  NSTabCharacter,		0x09,
  0x19,				0x09,  /* left tab->regular since pass shift */
  NSCarriageReturnCharacter,	0x0D,
  NSNewlineCharacter,		0x0D,
  NSEnterCharacter,		0x8D,

Alan Third's avatar
Alan Third committed
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
  0x41|NSEventModifierFlagNumericPad,	0xAE,  /* KP_Decimal */
  0x43|NSEventModifierFlagNumericPad,	0xAA,  /* KP_Multiply */
  0x45|NSEventModifierFlagNumericPad,	0xAB,  /* KP_Add */
  0x4B|NSEventModifierFlagNumericPad,	0xAF,  /* KP_Divide */
  0x4E|NSEventModifierFlagNumericPad,	0xAD,  /* KP_Subtract */
  0x51|NSEventModifierFlagNumericPad,	0xBD,  /* KP_Equal */
  0x52|NSEventModifierFlagNumericPad,	0xB0,  /* KP_0 */
  0x53|NSEventModifierFlagNumericPad,	0xB1,  /* KP_1 */
  0x54|NSEventModifierFlagNumericPad,	0xB2,  /* KP_2 */
  0x55|NSEventModifierFlagNumericPad,	0xB3,  /* KP_3 */
  0x56|NSEventModifierFlagNumericPad,	0xB4,  /* KP_4 */
  0x57|NSEventModifierFlagNumericPad,	0xB5,  /* KP_5 */
  0x58|NSEventModifierFlagNumericPad,	0xB6,  /* KP_6 */
  0x59|NSEventModifierFlagNumericPad,	0xB7,  /* KP_7 */
  0x5B|NSEventModifierFlagNumericPad,	0xB8,  /* KP_8 */
  0x5C|NSEventModifierFlagNumericPad,	0xB9,  /* KP_9 */
272

273 274 275
  0x1B,				0x1B   /* escape */
};

276
/* On macOS picks up the default NSGlobalDomain AppleAntiAliasingThreshold,
277
   the maximum font size to NOT antialias.  On GNUstep there is currently
278
   no way to control this behavior.  */
279 280
float ns_antialias_threshold;

281 282
NSArray *ns_send_types = 0, *ns_return_types = 0;
static NSArray *ns_drag_types = 0;
283
NSString *ns_app_name = @"Emacs";  /* default changed later */
284 285

/* Display variables */
286
struct ns_display_info *x_display_list; /* Chain of existing displays */
287 288 289
long context_menu_value = 0;

/* display update */
290 291
static struct frame *ns_updating_frame;
static NSView *focus_view = NULL;
292
static int ns_window_num = 0;
293
static BOOL gsaved = NO;
294
static BOOL ns_fake_keydown = NO;
295
#ifdef NS_IMPL_COCOA
296
static BOOL ns_menu_bar_is_hidden = NO;
297
#endif
298
/* static int debug_lock = 0; */
299 300

/* event loop */
301
static BOOL send_appdefined = YES;
302 303
#define NO_APPDEFINED_DATA (-8)
static int last_appdefined_event_data = NO_APPDEFINED_DATA;
304 305
static NSTimer *timed_entry = 0;
static NSTimer *scroll_repeat_entry = nil;
306 307 308 309 310 311
static fd_set select_readfds, select_writefds;
enum { SELECT_HAVE_READ = 1, SELECT_HAVE_WRITE = 2, SELECT_HAVE_TMO = 4 };
static int select_nfds = 0, select_valid = 0;
static struct timespec select_timeout = { 0, 0 };
static int selfds[2] = { -1, -1 };
static pthread_mutex_t select_mutex;
312 313 314 315 316 317
static NSAutoreleasePool *outerpool;
static struct input_event *emacs_event = NULL;
static struct input_event *q_event_ptr = NULL;
static int n_emacs_events_pending = 0;
static NSMutableArray *ns_pending_files, *ns_pending_service_names,
  *ns_pending_service_args;
318
static BOOL ns_do_open_file = NO;
319
static BOOL ns_last_use_native_fullscreen;
320

321 322 323 324 325
/* Non-zero means that a HELP_EVENT has been generated since Emacs
   start.  */

static BOOL any_help_event_p = NO;

326 327 328 329 330 331 332
static struct {
  struct input_event *q;
  int nr, cap;
} hold_event_q = {
  NULL, 0, 0
};

333
#ifdef NS_IMPL_COCOA
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
/*
 * State for pending menu activation:
 * MENU_NONE     Normal state
 * MENU_PENDING  A menu has been clicked on, but has been canceled so we can
 *               run lisp to update the menu.
 * MENU_OPENING  Menu is up to date, and the click event is redone so the menu
 *               will open.
 */
#define MENU_NONE 0
#define MENU_PENDING 1
#define MENU_OPENING 2
static int menu_will_open_state = MENU_NONE;

/* Saved position for menu click.  */
static CGPoint menu_mouse_point;
349
#endif
350

351
/* Convert modifiers in a NeXTstep event to emacs style modifiers.  */
352
#define NS_FUNCTION_KEY_MASK 0x800000
Alan Third's avatar
Alan Third committed
353 354 355 356 357 358
#define NSLeftControlKeyMask    (0x000001 | NSEventModifierFlagControl)
#define NSRightControlKeyMask   (0x002000 | NSEventModifierFlagControl)
#define NSLeftCommandKeyMask    (0x000008 | NSEventModifierFlagCommand)
#define NSRightCommandKeyMask   (0x000010 | NSEventModifierFlagCommand)
#define NSLeftAlternateKeyMask  (0x000020 | NSEventModifierFlagOption)
#define NSRightAlternateKeyMask (0x000040 | NSEventModifierFlagOption)
Alan Third's avatar
Alan Third committed
359

360 361 362 363 364 365 366 367 368 369 370 371 372
/* MODIFIER if a symbol; otherwise its property KIND, if a symbol.  */
static Lisp_Object
mod_of_kind (Lisp_Object modifier, Lisp_Object kind)
{
  if (SYMBOLP (modifier))
    return modifier;
  else
    {
      Lisp_Object val = Fplist_get (modifier, kind);
      return SYMBOLP (val) ? val : Qnil;
    }
}

Alan Third's avatar
Alan Third committed
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
static unsigned int
ev_modifiers_helper (unsigned int flags, unsigned int left_mask,
                     unsigned int right_mask, unsigned int either_mask,
                     Lisp_Object left_modifier, Lisp_Object right_modifier)
{
  unsigned int modifiers = 0;

  if (flags & either_mask)
    {
      BOOL left_key = (flags & left_mask) == left_mask;
      BOOL right_key = (flags & right_mask) == right_mask
        && ! EQ (right_modifier, Qleft);

      if (right_key)
        modifiers |= parse_solitary_modifier (right_modifier);

      /* GNUstep (and possibly macOS in certain circumstances) doesn't
         differentiate between the left and right keys, so if we can't
         identify which key it is, we use the left key setting.  */
      if (left_key || ! right_key)
        modifiers |= parse_solitary_modifier (left_modifier);
    }

  return modifiers;
}

399
#define EV_MODIFIERS2(flags, kind)                                      \
Alan Third's avatar
Alan Third committed
400 401 402 403
  (((flags & NSEventModifierFlagHelp) ?                                 \
    hyper_modifier : 0)                                                 \
   | ((flags & NSEventModifierFlagShift) ?                              \
      shift_modifier : 0)                                               \
404 405 406 407
   | ((flags & NS_FUNCTION_KEY_MASK)                                    \
      ? parse_solitary_modifier (mod_of_kind (ns_function_modifier,     \
                                              kind))                    \
      : 0)                                                              \
Alan Third's avatar
Alan Third committed
408 409 410
   | ev_modifiers_helper (flags, NSLeftControlKeyMask,                  \
                          NSRightControlKeyMask,                        \
                          NSEventModifierFlagControl,                   \
411 412 413
                          mod_of_kind (ns_control_modifier, kind),      \
                          mod_of_kind (ns_right_control_modifier,       \
                                       kind))                           \
Alan Third's avatar
Alan Third committed
414 415 416
   | ev_modifiers_helper (flags, NSLeftCommandKeyMask,                  \
                          NSRightCommandKeyMask,                        \
                          NSEventModifierFlagCommand,                   \
417 418 419
                          mod_of_kind (ns_command_modifier, kind),      \
                          mod_of_kind (ns_right_command_modifier,       \
                                       kind))                           \
Alan Third's avatar
Alan Third committed
420 421 422
   | ev_modifiers_helper (flags, NSLeftAlternateKeyMask,                \
                          NSRightAlternateKeyMask,                      \
                          NSEventModifierFlagOption,                    \
423 424 425
                          mod_of_kind (ns_alternate_modifier, kind),    \
                          mod_of_kind (ns_right_alternate_modifier,     \
                                       kind)))
Alan Third's avatar
Alan Third committed
426

427
#define EV_MODIFIERS(e) EV_MODIFIERS2 ([e modifierFlags], QCmouse)
428 429

#define EV_UDMODIFIERS(e)                                      \
Alan Third's avatar
Alan Third committed
430 431 432 433 434 435 436 437 438
    ((([e type] == NSEventTypeLeftMouseDown) ? down_modifier : 0)       \
     | (([e type] == NSEventTypeRightMouseDown) ? down_modifier : 0)    \
     | (([e type] == NSEventTypeOtherMouseDown) ? down_modifier : 0)    \
     | (([e type] == NSEventTypeLeftMouseDragged) ? down_modifier : 0)  \
     | (([e type] == NSEventTypeRightMouseDragged) ? down_modifier : 0) \
     | (([e type] == NSEventTypeOtherMouseDragged) ? down_modifier : 0) \
     | (([e type] == NSEventTypeLeftMouseUp)   ? up_modifier   : 0)     \
     | (([e type] == NSEventTypeRightMouseUp)   ? up_modifier   : 0)    \
     | (([e type] == NSEventTypeOtherMouseUp)   ? up_modifier   : 0))
439 440

#define EV_BUTTON(e)                                                         \
Alan Third's avatar
Alan Third committed
441 442
    ((([e type] == NSEventTypeLeftMouseDown) || ([e type] == NSEventTypeLeftMouseUp)) ? 0 :    \
      (([e type] == NSEventTypeRightMouseDown) || ([e type] == NSEventTypeRightMouseUp)) ? 2 : \
443
     [e buttonNumber] - 1)
444

445
/* Convert the time field to a timestamp in milliseconds.  */
446 447 448 449
#define EV_TIMESTAMP(e) ([e timestamp] * 1000)

/* This is a piece of code which is common to all the event handling
   methods.  Maybe it should even be a function.  */
450
#define EV_TRAILER(e)                                                   \
451 452 453 454 455 456 457
  {                                                                     \
    XSETFRAME (emacs_event->frame_or_window, emacsframe);               \
    EV_TRAILER2 (e);                                                    \
  }

#define EV_TRAILER2(e)                                                  \
  {                                                                     \
458 459 460
      if (e) emacs_event->timestamp = EV_TIMESTAMP (e);                 \
      if (q_event_ptr)                                                  \
        {                                                               \
Jan Djärv's avatar
Jan Djärv committed
461 462
          Lisp_Object tem = Vinhibit_quit;                              \
          Vinhibit_quit = Qt;                                           \
463 464
          n_emacs_events_pending++;                                     \
          kbd_buffer_store_event_hold (emacs_event, q_event_ptr);       \
Jan Djärv's avatar
Jan Djärv committed
465
          Vinhibit_quit = tem;                                          \
466 467
        }                                                               \
      else                                                              \
468
        hold_event (emacs_event);                                       \
469 470 471
      EVENT_INIT (*emacs_event);                                        \
      ns_send_appdefined (-1);                                          \
    }
472

Alan Third's avatar
Alan Third committed
473

474
/* These flags will be OR'd or XOR'd with the NSWindow's styleMask
475
   property depending on what we're doing.  */
Alan Third's avatar
Alan Third committed
476 477 478 479 480 481
#define FRAME_DECORATED_FLAGS (NSWindowStyleMaskTitled              \
                               | NSWindowStyleMaskResizable         \
                               | NSWindowStyleMaskMiniaturizable    \
                               | NSWindowStyleMaskClosable)
#define FRAME_UNDECORATED_FLAGS NSWindowStyleMaskBorderless

482
/* TODO: Get rid of need for these forward declarations.  */
483 484
static void ns_condemn_scroll_bars (struct frame *f);
static void ns_judge_scroll_bars (struct frame *f);
485 486 487 488 489 490 491 492


/* ==========================================================================

    Utilities

   ========================================================================== */

493
void
494
ns_init_events (struct input_event *ev)
495 496 497 498 499 500
{
  EVENT_INIT (*ev);
  emacs_event = ev;
}

void
501
ns_finish_events (void)
502 503 504 505
{
  emacs_event = NULL;
}

506 507 508 509 510 511 512
static void
hold_event (struct input_event *event)
{
  if (hold_event_q.nr == hold_event_q.cap)
    {
      if (hold_event_q.cap == 0) hold_event_q.cap = 10;
      else hold_event_q.cap *= 2;
Paul Eggert's avatar
Paul Eggert committed
513 514
      hold_event_q.q =
        xrealloc (hold_event_q.q, hold_event_q.cap * sizeof *hold_event_q.q);
515 516 517
    }

  hold_event_q.q[hold_event_q.nr++] = *event;
518
  /* Make sure ns_read_socket is called, i.e. we have input.  */
519
  raise (SIGIO);
520
  send_appdefined = YES;
521
}
522 523 524 525 526 527 528

static Lisp_Object
append2 (Lisp_Object list, Lisp_Object item)
/* --------------------------------------------------------------------------
   Utility to append to a list
   -------------------------------------------------------------------------- */
{
Paul Eggert's avatar
Paul Eggert committed
529
  return nconc2 (list, list (item));
530 531 532
}


533
const char *
534 535 536
ns_etc_directory (void)
/* If running as a self-contained app bundle, return as a string the
   filename of the etc directory, if present; else nil.  */
537
{
538 539 540 541 542 543 544 545 546 547 548 549
  NSBundle *bundle = [NSBundle mainBundle];
  NSString *resourceDir = [bundle resourcePath];
  NSString *resourcePath;
  NSFileManager *fileManager = [NSFileManager defaultManager];
  BOOL isDir;

  resourcePath = [resourceDir stringByAppendingPathComponent: @"etc"];
  if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
    {
      if (isDir) return [resourcePath UTF8String];
    }
  return NULL;
550 551
}

552 553 554 555 556 557 558 559 560 561 562 563 564 565

const char *
ns_exec_path (void)
/* If running as a self-contained app bundle, return as a path string
   the filenames of the libexec and bin directories, ie libexec:bin.
   Otherwise, return nil.
   Normally, Emacs does not add its own bin/ directory to the PATH.
   However, a self-contained NS build has a different layout, with
   bin/ and libexec/ subdirectories in the directory that contains
   Emacs.app itself.
   We put libexec first, because init_callproc_1 uses the first
   element to initialize exec-directory.  An alternative would be
   for init_callproc to check for invocation-directory/libexec.
*/
566 567
{
  NSBundle *bundle = [NSBundle mainBundle];
568 569
  NSString *resourceDir = [bundle resourcePath];
  NSString *binDir = [bundle bundlePath];
570 571
  NSString *resourcePath, *resourcePaths;
  NSRange range;
572
  NSString *pathSeparator = [NSString stringWithFormat: @"%c", SEPCHAR];
573
  NSFileManager *fileManager = [NSFileManager defaultManager];
574 575
  NSArray *paths;
  NSEnumerator *pathEnum;
576 577 578 579 580
  BOOL isDir;

  range = [resourceDir rangeOfString: @"Contents"];
  if (range.location != NSNotFound)
    {
581
      binDir = [binDir stringByAppendingPathComponent: @"Contents"];
582 583 584 585 586
#ifdef NS_IMPL_COCOA
      binDir = [binDir stringByAppendingPathComponent: @"MacOS"];
#endif
    }

587 588 589 590 591
  paths = [binDir stringsByAppendingPaths:
                [NSArray arrayWithObjects: @"libexec", @"bin", nil]];
  pathEnum = [paths objectEnumerator];
  resourcePaths = @"";

592
  while ((resourcePath = [pathEnum nextObject]))
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609
    {
      if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
        if (isDir)
          {
            if ([resourcePaths length] > 0)
              resourcePaths
                = [resourcePaths stringByAppendingString: pathSeparator];
            resourcePaths
              = [resourcePaths stringByAppendingString: resourcePath];
          }
    }
  if ([resourcePaths length] > 0) return [resourcePaths UTF8String];

  return NULL;
}


610 611 612
const char *
ns_load_path (void)
/* If running as a self-contained app bundle, return as a path string
613 614
   the filenames of the site-lisp and lisp directories.
   Ie, site-lisp:lisp.  Otherwise, return nil.  */
615 616 617 618
{
  NSBundle *bundle = [NSBundle mainBundle];
  NSString *resourceDir = [bundle resourcePath];
  NSString *resourcePath, *resourcePaths;
619
  NSString *pathSeparator = [NSString stringWithFormat: @"%c", SEPCHAR];
620 621
  NSFileManager *fileManager = [NSFileManager defaultManager];
  BOOL isDir;
622 623
  NSArray *paths = [resourceDir stringsByAppendingPaths:
                              [NSArray arrayWithObjects:
624
                                         @"site-lisp", @"lisp", nil]];
625 626
  NSEnumerator *pathEnum = [paths objectEnumerator];
  resourcePaths = @"";
627

628 629 630
  /* Hack to skip site-lisp.  */
  if (no_site_lisp) resourcePath = [pathEnum nextObject];

631
  while ((resourcePath = [pathEnum nextObject]))
632
    {
633 634 635 636 637 638 639 640 641
      if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
        if (isDir)
          {
            if ([resourcePaths length] > 0)
              resourcePaths
                = [resourcePaths stringByAppendingString: pathSeparator];
            resourcePaths
              = [resourcePaths stringByAppendingString: resourcePath];
          }
642
    }
643 644 645
  if ([resourcePaths length] > 0) return [resourcePaths UTF8String];

  return NULL;
646 647 648
}


Alan Third's avatar
Alan Third committed
649 650
void
ns_init_locale (void)
651
/* macOS doesn't set any environment variables for the locale when run
652
   from the GUI. Get the locale from the OS and set LANG.  */
Alan Third's avatar
Alan Third committed
653 654 655 656 657 658 659
{
  NSLocale *locale = [NSLocale currentLocale];

  NSTRACE ("ns_init_locale");

  @try
    {
660
      /* It seems macOS should probably use UTF-8 everywhere.
661 662
         'localeIdentifier' does not specify the encoding, and I can't
         find any way to get the OS to tell us which encoding to use,
663
         so hard-code '.UTF-8'.  */
664 665 666
      NSString *localeID = [NSString stringWithFormat:@"%@.UTF-8",
                                     [locale localeIdentifier]];

667
      /* Set LANG to locale, but not if LANG is already set.  */
668
      setenv("LANG", [localeID UTF8String], 0);
Alan Third's avatar
Alan Third committed
669 670 671 672 673 674 675 676
    }
  @catch (NSException *e)
    {
      NSLog (@"Locale detection failed: %@: %@", [e name], [e reason]);
    }
}


677 678 679 680 681 682 683 684 685 686 687 688 689
void
ns_release_object (void *obj)
/* --------------------------------------------------------------------------
    Release an object (callable from C)
   -------------------------------------------------------------------------- */
{
    [(id)obj release];
}


void
ns_retain_object (void *obj)
/* --------------------------------------------------------------------------
690
     Retain an object (callable from C)
691 692 693 694 695 696 697
   -------------------------------------------------------------------------- */
{
    [(id)obj retain];
}


void *
698
ns_alloc_autorelease_pool (void)
699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
/* --------------------------------------------------------------------------
     Allocate a pool for temporary objects (callable from C)
   -------------------------------------------------------------------------- */
{
  return [[NSAutoreleasePool alloc] init];
}


void
ns_release_autorelease_pool (void *pool)
/* --------------------------------------------------------------------------
     Free a pool and temporary objects it refers to (callable from C)
   -------------------------------------------------------------------------- */
{
  ns_release_object (pool);
}


717 718
static BOOL
ns_menu_bar_should_be_hidden (void)
719
/* True, if the menu bar should be hidden.  */
720 721 722 723 724 725
{
  return !NILP (ns_auto_hide_menu_bar)
    && [NSApp respondsToSelector:@selector(setPresentationOptions:)];
}


726 727 728 729 730 731 732
struct EmacsMargins
{
  CGFloat top;
  CGFloat bottom;
  CGFloat left;
  CGFloat right;
};
733

734 735 736 737

static struct EmacsMargins
ns_screen_margins (NSScreen *screen)
/* The parts of SCREEN used by the operating system.  */
738
{
739 740 741 742 743 744
  NSTRACE ("ns_screen_margins");

  struct EmacsMargins margins;

  NSRect screenFrame = [screen frame];
  NSRect screenVisibleFrame = [screen visibleFrame];
745

746 747
  /* Sometimes, visibleFrame isn't up-to-date with respect to a hidden
     menu bar, check this explicitly.  */
748 749
  if (ns_menu_bar_should_be_hidden())
    {
750
      margins.top = 0;
751 752 753 754 755 756 757
    }
  else
    {
      CGFloat frameTop = screenFrame.origin.y + screenFrame.size.height;
      CGFloat visibleFrameTop = (screenVisibleFrame.origin.y
                                 + screenVisibleFrame.size.height);

758 759 760 761 762 763 764 765 766 767 768 769
      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;
770

771 772 773 774 775 776 777 778 779 780 781
  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
782
   assumed to contain a hidden dock.  macOS currently use 4 pixels for
783 784 785 786 787 788
   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
789
   reserved for a hidden dock.  */
790 791 792 793 794
{
  NSTRACE ("ns_screen_margins_ignoring_hidden_dock");

  struct EmacsMargins margins = ns_screen_margins(screen);

795
  /* macOS (currently) reserved 4 pixels along the edge where a hidden
796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
     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;
    }
812
  /* Note: This doesn't occur in current versions of macOS, but
813 814 815 816
     included for completeness and future compatibility.  */
  if (margins.bottom <= DOCK_IGNORE_LIMIT)
    {
      margins.bottom = 0;
817 818
    }

819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839
  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);

  CGFloat res = margins.top;

840
  NSTRACE ("ns_menu_bar_height " NSTRACE_FMT_RETURN " %.0f", res);
841 842 843 844

  return res;
}

845

846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871
/* Get the frame rect, in system coordinates, of the parent window or,
   if there is no parent window, the main screen.  */
static inline NSRect
ns_parent_window_rect (struct frame *f)
{
  NSRect parentRect;

  if (FRAME_PARENT_FRAME (f) != NULL)
    {
      EmacsView *parentView = FRAME_NS_VIEW (FRAME_PARENT_FRAME (f));
      parentRect = [parentView convertRect:[parentView frame]
                                    toView:nil];
      parentRect = [[parentView window] convertRectToScreen:parentRect];
    }
  else
    parentRect = [[[NSScreen screens] objectAtIndex:0] frame];

  return parentRect;
}

/* Calculate system coordinates of the left and top of the parent
   window or, if there is no parent window, the main screen.  */
#define NS_PARENT_WINDOW_LEFT_POS(f) NSMinX (ns_parent_window_rect (f))
#define NS_PARENT_WINDOW_TOP_POS(f) NSMaxY (ns_parent_window_rect (f))


872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891
static NSRect
ns_row_rect (struct window *w, struct glyph_row *row,
               enum glyph_row_area area)
/* Get the row as an NSRect.  */
{
  NSRect rect;
  int window_x, window_y, window_width;

  window_box (w, area, &window_x, &window_y, &window_width, 0);

  rect.origin.x = window_x;
  rect.origin.y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, row->y));
  rect.origin.y = max (rect.origin.y, window_y);
  rect.size.width = window_width;
  rect.size.height = row->visible_height;

  return rect;
}


892 893 894 895 896 897
/* ==========================================================================

    Focus (clipping) and screen update

   ========================================================================== */

898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939
//
// Window constraining
// -------------------
//
// To ensure that the windows are not placed under the menu bar, they
// are typically moved by the call-back constrainFrameRect. However,
// by overriding it, it's possible to inhibit this, leaving the window
// in it's original position.
//
// It's possible to hide the menu bar. However, technically, it's only
// possible to hide it when the application is active. To ensure that
// this work properly, the menu bar and window constraining are
// deferred until the application becomes active.
//
// Even though it's not possible to manually move a window above the
// top of the screen, it is allowed if it's done programmatically,
// when the menu is hidden. This allows the editable area to cover the
// full screen height.
//
// Test cases
// ----------
//
// Use the following extra files:
//
//    init.el:
//       ;; Hide menu and place frame slightly above the top of the screen.
//       (setq ns-auto-hide-menu-bar t)
//       (set-frame-position (selected-frame) 0 -20)
//
// Test 1:
//
//    emacs -Q -l init.el
//
//    Result: No menu bar, and the title bar should be above the screen.
//
// Test 2:
//
//    emacs -Q
//
//    Result: Menu bar visible, frame placed immediately below the menu.
//

940
static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971
{
  NSTRACE ("constrain_frame_rect(" NSTRACE_FMT_RECT ")",
             NSTRACE_ARG_RECT (frameRect));

  // --------------------
  // Collect information about the screen the frame is covering.
  //

  NSArray *screens = [NSScreen screens];
  NSUInteger nr_screens = [screens count];

  int i;

  // The height of the menu bar, if present in any screen the frame is
  // displayed in.
  int menu_bar_height = 0;

  // A rectangle covering all the screen the frame is displayed in.
  NSRect multiscreenRect = NSMakeRect(0, 0, 0, 0);
  for (i = 0; i < nr_screens; ++i )
    {
      NSScreen *s = [screens objectAtIndex: i];
      NSRect scrRect = [s frame];

      NSTRACE_MSG ("Screen %d: " NSTRACE_FMT_RECT,
                   i, NSTRACE_ARG_RECT (scrRect));

      if (NSIntersectionRect (frameRect, scrRect).size.height != 0)
        {
          multiscreenRect = NSUnionRect (multiscreenRect, scrRect);

972 973 974 975 976
          if (!isFullscreen)
            {
              CGFloat screen_menu_bar_height = ns_menu_bar_height (s);
              menu_bar_height = max(menu_bar_height, screen_menu_bar_height);
            }
977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047
        }
    }

  NSTRACE_RECT ("multiscreenRect", multiscreenRect);

  NSTRACE_MSG ("menu_bar_height: %d", menu_bar_height);

  if (multiscreenRect.size.width == 0
      || multiscreenRect.size.height == 0)
    {
      // Failed to find any monitor, give up.
      NSTRACE_MSG ("multiscreenRect empty");
      NSTRACE_RETURN_RECT (frameRect);
      return frameRect;
    }


  // --------------------
  // Find a suitable placement.
  //

  if (ns_menu_bar_should_be_hidden())
    {
      // When the menu bar is hidden, the user may place part of the
      // frame above the top of the screen, for example to hide the
      // title bar.
      //
      // Hence, keep the original position.
    }
  else
    {
      // Ensure that the frame is below the menu bar, or below the top
      // of the screen.
      //
      // This assume that the menu bar is placed at the top in the
      // rectangle that covers the monitors.  (It doesn't have to be,
      // but if it's not it's hard to do anything useful.)
      CGFloat topOfWorkArea = (multiscreenRect.origin.y
                               + multiscreenRect.size.height
                               - menu_bar_height);

      CGFloat topOfFrame = frameRect.origin.y + frameRect.size.height;
      if (topOfFrame > topOfWorkArea)
        {
          frameRect.origin.y -= topOfFrame - topOfWorkArea;
          NSTRACE_RECT ("After placement adjust", frameRect);
        }
    }

  // Include the following section to restrict frame to the screens.
  // (If so, update it to allow the frame to stretch down below the
  // screen.)
#if 0
  // --------------------
  // Ensure frame doesn't stretch below the screens.
  //

  CGFloat diff = multiscreenRect.origin.y - frameRect.origin.y;

  if (diff > 0)
    {
      frameRect.origin.y = multiscreenRect.origin.y;
      frameRect.size.height -= diff;
    }
#endif

  NSTRACE_RETURN_RECT (frameRect);
  return frameRect;
}


1048 1049
static void
ns_constrain_all_frames (void)
1050 1051 1052
/* --------------------------------------------------------------------------
     Ensure that the menu bar doesn't cover any frames.
   -------------------------------------------------------------------------- */
1053 1054 1055
{
  Lisp_Object tail, frame;

1056 1057
  NSTRACE ("ns_constrain_all_frames");

Anders Lindgren's avatar
Anders Lindgren committed
1058 1059
  block_input ();

1060 1061 1062 1063 1064
  FOR_EACH_FRAME (tail, frame)
    {
      struct frame *f = XFRAME (frame);
      if (FRAME_NS_P (f))
        {
1065 1066 1067 1068 1069
          EmacsView *view = FRAME_NS_VIEW (f);

          if (![view isFullscreen])
            {
              [[view window]
1070
                setFrame:constrain_frame_rect([[view window] frame], false)
1071 1072
                 display:NO];
            }
1073 1074
        }
    }
Anders Lindgren's avatar
Anders Lindgren committed
1075 1076

  unblock_input ();
1077 1078 1079 1080 1081
}


static void
ns_update_auto_hide_menu_bar (void)
1082 1083 1084
/* --------------------------------------------------------------------------
     Show or hide the menu bar, based on user setting.
   -------------------------------------------------------------------------- */
1085
{
1086
#ifdef NS_IMPL_COCOA
1087
  NSTRACE ("ns_update_auto_hide_menu_bar");
1088

1089
  block_input ();
1090

1091
  if (NSApp != nil && [NSApp isActive])
1092 1093 1094 1095 1096 1097 1098 1099
    {
      // Note, "setPresentationOptions" triggers an error unless the
      // application is active.
      BOOL menu_bar_should_be_hidden = ns_menu_bar_should_be_hidden ();

      if (menu_bar_should_be_hidden != ns_menu_bar_is_hidden)
        {
          NSApplicationPresentationOptions options
1100
            = NSApplicationPresentationDefault;
1101 1102

          if (menu_bar_should_be_hidden)
1103 1104
            options |= NSApplicationPresentationAutoHideMenuBar
              | NSApplicationPresentationAutoHideDock;
1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116

          [NSApp setPresentationOptions: options];

          ns_menu_bar_is_hidden = menu_bar_should_be_hidden;

          if (!ns_menu_bar_is_hidden)
            {
              ns_constrain_all_frames ();
            }
        }
    }

1117
  unblock_input ();
1118
#endif
1119 1120 1121
}


1122 1123 1124 1125
static void
ns_update_begin (struct frame *f)
/* --------------------------------------------------------------------------
   Prepare for a grouped sequence of drawing calls
1126
   external (RIF) call; whole frame, called before gui_update_window_begin
1127 1128
   -------------------------------------------------------------------------- */
{
1129
  EmacsView *view = FRAME_NS_VIEW (f);
1130
  NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_begin");
1131

1132 1133
  ns_update_auto_hide_menu_bar ();

1134
#ifdef NS_IMPL_COCOA
1135 1136
  if ([view isFullscreen] && [view fsIsNative])
  {
1137 1138
    // Fix reappearing tool bar in fullscreen for Mac OS X 10.7
    BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (f) ? YES : NO;
1139
    NSToolbar *toolbar = [FRAME_NS_VIEW (f) toolbar];
1140 1141
    if (! tbar_visible != ! [toolbar isVisible])
      [toolbar setVisible: tbar_visible];
1142
  }
1143
#endif
1144 1145

  ns_updating_frame = f;
1146
#ifdef NS_DRAW_TO_BUFFER
1147 1148 1149
#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
  if ([FRAME_NS_VIEW (f) wantsUpdateLayer])
    {
1150
#endif
1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165
      [view focusOnDrawingBuffer];
#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
    }
  else
    {
#endif
#endif /* NS_DRAW_TO_BUFFER */

#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400
      [view lockFocus];
#endif
#if defined (NS_DRAW_TO_BUFFER) && MAC_OS_X_VERSION_MIN_REQUIRED < 101400
    }
#endif

1166 1167 1168 1169 1170 1171 1172
}


static void
ns_update_end (struct frame *f)
/* --------------------------------------------------------------------------
   Finished a grouped sequence of drawing calls
1173
   external (RIF) call; for whole frame, called after gui_update_window_end
1174 1175
   -------------------------------------------------------------------------- */
{
1176 1177
  EmacsView *view = FRAME_NS_VIEW (f);

1178
  NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_end");
1179

1180
/*   if (f == MOUSE_HL_INFO (f)->mouse_face_mouse_frame) */
1181
  MOUSE_HL_INFO (f)->mouse_face_defer = 0;
1182

1183
#ifdef NS_DRAW_TO_BUFFER
1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195
#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
  if ([FRAME_NS_VIEW (f) wantsUpdateLayer])
    {
#endif
      [NSGraphicsContext setCurrentContext:nil];
      [view setNeedsDisplay:YES];
#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
    }
  else
    {
#endif
#endif /* NS_DRAW_TO_BUFFER */
1196

1197 1198
#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400
      block_input ();
1199

1200 1201 1202 1203 1204 1205 1206
      [view unlockFocus];
      [[view window] flushWindow];

      unblock_input ();
#endif
#if defined (NS_DRAW_TO_BUFFER) && MAC_OS_X_VERSION_MIN_REQUIRED < 101400
    }
Alan Third's avatar
Alan Third committed
1207
#endif
1208 1209 1210 1211 1212
  ns_updating_frame = NULL;
}

static void
ns_focus (struct frame *f, NSRect *r, int n)
1213
/* --------------------------------------------------------------------------
1214 1215 1216 1217 1218 1219
   Internal: Focus on given frame.  During small local updates this is used to
     draw, however during large updates, ns_update_begin and ns_update_end are
     called to wrap the whole thing, in which case these calls are stubbed out.
     Except, on GNUstep, we accumulate the rectangle being drawn into, because
     the back end won't do this automatically, and will just end up flushing
     the entire window.
1220 1221
   -------------------------------------------------------------------------- */
{
Alan Third's avatar
Alan Third committed
1222 1223
  EmacsView *view = FRAME_NS_VIEW (f);

1224 1225
  NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_focus");
  if (r != NULL)
1226 1227
    {
      NSTRACE_RECT ("r", *r);
1228
    }
1229

1230 1231
  if (f != ns_updating_frame)
    {
1232 1233 1234
#ifdef NS_DRAW_TO_BUFFER
#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
      if ([FRAME_NS_VIEW (f) wantsUpdateLayer])
1235
        {
1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246
#endif
          [view focusOnDrawingBuffer];
#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
        }
      else
        {
#endif
#endif /* NS_DRAW_TO_BUFFER */

#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400
          if (view != focus_view)
1247
            {
1248 1249 1250 1251 1252
              if (focus_view != NULL)
                {
                  [focus_view unlockFocus];
                  [[focus_view window] flushWindow];
                }
1253

1254 1255 1256 1257 1258 1259
              if (view)
                [view lockFocus];
              focus_view = view;
            }
#endif
#if defined (NS_DRAW_TO_BUFFER) && MAC_OS_X_VERSION_MIN_REQUIRED < 101400
1260
        }
Alan Third's avatar
Alan Third committed
1261
#endif
1262 1263
    }

1264

1265 1266 1267 1268 1269 1270 1271 1272 1273 1274
  /* clipping */
  if (r)
    {
      [[NSGraphicsContext currentContext] saveGraphicsState];
      if (n == 2)
        NSRectClipList (r, 2);
      else
        NSRectClip (*r);
      gsaved = YES;
    }
1275 1276 1277 1278
}


static void
1279 1280 1281 1282
ns_unfocus (struct frame *f)
/* --------------------------------------------------------------------------
     Internal: Remove focus on given frame
   -------------------------------------------------------------------------- */
1283
{
1284
  NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_unfocus");
1285

1286 1287 1288 1289 1290
  if (gsaved)
    {
      [[NSGraphicsContext currentContext] restoreGraphicsState];
      gsaved = NO;
    }
1291

1292
#ifdef NS_DRAW_TO_BUFFER
1293 1294 1295 1296 1297 1298 1299 1300
  #if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
  if ([FRAME_NS_VIEW (f) wantsUpdateLayer])
    {
#endif
      [FRAME_NS_VIEW (f) setNeedsDisplay:YES];
#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
    }
  else
1301
    {
1302 1303 1304 1305 1306
#endif
#endif /* NS_DRAW_TO_BUFFER */

#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400
      if (f != ns_updating_frame)
1307
        {
1308 1309 1310 1311 1312 1313
          if (focus_view != NULL)
            {
              [focus_view unlockFocus];
              [[focus_view window] flushWindow];
              focus_view = NULL;
            }
1314
        }
1315 1316
#endif
#if defined (NS_DRAW_TO_BUFFER) && MAC_OS_X_VERSION_MIN_REQUIRED < 101400
1317
    }
Alan Third's avatar
Alan Third committed
1318
#endif
1319 1320 1321
}


1322 1323
/* ==========================================================================

Paul Eggert's avatar
Paul Eggert committed
1324
    Visible bell and beep.
1325 1326 1327 1328

   ========================================================================== */


1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348
// 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

1349 1350
@interface EmacsBell : NSImageView
{
1351
  // Number of currently active bells.
1352
  unsigned int nestCount;
1353
  NSView * mView;
1354
  bool isAttached;
1355 1356 1357
}
- (void)show:(NSView *)view;
- (void)hide;
1358
- (void)remove;
1359 1360 1361 1362
@end

@implementation EmacsBell

1363
- (id)init
1364
{
Anders Lindgren's avatar
Anders Lindgren committed
1365
  NSTRACE ("[EmacsBell init]");
1366 1367 1368
  if ((self = [super init]))
    {
      nestCount = 0;
1369
      isAttached = false;
Anders Lindgren's avatar
Anders Lindgren committed
1370 1371 1372 1373
#ifdef NS_IMPL_GNUSTEP
      // GNUstep doesn't provide named images.  This was reported in
      // 2011, see https://savannah.gnu.org/bugs/?33396
      //
Paul Eggert's avatar
Paul Eggert committed
1374
      // As a drop in replacement, a semitransparent gray square is used.
1375
      self.image = [[NSImage alloc] initWithSize:NSMakeSize(32 * 5, 32 * 5)];
Anders Lindgren's avatar
Anders Lindgren committed
1376 1377 1378 1379 1380
      [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
1381
      self.image = [NSImage imageNamed:NSImageNameCaution];
1382 1383
      [self.image setSize:NSMakeSize(self.image.size.width * 5,
                                     self.image.size.height * 5)];
Anders Lindgren's avatar
Anders Lindgren committed
1384
#endif
1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404
    }
  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];

1405
      isAttached = true;
1406
      mView = view;
1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422
      [[[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]");

1423 1424
  if (nestCount > 0)
    --nestCount;
1425 1426 1427

  // Remove the image once the last bell became inactive.
  if (nestCount == 0)
1428 1429 1430 1431 1432 1433 1434 1435
    {
      [self remove];
    }
}


-(void)remove
{
1436
  NSTRACE ("[EmacsBell remove]");
1437
  if (isAttached)
1438
    {
1439
      NSTRACE_MSG ("removeFromSuperview");
1440
      [self removeFromSuperview];