Allow 'browse-url-emacs' to fetch URL in the selected window
[emacs.git] / src / nsterm.m
blob75e0b837c6770d320d6394ac1dc20eb79c503358
1 /* NeXT/Open/GNUstep / macOS communication module.      -*- coding: utf-8 -*-
3 Copyright (C) 1989, 1993-1994, 2005-2006, 2008-2018 Free Software
4 Foundation, Inc.
6 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 the Free Software Foundation, either version 3 of the License, or (at
11 your option) any later version.
13 GNU Emacs is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
18 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/>.  */
22 Originally by Carl Edman
23 Updated by Christian Limpach (chris@nice.ch)
24 OpenStep/Rhapsody port by Scott Bender (sbender@harmony-ds.com)
25 macOS/Aqua port by Christophe de Dinechin (descubes@earthlink.net)
26 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. */
31 #include <config.h>
33 #include <fcntl.h>
34 #include <math.h>
35 #include <pthread.h>
36 #include <sys/types.h>
37 #include <time.h>
38 #include <signal.h>
39 #include <unistd.h>
40 #include <stdbool.h>
42 #include <c-ctype.h>
43 #include <c-strcase.h>
44 #include <ftoastr.h>
46 #include "lisp.h"
47 #include "blockinput.h"
48 #include "sysselect.h"
49 #include "nsterm.h"
50 #include "systime.h"
51 #include "character.h"
52 #include "fontset.h"
53 #include "composite.h"
54 #include "ccl.h"
56 #include "termhooks.h"
57 #include "termchar.h"
58 #include "menu.h"
59 #include "window.h"
60 #include "keyboard.h"
61 #include "buffer.h"
62 #include "font.h"
64 #ifdef NS_IMPL_GNUSTEP
65 #include "process.h"
66 #endif
68 #ifdef NS_IMPL_COCOA
69 #include "macfont.h"
70 #include <Carbon/Carbon.h>
71 #endif
73 static EmacsMenu *dockMenu;
74 #ifdef NS_IMPL_COCOA
75 static EmacsMenu *mainMenu;
76 #endif
78 /* ==========================================================================
80    NSTRACE, Trace support.
82    ========================================================================== */
84 #if NSTRACE_ENABLED
86 /* The following use "volatile" since they can be accessed from
87    parallel threads. */
88 volatile int nstrace_num = 0;
89 volatile int nstrace_depth = 0;
91 /* When 0, no trace is emitted.  This is used by NSTRACE_WHEN and
92    NSTRACE_UNLESS to silence functions called.
94    TODO: This should really be a thread-local variable, to avoid that
95    a function with disabled trace thread silence trace output in
96    another.  However, in practice this seldom is a problem. */
97 volatile int nstrace_enabled_global = 1;
99 /* Called when nstrace_enabled goes out of scope. */
100 void nstrace_leave(int * pointer_to_nstrace_enabled)
102   if (*pointer_to_nstrace_enabled)
103     {
104       --nstrace_depth;
105     }
109 /* Called when nstrace_saved_enabled_global goes out of scope. */
110 void nstrace_restore_global_trace_state(int * pointer_to_saved_enabled_global)
112   nstrace_enabled_global = *pointer_to_saved_enabled_global;
116 char const * nstrace_fullscreen_type_name (int fs_type)
118   switch (fs_type)
119     {
120     case -1:                   return "-1";
121     case FULLSCREEN_NONE:      return "FULLSCREEN_NONE";
122     case FULLSCREEN_WIDTH:     return "FULLSCREEN_WIDTH";
123     case FULLSCREEN_HEIGHT:    return "FULLSCREEN_HEIGHT";
124     case FULLSCREEN_BOTH:      return "FULLSCREEN_BOTH";
125     case FULLSCREEN_MAXIMIZED: return "FULLSCREEN_MAXIMIZED";
126     default:                   return "FULLSCREEN_?????";
127     }
129 #endif
132 /* ==========================================================================
134    NSColor, EmacsColor category.
136    ========================================================================== */
137 @implementation NSColor (EmacsColor)
138 + (NSColor *)colorForEmacsRed:(CGFloat)red green:(CGFloat)green
139                          blue:(CGFloat)blue alpha:(CGFloat)alpha
141 #if defined (NS_IMPL_COCOA) \
142   && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
143   if (ns_use_srgb_colorspace
144 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
145       && [NSColor respondsToSelector:
146                     @selector(colorWithSRGBRed:green:blue:alpha:)]
147 #endif
148       )
149     return [NSColor colorWithSRGBRed: red
150                                green: green
151                                 blue: blue
152                                alpha: alpha];
153 #endif
154   return [NSColor colorWithCalibratedRed: red
155                                    green: green
156                                     blue: blue
157                                    alpha: alpha];
160 - (NSColor *)colorUsingDefaultColorSpace
162   /* FIXMES: We're checking for colorWithSRGBRed here so this will
163      only work in the same place as in the method above.  It should
164      really be a check whether we're on macOS 10.7 or above. */
165 #if defined (NS_IMPL_COCOA) \
166   && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
167   if (ns_use_srgb_colorspace
168 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
169       && [NSColor respondsToSelector:
170                     @selector(colorWithSRGBRed:green:blue:alpha:)]
171 #endif
172       )
173     return [self colorUsingColorSpace: [NSColorSpace sRGBColorSpace]];
174 #endif
175   return [self colorUsingColorSpaceName: NSCalibratedRGBColorSpace];
178 @end
180 /* ==========================================================================
182     Local declarations
184    ========================================================================== */
186 /* Convert a symbol indexed with an NSxxx value to a value as defined
187    in keyboard.c (lispy_function_key). I hope this is a correct way
188    of doing things... */
189 static unsigned convert_ns_to_X_keysym[] =
191   NSHomeFunctionKey,            0x50,
192   NSLeftArrowFunctionKey,       0x51,
193   NSUpArrowFunctionKey,         0x52,
194   NSRightArrowFunctionKey,      0x53,
195   NSDownArrowFunctionKey,       0x54,
196   NSPageUpFunctionKey,          0x55,
197   NSPageDownFunctionKey,        0x56,
198   NSEndFunctionKey,             0x57,
199   NSBeginFunctionKey,           0x58,
200   NSSelectFunctionKey,          0x60,
201   NSPrintFunctionKey,           0x61,
202   NSClearLineFunctionKey,       0x0B,
203   NSExecuteFunctionKey,         0x62,
204   NSInsertFunctionKey,          0x63,
205   NSUndoFunctionKey,            0x65,
206   NSRedoFunctionKey,            0x66,
207   NSMenuFunctionKey,            0x67,
208   NSFindFunctionKey,            0x68,
209   NSHelpFunctionKey,            0x6A,
210   NSBreakFunctionKey,           0x6B,
212   NSF1FunctionKey,              0xBE,
213   NSF2FunctionKey,              0xBF,
214   NSF3FunctionKey,              0xC0,
215   NSF4FunctionKey,              0xC1,
216   NSF5FunctionKey,              0xC2,
217   NSF6FunctionKey,              0xC3,
218   NSF7FunctionKey,              0xC4,
219   NSF8FunctionKey,              0xC5,
220   NSF9FunctionKey,              0xC6,
221   NSF10FunctionKey,             0xC7,
222   NSF11FunctionKey,             0xC8,
223   NSF12FunctionKey,             0xC9,
224   NSF13FunctionKey,             0xCA,
225   NSF14FunctionKey,             0xCB,
226   NSF15FunctionKey,             0xCC,
227   NSF16FunctionKey,             0xCD,
228   NSF17FunctionKey,             0xCE,
229   NSF18FunctionKey,             0xCF,
230   NSF19FunctionKey,             0xD0,
231   NSF20FunctionKey,             0xD1,
232   NSF21FunctionKey,             0xD2,
233   NSF22FunctionKey,             0xD3,
234   NSF23FunctionKey,             0xD4,
235   NSF24FunctionKey,             0xD5,
237   NSBackspaceCharacter,         0x08,  /* 8: Not on some KBs. */
238   NSDeleteCharacter,            0xFF,  /* 127: Big 'delete' key upper right. */
239   NSDeleteFunctionKey,          0x9F,  /* 63272: Del forw key off main array. */
241   NSTabCharacter,               0x09,
242   0x19,                         0x09,  /* left tab->regular since pass shift */
243   NSCarriageReturnCharacter,    0x0D,
244   NSNewlineCharacter,           0x0D,
245   NSEnterCharacter,             0x8D,
247   0x41|NSEventModifierFlagNumericPad,   0xAE,  /* KP_Decimal */
248   0x43|NSEventModifierFlagNumericPad,   0xAA,  /* KP_Multiply */
249   0x45|NSEventModifierFlagNumericPad,   0xAB,  /* KP_Add */
250   0x4B|NSEventModifierFlagNumericPad,   0xAF,  /* KP_Divide */
251   0x4E|NSEventModifierFlagNumericPad,   0xAD,  /* KP_Subtract */
252   0x51|NSEventModifierFlagNumericPad,   0xBD,  /* KP_Equal */
253   0x52|NSEventModifierFlagNumericPad,   0xB0,  /* KP_0 */
254   0x53|NSEventModifierFlagNumericPad,   0xB1,  /* KP_1 */
255   0x54|NSEventModifierFlagNumericPad,   0xB2,  /* KP_2 */
256   0x55|NSEventModifierFlagNumericPad,   0xB3,  /* KP_3 */
257   0x56|NSEventModifierFlagNumericPad,   0xB4,  /* KP_4 */
258   0x57|NSEventModifierFlagNumericPad,   0xB5,  /* KP_5 */
259   0x58|NSEventModifierFlagNumericPad,   0xB6,  /* KP_6 */
260   0x59|NSEventModifierFlagNumericPad,   0xB7,  /* KP_7 */
261   0x5B|NSEventModifierFlagNumericPad,   0xB8,  /* KP_8 */
262   0x5C|NSEventModifierFlagNumericPad,   0xB9,  /* KP_9 */
264   0x1B,                         0x1B   /* escape */
267 /* On macOS picks up the default NSGlobalDomain AppleAntiAliasingThreshold,
268    the maximum font size to NOT antialias.  On GNUstep there is currently
269    no way to control this behavior. */
270 float ns_antialias_threshold;
272 NSArray *ns_send_types = 0, *ns_return_types = 0;
273 static NSArray *ns_drag_types = 0;
274 NSString *ns_app_name = @"Emacs";  /* default changed later */
276 /* Display variables */
277 struct ns_display_info *x_display_list; /* Chain of existing displays */
278 long context_menu_value = 0;
280 /* display update */
281 static struct frame *ns_updating_frame;
282 static NSView *focus_view = NULL;
283 static int ns_window_num = 0;
284 #ifdef NS_IMPL_GNUSTEP
285 static NSRect uRect;            // TODO: This is dead, remove it?
286 #endif
287 static BOOL gsaved = NO;
288 static BOOL ns_fake_keydown = NO;
289 #ifdef NS_IMPL_COCOA
290 static BOOL ns_menu_bar_is_hidden = NO;
291 #endif
292 /*static int debug_lock = 0; */
294 /* event loop */
295 static BOOL send_appdefined = YES;
296 #define NO_APPDEFINED_DATA (-8)
297 static int last_appdefined_event_data = NO_APPDEFINED_DATA;
298 static NSTimer *timed_entry = 0;
299 static NSTimer *scroll_repeat_entry = nil;
300 static fd_set select_readfds, select_writefds;
301 enum { SELECT_HAVE_READ = 1, SELECT_HAVE_WRITE = 2, SELECT_HAVE_TMO = 4 };
302 static int select_nfds = 0, select_valid = 0;
303 static struct timespec select_timeout = { 0, 0 };
304 static int selfds[2] = { -1, -1 };
305 static pthread_mutex_t select_mutex;
306 static NSAutoreleasePool *outerpool;
307 static struct input_event *emacs_event = NULL;
308 static struct input_event *q_event_ptr = NULL;
309 static int n_emacs_events_pending = 0;
310 static NSMutableArray *ns_pending_files, *ns_pending_service_names,
311   *ns_pending_service_args;
312 static BOOL ns_do_open_file = NO;
313 static BOOL ns_last_use_native_fullscreen;
315 /* Non-zero means that a HELP_EVENT has been generated since Emacs
316    start.  */
318 static BOOL any_help_event_p = NO;
320 static struct {
321   struct input_event *q;
322   int nr, cap;
323 } hold_event_q = {
324   NULL, 0, 0
327 static NSString *represented_filename = nil;
328 static struct frame *represented_frame = 0;
330 #ifdef NS_IMPL_COCOA
332  * State for pending menu activation:
333  * MENU_NONE     Normal state
334  * MENU_PENDING  A menu has been clicked on, but has been canceled so we can
335  *               run lisp to update the menu.
336  * MENU_OPENING  Menu is up to date, and the click event is redone so the menu
337  *               will open.
338  */
339 #define MENU_NONE 0
340 #define MENU_PENDING 1
341 #define MENU_OPENING 2
342 static int menu_will_open_state = MENU_NONE;
344 /* Saved position for menu click.  */
345 static CGPoint menu_mouse_point;
346 #endif
348 /* Convert modifiers in a NeXTstep event to emacs style modifiers.  */
349 #define NS_FUNCTION_KEY_MASK 0x800000
350 #define NSLeftControlKeyMask    (0x000001 | NSEventModifierFlagControl)
351 #define NSRightControlKeyMask   (0x002000 | NSEventModifierFlagControl)
352 #define NSLeftCommandKeyMask    (0x000008 | NSEventModifierFlagCommand)
353 #define NSRightCommandKeyMask   (0x000010 | NSEventModifierFlagCommand)
354 #define NSLeftAlternateKeyMask  (0x000020 | NSEventModifierFlagOption)
355 #define NSRightAlternateKeyMask (0x000040 | NSEventModifierFlagOption)
357 static unsigned int
358 ev_modifiers_helper (unsigned int flags, unsigned int left_mask,
359                      unsigned int right_mask, unsigned int either_mask,
360                      Lisp_Object left_modifier, Lisp_Object right_modifier)
362   unsigned int modifiers = 0;
364   if (flags & either_mask)
365     {
366       BOOL left_key = (flags & left_mask) == left_mask;
367       BOOL right_key = (flags & right_mask) == right_mask
368         && ! EQ (right_modifier, Qleft);
370       if (right_key)
371         modifiers |= parse_solitary_modifier (right_modifier);
373       /* GNUstep (and possibly macOS in certain circumstances) doesn't
374          differentiate between the left and right keys, so if we can't
375          identify which key it is, we use the left key setting.  */
376       if (left_key || ! right_key)
377         modifiers |= parse_solitary_modifier (left_modifier);
378     }
380   return modifiers;
383 #define EV_MODIFIERS2(flags)                                            \
384   (((flags & NSEventModifierFlagHelp) ?                                 \
385     hyper_modifier : 0)                                                 \
386    | ((flags & NSEventModifierFlagShift) ?                              \
387       shift_modifier : 0)                                               \
388    | ((flags & NS_FUNCTION_KEY_MASK) ?                                  \
389       parse_solitary_modifier (ns_function_modifier) : 0)               \
390    | ev_modifiers_helper (flags, NSLeftControlKeyMask,                  \
391                           NSRightControlKeyMask,                        \
392                           NSEventModifierFlagControl,                   \
393                           ns_control_modifier,                          \
394                           ns_right_control_modifier)                    \
395    | ev_modifiers_helper (flags, NSLeftCommandKeyMask,                  \
396                           NSRightCommandKeyMask,                        \
397                           NSEventModifierFlagCommand,                   \
398                           ns_command_modifier,                          \
399                           ns_right_command_modifier)                    \
400    | ev_modifiers_helper (flags, NSLeftAlternateKeyMask,                \
401                           NSRightAlternateKeyMask,                      \
402                           NSEventModifierFlagOption,                    \
403                           ns_alternate_modifier,                        \
404                           ns_right_alternate_modifier))
406 #define EV_MODIFIERS(e) EV_MODIFIERS2 ([e modifierFlags])
408 #define EV_UDMODIFIERS(e)                                      \
409     ((([e type] == NSEventTypeLeftMouseDown) ? down_modifier : 0)       \
410      | (([e type] == NSEventTypeRightMouseDown) ? down_modifier : 0)    \
411      | (([e type] == NSEventTypeOtherMouseDown) ? down_modifier : 0)    \
412      | (([e type] == NSEventTypeLeftMouseDragged) ? down_modifier : 0)  \
413      | (([e type] == NSEventTypeRightMouseDragged) ? down_modifier : 0) \
414      | (([e type] == NSEventTypeOtherMouseDragged) ? down_modifier : 0) \
415      | (([e type] == NSEventTypeLeftMouseUp)   ? up_modifier   : 0)     \
416      | (([e type] == NSEventTypeRightMouseUp)   ? up_modifier   : 0)    \
417      | (([e type] == NSEventTypeOtherMouseUp)   ? up_modifier   : 0))
419 #define EV_BUTTON(e)                                                         \
420     ((([e type] == NSEventTypeLeftMouseDown) || ([e type] == NSEventTypeLeftMouseUp)) ? 0 :    \
421       (([e type] == NSEventTypeRightMouseDown) || ([e type] == NSEventTypeRightMouseUp)) ? 2 : \
422      [e buttonNumber] - 1)
424 /* Convert the time field to a timestamp in milliseconds. */
425 #define EV_TIMESTAMP(e) ([e timestamp] * 1000)
427 /* This is a piece of code which is common to all the event handling
428    methods.  Maybe it should even be a function.  */
429 #define EV_TRAILER(e)                                                   \
430   {                                                                     \
431     XSETFRAME (emacs_event->frame_or_window, emacsframe);               \
432     EV_TRAILER2 (e);                                                    \
433   }
435 #define EV_TRAILER2(e)                                                  \
436   {                                                                     \
437       if (e) emacs_event->timestamp = EV_TIMESTAMP (e);                 \
438       if (q_event_ptr)                                                  \
439         {                                                               \
440           Lisp_Object tem = Vinhibit_quit;                              \
441           Vinhibit_quit = Qt;                                           \
442           n_emacs_events_pending++;                                     \
443           kbd_buffer_store_event_hold (emacs_event, q_event_ptr);       \
444           Vinhibit_quit = tem;                                          \
445         }                                                               \
446       else                                                              \
447         hold_event (emacs_event);                                       \
448       EVENT_INIT (*emacs_event);                                        \
449       ns_send_appdefined (-1);                                          \
450     }
453 /* These flags will be OR'd or XOR'd with the NSWindow's styleMask
454    property depending on what we're doing. */
455 #define FRAME_DECORATED_FLAGS (NSWindowStyleMaskTitled              \
456                                | NSWindowStyleMaskResizable         \
457                                | NSWindowStyleMaskMiniaturizable    \
458                                | NSWindowStyleMaskClosable)
459 #define FRAME_UNDECORATED_FLAGS NSWindowStyleMaskBorderless
461 /* TODO: get rid of need for these forward declarations */
462 static void ns_condemn_scroll_bars (struct frame *f);
463 static void ns_judge_scroll_bars (struct frame *f);
466 /* ==========================================================================
468     Utilities
470    ========================================================================== */
472 void
473 ns_set_represented_filename (struct frame *f)
475   Lisp_Object filename, encoded_filename;
476   Lisp_Object buf = XWINDOW (f->selected_window)->contents;
477   NSAutoreleasePool *pool;
478   NSString *fstr;
480   NSTRACE ("ns_set_represented_filename");
482   if (f->explicit_name || ! NILP (f->title))
483     return;
485   block_input ();
486   pool = [[NSAutoreleasePool alloc] init];
487   filename = BVAR (XBUFFER (buf), filename);
489   if (! NILP (filename))
490     {
491       encoded_filename = ENCODE_UTF_8 (filename);
493       fstr = [NSString stringWithUTF8String: SSDATA (encoded_filename)];
494       if (fstr == nil) fstr = @"";
495     }
496   else
497     fstr = @"";
499   represented_filename = [fstr retain];
500   represented_frame = f;
502   [pool release];
503   unblock_input ();
506 void
507 ns_init_events (struct input_event *ev)
509   EVENT_INIT (*ev);
510   emacs_event = ev;
513 void
514 ns_finish_events (void)
516   emacs_event = NULL;
519 static void
520 hold_event (struct input_event *event)
522   if (hold_event_q.nr == hold_event_q.cap)
523     {
524       if (hold_event_q.cap == 0) hold_event_q.cap = 10;
525       else hold_event_q.cap *= 2;
526       hold_event_q.q =
527         xrealloc (hold_event_q.q, hold_event_q.cap * sizeof *hold_event_q.q);
528     }
530   hold_event_q.q[hold_event_q.nr++] = *event;
531   /* Make sure ns_read_socket is called, i.e. we have input.  */
532   raise (SIGIO);
533   send_appdefined = YES;
536 static Lisp_Object
537 append2 (Lisp_Object list, Lisp_Object item)
538 /* --------------------------------------------------------------------------
539    Utility to append to a list
540    -------------------------------------------------------------------------- */
542   return CALLN (Fnconc, list, list1 (item));
546 const char *
547 ns_etc_directory (void)
548 /* If running as a self-contained app bundle, return as a string the
549    filename of the etc directory, if present; else nil.  */
551   NSBundle *bundle = [NSBundle mainBundle];
552   NSString *resourceDir = [bundle resourcePath];
553   NSString *resourcePath;
554   NSFileManager *fileManager = [NSFileManager defaultManager];
555   BOOL isDir;
557   resourcePath = [resourceDir stringByAppendingPathComponent: @"etc"];
558   if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
559     {
560       if (isDir) return [resourcePath UTF8String];
561     }
562   return NULL;
566 const char *
567 ns_exec_path (void)
568 /* If running as a self-contained app bundle, return as a path string
569    the filenames of the libexec and bin directories, ie libexec:bin.
570    Otherwise, return nil.
571    Normally, Emacs does not add its own bin/ directory to the PATH.
572    However, a self-contained NS build has a different layout, with
573    bin/ and libexec/ subdirectories in the directory that contains
574    Emacs.app itself.
575    We put libexec first, because init_callproc_1 uses the first
576    element to initialize exec-directory.  An alternative would be
577    for init_callproc to check for invocation-directory/libexec.
580   NSBundle *bundle = [NSBundle mainBundle];
581   NSString *resourceDir = [bundle resourcePath];
582   NSString *binDir = [bundle bundlePath];
583   NSString *resourcePath, *resourcePaths;
584   NSRange range;
585   NSString *pathSeparator = [NSString stringWithFormat: @"%c", SEPCHAR];
586   NSFileManager *fileManager = [NSFileManager defaultManager];
587   NSArray *paths;
588   NSEnumerator *pathEnum;
589   BOOL isDir;
591   range = [resourceDir rangeOfString: @"Contents"];
592   if (range.location != NSNotFound)
593     {
594       binDir = [binDir stringByAppendingPathComponent: @"Contents"];
595 #ifdef NS_IMPL_COCOA
596       binDir = [binDir stringByAppendingPathComponent: @"MacOS"];
597 #endif
598     }
600   paths = [binDir stringsByAppendingPaths:
601                 [NSArray arrayWithObjects: @"libexec", @"bin", nil]];
602   pathEnum = [paths objectEnumerator];
603   resourcePaths = @"";
605   while ((resourcePath = [pathEnum nextObject]))
606     {
607       if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
608         if (isDir)
609           {
610             if ([resourcePaths length] > 0)
611               resourcePaths
612                 = [resourcePaths stringByAppendingString: pathSeparator];
613             resourcePaths
614               = [resourcePaths stringByAppendingString: resourcePath];
615           }
616     }
617   if ([resourcePaths length] > 0) return [resourcePaths UTF8String];
619   return NULL;
623 const char *
624 ns_load_path (void)
625 /* If running as a self-contained app bundle, return as a path string
626    the filenames of the site-lisp and lisp directories.
627    Ie, site-lisp:lisp.  Otherwise, return nil.  */
629   NSBundle *bundle = [NSBundle mainBundle];
630   NSString *resourceDir = [bundle resourcePath];
631   NSString *resourcePath, *resourcePaths;
632   NSString *pathSeparator = [NSString stringWithFormat: @"%c", SEPCHAR];
633   NSFileManager *fileManager = [NSFileManager defaultManager];
634   BOOL isDir;
635   NSArray *paths = [resourceDir stringsByAppendingPaths:
636                               [NSArray arrayWithObjects:
637                                          @"site-lisp", @"lisp", nil]];
638   NSEnumerator *pathEnum = [paths objectEnumerator];
639   resourcePaths = @"";
641   /* Hack to skip site-lisp.  */
642   if (no_site_lisp) resourcePath = [pathEnum nextObject];
644   while ((resourcePath = [pathEnum nextObject]))
645     {
646       if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
647         if (isDir)
648           {
649             if ([resourcePaths length] > 0)
650               resourcePaths
651                 = [resourcePaths stringByAppendingString: pathSeparator];
652             resourcePaths
653               = [resourcePaths stringByAppendingString: resourcePath];
654           }
655     }
656   if ([resourcePaths length] > 0) return [resourcePaths UTF8String];
658   return NULL;
662 void
663 ns_init_locale (void)
664 /* macOS doesn't set any environment variables for the locale when run
665    from the GUI. Get the locale from the OS and set LANG. */
667   NSLocale *locale = [NSLocale currentLocale];
669   NSTRACE ("ns_init_locale");
671   @try
672     {
673       /* It seems macOS should probably use UTF-8 everywhere.
674          'localeIdentifier' does not specify the encoding, and I can't
675          find any way to get the OS to tell us which encoding to use,
676          so hard-code '.UTF-8'. */
677       NSString *localeID = [NSString stringWithFormat:@"%@.UTF-8",
678                                      [locale localeIdentifier]];
680       /* Set LANG to locale, but not if LANG is already set. */
681       setenv("LANG", [localeID UTF8String], 0);
682     }
683   @catch (NSException *e)
684     {
685       NSLog (@"Locale detection failed: %@: %@", [e name], [e reason]);
686     }
690 void
691 ns_release_object (void *obj)
692 /* --------------------------------------------------------------------------
693     Release an object (callable from C)
694    -------------------------------------------------------------------------- */
696     [(id)obj release];
700 void
701 ns_retain_object (void *obj)
702 /* --------------------------------------------------------------------------
703     Retain an object (callable from C)
704    -------------------------------------------------------------------------- */
706     [(id)obj retain];
710 void *
711 ns_alloc_autorelease_pool (void)
712 /* --------------------------------------------------------------------------
713      Allocate a pool for temporary objects (callable from C)
714    -------------------------------------------------------------------------- */
716   return [[NSAutoreleasePool alloc] init];
720 void
721 ns_release_autorelease_pool (void *pool)
722 /* --------------------------------------------------------------------------
723      Free a pool and temporary objects it refers to (callable from C)
724    -------------------------------------------------------------------------- */
726   ns_release_object (pool);
730 static BOOL
731 ns_menu_bar_should_be_hidden (void)
732 /* True, if the menu bar should be hidden.  */
734   return !NILP (ns_auto_hide_menu_bar)
735     && [NSApp respondsToSelector:@selector(setPresentationOptions:)];
739 struct EmacsMargins
741   CGFloat top;
742   CGFloat bottom;
743   CGFloat left;
744   CGFloat right;
748 static struct EmacsMargins
749 ns_screen_margins (NSScreen *screen)
750 /* The parts of SCREEN used by the operating system.  */
752   NSTRACE ("ns_screen_margins");
754   struct EmacsMargins margins;
756   NSRect screenFrame = [screen frame];
757   NSRect screenVisibleFrame = [screen visibleFrame];
759   /* Sometimes, visibleFrame isn't up-to-date with respect to a hidden
760      menu bar, check this explicitly.  */
761   if (ns_menu_bar_should_be_hidden())
762     {
763       margins.top = 0;
764     }
765   else
766     {
767       CGFloat frameTop = screenFrame.origin.y + screenFrame.size.height;
768       CGFloat visibleFrameTop = (screenVisibleFrame.origin.y
769                                  + screenVisibleFrame.size.height);
771       margins.top = frameTop - visibleFrameTop;
772     }
774   {
775     CGFloat frameRight = screenFrame.origin.x + screenFrame.size.width;
776     CGFloat visibleFrameRight = (screenVisibleFrame.origin.x
777                                  + screenVisibleFrame.size.width);
778     margins.right = frameRight - visibleFrameRight;
779   }
781   margins.bottom = screenVisibleFrame.origin.y - screenFrame.origin.y;
782   margins.left   = screenVisibleFrame.origin.x - screenFrame.origin.x;
784   NSTRACE_MSG ("left:%g right:%g top:%g bottom:%g",
785                margins.left,
786                margins.right,
787                margins.top,
788                margins.bottom);
790   return margins;
794 /* A screen margin between 1 and DOCK_IGNORE_LIMIT (inclusive) is
795    assumed to contain a hidden dock.  macOS currently use 4 pixels for
796    this, however, to be future compatible, a larger value is used.  */
797 #define DOCK_IGNORE_LIMIT 6
799 static struct EmacsMargins
800 ns_screen_margins_ignoring_hidden_dock (NSScreen *screen)
801 /* The parts of SCREEN used by the operating system, excluding the parts
802 reserved for an hidden dock.  */
804   NSTRACE ("ns_screen_margins_ignoring_hidden_dock");
806   struct EmacsMargins margins = ns_screen_margins(screen);
808   /* macOS (currently) reserved 4 pixels along the edge where a hidden
809      dock is located.  Unfortunately, it's not possible to find the
810      location and information about if the dock is hidden.  Instead,
811      it is assumed that if the margin of an edge is less than
812      DOCK_IGNORE_LIMIT, it contains a hidden dock.  */
813   if (margins.left <= DOCK_IGNORE_LIMIT)
814     {
815       margins.left = 0;
816     }
817   if (margins.right <= DOCK_IGNORE_LIMIT)
818     {
819       margins.right = 0;
820     }
821   if (margins.top <= DOCK_IGNORE_LIMIT)
822     {
823       margins.top = 0;
824     }
825   /* Note: This doesn't occur in current versions of macOS, but
826      included for completeness and future compatibility.  */
827   if (margins.bottom <= DOCK_IGNORE_LIMIT)
828     {
829       margins.bottom = 0;
830     }
832   NSTRACE_MSG ("left:%g right:%g top:%g bottom:%g",
833                margins.left,
834                margins.right,
835                margins.top,
836                margins.bottom);
838   return margins;
842 static CGFloat
843 ns_menu_bar_height (NSScreen *screen)
844 /* The height of the menu bar, if visible.
846    Note: Don't use this when fullscreen is enabled -- the screen
847    sometimes includes, sometimes excludes the menu bar area.  */
849   struct EmacsMargins margins = ns_screen_margins(screen);
851   CGFloat res = margins.top;
853   NSTRACE ("ns_menu_bar_height " NSTRACE_FMT_RETURN " %.0f", res);
855   return res;
859 /* ==========================================================================
861     Focus (clipping) and screen update
863    ========================================================================== */
866 // Window constraining
867 // -------------------
869 // To ensure that the windows are not placed under the menu bar, they
870 // are typically moved by the call-back constrainFrameRect. However,
871 // by overriding it, it's possible to inhibit this, leaving the window
872 // in it's original position.
874 // It's possible to hide the menu bar. However, technically, it's only
875 // possible to hide it when the application is active. To ensure that
876 // this work properly, the menu bar and window constraining are
877 // deferred until the application becomes active.
879 // Even though it's not possible to manually move a window above the
880 // top of the screen, it is allowed if it's done programmatically,
881 // when the menu is hidden. This allows the editable area to cover the
882 // full screen height.
884 // Test cases
885 // ----------
887 // Use the following extra files:
889 //    init.el:
890 //       ;; Hide menu and place frame slightly above the top of the screen.
891 //       (setq ns-auto-hide-menu-bar t)
892 //       (set-frame-position (selected-frame) 0 -20)
894 // Test 1:
896 //    emacs -Q -l init.el
898 //    Result: No menu bar, and the title bar should be above the screen.
900 // Test 2:
902 //    emacs -Q
904 //    Result: Menu bar visible, frame placed immediately below the menu.
907 static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
909   NSTRACE ("constrain_frame_rect(" NSTRACE_FMT_RECT ")",
910              NSTRACE_ARG_RECT (frameRect));
912   // --------------------
913   // Collect information about the screen the frame is covering.
914   //
916   NSArray *screens = [NSScreen screens];
917   NSUInteger nr_screens = [screens count];
919   int i;
921   // The height of the menu bar, if present in any screen the frame is
922   // displayed in.
923   int menu_bar_height = 0;
925   // A rectangle covering all the screen the frame is displayed in.
926   NSRect multiscreenRect = NSMakeRect(0, 0, 0, 0);
927   for (i = 0; i < nr_screens; ++i )
928     {
929       NSScreen *s = [screens objectAtIndex: i];
930       NSRect scrRect = [s frame];
932       NSTRACE_MSG ("Screen %d: " NSTRACE_FMT_RECT,
933                    i, NSTRACE_ARG_RECT (scrRect));
935       if (NSIntersectionRect (frameRect, scrRect).size.height != 0)
936         {
937           multiscreenRect = NSUnionRect (multiscreenRect, scrRect);
939           if (!isFullscreen)
940             {
941               CGFloat screen_menu_bar_height = ns_menu_bar_height (s);
942               menu_bar_height = max(menu_bar_height, screen_menu_bar_height);
943             }
944         }
945     }
947   NSTRACE_RECT ("multiscreenRect", multiscreenRect);
949   NSTRACE_MSG ("menu_bar_height: %d", menu_bar_height);
951   if (multiscreenRect.size.width == 0
952       || multiscreenRect.size.height == 0)
953     {
954       // Failed to find any monitor, give up.
955       NSTRACE_MSG ("multiscreenRect empty");
956       NSTRACE_RETURN_RECT (frameRect);
957       return frameRect;
958     }
961   // --------------------
962   // Find a suitable placement.
963   //
965   if (ns_menu_bar_should_be_hidden())
966     {
967       // When the menu bar is hidden, the user may place part of the
968       // frame above the top of the screen, for example to hide the
969       // title bar.
970       //
971       // Hence, keep the original position.
972     }
973   else
974     {
975       // Ensure that the frame is below the menu bar, or below the top
976       // of the screen.
977       //
978       // This assume that the menu bar is placed at the top in the
979       // rectangle that covers the monitors.  (It doesn't have to be,
980       // but if it's not it's hard to do anything useful.)
981       CGFloat topOfWorkArea = (multiscreenRect.origin.y
982                                + multiscreenRect.size.height
983                                - menu_bar_height);
985       CGFloat topOfFrame = frameRect.origin.y + frameRect.size.height;
986       if (topOfFrame > topOfWorkArea)
987         {
988           frameRect.origin.y -= topOfFrame - topOfWorkArea;
989           NSTRACE_RECT ("After placement adjust", frameRect);
990         }
991     }
993   // Include the following section to restrict frame to the screens.
994   // (If so, update it to allow the frame to stretch down below the
995   // screen.)
996 #if 0
997   // --------------------
998   // Ensure frame doesn't stretch below the screens.
999   //
1001   CGFloat diff = multiscreenRect.origin.y - frameRect.origin.y;
1003   if (diff > 0)
1004     {
1005       frameRect.origin.y = multiscreenRect.origin.y;
1006       frameRect.size.height -= diff;
1007     }
1008 #endif
1010   NSTRACE_RETURN_RECT (frameRect);
1011   return frameRect;
1015 static void
1016 ns_constrain_all_frames (void)
1017 /* --------------------------------------------------------------------------
1018      Ensure that the menu bar doesn't cover any frames.
1019    -------------------------------------------------------------------------- */
1021   Lisp_Object tail, frame;
1023   NSTRACE ("ns_constrain_all_frames");
1025   block_input ();
1027   FOR_EACH_FRAME (tail, frame)
1028     {
1029       struct frame *f = XFRAME (frame);
1030       if (FRAME_NS_P (f))
1031         {
1032           EmacsView *view = FRAME_NS_VIEW (f);
1034           if (![view isFullscreen])
1035             {
1036               [[view window]
1037                 setFrame:constrain_frame_rect([[view window] frame], false)
1038                  display:NO];
1039             }
1040         }
1041     }
1043   unblock_input ();
1047 static void
1048 ns_update_auto_hide_menu_bar (void)
1049 /* --------------------------------------------------------------------------
1050      Show or hide the menu bar, based on user setting.
1051    -------------------------------------------------------------------------- */
1053 #ifdef NS_IMPL_COCOA
1054   NSTRACE ("ns_update_auto_hide_menu_bar");
1056   block_input ();
1058   if (NSApp != nil && [NSApp isActive])
1059     {
1060       // Note, "setPresentationOptions" triggers an error unless the
1061       // application is active.
1062       BOOL menu_bar_should_be_hidden = ns_menu_bar_should_be_hidden ();
1064       if (menu_bar_should_be_hidden != ns_menu_bar_is_hidden)
1065         {
1066           NSApplicationPresentationOptions options
1067             = NSApplicationPresentationDefault;
1069           if (menu_bar_should_be_hidden)
1070             options |= NSApplicationPresentationAutoHideMenuBar
1071               | NSApplicationPresentationAutoHideDock;
1073           [NSApp setPresentationOptions: options];
1075           ns_menu_bar_is_hidden = menu_bar_should_be_hidden;
1077           if (!ns_menu_bar_is_hidden)
1078             {
1079               ns_constrain_all_frames ();
1080             }
1081         }
1082     }
1084   unblock_input ();
1085 #endif
1089 static void
1090 ns_update_begin (struct frame *f)
1091 /* --------------------------------------------------------------------------
1092    Prepare for a grouped sequence of drawing calls
1093    external (RIF) call; whole frame, called before update_window_begin
1094    -------------------------------------------------------------------------- */
1096   EmacsView *view = FRAME_NS_VIEW (f);
1097   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_begin");
1099   ns_update_auto_hide_menu_bar ();
1101 #ifdef NS_IMPL_COCOA
1102   if ([view isFullscreen] && [view fsIsNative])
1103   {
1104     // Fix reappearing tool bar in fullscreen for Mac OS X 10.7
1105     BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (f) ? YES : NO;
1106     NSToolbar *toolbar = [FRAME_NS_VIEW (f) toolbar];
1107     if (! tbar_visible != ! [toolbar isVisible])
1108       [toolbar setVisible: tbar_visible];
1109   }
1110 #endif
1112   ns_updating_frame = f;
1113   [view lockFocus];
1115   /* drawRect may have been called for say the minibuffer, and then clip path
1116      is for the minibuffer.  But the display engine may draw more because
1117      we have set the frame as garbaged.  So reset clip path to the whole
1118      view.  */
1119 #ifdef NS_IMPL_COCOA
1120   {
1121     NSBezierPath *bp;
1122     NSRect r = [view frame];
1123     NSRect cr = [[view window] frame];
1124     /* If a large frame size is set, r may be larger than the window frame
1125        before constrained.  In that case don't change the clip path, as we
1126        will clear in to the tool bar and title bar.  */
1127     if (r.size.height
1128         + FRAME_NS_TITLEBAR_HEIGHT (f)
1129         + FRAME_TOOLBAR_HEIGHT (f) <= cr.size.height)
1130       {
1131         bp = [[NSBezierPath bezierPathWithRect: r] retain];
1132         [bp setClip];
1133         [bp release];
1134       }
1135   }
1136 #endif
1138 #ifdef NS_IMPL_GNUSTEP
1139   uRect = NSMakeRect (0, 0, 0, 0);
1140 #endif
1144 static void
1145 ns_update_window_begin (struct window *w)
1146 /* --------------------------------------------------------------------------
1147    Prepare for a grouped sequence of drawing calls
1148    external (RIF) call; for one window, called after update_begin
1149    -------------------------------------------------------------------------- */
1151   struct frame *f = XFRAME (WINDOW_FRAME (w));
1152   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
1154   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_window_begin");
1155   w->output_cursor = w->cursor;
1157   block_input ();
1159   if (f == hlinfo->mouse_face_mouse_frame)
1160     {
1161       /* Don't do highlighting for mouse motion during the update.  */
1162       hlinfo->mouse_face_defer = 1;
1164         /* If the frame needs to be redrawn,
1165            simply forget about any prior mouse highlighting.  */
1166       if (FRAME_GARBAGED_P (f))
1167         hlinfo->mouse_face_window = Qnil;
1169       /* (further code for mouse faces ifdef'd out in other terms elided) */
1170     }
1172   unblock_input ();
1176 static void
1177 ns_update_window_end (struct window *w, bool cursor_on_p,
1178                       bool mouse_face_overwritten_p)
1179 /* --------------------------------------------------------------------------
1180    Finished a grouped sequence of drawing calls
1181    external (RIF) call; for one window called before update_end
1182    -------------------------------------------------------------------------- */
1184   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_window_end");
1186   /* note: this fn is nearly identical in all terms */
1187   if (!w->pseudo_window_p)
1188     {
1189       block_input ();
1191       if (cursor_on_p)
1192         display_and_set_cursor (w, 1,
1193                                 w->output_cursor.hpos, w->output_cursor.vpos,
1194                                 w->output_cursor.x, w->output_cursor.y);
1196       if (draw_window_fringes (w, 1))
1197         {
1198           if (WINDOW_RIGHT_DIVIDER_WIDTH (w))
1199             x_draw_right_divider (w);
1200           else
1201             x_draw_vertical_border (w);
1202         }
1204       unblock_input ();
1205     }
1207   /* If a row with mouse-face was overwritten, arrange for
1208      frame_up_to_date to redisplay the mouse highlight.  */
1209   if (mouse_face_overwritten_p)
1210     reset_mouse_highlight (MOUSE_HL_INFO (XFRAME (w->frame)));
1214 static void
1215 ns_update_end (struct frame *f)
1216 /* --------------------------------------------------------------------------
1217    Finished a grouped sequence of drawing calls
1218    external (RIF) call; for whole frame, called after update_window_end
1219    -------------------------------------------------------------------------- */
1221   EmacsView *view = FRAME_NS_VIEW (f);
1223   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_end");
1225 /*   if (f == MOUSE_HL_INFO (f)->mouse_face_mouse_frame) */
1226   MOUSE_HL_INFO (f)->mouse_face_defer = 0;
1228   block_input ();
1230   [view unlockFocus];
1231   [[view window] flushWindow];
1233   unblock_input ();
1234   ns_updating_frame = NULL;
1237 static void
1238 ns_focus (struct frame *f, NSRect *r, int n)
1239 /* --------------------------------------------------------------------------
1240    Internal: Focus on given frame.  During small local updates this is used to
1241      draw, however during large updates, ns_update_begin and ns_update_end are
1242      called to wrap the whole thing, in which case these calls are stubbed out.
1243      Except, on GNUstep, we accumulate the rectangle being drawn into, because
1244      the back end won't do this automatically, and will just end up flushing
1245      the entire window.
1246    -------------------------------------------------------------------------- */
1248   NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_focus");
1249   if (r != NULL)
1250     {
1251       NSTRACE_RECT ("r", *r);
1252     }
1254   if (f != ns_updating_frame)
1255     {
1256       NSView *view = FRAME_NS_VIEW (f);
1257       if (view != focus_view)
1258         {
1259           if (focus_view != NULL)
1260             {
1261               [focus_view unlockFocus];
1262               [[focus_view window] flushWindow];
1263 /*debug_lock--; */
1264             }
1266           if (view)
1267             [view lockFocus];
1268           focus_view = view;
1269 /*if (view) debug_lock++; */
1270         }
1271     }
1273   /* clipping */
1274   if (r)
1275     {
1276       [[NSGraphicsContext currentContext] saveGraphicsState];
1277       if (n == 2)
1278         NSRectClipList (r, 2);
1279       else
1280         NSRectClip (*r);
1281       gsaved = YES;
1282     }
1286 static void
1287 ns_unfocus (struct frame *f)
1288 /* --------------------------------------------------------------------------
1289      Internal: Remove focus on given frame
1290    -------------------------------------------------------------------------- */
1292   NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_unfocus");
1294   if (gsaved)
1295     {
1296       [[NSGraphicsContext currentContext] restoreGraphicsState];
1297       gsaved = NO;
1298     }
1300   if (f != ns_updating_frame)
1301     {
1302       if (focus_view != NULL)
1303         {
1304           [focus_view unlockFocus];
1305           [[focus_view window] flushWindow];
1306           focus_view = NULL;
1307 /*debug_lock--; */
1308         }
1309     }
1313 static void
1314 ns_clip_to_row (struct window *w, struct glyph_row *row,
1315                 enum glyph_row_area area, BOOL gc)
1316 /* --------------------------------------------------------------------------
1317      Internal (but parallels other terms): Focus drawing on given row
1318    -------------------------------------------------------------------------- */
1320   struct frame *f = XFRAME (WINDOW_FRAME (w));
1321   NSRect clip_rect;
1322   int window_x, window_y, window_width;
1324   window_box (w, area, &window_x, &window_y, &window_width, 0);
1326   clip_rect.origin.x = window_x;
1327   clip_rect.origin.y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, row->y));
1328   clip_rect.origin.y = max (clip_rect.origin.y, window_y);
1329   clip_rect.size.width = window_width;
1330   clip_rect.size.height = row->visible_height;
1332   ns_focus (f, &clip_rect, 1);
1336 /* ==========================================================================
1338     Visible bell and beep.
1340    ========================================================================== */
1343 // This bell implementation shows the visual bell image asynchronously
1344 // from the rest of Emacs. This is done by adding a NSView to the
1345 // superview of the Emacs window and removing it using a timer.
1347 // Unfortunately, some Emacs operations, like scrolling, is done using
1348 // low-level primitives that copy the content of the window, including
1349 // the bell image. To some extent, this is handled by removing the
1350 // image prior to scrolling and marking that the window is in need for
1351 // redisplay.
1353 // To test this code, make sure that there is no artifacts of the bell
1354 // image in the following situations. Use a non-empty buffer (like the
1355 // tutorial) to ensure that a scroll is performed:
1357 // * Single-window: C-g C-v
1359 // * Side-by-windows: C-x 3 C-g C-v
1361 // * Windows above each other: C-x 2 C-g C-v
1363 @interface EmacsBell : NSImageView
1365   // Number of currently active bell:s.
1366   unsigned int nestCount;
1367   NSView * mView;
1368   bool isAttached;
1370 - (void)show:(NSView *)view;
1371 - (void)hide;
1372 - (void)remove;
1373 @end
1375 @implementation EmacsBell
1377 - (id)init
1379   NSTRACE ("[EmacsBell init]");
1380   if ((self = [super init]))
1381     {
1382       nestCount = 0;
1383       isAttached = false;
1384 #ifdef NS_IMPL_GNUSTEP
1385       // GNUstep doesn't provide named images.  This was reported in
1386       // 2011, see https://savannah.gnu.org/bugs/?33396
1387       //
1388       // As a drop in replacement, a semitransparent gray square is used.
1389       self.image = [[NSImage alloc] initWithSize:NSMakeSize(32 * 5, 32 * 5)];
1390       [self.image lockFocus];
1391       [[NSColor colorForEmacsRed:0.5 green:0.5 blue:0.5 alpha:0.5] set];
1392       NSRectFill(NSMakeRect(0, 0, 32, 32));
1393       [self.image unlockFocus];
1394 #else
1395       self.image = [NSImage imageNamed:NSImageNameCaution];
1396       [self.image setSize:NSMakeSize(self.image.size.width * 5,
1397                                      self.image.size.height * 5)];
1398 #endif
1399     }
1400   return self;
1403 - (void)show:(NSView *)view
1405   NSTRACE ("[EmacsBell show:]");
1406   NSTRACE_MSG ("nestCount: %u", nestCount);
1408   // Show the image, unless it's already shown.
1409   if (nestCount == 0)
1410     {
1411       NSRect rect = [view bounds];
1412       NSPoint pos;
1413       pos.x = rect.origin.x + (rect.size.width  - self.image.size.width )/2;
1414       pos.y = rect.origin.y + (rect.size.height - self.image.size.height)/2;
1416       [self setFrameOrigin:pos];
1417       [self setFrameSize:self.image.size];
1419       isAttached = true;
1420       mView = view;
1421       [[[view window] contentView] addSubview:self
1422                                    positioned:NSWindowAbove
1423                                    relativeTo:nil];
1424     }
1426   ++nestCount;
1428   [self performSelector:@selector(hide) withObject:self afterDelay:0.5];
1432 - (void)hide
1434   // Note: Trace output from this method isn't shown, reason unknown.
1435   // NSTRACE ("[EmacsBell hide]");
1437   if (nestCount > 0)
1438     --nestCount;
1440   // Remove the image once the last bell became inactive.
1441   if (nestCount == 0)
1442     {
1443       [self remove];
1444     }
1448 -(void)remove
1450   NSTRACE ("[EmacsBell remove]");
1451   if (isAttached)
1452     {
1453       NSTRACE_MSG ("removeFromSuperview");
1454       [self removeFromSuperview];
1455       mView.needsDisplay = YES;
1456       isAttached = false;
1457     }
1460 @end
1463 static EmacsBell * bell_view = nil;
1465 static void
1466 ns_ring_bell (struct frame *f)
1467 /* --------------------------------------------------------------------------
1468      "Beep" routine
1469    -------------------------------------------------------------------------- */
1471   NSTRACE ("ns_ring_bell");
1472   if (visible_bell)
1473     {
1474       struct frame *frame = SELECTED_FRAME ();
1475       NSView *view;
1477       if (bell_view == nil)
1478         {
1479           bell_view = [[EmacsBell alloc] init];
1480           [bell_view retain];
1481         }
1483       block_input ();
1485       view = FRAME_NS_VIEW (frame);
1486       if (view != nil)
1487         {
1488           [bell_view show:view];
1489         }
1491       unblock_input ();
1492     }
1493   else
1494     {
1495       NSBeep ();
1496     }
1500 static void
1501 hide_bell (void)
1502 /* --------------------------------------------------------------------------
1503      Ensure the bell is hidden.
1504    -------------------------------------------------------------------------- */
1506   NSTRACE ("hide_bell");
1508   if (bell_view != nil)
1509     {
1510       [bell_view remove];
1511     }
1515 /* ==========================================================================
1517     Frame / window manager related functions
1519    ========================================================================== */
1522 static void
1523 ns_raise_frame (struct frame *f, BOOL make_key)
1524 /* --------------------------------------------------------------------------
1525      Bring window to foreground and if make_key is YES, give it focus.
1526    -------------------------------------------------------------------------- */
1528   NSView *view;
1530   check_window_system (f);
1531   view = FRAME_NS_VIEW (f);
1532   block_input ();
1533   if (FRAME_VISIBLE_P (f))
1534     {
1535       if (make_key)
1536         [[view window] makeKeyAndOrderFront: NSApp];
1537       else
1538         [[view window] orderFront: NSApp];
1539     }
1540   unblock_input ();
1544 static void
1545 ns_lower_frame (struct frame *f)
1546 /* --------------------------------------------------------------------------
1547      Send window to back
1548    -------------------------------------------------------------------------- */
1550   NSView *view;
1552   check_window_system (f);
1553   view = FRAME_NS_VIEW (f);
1554   block_input ();
1555   [[view window] orderBack: NSApp];
1556   unblock_input ();
1560 static void
1561 ns_frame_raise_lower (struct frame *f, bool raise)
1562 /* --------------------------------------------------------------------------
1563      External (hook)
1564    -------------------------------------------------------------------------- */
1566   NSTRACE ("ns_frame_raise_lower");
1568   if (raise)
1569     ns_raise_frame (f, YES);
1570   else
1571     ns_lower_frame (f);
1575 static void
1576 ns_frame_rehighlight (struct frame *frame)
1577 /* --------------------------------------------------------------------------
1578      External (hook): called on things like window switching within frame
1579    -------------------------------------------------------------------------- */
1581   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
1582   struct frame *old_highlight = dpyinfo->x_highlight_frame;
1584   NSTRACE ("ns_frame_rehighlight");
1585   if (dpyinfo->x_focus_frame)
1586     {
1587       dpyinfo->x_highlight_frame
1588         = (FRAMEP (FRAME_FOCUS_FRAME (dpyinfo->x_focus_frame))
1589            ? XFRAME (FRAME_FOCUS_FRAME (dpyinfo->x_focus_frame))
1590            : dpyinfo->x_focus_frame);
1591       if (!FRAME_LIVE_P (dpyinfo->x_highlight_frame))
1592         {
1593           fset_focus_frame (dpyinfo->x_focus_frame, Qnil);
1594           dpyinfo->x_highlight_frame = dpyinfo->x_focus_frame;
1595         }
1596     }
1597   else
1598       dpyinfo->x_highlight_frame = 0;
1600   if (dpyinfo->x_highlight_frame &&
1601          dpyinfo->x_highlight_frame != old_highlight)
1602     {
1603       if (old_highlight)
1604         {
1605           x_update_cursor (old_highlight, 1);
1606           x_set_frame_alpha (old_highlight);
1607         }
1608       if (dpyinfo->x_highlight_frame)
1609         {
1610           x_update_cursor (dpyinfo->x_highlight_frame, 1);
1611           x_set_frame_alpha (dpyinfo->x_highlight_frame);
1612         }
1613     }
1617 void
1618 x_make_frame_visible (struct frame *f)
1619 /* --------------------------------------------------------------------------
1620      External: Show the window (X11 semantics)
1621    -------------------------------------------------------------------------- */
1623   NSTRACE ("x_make_frame_visible");
1624   /* XXX: at some points in past this was not needed, as the only place that
1625      called this (frame.c:Fraise_frame ()) also called raise_lower;
1626      if this ends up the case again, comment this out again. */
1627   if (!FRAME_VISIBLE_P (f))
1628     {
1629       EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
1630       NSWindow *window = [view window];
1632       SET_FRAME_VISIBLE (f, 1);
1633       ns_raise_frame (f, ! FRAME_NO_FOCUS_ON_MAP (f));
1635       /* Making a new frame from a fullscreen frame will make the new frame
1636          fullscreen also.  So skip handleFS as this will print an error.  */
1637       if ([view fsIsNative] && f->want_fullscreen == FULLSCREEN_BOTH
1638           && [view isFullscreen])
1639         return;
1641       if (f->want_fullscreen != FULLSCREEN_NONE)
1642         {
1643           block_input ();
1644           [view handleFS];
1645           unblock_input ();
1646         }
1648       /* Making a frame invisible seems to break the parent->child
1649          relationship, so reinstate it. */
1650       if ([window parentWindow] == nil && FRAME_PARENT_FRAME (f) != NULL)
1651         {
1652           NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
1654           block_input ();
1655           [parent addChildWindow: window
1656                          ordered: NSWindowAbove];
1657           unblock_input ();
1659           /* If the parent frame moved while the child frame was
1660              invisible, the child frame's position won't have been
1661              updated.  Make sure it's in the right place now. */
1662           x_set_offset(f, f->left_pos, f->top_pos, 0);
1663         }
1664     }
1668 void
1669 x_make_frame_invisible (struct frame *f)
1670 /* --------------------------------------------------------------------------
1671      External: Hide the window (X11 semantics)
1672    -------------------------------------------------------------------------- */
1674   NSView *view;
1675   NSTRACE ("x_make_frame_invisible");
1676   check_window_system (f);
1677   view = FRAME_NS_VIEW (f);
1678   [[view window] orderOut: NSApp];
1679   SET_FRAME_VISIBLE (f, 0);
1680   SET_FRAME_ICONIFIED (f, 0);
1684 void
1685 x_iconify_frame (struct frame *f)
1686 /* --------------------------------------------------------------------------
1687      External: Iconify window
1688    -------------------------------------------------------------------------- */
1690   NSView *view;
1691   struct ns_display_info *dpyinfo;
1693   NSTRACE ("x_iconify_frame");
1694   check_window_system (f);
1695   view = FRAME_NS_VIEW (f);
1696   dpyinfo = FRAME_DISPLAY_INFO (f);
1698   if (dpyinfo->x_highlight_frame == f)
1699     dpyinfo->x_highlight_frame = 0;
1701   if ([[view window] windowNumber] <= 0)
1702     {
1703       /* the window is still deferred.  Make it very small, bring it
1704          on screen and order it out. */
1705       NSRect s = { { 100, 100}, {0, 0} };
1706       NSRect t;
1707       t = [[view window] frame];
1708       [[view window] setFrame: s display: NO];
1709       [[view window] orderBack: NSApp];
1710       [[view window] orderOut: NSApp];
1711       [[view window] setFrame: t display: NO];
1712     }
1714   /* Processing input while Emacs is being minimized can cause a
1715      crash, so block it for the duration. */
1716   block_input();
1717   [[view window] miniaturize: NSApp];
1718   unblock_input();
1721 /* Free X resources of frame F.  */
1723 void
1724 x_free_frame_resources (struct frame *f)
1726   NSView *view;
1727   struct ns_display_info *dpyinfo;
1728   Mouse_HLInfo *hlinfo;
1730   NSTRACE ("x_free_frame_resources");
1731   check_window_system (f);
1732   view = FRAME_NS_VIEW (f);
1733   dpyinfo = FRAME_DISPLAY_INFO (f);
1734   hlinfo = MOUSE_HL_INFO (f);
1736   [(EmacsView *)view setWindowClosing: YES]; /* may not have been informed */
1738   block_input ();
1740   free_frame_menubar (f);
1741   free_frame_faces (f);
1743   if (f == dpyinfo->x_focus_frame)
1744     dpyinfo->x_focus_frame = 0;
1745   if (f == dpyinfo->x_highlight_frame)
1746     dpyinfo->x_highlight_frame = 0;
1747   if (f == hlinfo->mouse_face_mouse_frame)
1748     reset_mouse_highlight (hlinfo);
1750   if (f->output_data.ns->miniimage != nil)
1751     [f->output_data.ns->miniimage release];
1753   [[view window] close];
1754   [view release];
1756   xfree (f->output_data.ns);
1758   unblock_input ();
1761 void
1762 x_destroy_window (struct frame *f)
1763 /* --------------------------------------------------------------------------
1764      External: Delete the window
1765    -------------------------------------------------------------------------- */
1767   NSTRACE ("x_destroy_window");
1769   /* If this frame has a parent window, detach it as not doing so can
1770      cause a crash in GNUStep. */
1771   if (FRAME_PARENT_FRAME (f) != NULL)
1772     {
1773       NSWindow *child = [FRAME_NS_VIEW (f) window];
1774       NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
1776       [parent removeChildWindow: child];
1777     }
1779   check_window_system (f);
1780   x_free_frame_resources (f);
1781   ns_window_num--;
1785 void
1786 x_set_offset (struct frame *f, int xoff, int yoff, int change_grav)
1787 /* --------------------------------------------------------------------------
1788      External: Position the window
1789    -------------------------------------------------------------------------- */
1791   NSView *view = FRAME_NS_VIEW (f);
1792   NSScreen *screen = [[view window] screen];
1794   NSTRACE ("x_set_offset");
1796   block_input ();
1798   f->left_pos = xoff;
1799   f->top_pos = yoff;
1801   if (view != nil)
1802     {
1803       if (FRAME_PARENT_FRAME (f) == NULL && screen)
1804         {
1805           f->left_pos = f->size_hint_flags & XNegative
1806             ? [screen visibleFrame].size.width + f->left_pos - FRAME_PIXEL_WIDTH (f)
1807             : f->left_pos;
1808           /* We use visibleFrame here to take menu bar into account.
1809              Ideally we should also adjust left/top with visibleFrame.origin.  */
1811           f->top_pos = f->size_hint_flags & YNegative
1812             ? ([screen visibleFrame].size.height + f->top_pos
1813                - FRAME_PIXEL_HEIGHT (f) - FRAME_NS_TITLEBAR_HEIGHT (f)
1814                - FRAME_TOOLBAR_HEIGHT (f))
1815             : f->top_pos;
1816 #ifdef NS_IMPL_GNUSTEP
1817           if (f->left_pos < 100)
1818             f->left_pos = 100;  /* don't overlap menu */
1819 #endif
1820         }
1821       else if (FRAME_PARENT_FRAME (f) != NULL)
1822         {
1823           struct frame *parent = FRAME_PARENT_FRAME (f);
1825           /* On X negative values for child frames always result in
1826              positioning relative to the bottom right corner of the
1827              parent frame.  */
1828           if (f->left_pos < 0)
1829             f->left_pos = FRAME_PIXEL_WIDTH (parent) - FRAME_PIXEL_WIDTH (f) + f->left_pos;
1831           if (f->top_pos < 0)
1832             f->top_pos = FRAME_PIXEL_HEIGHT (parent) + FRAME_TOOLBAR_HEIGHT (parent)
1833               - FRAME_PIXEL_HEIGHT (f) + f->top_pos;
1834         }
1836       /* Constrain the setFrameTopLeftPoint so we don't move behind the
1837          menu bar.  */
1838       NSPoint pt = NSMakePoint (SCREENMAXBOUND (f->left_pos
1839                                                 + NS_PARENT_WINDOW_LEFT_POS (f)),
1840                                 SCREENMAXBOUND (NS_PARENT_WINDOW_TOP_POS (f)
1841                                                 - f->top_pos));
1842       NSTRACE_POINT ("setFrameTopLeftPoint", pt);
1843       [[view window] setFrameTopLeftPoint: pt];
1844       f->size_hint_flags &= ~(XNegative|YNegative);
1845     }
1847   unblock_input ();
1851 void
1852 x_set_window_size (struct frame *f,
1853                    bool change_gravity,
1854                    int width,
1855                    int height,
1856                    bool pixelwise)
1857 /* --------------------------------------------------------------------------
1858      Adjust window pixel size based on given character grid size
1859      Impl is a bit more complex than other terms, need to do some
1860      internal clipping.
1861    -------------------------------------------------------------------------- */
1863   EmacsView *view = FRAME_NS_VIEW (f);
1864   NSWindow *window = [view window];
1865   NSRect wr = [window frame];
1866   int pixelwidth, pixelheight;
1867   int orig_height = wr.size.height;
1869   NSTRACE ("x_set_window_size");
1871   if (view == nil)
1872     return;
1874   NSTRACE_RECT ("current", wr);
1875   NSTRACE_MSG ("Width:%d Height:%d Pixelwise:%d", width, height, pixelwise);
1876   NSTRACE_MSG ("Font %d x %d", FRAME_COLUMN_WIDTH (f), FRAME_LINE_HEIGHT (f));
1878   block_input ();
1880   if (pixelwise)
1881     {
1882       pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
1883       pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
1884     }
1885   else
1886     {
1887       pixelwidth =  FRAME_TEXT_COLS_TO_PIXEL_WIDTH   (f, width);
1888       pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, height);
1889     }
1891   wr.size.width = pixelwidth + f->border_width;
1892   wr.size.height = pixelheight;
1893   if (! [view isFullscreen])
1894     wr.size.height += FRAME_NS_TITLEBAR_HEIGHT (f)
1895       + FRAME_TOOLBAR_HEIGHT (f);
1897   /* Do not try to constrain to this screen.  We may have multiple
1898      screens, and want Emacs to span those.  Constraining to screen
1899      prevents that, and that is not nice to the user.  */
1900  if (f->output_data.ns->zooming)
1901    f->output_data.ns->zooming = 0;
1902  else
1903    wr.origin.y += orig_height - wr.size.height;
1905  frame_size_history_add
1906    (f, Qx_set_window_size_1, width, height,
1907     list5 (Fcons (make_number (pixelwidth), make_number (pixelheight)),
1908            Fcons (make_number (wr.size.width), make_number (wr.size.height)),
1909            make_number (f->border_width),
1910            make_number (FRAME_NS_TITLEBAR_HEIGHT (f)),
1911            make_number (FRAME_TOOLBAR_HEIGHT (f))));
1913   [window setFrame: wr display: YES];
1915   [view updateFrameSize: NO];
1916   unblock_input ();
1919 #ifdef NS_IMPL_COCOA
1920 void
1921 x_set_undecorated (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1922 /* --------------------------------------------------------------------------
1923      Set frame F's `undecorated' parameter.  If non-nil, F's window-system
1924      window is drawn without decorations, title, minimize/maximize boxes
1925      and external borders.  This usually means that the window cannot be
1926      dragged, resized, iconified, maximized or deleted with the mouse.  If
1927      nil, draw the frame with all the elements listed above unless these
1928      have been suspended via window manager settings.
1930      GNUStep cannot change an existing window's style.
1931    -------------------------------------------------------------------------- */
1933   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
1934   NSWindow *window = [view window];
1936   NSTRACE ("x_set_undecorated");
1938   if (!EQ (new_value, old_value))
1939     {
1940       block_input ();
1942       if (NILP (new_value))
1943         {
1944           FRAME_UNDECORATED (f) = false;
1945           [window setStyleMask: ((window.styleMask | FRAME_DECORATED_FLAGS)
1946                                   ^ FRAME_UNDECORATED_FLAGS)];
1948           [view createToolbar: f];
1949         }
1950       else
1951         {
1952           [window setToolbar: nil];
1953           /* Do I need to release the toolbar here? */
1955           FRAME_UNDECORATED (f) = true;
1956           [window setStyleMask: ((window.styleMask | FRAME_UNDECORATED_FLAGS)
1957                                  ^ FRAME_DECORATED_FLAGS)];
1958         }
1960       /* At this point it seems we don't have an active NSResponder,
1961          so some key presses (TAB) are swallowed by the system. */
1962       [window makeFirstResponder: view];
1964       [view updateFrameSize: NO];
1965       unblock_input ();
1966     }
1968 #endif /* NS_IMPL_COCOA */
1970 void
1971 x_set_parent_frame (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1972 /* --------------------------------------------------------------------------
1973      Set frame F's `parent-frame' parameter.  If non-nil, make F a child
1974      frame of the frame specified by that parameter.  Technically, this
1975      makes F's window-system window a child window of the parent frame's
1976      window-system window.  If nil, make F's window-system window a
1977      top-level window--a child of its display's root window.
1979      A child frame's `left' and `top' parameters specify positions
1980      relative to the top-left corner of its parent frame's native
1981      rectangle.  On macOS moving a parent frame moves all its child
1982      frames too, keeping their position relative to the parent
1983      unaltered.  When a parent frame is iconified or made invisible, its
1984      child frames are made invisible.  When a parent frame is deleted,
1985      its child frames are deleted too.
1987      Whether a child frame has a tool bar may be window-system or window
1988      manager dependent.  It's advisable to disable it via the frame
1989      parameter settings.
1991      Some window managers may not honor this parameter.
1992    -------------------------------------------------------------------------- */
1994   struct frame *p = NULL;
1995   NSWindow *parent, *child;
1997   NSTRACE ("x_set_parent_frame");
1999   if (!NILP (new_value)
2000       && (!FRAMEP (new_value)
2001           || !FRAME_LIVE_P (p = XFRAME (new_value))
2002           || !FRAME_NS_P (p)))
2003     {
2004       store_frame_param (f, Qparent_frame, old_value);
2005       error ("Invalid specification of `parent-frame'");
2006     }
2008   if (p != FRAME_PARENT_FRAME (f))
2009     {
2010       parent = [FRAME_NS_VIEW (p) window];
2011       child = [FRAME_NS_VIEW (f) window];
2013       block_input ();
2014       [parent addChildWindow: child
2015                      ordered: NSWindowAbove];
2016       unblock_input ();
2018       fset_parent_frame (f, new_value);
2019     }
2022 void
2023 x_set_no_focus_on_map (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
2024 /* Set frame F's `no-focus-on-map' parameter which, if non-nil, means
2025  * that F's window-system window does not want to receive input focus
2026  * when it is mapped.  (A frame's window is mapped when the frame is
2027  * displayed for the first time and when the frame changes its state
2028  * from `iconified' or `invisible' to `visible'.)
2030  * Some window managers may not honor this parameter. */
2032   NSTRACE ("x_set_no_focus_on_map");
2034   if (!EQ (new_value, old_value))
2035     {
2036       FRAME_NO_FOCUS_ON_MAP (f) = !NILP (new_value);
2037     }
2040 void
2041 x_set_no_accept_focus (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
2042 /*  Set frame F's `no-accept-focus' parameter which, if non-nil, hints
2043  * that F's window-system window does not want to receive input focus
2044  * via mouse clicks or by moving the mouse into it.
2046  * If non-nil, this may have the unwanted side-effect that a user cannot
2047  * scroll a non-selected frame with the mouse.
2049  * Some window managers may not honor this parameter. */
2051   NSTRACE ("x_set_no_accept_focus");
2053   if (!EQ (new_value, old_value))
2054     FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value);
2057 void
2058 x_set_z_group (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
2059 /* Set frame F's `z-group' parameter.  If `above', F's window-system
2060    window is displayed above all windows that do not have the `above'
2061    property set.  If nil, F's window is shown below all windows that
2062    have the `above' property set and above all windows that have the
2063    `below' property set.  If `below', F's window is displayed below
2064    all windows that do.
2066    Some window managers may not honor this parameter. */
2068   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2069   NSWindow *window = [view window];
2071   NSTRACE ("x_set_z_group");
2073   if (NILP (new_value))
2074     {
2075       window.level = NSNormalWindowLevel;
2076       FRAME_Z_GROUP (f) = z_group_none;
2077     }
2078   else if (EQ (new_value, Qabove))
2079     {
2080       window.level = NSNormalWindowLevel + 1;
2081       FRAME_Z_GROUP (f) = z_group_above;
2082     }
2083   else if (EQ (new_value, Qabove_suspended))
2084     {
2085       /* Not sure what level this should be. */
2086       window.level = NSNormalWindowLevel + 1;
2087       FRAME_Z_GROUP (f) = z_group_above_suspended;
2088     }
2089   else if (EQ (new_value, Qbelow))
2090     {
2091       window.level = NSNormalWindowLevel - 1;
2092       FRAME_Z_GROUP (f) = z_group_below;
2093     }
2094   else
2095     error ("Invalid z-group specification");
2098 #ifdef NS_IMPL_COCOA
2099 void
2100 ns_set_appearance (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
2102 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
2103   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2104   NSWindow *window = [view window];
2106   NSTRACE ("ns_set_appearance");
2108 #ifndef NSAppKitVersionNumber10_10
2109 #define NSAppKitVersionNumber10_10 1343
2110 #endif
2112   if (NSAppKitVersionNumber < NSAppKitVersionNumber10_10)
2113     return;
2115   if (EQ (new_value, Qdark))
2116     {
2117       window.appearance = [NSAppearance
2118                             appearanceNamed: NSAppearanceNameVibrantDark];
2119       FRAME_NS_APPEARANCE (f) = ns_appearance_vibrant_dark;
2120     }
2121   else
2122     {
2123       window.appearance = [NSAppearance
2124                             appearanceNamed: NSAppearanceNameAqua];
2125       FRAME_NS_APPEARANCE (f) = ns_appearance_aqua;
2126     }
2127 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 */
2130 void
2131 ns_set_transparent_titlebar (struct frame *f, Lisp_Object new_value,
2132                              Lisp_Object old_value)
2134 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
2135   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2136   NSWindow *window = [view window];
2138   NSTRACE ("ns_set_transparent_titlebar");
2140   if ([window respondsToSelector: @selector(titlebarAppearsTransparent)]
2141       && !EQ (new_value, old_value))
2142     {
2143       window.titlebarAppearsTransparent = !NILP (new_value);
2144       FRAME_NS_TRANSPARENT_TITLEBAR (f) = !NILP (new_value);
2145     }
2146 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 */
2148 #endif /* NS_IMPL_COCOA */
2150 static void
2151 ns_fullscreen_hook (struct frame *f)
2153   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2155   NSTRACE ("ns_fullscreen_hook");
2157   if (!FRAME_VISIBLE_P (f))
2158     return;
2160    if (! [view fsIsNative] && f->want_fullscreen == FULLSCREEN_BOTH)
2161     {
2162       /* Old style fs don't initiate correctly if created from
2163          init/default-frame alist, so use a timer (not nice...).
2164       */
2165       [NSTimer scheduledTimerWithTimeInterval: 0.5 target: view
2166                                      selector: @selector (handleFS)
2167                                      userInfo: nil repeats: NO];
2168       return;
2169     }
2171   block_input ();
2172   [view handleFS];
2173   unblock_input ();
2176 /* ==========================================================================
2178     Color management
2180    ========================================================================== */
2183 NSColor *
2184 ns_lookup_indexed_color (unsigned long idx, struct frame *f)
2186   struct ns_color_table *color_table = FRAME_DISPLAY_INFO (f)->color_table;
2187   if (idx < 1 || idx >= color_table->avail)
2188     return nil;
2189   return color_table->colors[idx];
2193 unsigned long
2194 ns_index_color (NSColor *color, struct frame *f)
2196   struct ns_color_table *color_table = FRAME_DISPLAY_INFO (f)->color_table;
2197   ptrdiff_t idx;
2198   ptrdiff_t i;
2200   if (!color_table->colors)
2201     {
2202       color_table->size = NS_COLOR_CAPACITY;
2203       color_table->avail = 1; /* skip idx=0 as marker */
2204       color_table->colors = xmalloc (color_table->size * sizeof (NSColor *));
2205       color_table->colors[0] = nil;
2206       color_table->empty_indices = [[NSMutableSet alloc] init];
2207     }
2209   /* Do we already have this color?  */
2210   for (i = 1; i < color_table->avail; i++)
2211     if (color_table->colors[i] && [color_table->colors[i] isEqual: color])
2212       return i;
2214   if ([color_table->empty_indices count] > 0)
2215     {
2216       NSNumber *index = [color_table->empty_indices anyObject];
2217       [color_table->empty_indices removeObject: index];
2218       idx = [index unsignedLongValue];
2219     }
2220   else
2221     {
2222       if (color_table->avail == color_table->size)
2223         color_table->colors =
2224           xpalloc (color_table->colors, &color_table->size, 1,
2225                    min (ULONG_MAX, PTRDIFF_MAX), sizeof *color_table->colors);
2226       idx = color_table->avail++;
2227     }
2229   color_table->colors[idx] = color;
2230   [color retain];
2231 /*fprintf(stderr, "color_table: allocated %d\n",idx);*/
2232   return idx;
2236 static int
2237 ns_get_color (const char *name, NSColor **col)
2238 /* --------------------------------------------------------------------------
2239      Parse a color name
2240    -------------------------------------------------------------------------- */
2241 /* On *Step, we attempt to mimic the X11 platform here, down to installing an
2242    X11 rgb.txt-compatible color list in Emacs.clr (see ns_term_init()).
2243    See: http://thread.gmane.org/gmane.emacs.devel/113050/focus=113272). */
2245   NSColor *new = nil;
2246   static char hex[20];
2247   int scaling = 0;
2248   float r = -1.0, g, b;
2249   NSString *nsname = [NSString stringWithUTF8String: name];
2251   NSTRACE ("ns_get_color(%s, **)", name);
2253   block_input ();
2255   if ([nsname isEqualToString: @"ns_selection_bg_color"])
2256     {
2257 #ifdef NS_IMPL_COCOA
2258       NSString *defname = [[NSUserDefaults standardUserDefaults]
2259                             stringForKey: @"AppleHighlightColor"];
2260       if (defname != nil)
2261         nsname = defname;
2262       else
2263 #endif
2264       if ((new = [NSColor selectedTextBackgroundColor]) != nil)
2265         {
2266           *col = [new colorUsingDefaultColorSpace];
2267           unblock_input ();
2268           return 0;
2269         }
2270       else
2271         nsname = NS_SELECTION_BG_COLOR_DEFAULT;
2273       name = [nsname UTF8String];
2274     }
2275   else if ([nsname isEqualToString: @"ns_selection_fg_color"])
2276     {
2277       /* NOTE: macOS applications normally don't set foreground
2278          selection, but text may be unreadable if we don't.
2279       */
2280       if ((new = [NSColor selectedTextColor]) != nil)
2281         {
2282           *col = [new colorUsingDefaultColorSpace];
2283           unblock_input ();
2284           return 0;
2285         }
2287       nsname = NS_SELECTION_FG_COLOR_DEFAULT;
2288       name = [nsname UTF8String];
2289     }
2291   /* First, check for some sort of numeric specification. */
2292   hex[0] = '\0';
2294   if (name[0] == '0' || name[0] == '1' || name[0] == '.')  /* RGB decimal */
2295     {
2296       NSScanner *scanner = [NSScanner scannerWithString: nsname];
2297       [scanner scanFloat: &r];
2298       [scanner scanFloat: &g];
2299       [scanner scanFloat: &b];
2300     }
2301   else if (!strncmp(name, "rgb:", 4))  /* A newer X11 format -- rgb:r/g/b */
2302     scaling = (snprintf (hex, sizeof hex, "%s", name + 4) - 2) / 3;
2303   else if (name[0] == '#')        /* An old X11 format; convert to newer */
2304     {
2305       int len = (strlen(name) - 1);
2306       int start = (len % 3 == 0) ? 1 : len / 4 + 1;
2307       int i;
2308       scaling = strlen(name+start) / 3;
2309       for (i = 0; i < 3; i++)
2310         sprintf (hex + i * (scaling + 1), "%.*s/", scaling,
2311                  name + start + i * scaling);
2312       hex[3 * (scaling + 1) - 1] = '\0';
2313     }
2315   if (hex[0])
2316     {
2317       unsigned int rr, gg, bb;
2318       float fscale = scaling == 4 ? 65535.0 : (scaling == 2 ? 255.0 : 15.0);
2319       if (sscanf (hex, "%x/%x/%x", &rr, &gg, &bb))
2320         {
2321           r = rr / fscale;
2322           g = gg / fscale;
2323           b = bb / fscale;
2324         }
2325     }
2327   if (r >= 0.0F)
2328     {
2329       *col = [NSColor colorForEmacsRed: r green: g blue: b alpha: 1.0];
2330       unblock_input ();
2331       return 0;
2332     }
2334   /* Otherwise, color is expected to be from a list */
2335   {
2336     NSEnumerator *lenum, *cenum;
2337     NSString *name;
2338     NSColorList *clist;
2340 #ifdef NS_IMPL_GNUSTEP
2341     /* XXX: who is wrong, the requestor or the implementation? */
2342     if ([nsname compare: @"Highlight" options: NSCaseInsensitiveSearch]
2343         == NSOrderedSame)
2344       nsname = @"highlightColor";
2345 #endif
2347     lenum = [[NSColorList availableColorLists] objectEnumerator];
2348     while ( (clist = [lenum nextObject]) && new == nil)
2349       {
2350         cenum = [[clist allKeys] objectEnumerator];
2351         while ( (name = [cenum nextObject]) && new == nil )
2352           {
2353             if ([name compare: nsname
2354                       options: NSCaseInsensitiveSearch] == NSOrderedSame )
2355               new = [clist colorWithKey: name];
2356           }
2357       }
2358   }
2360   if (new)
2361     *col = [new colorUsingDefaultColorSpace];
2362   unblock_input ();
2363   return new ? 0 : 1;
2368 ns_lisp_to_color (Lisp_Object color, NSColor **col)
2369 /* --------------------------------------------------------------------------
2370      Convert a Lisp string object to a NS color
2371    -------------------------------------------------------------------------- */
2373   NSTRACE ("ns_lisp_to_color");
2374   if (STRINGP (color))
2375     return ns_get_color (SSDATA (color), col);
2376   else if (SYMBOLP (color))
2377     return ns_get_color (SSDATA (SYMBOL_NAME (color)), col);
2378   return 1;
2382 void
2383 ns_query_color(void *col, XColor *color_def, int setPixel)
2384 /* --------------------------------------------------------------------------
2385          Get ARGB values out of NSColor col and put them into color_def.
2386          If setPixel, set the pixel to a concatenated version.
2387          and set color_def pixel to the resulting index.
2388    -------------------------------------------------------------------------- */
2390   EmacsCGFloat r, g, b, a;
2392   [((NSColor *)col) getRed: &r green: &g blue: &b alpha: &a];
2393   color_def->red   = r * 65535;
2394   color_def->green = g * 65535;
2395   color_def->blue  = b * 65535;
2397   if (setPixel == YES)
2398     color_def->pixel
2399       = ARGB_TO_ULONG((int)(a*255),
2400                       (int)(r*255), (int)(g*255), (int)(b*255));
2404 bool
2405 ns_defined_color (struct frame *f,
2406                   const char *name,
2407                   XColor *color_def,
2408                   bool alloc,
2409                   bool makeIndex)
2410 /* --------------------------------------------------------------------------
2411          Return true if named color found, and set color_def rgb accordingly.
2412          If makeIndex and alloc are nonzero put the color in the color_table,
2413          and set color_def pixel to the resulting index.
2414          If makeIndex is zero, set color_def pixel to ARGB.
2415          Return false if not found
2416    -------------------------------------------------------------------------- */
2418   NSColor *col;
2419   NSTRACE_WHEN (NSTRACE_GROUP_COLOR, "ns_defined_color");
2421   block_input ();
2422   if (ns_get_color (name, &col) != 0) /* Color not found  */
2423     {
2424       unblock_input ();
2425       return 0;
2426     }
2427   if (makeIndex && alloc)
2428     color_def->pixel = ns_index_color (col, f);
2429   ns_query_color (col, color_def, !makeIndex);
2430   unblock_input ();
2431   return 1;
2435 void
2436 x_set_frame_alpha (struct frame *f)
2437 /* --------------------------------------------------------------------------
2438      change the entire-frame transparency
2439    -------------------------------------------------------------------------- */
2441   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
2442   double alpha = 1.0;
2443   double alpha_min = 1.0;
2445   NSTRACE ("x_set_frame_alpha");
2447   if (dpyinfo->x_highlight_frame == f)
2448     alpha = f->alpha[0];
2449   else
2450     alpha = f->alpha[1];
2452   if (FLOATP (Vframe_alpha_lower_limit))
2453     alpha_min = XFLOAT_DATA (Vframe_alpha_lower_limit);
2454   else if (INTEGERP (Vframe_alpha_lower_limit))
2455     alpha_min = (XINT (Vframe_alpha_lower_limit)) / 100.0;
2457   if (alpha < 0.0)
2458     return;
2459   else if (1.0 < alpha)
2460     alpha = 1.0;
2461   else if (0.0 <= alpha && alpha < alpha_min && alpha_min <= 1.0)
2462     alpha = alpha_min;
2464 #ifdef NS_IMPL_COCOA
2465   {
2466     EmacsView *view = FRAME_NS_VIEW (f);
2467   [[view window] setAlphaValue: alpha];
2468   }
2469 #endif
2473 /* ==========================================================================
2475     Mouse handling
2477    ========================================================================== */
2480 void
2481 frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y)
2482 /* --------------------------------------------------------------------------
2483      Programmatically reposition mouse pointer in pixel coordinates
2484    -------------------------------------------------------------------------- */
2486   NSTRACE ("frame_set_mouse_pixel_position");
2488   /* FIXME: what about GNUstep? */
2489 #ifdef NS_IMPL_COCOA
2490   CGPoint mouse_pos =
2491     CGPointMake(f->left_pos + pix_x,
2492                 f->top_pos + pix_y +
2493                 FRAME_NS_TITLEBAR_HEIGHT(f) + FRAME_TOOLBAR_HEIGHT(f));
2494   CGWarpMouseCursorPosition (mouse_pos);
2495 #endif
2498 static int
2499 note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y)
2500 /*   ------------------------------------------------------------------------
2501      Called by EmacsView on mouseMovement events.  Passes on
2502      to emacs mainstream code if we moved off of a rect of interest
2503      known as last_mouse_glyph.
2504      ------------------------------------------------------------------------ */
2506   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
2507   NSRect *r;
2509 //  NSTRACE ("note_mouse_movement");
2511   dpyinfo->last_mouse_motion_frame = frame;
2512   r = &dpyinfo->last_mouse_glyph;
2514   /* Note, this doesn't get called for enter/leave, since we don't have a
2515      position.  Those are taken care of in the corresponding NSView methods. */
2517   /* has movement gone beyond last rect we were tracking? */
2518   if (x < r->origin.x || x >= r->origin.x + r->size.width
2519       || y < r->origin.y || y >= r->origin.y + r->size.height)
2520     {
2521       ns_update_begin (frame);
2522       frame->mouse_moved = 1;
2523       note_mouse_highlight (frame, x, y);
2524       remember_mouse_glyph (frame, x, y, r);
2525       ns_update_end (frame);
2526       return 1;
2527     }
2529   return 0;
2533 static void
2534 ns_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window,
2535                    enum scroll_bar_part *part, Lisp_Object *x, Lisp_Object *y,
2536                    Time *time)
2537 /* --------------------------------------------------------------------------
2538     External (hook): inform emacs about mouse position and hit parts.
2539     If a scrollbar is being dragged, set bar_window, part, x, y, time.
2540     x & y should be position in the scrollbar (the whole bar, not the handle)
2541     and length of scrollbar respectively
2542    -------------------------------------------------------------------------- */
2544   id view;
2545   NSPoint position;
2546   Lisp_Object frame, tail;
2547   struct frame *f;
2548   struct ns_display_info *dpyinfo;
2550   NSTRACE ("ns_mouse_position");
2552   if (*fp == NULL)
2553     {
2554       fprintf (stderr, "Warning: ns_mouse_position () called with null *fp.\n");
2555       return;
2556     }
2558   dpyinfo = FRAME_DISPLAY_INFO (*fp);
2560   block_input ();
2562   /* Clear the mouse-moved flag for every frame on this display.  */
2563   FOR_EACH_FRAME (tail, frame)
2564     if (FRAME_NS_P (XFRAME (frame))
2565         && FRAME_NS_DISPLAY (XFRAME (frame)) == FRAME_NS_DISPLAY (*fp))
2566       XFRAME (frame)->mouse_moved = 0;
2568   dpyinfo->last_mouse_scroll_bar = nil;
2569   if (dpyinfo->last_mouse_frame
2570       && FRAME_LIVE_P (dpyinfo->last_mouse_frame))
2571     f = dpyinfo->last_mouse_frame;
2572   else
2573     f = dpyinfo->x_focus_frame ? dpyinfo->x_focus_frame : SELECTED_FRAME ();
2575   if (f && FRAME_NS_P (f))
2576     {
2577       view = FRAME_NS_VIEW (*fp);
2579       position = [[view window] mouseLocationOutsideOfEventStream];
2580       position = [view convertPoint: position fromView: nil];
2581       remember_mouse_glyph (f, position.x, position.y,
2582                             &dpyinfo->last_mouse_glyph);
2583       NSTRACE_POINT ("position", position);
2585       if (bar_window) *bar_window = Qnil;
2586       if (part) *part = scroll_bar_above_handle;
2588       if (x) XSETINT (*x, lrint (position.x));
2589       if (y) XSETINT (*y, lrint (position.y));
2590       if (time)
2591         *time = dpyinfo->last_mouse_movement_time;
2592       *fp = f;
2593     }
2595   unblock_input ();
2599 static void
2600 ns_frame_up_to_date (struct frame *f)
2601 /* --------------------------------------------------------------------------
2602     External (hook): Fix up mouse highlighting right after a full update.
2603     Can't use FRAME_MOUSE_UPDATE due to ns_frame_begin and ns_frame_end calls.
2604    -------------------------------------------------------------------------- */
2606   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_frame_up_to_date");
2608   if (FRAME_NS_P (f))
2609     {
2610       Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
2611       if (f == hlinfo->mouse_face_mouse_frame)
2612         {
2613           block_input ();
2614           ns_update_begin(f);
2615           note_mouse_highlight (hlinfo->mouse_face_mouse_frame,
2616                                 hlinfo->mouse_face_mouse_x,
2617                                 hlinfo->mouse_face_mouse_y);
2618           ns_update_end(f);
2619           unblock_input ();
2620         }
2621     }
2625 static void
2626 ns_define_frame_cursor (struct frame *f, Cursor cursor)
2627 /* --------------------------------------------------------------------------
2628     External (RIF): set frame mouse pointer type.
2629    -------------------------------------------------------------------------- */
2631   NSTRACE ("ns_define_frame_cursor");
2632   if (FRAME_POINTER_TYPE (f) != cursor)
2633     {
2634       EmacsView *view = FRAME_NS_VIEW (f);
2635       FRAME_POINTER_TYPE (f) = cursor;
2636       [[view window] invalidateCursorRectsForView: view];
2637       /* Redisplay assumes this function also draws the changed frame
2638          cursor, but this function doesn't, so do it explicitly.  */
2639       x_update_cursor (f, 1);
2640     }
2645 /* ==========================================================================
2647     Keyboard handling
2649    ========================================================================== */
2652 static unsigned
2653 ns_convert_key (unsigned code)
2654 /* --------------------------------------------------------------------------
2655     Internal call used by NSView-keyDown.
2656    -------------------------------------------------------------------------- */
2658   const unsigned last_keysym = ARRAYELTS (convert_ns_to_X_keysym);
2659   unsigned keysym;
2660   /* An array would be faster, but less easy to read. */
2661   for (keysym = 0; keysym < last_keysym; keysym += 2)
2662     if (code == convert_ns_to_X_keysym[keysym])
2663       return 0xFF00 | convert_ns_to_X_keysym[keysym+1];
2664   return 0;
2665 /* if decide to use keyCode and Carbon table, use this line:
2666      return code > 0xff ? 0 : 0xFF00 | ns_keycode_to_xkeysym_table[code]; */
2670 char *
2671 x_get_keysym_name (int keysym)
2672 /* --------------------------------------------------------------------------
2673     Called by keyboard.c.  Not sure if the return val is important, except
2674     that it be unique.
2675    -------------------------------------------------------------------------- */
2677   static char value[16];
2678   NSTRACE ("x_get_keysym_name");
2679   sprintf (value, "%d", keysym);
2680   return value;
2683 #ifdef NS_IMPL_COCOA
2684 static UniChar
2685 ns_get_shifted_character (NSEvent *event)
2686 /* Look up the character corresponding to the key pressed on the
2687    current keyboard layout and the currently configured shift-like
2688    modifiers.  This ignores the control-like modifiers that cause
2689    [event characters] to give us the wrong result.
2691    Although UCKeyTranslate doesn't require the Carbon framework, some
2692    of the surrounding paraphernalia does, so this function makes
2693    Carbon a requirement.  */
2695   static UInt32 dead_key_state;
2697   /* UCKeyTranslate may return up to 255 characters.  If the buffer
2698      isn't large enough then it produces an error.  What kind of
2699      keyboard inputs 255 characters in a single keypress?  */
2700   UniChar buf[255];
2701   UniCharCount max_string_length = 255;
2702   UniCharCount actual_string_length = 0;
2703   OSStatus result;
2705   CFDataRef layout_ref = (CFDataRef) TISGetInputSourceProperty
2706     (TISCopyCurrentKeyboardLayoutInputSource (), kTISPropertyUnicodeKeyLayoutData);
2707   UCKeyboardLayout* layout = (UCKeyboardLayout*) CFDataGetBytePtr (layout_ref);
2709   UInt32 flags = [event modifierFlags];
2710   UInt32 modifiers = (flags & NSEventModifierFlagShift) ? shiftKey : 0;
2712   NSTRACE ("ns_get_shifted_character");
2714   if ((flags & NSRightAlternateKeyMask) == NSRightAlternateKeyMask
2715       && (EQ (ns_right_alternate_modifier, Qnone)
2716           || (EQ (ns_right_alternate_modifier, Qleft)
2717               && EQ (ns_alternate_modifier, Qnone))))
2718     modifiers |= rightOptionKey;
2720   if ((flags & NSLeftAlternateKeyMask) == NSLeftAlternateKeyMask
2721       && EQ (ns_alternate_modifier, Qnone))
2722     modifiers |= optionKey;
2724   if ((flags & NSRightCommandKeyMask) == NSRightCommandKeyMask
2725       && (EQ (ns_right_command_modifier, Qnone)
2726           || (EQ (ns_right_command_modifier, Qleft)
2727               && EQ (ns_command_modifier, Qnone))))
2728     /* Carbon doesn't differentiate between left and right command
2729        keys.  */
2730     modifiers |= cmdKey;
2732   if ((flags & NSLeftCommandKeyMask) == NSLeftCommandKeyMask
2733       && EQ (ns_command_modifier, Qnone))
2734     modifiers |= cmdKey;
2736   result = UCKeyTranslate (layout, [event keyCode], kUCKeyActionDown,
2737                            (modifiers >> 8) & 0xFF, LMGetKbdType (),
2738                            kUCKeyTranslateNoDeadKeysBit, &dead_key_state,
2739                            max_string_length, &actual_string_length, buf);
2741   if (result != 0)
2742     {
2743       NSLog(@"Failed to translate character '%@' with modifiers %x",
2744             [event characters], modifiers);
2745       return 0;
2746     }
2748   /* FIXME: What do we do if more than one code unit is returned?  */
2749   if (actual_string_length > 0)
2750     return buf[0];
2752   return 0;
2754 #endif /* NS_IMPL_COCOA */
2756 /* ==========================================================================
2758     Block drawing operations
2760    ========================================================================== */
2763 static void
2764 ns_redraw_scroll_bars (struct frame *f)
2766   int i;
2767   id view;
2768   NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews];
2769   NSTRACE ("ns_redraw_scroll_bars");
2770   for (i =[subviews count]-1; i >= 0; i--)
2771     {
2772       view = [subviews objectAtIndex: i];
2773       if (![view isKindOfClass: [EmacsScroller class]]) continue;
2774       [view display];
2775     }
2779 void
2780 ns_clear_frame (struct frame *f)
2781 /* --------------------------------------------------------------------------
2782       External (hook): Erase the entire frame
2783    -------------------------------------------------------------------------- */
2785   NSView *view = FRAME_NS_VIEW (f);
2786   NSRect r;
2788   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_clear_frame");
2790  /* comes on initial frame because we have
2791     after-make-frame-functions = select-frame */
2792  if (!FRAME_DEFAULT_FACE (f))
2793    return;
2795   mark_window_cursors_off (XWINDOW (FRAME_ROOT_WINDOW (f)));
2797   r = [view bounds];
2799   block_input ();
2800   ns_focus (f, &r, 1);
2801   [ns_lookup_indexed_color (NS_FACE_BACKGROUND
2802                             (FACE_FROM_ID (f, DEFAULT_FACE_ID)), f) set];
2803   NSRectFill (r);
2804   ns_unfocus (f);
2806   /* as of 2006/11 or so this is now needed */
2807   ns_redraw_scroll_bars (f);
2808   unblock_input ();
2812 static void
2813 ns_clear_frame_area (struct frame *f, int x, int y, int width, int height)
2814 /* --------------------------------------------------------------------------
2815     External (RIF):  Clear section of frame
2816    -------------------------------------------------------------------------- */
2818   NSRect r = NSMakeRect (x, y, width, height);
2819   NSView *view = FRAME_NS_VIEW (f);
2820   struct face *face = FRAME_DEFAULT_FACE (f);
2822   if (!view || !face)
2823     return;
2825   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_clear_frame_area");
2827   r = NSIntersectionRect (r, [view frame]);
2828   ns_focus (f, &r, 1);
2829   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), f) set];
2831   NSRectFill (r);
2833   ns_unfocus (f);
2834   return;
2837 static void
2838 ns_copy_bits (struct frame *f, NSRect src, NSRect dest)
2840   NSTRACE ("ns_copy_bits");
2842   if (FRAME_NS_VIEW (f))
2843     {
2844       hide_bell();              // Ensure the bell image isn't scrolled.
2846       ns_focus (f, &dest, 1);
2847       [FRAME_NS_VIEW (f) scrollRect: src
2848                                  by: NSMakeSize (dest.origin.x - src.origin.x,
2849                                                  dest.origin.y - src.origin.y)];
2850       ns_unfocus (f);
2851     }
2854 static void
2855 ns_scroll_run (struct window *w, struct run *run)
2856 /* --------------------------------------------------------------------------
2857     External (RIF):  Insert or delete n lines at line vpos
2858    -------------------------------------------------------------------------- */
2860   struct frame *f = XFRAME (w->frame);
2861   int x, y, width, height, from_y, to_y, bottom_y;
2863   NSTRACE ("ns_scroll_run");
2865   /* begin copy from other terms */
2866   /* Get frame-relative bounding box of the text display area of W,
2867      without mode lines.  Include in this box the left and right
2868      fringe of W.  */
2869   window_box (w, ANY_AREA, &x, &y, &width, &height);
2871   from_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->current_y);
2872   to_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->desired_y);
2873   bottom_y = y + height;
2875   if (to_y < from_y)
2876     {
2877       /* Scrolling up.  Make sure we don't copy part of the mode
2878          line at the bottom.  */
2879       if (from_y + run->height > bottom_y)
2880         height = bottom_y - from_y;
2881       else
2882         height = run->height;
2883     }
2884   else
2885     {
2886       /* Scrolling down.  Make sure we don't copy over the mode line.
2887          at the bottom.  */
2888       if (to_y + run->height > bottom_y)
2889         height = bottom_y - to_y;
2890       else
2891         height = run->height;
2892     }
2893   /* end copy from other terms */
2895   if (height == 0)
2896       return;
2898   block_input ();
2900   x_clear_cursor (w);
2902   {
2903     NSRect srcRect = NSMakeRect (x, from_y, width, height);
2904     NSRect dstRect = NSMakeRect (x, to_y, width, height);
2906     ns_copy_bits (f, srcRect , dstRect);
2907   }
2909   unblock_input ();
2913 static void
2914 ns_after_update_window_line (struct window *w, struct glyph_row *desired_row)
2915 /* --------------------------------------------------------------------------
2916     External (RIF): preparatory to fringe update after text was updated
2917    -------------------------------------------------------------------------- */
2919   struct frame *f;
2920   int width, height;
2922   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_after_update_window_line");
2924   /* begin copy from other terms */
2925   eassert (w);
2927   if (!desired_row->mode_line_p && !w->pseudo_window_p)
2928     desired_row->redraw_fringe_bitmaps_p = 1;
2930   /* When a window has disappeared, make sure that no rest of
2931      full-width rows stays visible in the internal border.  */
2932   if (windows_or_buffers_changed
2933       && desired_row->full_width_p
2934       && (f = XFRAME (w->frame),
2935           width = FRAME_INTERNAL_BORDER_WIDTH (f),
2936           width != 0)
2937       && (height = desired_row->visible_height,
2938           height > 0))
2939     {
2940       int y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, desired_row->y));
2942       block_input ();
2943       ns_clear_frame_area (f, 0, y, width, height);
2944       ns_clear_frame_area (f,
2945                            FRAME_PIXEL_WIDTH (f) - width,
2946                            y, width, height);
2947       unblock_input ();
2948     }
2952 static void
2953 ns_shift_glyphs_for_insert (struct frame *f,
2954                            int x, int y, int width, int height,
2955                            int shift_by)
2956 /* --------------------------------------------------------------------------
2957     External (RIF): copy an area horizontally, don't worry about clearing src
2958    -------------------------------------------------------------------------- */
2960   NSRect srcRect = NSMakeRect (x, y, width, height);
2961   NSRect dstRect = NSMakeRect (x+shift_by, y, width, height);
2963   NSTRACE ("ns_shift_glyphs_for_insert");
2965   ns_copy_bits (f, srcRect, dstRect);
2970 /* ==========================================================================
2972     Character encoding and metrics
2974    ========================================================================== */
2977 static void
2978 ns_compute_glyph_string_overhangs (struct glyph_string *s)
2979 /* --------------------------------------------------------------------------
2980      External (RIF); compute left/right overhang of whole string and set in s
2981    -------------------------------------------------------------------------- */
2983   struct font *font = s->font;
2985   if (s->char2b)
2986     {
2987       struct font_metrics metrics;
2988       unsigned int codes[2];
2989       codes[0] = *(s->char2b);
2990       codes[1] = *(s->char2b + s->nchars - 1);
2992       font->driver->text_extents (font, codes, 2, &metrics);
2993       s->left_overhang = -metrics.lbearing;
2994       s->right_overhang
2995         = metrics.rbearing > metrics.width
2996         ? metrics.rbearing - metrics.width : 0;
2997     }
2998   else
2999     {
3000       s->left_overhang = 0;
3001       if (EQ (font->driver->type, Qns))
3002         s->right_overhang = ((struct nsfont_info *)font)->ital ?
3003           FONT_HEIGHT (font) * 0.2 : 0;
3004       else
3005         s->right_overhang = 0;
3006     }
3011 /* ==========================================================================
3013     Fringe and cursor drawing
3015    ========================================================================== */
3018 extern int max_used_fringe_bitmap;
3019 static void
3020 ns_draw_fringe_bitmap (struct window *w, struct glyph_row *row,
3021                       struct draw_fringe_bitmap_params *p)
3022 /* --------------------------------------------------------------------------
3023     External (RIF); fringe-related
3024    -------------------------------------------------------------------------- */
3026   /* Fringe bitmaps comes in two variants, normal and periodic.  A
3027      periodic bitmap is used to create a continuous pattern.  Since a
3028      bitmap is rendered one text line at a time, the start offset (dh)
3029      of the bitmap varies.  Concretely, this is used for the empty
3030      line indicator.
3032      For a bitmap, "h + dh" is the full height and is always
3033      invariant.  For a normal bitmap "dh" is zero.
3035      For example, when the period is three and the full height is 72
3036      the following combinations exists:
3038        h=72 dh=0
3039        h=71 dh=1
3040        h=70 dh=2 */
3042   struct frame *f = XFRAME (WINDOW_FRAME (w));
3043   struct face *face = p->face;
3044   static EmacsImage **bimgs = NULL;
3045   static int nBimgs = 0;
3047   NSTRACE_WHEN (NSTRACE_GROUP_FRINGE, "ns_draw_fringe_bitmap");
3048   NSTRACE_MSG ("which:%d cursor:%d overlay:%d width:%d height:%d period:%d",
3049                p->which, p->cursor_p, p->overlay_p, p->wd, p->h, p->dh);
3051   /* grow bimgs if needed */
3052   if (nBimgs < max_used_fringe_bitmap)
3053     {
3054       bimgs = xrealloc (bimgs, max_used_fringe_bitmap * sizeof *bimgs);
3055       memset (bimgs + nBimgs, 0,
3056               (max_used_fringe_bitmap - nBimgs) * sizeof *bimgs);
3057       nBimgs = max_used_fringe_bitmap;
3058     }
3060   /* Must clip because of partially visible lines.  */
3061   ns_clip_to_row (w, row, ANY_AREA, YES);
3063   if (!p->overlay_p)
3064     {
3065       int bx = p->bx, by = p->by, nx = p->nx, ny = p->ny;
3067       if (bx >= 0 && nx > 0)
3068         {
3069           NSRect r = NSMakeRect (bx, by, nx, ny);
3070           NSRectClip (r);
3071           [ns_lookup_indexed_color (face->background, f) set];
3072           NSRectFill (r);
3073         }
3074     }
3076   if (p->which)
3077     {
3078       NSRect r = NSMakeRect (p->x, p->y, p->wd, p->h);
3079       EmacsImage *img = bimgs[p->which - 1];
3081       if (!img)
3082         {
3083           // Note: For "periodic" images, allocate one EmacsImage for
3084           // the base image, and use it for all dh:s.
3085           unsigned short *bits = p->bits;
3086           int full_height = p->h + p->dh;
3087           int i;
3088           unsigned char *cbits = xmalloc (full_height);
3090           for (i = 0; i < full_height; i++)
3091             cbits[i] = bits[i];
3092           img = [[EmacsImage alloc] initFromXBM: cbits width: 8
3093                                          height: full_height
3094                                              fg: 0 bg: 0];
3095           bimgs[p->which - 1] = img;
3096           xfree (cbits);
3097         }
3099       NSTRACE_RECT ("r", r);
3101       NSRectClip (r);
3102       /* Since we composite the bitmap instead of just blitting it, we need
3103          to erase the whole background. */
3104       [ns_lookup_indexed_color(face->background, f) set];
3105       NSRectFill (r);
3107       {
3108         NSColor *bm_color;
3109         if (!p->cursor_p)
3110           bm_color = ns_lookup_indexed_color(face->foreground, f);
3111         else if (p->overlay_p)
3112           bm_color = ns_lookup_indexed_color(face->background, f);
3113         else
3114           bm_color = f->output_data.ns->cursor_color;
3115         [img setXBMColor: bm_color];
3116       }
3118 #ifdef NS_IMPL_COCOA
3119       // Note: For periodic images, the full image height is "h + hd".
3120       // By using the height h, a suitable part of the image is used.
3121       NSRect fromRect = NSMakeRect(0, 0, p->wd, p->h);
3123       NSTRACE_RECT ("fromRect", fromRect);
3125       [img drawInRect: r
3126               fromRect: fromRect
3127              operation: NSCompositingOperationSourceOver
3128               fraction: 1.0
3129            respectFlipped: YES
3130                 hints: nil];
3131 #else
3132       {
3133         NSPoint pt = r.origin;
3134         pt.y += p->h;
3135         [img compositeToPoint: pt operation: NSCompositingOperationSourceOver];
3136       }
3137 #endif
3138     }
3139   ns_unfocus (f);
3143 static void
3144 ns_draw_window_cursor (struct window *w, struct glyph_row *glyph_row,
3145                        int x, int y, enum text_cursor_kinds cursor_type,
3146                        int cursor_width, bool on_p, bool active_p)
3147 /* --------------------------------------------------------------------------
3148      External call (RIF): draw cursor.
3149      Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
3150    -------------------------------------------------------------------------- */
3152   NSRect r, s;
3153   int fx, fy, h, cursor_height;
3154   struct frame *f = WINDOW_XFRAME (w);
3155   struct glyph *phys_cursor_glyph;
3156   struct glyph *cursor_glyph;
3157   struct face *face;
3158   NSColor *hollow_color = FRAME_BACKGROUND_COLOR (f);
3160   /* If cursor is out of bounds, don't draw garbage.  This can happen
3161      in mini-buffer windows when switching between echo area glyphs
3162      and mini-buffer.  */
3164   NSTRACE ("ns_draw_window_cursor");
3166   if (!on_p)
3167     return;
3169   w->phys_cursor_type = cursor_type;
3170   w->phys_cursor_on_p = on_p;
3172   if (cursor_type == NO_CURSOR)
3173     {
3174       w->phys_cursor_width = 0;
3175       return;
3176     }
3178   if ((phys_cursor_glyph = get_phys_cursor_glyph (w)) == NULL)
3179     {
3180       if (glyph_row->exact_window_width_line_p
3181           && w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])
3182         {
3183           glyph_row->cursor_in_fringe_p = 1;
3184           draw_fringe_bitmap (w, glyph_row, 0);
3185         }
3186       return;
3187     }
3189   /* We draw the cursor (with NSRectFill), then draw the glyph on top
3190      (other terminals do it the other way round).  We must set
3191      w->phys_cursor_width to the cursor width.  For bar cursors, that
3192      is CURSOR_WIDTH; for box cursors, it is the glyph width.  */
3193   get_phys_cursor_geometry (w, glyph_row, phys_cursor_glyph, &fx, &fy, &h);
3195   /* The above get_phys_cursor_geometry call set w->phys_cursor_width
3196      to the glyph width; replace with CURSOR_WIDTH for (V)BAR cursors. */
3197   if (cursor_type == BAR_CURSOR)
3198     {
3199       if (cursor_width < 1)
3200         cursor_width = max (FRAME_CURSOR_WIDTH (f), 1);
3202       /* The bar cursor should never be wider than the glyph. */
3203       if (cursor_width < w->phys_cursor_width)
3204         w->phys_cursor_width = cursor_width;
3205     }
3206   /* If we have an HBAR, "cursor_width" MAY specify height. */
3207   else if (cursor_type == HBAR_CURSOR)
3208     {
3209       cursor_height = (cursor_width < 1) ? lrint (0.25 * h) : cursor_width;
3210       if (cursor_height > glyph_row->height)
3211         cursor_height = glyph_row->height;
3212       if (h > cursor_height) // Cursor smaller than line height, move down
3213         fy += h - cursor_height;
3214       h = cursor_height;
3215     }
3217   r.origin.x = fx, r.origin.y = fy;
3218   r.size.height = h;
3219   r.size.width = w->phys_cursor_width;
3221   /* Prevent the cursor from being drawn outside the text area. */
3222   ns_clip_to_row (w, glyph_row, TEXT_AREA, NO); /* do ns_focus(f, &r, 1); if remove */
3225   face = FACE_FROM_ID_OR_NULL (f, phys_cursor_glyph->face_id);
3226   if (face && NS_FACE_BACKGROUND (face)
3227       == ns_index_color (FRAME_CURSOR_COLOR (f), f))
3228     {
3229       [ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), f) set];
3230       hollow_color = FRAME_CURSOR_COLOR (f);
3231     }
3232   else
3233     [FRAME_CURSOR_COLOR (f) set];
3235 #ifdef NS_IMPL_COCOA
3236   /* TODO: This makes drawing of cursor plus that of phys_cursor_glyph
3237            atomic.  Cleaner ways of doing this should be investigated.
3238            One way would be to set a global variable DRAWING_CURSOR
3239            when making the call to draw_phys..(), don't focus in that
3240            case, then move the ns_unfocus() here after that call. */
3241   NSDisableScreenUpdates ();
3242 #endif
3244   switch (cursor_type)
3245     {
3246     case DEFAULT_CURSOR:
3247     case NO_CURSOR:
3248       break;
3249     case FILLED_BOX_CURSOR:
3250       NSRectFill (r);
3251       break;
3252     case HOLLOW_BOX_CURSOR:
3253       NSRectFill (r);
3254       [hollow_color set];
3255       NSRectFill (NSInsetRect (r, 1, 1));
3256       [FRAME_CURSOR_COLOR (f) set];
3257       break;
3258     case HBAR_CURSOR:
3259       NSRectFill (r);
3260       break;
3261     case BAR_CURSOR:
3262       s = r;
3263       /* If the character under cursor is R2L, draw the bar cursor
3264          on the right of its glyph, rather than on the left.  */
3265       cursor_glyph = get_phys_cursor_glyph (w);
3266       if ((cursor_glyph->resolved_level & 1) != 0)
3267         s.origin.x += cursor_glyph->pixel_width - s.size.width;
3269       NSRectFill (s);
3270       break;
3271     }
3272   ns_unfocus (f);
3274   /* draw the character under the cursor */
3275   if (cursor_type != NO_CURSOR)
3276     draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
3278 #ifdef NS_IMPL_COCOA
3279   NSEnableScreenUpdates ();
3280 #endif
3285 static void
3286 ns_draw_vertical_window_border (struct window *w, int x, int y0, int y1)
3287 /* --------------------------------------------------------------------------
3288      External (RIF): Draw a vertical line.
3289    -------------------------------------------------------------------------- */
3291   struct frame *f = XFRAME (WINDOW_FRAME (w));
3292   struct face *face;
3293   NSRect r = NSMakeRect (x, y0, 1, y1-y0);
3295   NSTRACE ("ns_draw_vertical_window_border");
3297   face = FACE_FROM_ID_OR_NULL (f, VERTICAL_BORDER_FACE_ID);
3299   ns_focus (f, &r, 1);
3300   if (face)
3301     [ns_lookup_indexed_color(face->foreground, f) set];
3303   NSRectFill(r);
3304   ns_unfocus (f);
3308 static void
3309 ns_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1)
3310 /* --------------------------------------------------------------------------
3311      External (RIF): Draw a window divider.
3312    -------------------------------------------------------------------------- */
3314   struct frame *f = XFRAME (WINDOW_FRAME (w));
3315   struct face *face = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FACE_ID);
3316   struct face *face_first
3317     = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FIRST_PIXEL_FACE_ID);
3318   struct face *face_last
3319     = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_LAST_PIXEL_FACE_ID);
3320   unsigned long color = face ? face->foreground : FRAME_FOREGROUND_PIXEL (f);
3321   unsigned long color_first = (face_first
3322                                ? face_first->foreground
3323                                : FRAME_FOREGROUND_PIXEL (f));
3324   unsigned long color_last = (face_last
3325                               ? face_last->foreground
3326                               : FRAME_FOREGROUND_PIXEL (f));
3327   NSRect divider = NSMakeRect (x0, y0, x1-x0, y1-y0);
3329   NSTRACE ("ns_draw_window_divider");
3331   ns_focus (f, &divider, 1);
3333   if ((y1 - y0 > x1 - x0) && (x1 - x0 >= 3))
3334     /* A vertical divider, at least three pixels wide: Draw first and
3335        last pixels differently.  */
3336     {
3337       [ns_lookup_indexed_color(color_first, f) set];
3338       NSRectFill(NSMakeRect (x0, y0, 1, y1 - y0));
3339       [ns_lookup_indexed_color(color, f) set];
3340       NSRectFill(NSMakeRect (x0 + 1, y0, x1 - x0 - 2, y1 - y0));
3341       [ns_lookup_indexed_color(color_last, f) set];
3342       NSRectFill(NSMakeRect (x1 - 1, y0, 1, y1 - y0));
3343     }
3344   else if ((x1 - x0 > y1 - y0) && (y1 - y0 >= 3))
3345     /* A horizontal divider, at least three pixels high: Draw first and
3346        last pixels differently.  */
3347     {
3348       [ns_lookup_indexed_color(color_first, f) set];
3349       NSRectFill(NSMakeRect (x0, y0, x1 - x0, 1));
3350       [ns_lookup_indexed_color(color, f) set];
3351       NSRectFill(NSMakeRect (x0, y0 + 1, x1 - x0, y1 - y0 - 2));
3352       [ns_lookup_indexed_color(color_last, f) set];
3353       NSRectFill(NSMakeRect (x0, y1 - 1, x1 - x0, 1));
3354     }
3355   else
3356     {
3357       /* In any other case do not draw the first and last pixels
3358          differently.  */
3359       [ns_lookup_indexed_color(color, f) set];
3360       NSRectFill(divider);
3361     }
3363   ns_unfocus (f);
3366 static void
3367 ns_show_hourglass (struct frame *f)
3369   /* TODO: add NSProgressIndicator to all frames.  */
3372 static void
3373 ns_hide_hourglass (struct frame *f)
3375   /* TODO: remove NSProgressIndicator from all frames.  */
3378 /* ==========================================================================
3380     Glyph drawing operations
3382    ========================================================================== */
3384 static int
3385 ns_get_glyph_string_clip_rect (struct glyph_string *s, NativeRectangle *nr)
3386 /* --------------------------------------------------------------------------
3387     Wrapper utility to account for internal border width on full-width lines,
3388     and allow top full-width rows to hit the frame top.  nr should be pointer
3389     to two successive NSRects.  Number of rects actually used is returned.
3390    -------------------------------------------------------------------------- */
3392   int n = get_glyph_string_clip_rects (s, nr, 2);
3393   return n;
3396 /* --------------------------------------------------------------------
3397    Draw a wavy line under glyph string s. The wave fills wave_height
3398    pixels from y.
3400                     x          wave_length = 2
3401                                  --
3402                 y    *   *   *   *   *
3403                      |* * * * * * * * *
3404     wave_height = 3  | *   *   *   *
3405   --------------------------------------------------------------------- */
3407 static void
3408 ns_draw_underwave (struct glyph_string *s, EmacsCGFloat width, EmacsCGFloat x)
3410   int wave_height = 3, wave_length = 2;
3411   int y, dx, dy, odd, xmax;
3412   NSPoint a, b;
3413   NSRect waveClip;
3415   dx = wave_length;
3416   dy = wave_height - 1;
3417   y =  s->ybase - wave_height + 3;
3418   xmax = x + width;
3420   /* Find and set clipping rectangle */
3421   waveClip = NSMakeRect (x, y, width, wave_height);
3422   [[NSGraphicsContext currentContext] saveGraphicsState];
3423   NSRectClip (waveClip);
3425   /* Draw the waves */
3426   a.x = x - ((int)(x) % dx) + (EmacsCGFloat) 0.5;
3427   b.x = a.x + dx;
3428   odd = (int)(a.x/dx) % 2;
3429   a.y = b.y = y + 0.5;
3431   if (odd)
3432     a.y += dy;
3433   else
3434     b.y += dy;
3436   while (a.x <= xmax)
3437     {
3438       [NSBezierPath strokeLineFromPoint:a toPoint:b];
3439       a.x = b.x, a.y = b.y;
3440       b.x += dx, b.y = y + 0.5 + odd*dy;
3441       odd = !odd;
3442     }
3444   /* Restore previous clipping rectangle(s) */
3445   [[NSGraphicsContext currentContext] restoreGraphicsState];
3450 static void
3451 ns_draw_text_decoration (struct glyph_string *s, struct face *face,
3452                          NSColor *defaultCol, CGFloat width, CGFloat x)
3453 /* --------------------------------------------------------------------------
3454    Draw underline, overline, and strike-through on glyph string s.
3455    -------------------------------------------------------------------------- */
3457   if (s->for_overlaps)
3458     return;
3460   /* Do underline. */
3461   if (face->underline_p)
3462     {
3463       if (s->face->underline_type == FACE_UNDER_WAVE)
3464         {
3465           if (face->underline_defaulted_p)
3466             [defaultCol set];
3467           else
3468             [ns_lookup_indexed_color (face->underline_color, s->f) set];
3470           ns_draw_underwave (s, width, x);
3471         }
3472       else if (s->face->underline_type == FACE_UNDER_LINE)
3473         {
3475           NSRect r;
3476           unsigned long thickness, position;
3478           /* If the prev was underlined, match its appearance. */
3479           if (s->prev && s->prev->face->underline_p
3480               && s->prev->face->underline_type == FACE_UNDER_LINE
3481               && s->prev->underline_thickness > 0)
3482             {
3483               thickness = s->prev->underline_thickness;
3484               position = s->prev->underline_position;
3485             }
3486           else
3487             {
3488               struct font *font = font_for_underline_metrics (s);
3489               unsigned long descent = s->y + s->height - s->ybase;
3490               unsigned long minimum_offset;
3491               BOOL underline_at_descent_line, use_underline_position_properties;
3492               Lisp_Object val = buffer_local_value (Qunderline_minimum_offset,
3493                                                     s->w->contents);
3494               if (INTEGERP (val))
3495                 minimum_offset = XFASTINT (val);
3496               else
3497                 minimum_offset = 1;
3498               val = buffer_local_value (Qx_underline_at_descent_line,
3499                                         s->w->contents);
3500               underline_at_descent_line = !(NILP (val) || EQ (val, Qunbound));
3501               val = buffer_local_value (Qx_use_underline_position_properties,
3502                                         s->w->contents);
3503               use_underline_position_properties =
3504                 !(NILP (val) || EQ (val, Qunbound));
3506               /* Use underline thickness of font, defaulting to 1. */
3507               thickness = (font && font->underline_thickness > 0)
3508                 ? font->underline_thickness : 1;
3510               /* Determine the offset of underlining from the baseline. */
3511               if (underline_at_descent_line)
3512                 position = descent - thickness;
3513               else if (use_underline_position_properties
3514                        && font && font->underline_position >= 0)
3515                 position = font->underline_position;
3516               else if (font)
3517                 position = lround (font->descent / 2);
3518               else
3519                 position = minimum_offset;
3521               position = max (position, minimum_offset);
3523               /* Ensure underlining is not cropped. */
3524               if (descent <= position)
3525                 {
3526                   position = descent - 1;
3527                   thickness = 1;
3528                 }
3529               else if (descent < position + thickness)
3530                 thickness = 1;
3531             }
3533           s->underline_thickness = thickness;
3534           s->underline_position = position;
3536           r = NSMakeRect (x, s->ybase + position, width, thickness);
3538           if (face->underline_defaulted_p)
3539             [defaultCol set];
3540           else
3541             [ns_lookup_indexed_color (face->underline_color, s->f) set];
3542           NSRectFill (r);
3543         }
3544     }
3545   /* Do overline. We follow other terms in using a thickness of 1
3546      and ignoring overline_margin. */
3547   if (face->overline_p)
3548     {
3549       NSRect r;
3550       r = NSMakeRect (x, s->y, width, 1);
3552       if (face->overline_color_defaulted_p)
3553         [defaultCol set];
3554       else
3555         [ns_lookup_indexed_color (face->overline_color, s->f) set];
3556       NSRectFill (r);
3557     }
3559   /* Do strike-through.  We follow other terms for thickness and
3560      vertical position.*/
3561   if (face->strike_through_p)
3562     {
3563       NSRect r;
3564       /* Y-coordinate and height of the glyph string's first glyph.
3565          We cannot use s->y and s->height because those could be
3566          larger if there are taller display elements (e.g., characters
3567          displayed with a larger font) in the same glyph row.  */
3568       int glyph_y = s->ybase - s->first_glyph->ascent;
3569       int glyph_height = s->first_glyph->ascent + s->first_glyph->descent;
3570       /* Strike-through width and offset from the glyph string's
3571          top edge.  */
3572       unsigned long h = 1;
3573       unsigned long dy;
3575       dy = lrint ((glyph_height - h) / 2);
3576       r = NSMakeRect (x, glyph_y + dy, width, 1);
3578       if (face->strike_through_color_defaulted_p)
3579         [defaultCol set];
3580       else
3581         [ns_lookup_indexed_color (face->strike_through_color, s->f) set];
3582       NSRectFill (r);
3583     }
3586 static void
3587 ns_draw_box (NSRect r, CGFloat thickness, NSColor *col,
3588              char left_p, char right_p)
3589 /* --------------------------------------------------------------------------
3590     Draw an unfilled rect inside r, optionally leaving left and/or right open.
3591     Note we can't just use an NSDrawRect command, because of the possibility
3592     of some sides not being drawn, and because the rect will be filled.
3593    -------------------------------------------------------------------------- */
3595   NSRect s = r;
3596   [col set];
3598   /* top, bottom */
3599   s.size.height = thickness;
3600   NSRectFill (s);
3601   s.origin.y += r.size.height - thickness;
3602   NSRectFill (s);
3604   s.size.height = r.size.height;
3605   s.origin.y = r.origin.y;
3607   /* left, right (optional) */
3608   s.size.width = thickness;
3609   if (left_p)
3610     NSRectFill (s);
3611   if (right_p)
3612     {
3613       s.origin.x += r.size.width - thickness;
3614       NSRectFill (s);
3615     }
3619 static void
3620 ns_draw_relief (NSRect r, int thickness, char raised_p,
3621                char top_p, char bottom_p, char left_p, char right_p,
3622                struct glyph_string *s)
3623 /* --------------------------------------------------------------------------
3624     Draw a relief rect inside r, optionally leaving some sides open.
3625     Note we can't just use an NSDrawBezel command, because of the possibility
3626     of some sides not being drawn, and because the rect will be filled.
3627    -------------------------------------------------------------------------- */
3629   static NSColor *baseCol = nil, *lightCol = nil, *darkCol = nil;
3630   NSColor *newBaseCol = nil;
3631   NSRect sr = r;
3633   NSTRACE ("ns_draw_relief");
3635   /* set up colors */
3637   if (s->face->use_box_color_for_shadows_p)
3638     {
3639       newBaseCol = ns_lookup_indexed_color (s->face->box_color, s->f);
3640     }
3641 /*     else if (s->first_glyph->type == IMAGE_GLYPH
3642            && s->img->pixmap
3643            && !IMAGE_BACKGROUND_TRANSPARENT (s->img, s->f, 0))
3644        {
3645          newBaseCol = IMAGE_BACKGROUND  (s->img, s->f, 0);
3646        } */
3647   else
3648     {
3649       newBaseCol = ns_lookup_indexed_color (s->face->background, s->f);
3650     }
3652   if (newBaseCol == nil)
3653     newBaseCol = [NSColor grayColor];
3655   if (newBaseCol != baseCol)  /* TODO: better check */
3656     {
3657       [baseCol release];
3658       baseCol = [newBaseCol retain];
3659       [lightCol release];
3660       lightCol = [[baseCol highlightWithLevel: 0.2] retain];
3661       [darkCol release];
3662       darkCol = [[baseCol shadowWithLevel: 0.3] retain];
3663     }
3665   [(raised_p ? lightCol : darkCol) set];
3667   /* TODO: mitering. Using NSBezierPath doesn't work because of color switch. */
3669   /* top */
3670   sr.size.height = thickness;
3671   if (top_p) NSRectFill (sr);
3673   /* left */
3674   sr.size.height = r.size.height;
3675   sr.size.width = thickness;
3676   if (left_p) NSRectFill (sr);
3678   [(raised_p ? darkCol : lightCol) set];
3680   /* bottom */
3681   sr.size.width = r.size.width;
3682   sr.size.height = thickness;
3683   sr.origin.y += r.size.height - thickness;
3684   if (bottom_p) NSRectFill (sr);
3686   /* right */
3687   sr.size.height = r.size.height;
3688   sr.origin.y = r.origin.y;
3689   sr.size.width = thickness;
3690   sr.origin.x += r.size.width - thickness;
3691   if (right_p) NSRectFill (sr);
3695 static void
3696 ns_dumpglyphs_box_or_relief (struct glyph_string *s)
3697 /* --------------------------------------------------------------------------
3698       Function modeled after x_draw_glyph_string_box ().
3699       Sets up parameters for drawing.
3700    -------------------------------------------------------------------------- */
3702   int right_x, last_x;
3703   char left_p, right_p;
3704   struct glyph *last_glyph;
3705   NSRect r;
3706   int thickness;
3707   struct face *face;
3709   if (s->hl == DRAW_MOUSE_FACE)
3710     {
3711       face = FACE_FROM_ID_OR_NULL (s->f,
3712                                    MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3713       if (!face)
3714         face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3715     }
3716   else
3717     face = s->face;
3719   thickness = face->box_line_width;
3721   NSTRACE ("ns_dumpglyphs_box_or_relief");
3723   last_x = ((s->row->full_width_p && !s->w->pseudo_window_p)
3724             ? WINDOW_RIGHT_EDGE_X (s->w)
3725             : window_box_right (s->w, s->area));
3726   last_glyph = (s->cmp || s->img
3727                 ? s->first_glyph : s->first_glyph + s->nchars-1);
3729   right_x = ((s->row->full_width_p && s->extends_to_end_of_line_p
3730               ? last_x - 1 : min (last_x, s->x + s->background_width) - 1));
3732   left_p = (s->first_glyph->left_box_line_p
3733             || (s->hl == DRAW_MOUSE_FACE
3734                 && (s->prev == NULL || s->prev->hl != s->hl)));
3735   right_p = (last_glyph->right_box_line_p
3736              || (s->hl == DRAW_MOUSE_FACE
3737                  && (s->next == NULL || s->next->hl != s->hl)));
3739   r = NSMakeRect (s->x, s->y, right_x - s->x + 1, s->height);
3741   /* TODO: Sometimes box_color is 0 and this seems wrong; should investigate. */
3742   if (s->face->box == FACE_SIMPLE_BOX && s->face->box_color)
3743     {
3744       ns_draw_box (r, abs (thickness),
3745                    ns_lookup_indexed_color (face->box_color, s->f),
3746                   left_p, right_p);
3747     }
3748   else
3749     {
3750       ns_draw_relief (r, abs (thickness), s->face->box == FACE_RAISED_BOX,
3751                      1, 1, left_p, right_p, s);
3752     }
3756 static void
3757 ns_maybe_dumpglyphs_background (struct glyph_string *s, char force_p)
3758 /* --------------------------------------------------------------------------
3759       Modeled after x_draw_glyph_string_background, which draws BG in
3760       certain cases.  Others are left to the text rendering routine.
3761    -------------------------------------------------------------------------- */
3763   NSTRACE ("ns_maybe_dumpglyphs_background");
3765   if (!s->background_filled_p/* || s->hl == DRAW_MOUSE_FACE*/)
3766     {
3767       int box_line_width = max (s->face->box_line_width, 0);
3768       if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width
3769           /* When xdisp.c ignores FONT_HEIGHT, we cannot trust font
3770              dimensions, since the actual glyphs might be much
3771              smaller.  So in that case we always clear the rectangle
3772              with background color.  */
3773           || FONT_TOO_HIGH (s->font)
3774           || s->font_not_found_p || s->extends_to_end_of_line_p || force_p)
3775         {
3776           struct face *face;
3777           if (s->hl == DRAW_MOUSE_FACE)
3778             {
3779               face
3780                 = FACE_FROM_ID_OR_NULL (s->f,
3781                                         MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3782               if (!face)
3783                 face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3784             }
3785           else
3786             face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3787           if (!face->stipple)
3788             [(NS_FACE_BACKGROUND (face) != 0
3789               ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
3790               : FRAME_BACKGROUND_COLOR (s->f)) set];
3791           else
3792             {
3793               struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
3794               [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set];
3795             }
3797           if (s->hl != DRAW_CURSOR)
3798             {
3799               NSRect r = NSMakeRect (s->x, s->y + box_line_width,
3800                                     s->background_width,
3801                                     s->height-2*box_line_width);
3802               NSRectFill (r);
3803             }
3805           s->background_filled_p = 1;
3806         }
3807     }
3811 static void
3812 ns_dumpglyphs_image (struct glyph_string *s, NSRect r)
3813 /* --------------------------------------------------------------------------
3814       Renders an image and associated borders.
3815    -------------------------------------------------------------------------- */
3817   EmacsImage *img = s->img->pixmap;
3818   int box_line_vwidth = max (s->face->box_line_width, 0);
3819   int x = s->x, y = s->ybase - image_ascent (s->img, s->face, &s->slice);
3820   int bg_x, bg_y, bg_height;
3821   int th;
3822   char raised_p;
3823   NSRect br;
3824   struct face *face;
3825   NSColor *tdCol;
3827   NSTRACE ("ns_dumpglyphs_image");
3829   if (s->face->box != FACE_NO_BOX
3830       && s->first_glyph->left_box_line_p && s->slice.x == 0)
3831     x += abs (s->face->box_line_width);
3833   bg_x = x;
3834   bg_y =  s->slice.y == 0 ? s->y : s->y + box_line_vwidth;
3835   bg_height = s->height;
3836   /* other terms have this, but was causing problems w/tabbar mode */
3837   /* - 2 * box_line_vwidth; */
3839   if (s->slice.x == 0) x += s->img->hmargin;
3840   if (s->slice.y == 0) y += s->img->vmargin;
3842   /* Draw BG: if we need larger area than image itself cleared, do that,
3843      otherwise, since we composite the image under NS (instead of mucking
3844      with its background color), we must clear just the image area. */
3845   if (s->hl == DRAW_MOUSE_FACE)
3846     {
3847       face = FACE_FROM_ID_OR_NULL (s->f,
3848                                    MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3849       if (!face)
3850        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3851     }
3852   else
3853     face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3855   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f) set];
3857   if (bg_height > s->slice.height || s->img->hmargin || s->img->vmargin
3858       || s->img->mask || s->img->pixmap == 0 || s->width != s->background_width)
3859     {
3860       br = NSMakeRect (bg_x, bg_y, s->background_width, bg_height);
3861       s->background_filled_p = 1;
3862     }
3863   else
3864     {
3865       br = NSMakeRect (x, y, s->slice.width, s->slice.height);
3866     }
3868   NSRectFill (br);
3870   /* Draw the image.. do we need to draw placeholder if img ==nil? */
3871   if (img != nil)
3872     {
3873 #ifdef NS_IMPL_COCOA
3874       NSRect dr = NSMakeRect (x, y, s->slice.width, s->slice.height);
3875       NSRect ir = NSMakeRect (s->slice.x,
3876                               s->img->height - s->slice.y - s->slice.height,
3877                               s->slice.width, s->slice.height);
3878       [img drawInRect: dr
3879              fromRect: ir
3880              operation: NSCompositingOperationSourceOver
3881               fraction: 1.0
3882            respectFlipped: YES
3883                 hints: nil];
3884 #else
3885       [img compositeToPoint: NSMakePoint (x, y + s->slice.height)
3886                   operation: NSCompositingOperationSourceOver];
3887 #endif
3888     }
3890   if (s->hl == DRAW_CURSOR)
3891     {
3892     [FRAME_CURSOR_COLOR (s->f) set];
3893     if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
3894       tdCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
3895     else
3896       /* Currently on NS img->mask is always 0. Since
3897          get_window_cursor_type specifies a hollow box cursor when on
3898          a non-masked image we never reach this clause. But we put it
3899          in, in anticipation of better support for image masks on
3900          NS. */
3901       tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3902     }
3903   else
3904     {
3905       tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3906     }
3908   /* Draw underline, overline, strike-through. */
3909   ns_draw_text_decoration (s, face, tdCol, br.size.width, br.origin.x);
3911   /* Draw relief, if requested */
3912   if (s->img->relief || s->hl ==DRAW_IMAGE_RAISED || s->hl ==DRAW_IMAGE_SUNKEN)
3913     {
3914       if (s->hl == DRAW_IMAGE_SUNKEN || s->hl == DRAW_IMAGE_RAISED)
3915         {
3916           th = tool_bar_button_relief >= 0 ?
3917             tool_bar_button_relief : DEFAULT_TOOL_BAR_BUTTON_RELIEF;
3918           raised_p = (s->hl == DRAW_IMAGE_RAISED);
3919         }
3920       else
3921         {
3922           th = abs (s->img->relief);
3923           raised_p = (s->img->relief > 0);
3924         }
3926       r.origin.x = x - th;
3927       r.origin.y = y - th;
3928       r.size.width = s->slice.width + 2*th-1;
3929       r.size.height = s->slice.height + 2*th-1;
3930       ns_draw_relief (r, th, raised_p,
3931                       s->slice.y == 0,
3932                       s->slice.y + s->slice.height == s->img->height,
3933                       s->slice.x == 0,
3934                       s->slice.x + s->slice.width == s->img->width, s);
3935     }
3937   /* If there is no mask, the background won't be seen,
3938      so draw a rectangle on the image for the cursor.
3939      Do this for all images, getting transparency right is not reliable.  */
3940   if (s->hl == DRAW_CURSOR)
3941     {
3942       int thickness = abs (s->img->relief);
3943       if (thickness == 0) thickness = 1;
3944       ns_draw_box (br, thickness, FRAME_CURSOR_COLOR (s->f), 1, 1);
3945     }
3949 static void
3950 ns_dumpglyphs_stretch (struct glyph_string *s)
3952   NSRect r[2];
3953   int n, i;
3954   struct face *face;
3955   NSColor *fgCol, *bgCol;
3957   if (!s->background_filled_p)
3958     {
3959       n = ns_get_glyph_string_clip_rect (s, r);
3960       *r = NSMakeRect (s->x, s->y, s->background_width, s->height);
3962       ns_focus (s->f, r, n);
3964       if (s->hl == DRAW_MOUSE_FACE)
3965        {
3966          face = FACE_FROM_ID_OR_NULL (s->f,
3967                                       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3968          if (!face)
3969            face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3970        }
3971       else
3972        face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3974       bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
3975       fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3977       for (i = 0; i < n; ++i)
3978         {
3979           if (!s->row->full_width_p)
3980             {
3981               int overrun, leftoverrun;
3983               /* truncate to avoid overwriting fringe and/or scrollbar */
3984               overrun = max (0, (s->x + s->background_width)
3985                              - (WINDOW_BOX_RIGHT_EDGE_X (s->w)
3986                                 - WINDOW_RIGHT_FRINGE_WIDTH (s->w)));
3987               r[i].size.width -= overrun;
3989               /* truncate to avoid overwriting to left of the window box */
3990               leftoverrun = (WINDOW_BOX_LEFT_EDGE_X (s->w)
3991                              + WINDOW_LEFT_FRINGE_WIDTH (s->w)) - s->x;
3993               if (leftoverrun > 0)
3994                 {
3995                   r[i].origin.x += leftoverrun;
3996                   r[i].size.width -= leftoverrun;
3997                 }
3999               /* XXX: Try to work between problem where a stretch glyph on
4000                  a partially-visible bottom row will clear part of the
4001                  modeline, and another where list-buffers headers and similar
4002                  rows erroneously have visible_height set to 0.  Not sure
4003                  where this is coming from as other terms seem not to show. */
4004               r[i].size.height = min (s->height, s->row->visible_height);
4005             }
4007           [bgCol set];
4009           /* NOTE: under NS this is NOT used to draw cursors, but we must avoid
4010              overwriting cursor (usually when cursor on a tab) */
4011           if (s->hl == DRAW_CURSOR)
4012             {
4013               CGFloat x, width;
4015               x = r[i].origin.x;
4016               width = s->w->phys_cursor_width;
4017               r[i].size.width -= width;
4018               r[i].origin.x += width;
4020               NSRectFill (r[i]);
4022               /* Draw overlining, etc. on the cursor. */
4023               if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
4024                 ns_draw_text_decoration (s, face, bgCol, width, x);
4025               else
4026                 ns_draw_text_decoration (s, face, fgCol, width, x);
4027             }
4028           else
4029             {
4030               NSRectFill (r[i]);
4031             }
4033           /* Draw overlining, etc. on the stretch glyph (or the part
4034              of the stretch glyph after the cursor). */
4035           ns_draw_text_decoration (s, face, fgCol, r[i].size.width,
4036                                    r[i].origin.x);
4037         }
4038       ns_unfocus (s->f);
4039       s->background_filled_p = 1;
4040     }
4044 static void
4045 ns_draw_glyph_string_foreground (struct glyph_string *s)
4047   int x, flags;
4048   struct font *font = s->font;
4050   /* If first glyph of S has a left box line, start drawing the text
4051      of S to the right of that box line.  */
4052   if (s->face && s->face->box != FACE_NO_BOX
4053       && s->first_glyph->left_box_line_p)
4054     x = s->x + eabs (s->face->box_line_width);
4055   else
4056     x = s->x;
4058   flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
4059     (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
4060      (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
4061       NS_DUMPGLYPH_NORMAL));
4063   font->driver->draw
4064     (s, s->cmp_from, s->nchars, x, s->ybase,
4065      (flags == NS_DUMPGLYPH_NORMAL && !s->background_filled_p)
4066      || flags == NS_DUMPGLYPH_MOUSEFACE);
4070 static void
4071 ns_draw_composite_glyph_string_foreground (struct glyph_string *s)
4073   int i, j, x;
4074   struct font *font = s->font;
4076   /* If first glyph of S has a left box line, start drawing the text
4077      of S to the right of that box line.  */
4078   if (s->face && s->face->box != FACE_NO_BOX
4079       && s->first_glyph->left_box_line_p)
4080     x = s->x + eabs (s->face->box_line_width);
4081   else
4082     x = s->x;
4084   /* S is a glyph string for a composition.  S->cmp_from is the index
4085      of the first character drawn for glyphs of this composition.
4086      S->cmp_from == 0 means we are drawing the very first character of
4087      this composition.  */
4089   /* Draw a rectangle for the composition if the font for the very
4090      first character of the composition could not be loaded.  */
4091   if (s->font_not_found_p)
4092     {
4093       if (s->cmp_from == 0)
4094         {
4095           NSRect r = NSMakeRect (s->x, s->y, s->width-1, s->height -1);
4096           ns_draw_box (r, 1, FRAME_CURSOR_COLOR (s->f), 1, 1);
4097         }
4098     }
4099   else if (! s->first_glyph->u.cmp.automatic)
4100     {
4101       int y = s->ybase;
4103       for (i = 0, j = s->cmp_from; i < s->nchars; i++, j++)
4104         /* TAB in a composition means display glyphs with padding
4105            space on the left or right.  */
4106         if (COMPOSITION_GLYPH (s->cmp, j) != '\t')
4107           {
4108             int xx = x + s->cmp->offsets[j * 2];
4109             int yy = y - s->cmp->offsets[j * 2 + 1];
4111             font->driver->draw (s, j, j + 1, xx, yy, false);
4112             if (s->face->overstrike)
4113               font->driver->draw (s, j, j + 1, xx + 1, yy, false);
4114           }
4115     }
4116   else
4117     {
4118       Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
4119       Lisp_Object glyph;
4120       int y = s->ybase;
4121       int width = 0;
4123       for (i = j = s->cmp_from; i < s->cmp_to; i++)
4124         {
4125           glyph = LGSTRING_GLYPH (gstring, i);
4126           if (NILP (LGLYPH_ADJUSTMENT (glyph)))
4127             width += LGLYPH_WIDTH (glyph);
4128           else
4129             {
4130               int xoff, yoff, wadjust;
4132               if (j < i)
4133                 {
4134                   font->driver->draw (s, j, i, x, y, false);
4135                   if (s->face->overstrike)
4136                     font->driver->draw (s, j, i, x + 1, y, false);
4137                   x += width;
4138                 }
4139               xoff = LGLYPH_XOFF (glyph);
4140               yoff = LGLYPH_YOFF (glyph);
4141               wadjust = LGLYPH_WADJUST (glyph);
4142               font->driver->draw (s, i, i + 1, x + xoff, y + yoff, false);
4143               if (s->face->overstrike)
4144                 font->driver->draw (s, i, i + 1, x + xoff + 1, y + yoff,
4145                                     false);
4146               x += wadjust;
4147               j = i + 1;
4148               width = 0;
4149             }
4150         }
4151       if (j < i)
4152         {
4153           font->driver->draw (s, j, i, x, y, false);
4154           if (s->face->overstrike)
4155             font->driver->draw (s, j, i, x + 1, y, false);
4156         }
4157     }
4160 static void
4161 ns_draw_glyph_string (struct glyph_string *s)
4162 /* --------------------------------------------------------------------------
4163       External (RIF): Main draw-text call.
4164    -------------------------------------------------------------------------- */
4166   /* TODO (optimize): focus for box and contents draw */
4167   NSRect r[2];
4168   int n;
4169   char box_drawn_p = 0;
4170   struct font *font = s->face->font;
4171   if (! font) font = FRAME_FONT (s->f);
4173   NSTRACE_WHEN (NSTRACE_GROUP_GLYPHS, "ns_draw_glyph_string");
4175   if (s->next && s->right_overhang && !s->for_overlaps/*&&s->hl!=DRAW_CURSOR*/)
4176     {
4177       int width;
4178       struct glyph_string *next;
4180       for (width = 0, next = s->next;
4181            next && width < s->right_overhang;
4182            width += next->width, next = next->next)
4183         if (next->first_glyph->type != IMAGE_GLYPH)
4184           {
4185             if (next->first_glyph->type != STRETCH_GLYPH)
4186               {
4187                 n = ns_get_glyph_string_clip_rect (s->next, r);
4188                 ns_focus (s->f, r, n);
4189                 ns_maybe_dumpglyphs_background (s->next, 1);
4190                 ns_unfocus (s->f);
4191               }
4192             else
4193               {
4194                 ns_dumpglyphs_stretch (s->next);
4195               }
4196             next->num_clips = 0;
4197           }
4198     }
4200   if (!s->for_overlaps && s->face->box != FACE_NO_BOX
4201         && (s->first_glyph->type == CHAR_GLYPH
4202             || s->first_glyph->type == COMPOSITE_GLYPH))
4203     {
4204       n = ns_get_glyph_string_clip_rect (s, r);
4205       ns_focus (s->f, r, n);
4206       ns_maybe_dumpglyphs_background (s, 1);
4207       ns_dumpglyphs_box_or_relief (s);
4208       ns_unfocus (s->f);
4209       box_drawn_p = 1;
4210     }
4212   switch (s->first_glyph->type)
4213     {
4215     case IMAGE_GLYPH:
4216       n = ns_get_glyph_string_clip_rect (s, r);
4217       ns_focus (s->f, r, n);
4218       ns_dumpglyphs_image (s, r[0]);
4219       ns_unfocus (s->f);
4220       break;
4222     case STRETCH_GLYPH:
4223       ns_dumpglyphs_stretch (s);
4224       break;
4226     case CHAR_GLYPH:
4227     case COMPOSITE_GLYPH:
4228       n = ns_get_glyph_string_clip_rect (s, r);
4229       ns_focus (s->f, r, n);
4231       if (s->for_overlaps || (s->cmp_from > 0
4232                               && ! s->first_glyph->u.cmp.automatic))
4233         s->background_filled_p = 1;
4234       else
4235         ns_maybe_dumpglyphs_background
4236           (s, s->first_glyph->type == COMPOSITE_GLYPH);
4238       if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
4239         {
4240           unsigned long tmp = NS_FACE_BACKGROUND (s->face);
4241           NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
4242           NS_FACE_FOREGROUND (s->face) = tmp;
4243         }
4245       {
4246         BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
4248         if (isComposite)
4249           ns_draw_composite_glyph_string_foreground (s);
4250         else
4251           ns_draw_glyph_string_foreground (s);
4252       }
4254       {
4255         NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
4256                         ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face),
4257                                                    s->f)
4258                         : FRAME_FOREGROUND_COLOR (s->f));
4259         [col set];
4261         /* Draw underline, overline, strike-through. */
4262         ns_draw_text_decoration (s, s->face, col, s->width, s->x);
4263       }
4265       if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
4266         {
4267           unsigned long tmp = NS_FACE_BACKGROUND (s->face);
4268           NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
4269           NS_FACE_FOREGROUND (s->face) = tmp;
4270         }
4272       ns_unfocus (s->f);
4273       break;
4275     case GLYPHLESS_GLYPH:
4276       n = ns_get_glyph_string_clip_rect (s, r);
4277       ns_focus (s->f, r, n);
4279       if (s->for_overlaps || (s->cmp_from > 0
4280                               && ! s->first_glyph->u.cmp.automatic))
4281         s->background_filled_p = 1;
4282       else
4283         ns_maybe_dumpglyphs_background
4284           (s, s->first_glyph->type == COMPOSITE_GLYPH);
4285       /* ... */
4286       /* Not yet implemented.  */
4287       /* ... */
4288       ns_unfocus (s->f);
4289       break;
4291     default:
4292       emacs_abort ();
4293     }
4295   /* Draw box if not done already. */
4296   if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX)
4297     {
4298       n = ns_get_glyph_string_clip_rect (s, r);
4299       ns_focus (s->f, r, n);
4300       ns_dumpglyphs_box_or_relief (s);
4301       ns_unfocus (s->f);
4302     }
4304   s->num_clips = 0;
4309 /* ==========================================================================
4311     Event loop
4313    ========================================================================== */
4316 static void
4317 ns_send_appdefined (int value)
4318 /* --------------------------------------------------------------------------
4319     Internal: post an appdefined event which EmacsApp-sendEvent will
4320               recognize and take as a command to halt the event loop.
4321    -------------------------------------------------------------------------- */
4323   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_send_appdefined(%d)", value);
4325   // GNUstep needs postEvent to happen on the main thread.
4326   // Cocoa needs nextEventMatchingMask to happen on the main thread too.
4327   if (! [[NSThread currentThread] isMainThread])
4328     {
4329       EmacsApp *app = (EmacsApp *)NSApp;
4330       app->nextappdefined = value;
4331       [app performSelectorOnMainThread:@selector (sendFromMainThread:)
4332                             withObject:nil
4333                          waitUntilDone:NO];
4334       return;
4335     }
4337   /* Only post this event if we haven't already posted one.  This will end
4338        the [NXApp run] main loop after having processed all events queued at
4339        this moment.  */
4341 #ifdef NS_IMPL_COCOA
4342   if (! send_appdefined)
4343     {
4344       /* OS X 10.10.1 swallows the AppDefined event we are sending ourselves
4345          in certain situations (rapid incoming events).
4346          So check if we have one, if not add one.  */
4347       NSEvent *appev = [NSApp nextEventMatchingMask:NSEventMaskApplicationDefined
4348                                           untilDate:[NSDate distantPast]
4349                                              inMode:NSDefaultRunLoopMode
4350                                             dequeue:NO];
4351       if (! appev) send_appdefined = YES;
4352     }
4353 #endif
4355   if (send_appdefined)
4356     {
4357       NSEvent *nxev;
4359       /* We only need one NX_APPDEFINED event to stop NXApp from running.  */
4360       send_appdefined = NO;
4362       /* Don't need wakeup timer any more */
4363       if (timed_entry)
4364         {
4365           [timed_entry invalidate];
4366           [timed_entry release];
4367           timed_entry = nil;
4368         }
4370       nxev = [NSEvent otherEventWithType: NSEventTypeApplicationDefined
4371                                 location: NSMakePoint (0, 0)
4372                            modifierFlags: 0
4373                                timestamp: 0
4374                             windowNumber: [[NSApp mainWindow] windowNumber]
4375                                  context: [NSApp context]
4376                                  subtype: 0
4377                                    data1: value
4378                                    data2: 0];
4380       /* Post an application defined event on the event queue.  When this is
4381          received the [NXApp run] will return, thus having processed all
4382          events which are currently queued.  */
4383       [NSApp postEvent: nxev atStart: NO];
4384     }
4387 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4388 static void
4389 check_native_fs ()
4391   Lisp_Object frame, tail;
4393   if (ns_last_use_native_fullscreen == ns_use_native_fullscreen)
4394     return;
4396   ns_last_use_native_fullscreen = ns_use_native_fullscreen;
4398   FOR_EACH_FRAME (tail, frame)
4399     {
4400       struct frame *f = XFRAME (frame);
4401       if (FRAME_NS_P (f))
4402         {
4403           EmacsView *view = FRAME_NS_VIEW (f);
4404           [view updateCollectionBehavior];
4405         }
4406     }
4408 #endif
4410 /* GNUstep does not have cancelTracking.  */
4411 #ifdef NS_IMPL_COCOA
4412 /* Check if menu open should be canceled or continued as normal.  */
4413 void
4414 ns_check_menu_open (NSMenu *menu)
4416   /* Click in menu bar? */
4417   NSArray *a = [[NSApp mainMenu] itemArray];
4418   int i;
4419   BOOL found = NO;
4421   if (menu == nil) // Menu tracking ended.
4422     {
4423       if (menu_will_open_state == MENU_OPENING)
4424         menu_will_open_state = MENU_NONE;
4425       return;
4426     }
4428   for (i = 0; ! found && i < [a count]; i++)
4429     found = menu == [[a objectAtIndex:i] submenu];
4430   if (found)
4431     {
4432       if (menu_will_open_state == MENU_NONE && emacs_event)
4433         {
4434           NSEvent *theEvent = [NSApp currentEvent];
4435           struct frame *emacsframe = SELECTED_FRAME ();
4437           [menu cancelTracking];
4438           menu_will_open_state = MENU_PENDING;
4439           emacs_event->kind = MENU_BAR_ACTIVATE_EVENT;
4440           EV_TRAILER (theEvent);
4442           CGEventRef ourEvent = CGEventCreate (NULL);
4443           menu_mouse_point = CGEventGetLocation (ourEvent);
4444           CFRelease (ourEvent);
4445         }
4446       else if (menu_will_open_state == MENU_OPENING)
4447         {
4448           menu_will_open_state = MENU_NONE;
4449         }
4450     }
4453 /* Redo saved menu click if state is MENU_PENDING.  */
4454 void
4455 ns_check_pending_open_menu ()
4457   if (menu_will_open_state == MENU_PENDING)
4458     {
4459       CGEventSourceRef source
4460         = CGEventSourceCreate (kCGEventSourceStateHIDSystemState);
4462       CGEventRef event = CGEventCreateMouseEvent (source,
4463                                                   kCGEventLeftMouseDown,
4464                                                   menu_mouse_point,
4465                                                   kCGMouseButtonLeft);
4466       CGEventSetType (event, kCGEventLeftMouseDown);
4467       CGEventPost (kCGHIDEventTap, event);
4468       CFRelease (event);
4469       CFRelease (source);
4471       menu_will_open_state = MENU_OPENING;
4472     }
4474 #endif /* NS_IMPL_COCOA */
4476 static int
4477 ns_read_socket (struct terminal *terminal, struct input_event *hold_quit)
4478 /* --------------------------------------------------------------------------
4479      External (hook): Post an event to ourself and keep reading events until
4480      we read it back again.  In effect process all events which were waiting.
4481      From 21+ we have to manage the event buffer ourselves.
4482    -------------------------------------------------------------------------- */
4484   struct input_event ev;
4485   int nevents;
4487   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_read_socket");
4489 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4490   check_native_fs ();
4491 #endif
4493   if ([NSApp modalWindow] != nil)
4494     return -1;
4496   if (hold_event_q.nr > 0)
4497     {
4498       int i;
4499       for (i = 0; i < hold_event_q.nr; ++i)
4500         kbd_buffer_store_event_hold (&hold_event_q.q[i], hold_quit);
4501       hold_event_q.nr = 0;
4502       return i;
4503     }
4505   if ([NSThread isMainThread])
4506     {
4507       block_input ();
4508       n_emacs_events_pending = 0;
4509       ns_init_events (&ev);
4510       q_event_ptr = hold_quit;
4512       /* we manage autorelease pools by allocate/reallocate each time around
4513          the loop; strict nesting is occasionally violated but seems not to
4514          matter.. earlier methods using full nesting caused major memory leaks */
4515       [outerpool release];
4516       outerpool = [[NSAutoreleasePool alloc] init];
4518       /* If have pending open-file requests, attend to the next one of those. */
4519       if (ns_pending_files && [ns_pending_files count] != 0
4520           && [(EmacsApp *)NSApp openFile: [ns_pending_files objectAtIndex: 0]])
4521         {
4522           [ns_pending_files removeObjectAtIndex: 0];
4523         }
4524       /* Deal with pending service requests. */
4525       else if (ns_pending_service_names && [ns_pending_service_names count] != 0
4526                && [(EmacsApp *)
4527                     NSApp fulfillService: [ns_pending_service_names objectAtIndex: 0]
4528                                  withArg: [ns_pending_service_args objectAtIndex: 0]])
4529         {
4530           [ns_pending_service_names removeObjectAtIndex: 0];
4531           [ns_pending_service_args removeObjectAtIndex: 0];
4532         }
4533       else
4534         {
4535           /* Run and wait for events.  We must always send one NX_APPDEFINED event
4536              to ourself, otherwise [NXApp run] will never exit.  */
4537           send_appdefined = YES;
4538           ns_send_appdefined (-1);
4540           [NSApp run];
4541         }
4543       nevents = n_emacs_events_pending;
4544       n_emacs_events_pending = 0;
4545       ns_finish_events ();
4546       q_event_ptr = NULL;
4547       unblock_input ();
4548     }
4549   else
4550     return -1;
4552   return nevents;
4557 ns_select (int nfds, fd_set *readfds, fd_set *writefds,
4558            fd_set *exceptfds, struct timespec *timeout,
4559            sigset_t *sigmask)
4560 /* --------------------------------------------------------------------------
4561      Replacement for select, checking for events
4562    -------------------------------------------------------------------------- */
4564   int result;
4565   int t, k, nr = 0;
4566   struct input_event event;
4567   char c;
4569   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_select");
4571 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4572   check_native_fs ();
4573 #endif
4575   if (hold_event_q.nr > 0)
4576     {
4577       /* We already have events pending. */
4578       raise (SIGIO);
4579       errno = EINTR;
4580       return -1;
4581     }
4583   for (k = 0; k < nfds+1; k++)
4584     {
4585       if (readfds && FD_ISSET(k, readfds)) ++nr;
4586       if (writefds && FD_ISSET(k, writefds)) ++nr;
4587     }
4589   if (NSApp == nil
4590       || ![NSThread isMainThread]
4591       || (timeout && timeout->tv_sec == 0 && timeout->tv_nsec == 0))
4592     return thread_select(pselect, nfds, readfds, writefds,
4593                          exceptfds, timeout, sigmask);
4594   else
4595     {
4596       struct timespec t = {0, 0};
4597       thread_select(pselect, 0, NULL, NULL, NULL, &t, sigmask);
4598     }
4600   [outerpool release];
4601   outerpool = [[NSAutoreleasePool alloc] init];
4604   send_appdefined = YES;
4605   if (nr > 0)
4606     {
4607       pthread_mutex_lock (&select_mutex);
4608       select_nfds = nfds;
4609       select_valid = 0;
4610       if (readfds)
4611         {
4612           select_readfds = *readfds;
4613           select_valid += SELECT_HAVE_READ;
4614         }
4615       if (writefds)
4616         {
4617           select_writefds = *writefds;
4618           select_valid += SELECT_HAVE_WRITE;
4619         }
4621       if (timeout)
4622         {
4623           select_timeout = *timeout;
4624           select_valid += SELECT_HAVE_TMO;
4625         }
4627       pthread_mutex_unlock (&select_mutex);
4629       /* Inform fd_handler that select should be called */
4630       c = 'g';
4631       emacs_write_sig (selfds[1], &c, 1);
4632     }
4633   else if (nr == 0 && timeout)
4634     {
4635       /* No file descriptor, just a timeout, no need to wake fd_handler  */
4636       double time = timespectod (*timeout);
4637       timed_entry = [[NSTimer scheduledTimerWithTimeInterval: time
4638                                                       target: NSApp
4639                                                     selector:
4640                                   @selector (timeout_handler:)
4641                                                     userInfo: 0
4642                                                      repeats: NO]
4643                       retain];
4644     }
4645   else /* No timeout and no file descriptors, can this happen?  */
4646     {
4647       /* Send appdefined so we exit from the loop */
4648       ns_send_appdefined (-1);
4649     }
4651   block_input ();
4652   ns_init_events (&event);
4654   [NSApp run];
4656   ns_finish_events ();
4657   if (nr > 0 && readfds)
4658     {
4659       c = 's';
4660       emacs_write_sig (selfds[1], &c, 1);
4661     }
4662   unblock_input ();
4664   t = last_appdefined_event_data;
4666   if (t != NO_APPDEFINED_DATA)
4667     {
4668       last_appdefined_event_data = NO_APPDEFINED_DATA;
4670       if (t == -2)
4671         {
4672           /* The NX_APPDEFINED event we received was a timeout. */
4673           result = 0;
4674         }
4675       else if (t == -1)
4676         {
4677           /* The NX_APPDEFINED event we received was the result of
4678              at least one real input event arriving.  */
4679           errno = EINTR;
4680           result = -1;
4681         }
4682       else
4683         {
4684           /* Received back from select () in fd_handler; copy the results */
4685           pthread_mutex_lock (&select_mutex);
4686           if (readfds) *readfds = select_readfds;
4687           if (writefds) *writefds = select_writefds;
4688           pthread_mutex_unlock (&select_mutex);
4689           result = t;
4690         }
4691     }
4692   else
4693     {
4694       errno = EINTR;
4695       result = -1;
4696     }
4698   return result;
4701 #ifdef HAVE_PTHREAD
4702 void
4703 ns_run_loop_break ()
4704 /* Break out of the NS run loop in ns_select or ns_read_socket. */
4706   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_run_loop_break");
4708   /* If we don't have a GUI, don't send the event. */
4709   if (NSApp != NULL)
4710     ns_send_appdefined(-1);
4712 #endif
4715 /* ==========================================================================
4717     Scrollbar handling
4719    ========================================================================== */
4722 static void
4723 ns_set_vertical_scroll_bar (struct window *window,
4724                            int portion, int whole, int position)
4725 /* --------------------------------------------------------------------------
4726       External (hook): Update or add scrollbar
4727    -------------------------------------------------------------------------- */
4729   Lisp_Object win;
4730   NSRect r, v;
4731   struct frame *f = XFRAME (WINDOW_FRAME (window));
4732   EmacsView *view = FRAME_NS_VIEW (f);
4733   EmacsScroller *bar;
4734   int window_y, window_height;
4735   int top, left, height, width;
4736   BOOL update_p = YES;
4738   /* optimization; display engine sends WAY too many of these.. */
4739   if (!NILP (window->vertical_scroll_bar))
4740     {
4741       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4742       if ([bar checkSamePosition: position portion: portion whole: whole])
4743         {
4744           if (view->scrollbarsNeedingUpdate == 0)
4745             {
4746               if (!windows_or_buffers_changed)
4747                   return;
4748             }
4749           else
4750             view->scrollbarsNeedingUpdate--;
4751           update_p = NO;
4752         }
4753     }
4755   NSTRACE ("ns_set_vertical_scroll_bar");
4757   /* Get dimensions.  */
4758   window_box (window, ANY_AREA, 0, &window_y, 0, &window_height);
4759   top = window_y;
4760   height = window_height;
4761   width = NS_SCROLL_BAR_WIDTH (f);
4762   left = WINDOW_SCROLL_BAR_AREA_X (window);
4764   r = NSMakeRect (left, top, width, height);
4765   /* the parent view is flipped, so we need to flip y value */
4766   v = [view frame];
4767   r.origin.y = (v.size.height - r.size.height - r.origin.y);
4769   XSETWINDOW (win, window);
4770   block_input ();
4772   /* we want at least 5 lines to display a scrollbar */
4773   if (WINDOW_TOTAL_LINES (window) < 5)
4774     {
4775       if (!NILP (window->vertical_scroll_bar))
4776         {
4777           bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4778           [bar removeFromSuperview];
4779           wset_vertical_scroll_bar (window, Qnil);
4780           [bar release];
4781         }
4782       ns_clear_frame_area (f, left, top, width, height);
4783       unblock_input ();
4784       return;
4785     }
4787   if (NILP (window->vertical_scroll_bar))
4788     {
4789       if (width > 0 && height > 0)
4790         ns_clear_frame_area (f, left, top, width, height);
4792       bar = [[EmacsScroller alloc] initFrame: r window: win];
4793       wset_vertical_scroll_bar (window, make_save_ptr (bar));
4794       update_p = YES;
4795     }
4796   else
4797     {
4798       NSRect oldRect;
4799       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4800       oldRect = [bar frame];
4801       r.size.width = oldRect.size.width;
4802       if (FRAME_LIVE_P (f) && !NSEqualRects (oldRect, r))
4803         {
4804           if (oldRect.origin.x != r.origin.x)
4805               ns_clear_frame_area (f, left, top, width, height);
4806           [bar setFrame: r];
4807         }
4808     }
4810   if (update_p)
4811     [bar setPosition: position portion: portion whole: whole];
4812   unblock_input ();
4816 static void
4817 ns_set_horizontal_scroll_bar (struct window *window,
4818                               int portion, int whole, int position)
4819 /* --------------------------------------------------------------------------
4820       External (hook): Update or add scrollbar
4821    -------------------------------------------------------------------------- */
4823   Lisp_Object win;
4824   NSRect r, v;
4825   struct frame *f = XFRAME (WINDOW_FRAME (window));
4826   EmacsView *view = FRAME_NS_VIEW (f);
4827   EmacsScroller *bar;
4828   int top, height, left, width;
4829   int window_x, window_width;
4830   BOOL update_p = YES;
4832   /* optimization; display engine sends WAY too many of these.. */
4833   if (!NILP (window->horizontal_scroll_bar))
4834     {
4835       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4836       if ([bar checkSamePosition: position portion: portion whole: whole])
4837         {
4838           if (view->scrollbarsNeedingUpdate == 0)
4839             {
4840               if (!windows_or_buffers_changed)
4841                   return;
4842             }
4843           else
4844             view->scrollbarsNeedingUpdate--;
4845           update_p = NO;
4846         }
4847     }
4849   NSTRACE ("ns_set_horizontal_scroll_bar");
4851   /* Get dimensions.  */
4852   window_box (window, ANY_AREA, &window_x, 0, &window_width, 0);
4853   left = window_x;
4854   width = window_width;
4855   height = NS_SCROLL_BAR_HEIGHT (f);
4856   top = WINDOW_SCROLL_BAR_AREA_Y (window);
4858   r = NSMakeRect (left, top, width, height);
4859   /* the parent view is flipped, so we need to flip y value */
4860   v = [view frame];
4861   r.origin.y = (v.size.height - r.size.height - r.origin.y);
4863   XSETWINDOW (win, window);
4864   block_input ();
4866   if (NILP (window->horizontal_scroll_bar))
4867     {
4868       if (width > 0 && height > 0)
4869         ns_clear_frame_area (f, left, top, width, height);
4871       bar = [[EmacsScroller alloc] initFrame: r window: win];
4872       wset_horizontal_scroll_bar (window, make_save_ptr (bar));
4873       update_p = YES;
4874     }
4875   else
4876     {
4877       NSRect oldRect;
4878       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4879       oldRect = [bar frame];
4880       if (FRAME_LIVE_P (f) && !NSEqualRects (oldRect, r))
4881         {
4882           if (oldRect.origin.y != r.origin.y)
4883             ns_clear_frame_area (f, left, top, width, height);
4884           [bar setFrame: r];
4885           update_p = YES;
4886         }
4887     }
4889   /* If there are both horizontal and vertical scroll-bars they leave
4890      a square that belongs to neither. We need to clear it otherwise
4891      it fills with junk. */
4892   if (!NILP (window->vertical_scroll_bar))
4893     ns_clear_frame_area (f, WINDOW_SCROLL_BAR_AREA_X (window), top,
4894                          NS_SCROLL_BAR_HEIGHT (f), height);
4896   if (update_p)
4897     [bar setPosition: position portion: portion whole: whole];
4898   unblock_input ();
4902 static void
4903 ns_condemn_scroll_bars (struct frame *f)
4904 /* --------------------------------------------------------------------------
4905      External (hook): arrange for all frame's scrollbars to be removed
4906      at next call to judge_scroll_bars, except for those redeemed.
4907    -------------------------------------------------------------------------- */
4909   int i;
4910   id view;
4911   NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews];
4913   NSTRACE ("ns_condemn_scroll_bars");
4915   for (i =[subviews count]-1; i >= 0; i--)
4916     {
4917       view = [subviews objectAtIndex: i];
4918       if ([view isKindOfClass: [EmacsScroller class]])
4919         [view condemn];
4920     }
4924 static void
4925 ns_redeem_scroll_bar (struct window *window)
4926 /* --------------------------------------------------------------------------
4927      External (hook): arrange to spare this window's scrollbar
4928      at next call to judge_scroll_bars.
4929    -------------------------------------------------------------------------- */
4931   id bar;
4932   NSTRACE ("ns_redeem_scroll_bar");
4933   if (!NILP (window->vertical_scroll_bar)
4934       && WINDOW_HAS_VERTICAL_SCROLL_BAR (window))
4935     {
4936       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4937       [bar reprieve];
4938     }
4940   if (!NILP (window->horizontal_scroll_bar)
4941       && WINDOW_HAS_HORIZONTAL_SCROLL_BAR (window))
4942     {
4943       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4944       [bar reprieve];
4945     }
4949 static void
4950 ns_judge_scroll_bars (struct frame *f)
4951 /* --------------------------------------------------------------------------
4952      External (hook): destroy all scrollbars on frame that weren't
4953      redeemed after call to condemn_scroll_bars.
4954    -------------------------------------------------------------------------- */
4956   int i;
4957   id view;
4958   EmacsView *eview = FRAME_NS_VIEW (f);
4959   NSArray *subviews = [[eview superview] subviews];
4960   BOOL removed = NO;
4962   NSTRACE ("ns_judge_scroll_bars");
4963   for (i = [subviews count]-1; i >= 0; --i)
4964     {
4965       view = [subviews objectAtIndex: i];
4966       if (![view isKindOfClass: [EmacsScroller class]]) continue;
4967       if ([view judge])
4968         removed = YES;
4969     }
4971   if (removed)
4972     [eview updateFrameSize: NO];
4975 /* ==========================================================================
4977     Initialization
4979    ========================================================================== */
4982 x_display_pixel_height (struct ns_display_info *dpyinfo)
4984   NSArray *screens = [NSScreen screens];
4985   NSEnumerator *enumerator = [screens objectEnumerator];
4986   NSScreen *screen;
4987   NSRect frame;
4989   frame = NSZeroRect;
4990   while ((screen = [enumerator nextObject]) != nil)
4991     frame = NSUnionRect (frame, [screen frame]);
4993   return NSHeight (frame);
4997 x_display_pixel_width (struct ns_display_info *dpyinfo)
4999   NSArray *screens = [NSScreen screens];
5000   NSEnumerator *enumerator = [screens objectEnumerator];
5001   NSScreen *screen;
5002   NSRect frame;
5004   frame = NSZeroRect;
5005   while ((screen = [enumerator nextObject]) != nil)
5006     frame = NSUnionRect (frame, [screen frame]);
5008   return NSWidth (frame);
5012 static Lisp_Object ns_string_to_lispmod (const char *s)
5013 /* --------------------------------------------------------------------------
5014      Convert modifier name to lisp symbol
5015    -------------------------------------------------------------------------- */
5017   if (!strncmp (SSDATA (SYMBOL_NAME (Qmeta)), s, 10))
5018     return Qmeta;
5019   else if (!strncmp (SSDATA (SYMBOL_NAME (Qsuper)), s, 10))
5020     return Qsuper;
5021   else if (!strncmp (SSDATA (SYMBOL_NAME (Qcontrol)), s, 10))
5022     return Qcontrol;
5023   else if (!strncmp (SSDATA (SYMBOL_NAME (Qalt)), s, 10))
5024     return Qalt;
5025   else if (!strncmp (SSDATA (SYMBOL_NAME (Qhyper)), s, 10))
5026     return Qhyper;
5027   else if (!strncmp (SSDATA (SYMBOL_NAME (Qnone)), s, 10))
5028     return Qnone;
5029   else
5030     return Qnil;
5034 static void
5035 ns_default (const char *parameter, Lisp_Object *result,
5036            Lisp_Object yesval, Lisp_Object noval,
5037            BOOL is_float, BOOL is_modstring)
5038 /* --------------------------------------------------------------------------
5039       Check a parameter value in user's preferences
5040    -------------------------------------------------------------------------- */
5042   const char *value = ns_get_defaults_value (parameter);
5044   if (value)
5045     {
5046       double f;
5047       char *pos;
5048       if (c_strcasecmp (value, "YES") == 0)
5049         *result = yesval;
5050       else if (c_strcasecmp (value, "NO") == 0)
5051         *result = noval;
5052       else if (is_float && (f = strtod (value, &pos), pos != value))
5053         *result = make_float (f);
5054       else if (is_modstring && value)
5055         *result = ns_string_to_lispmod (value);
5056       else fprintf (stderr,
5057                    "Bad value for default \"%s\": \"%s\"\n", parameter, value);
5058     }
5062 static void
5063 ns_initialize_display_info (struct ns_display_info *dpyinfo)
5064 /* --------------------------------------------------------------------------
5065       Initialize global info and storage for display.
5066    -------------------------------------------------------------------------- */
5068     NSScreen *screen = [NSScreen mainScreen];
5069     NSWindowDepth depth = [screen depth];
5071     dpyinfo->resx = 72.27; /* used 75.0, but this makes pt == pixel, expected */
5072     dpyinfo->resy = 72.27;
5073     dpyinfo->color_p = ![NSDeviceWhiteColorSpace isEqualToString:
5074                                                   NSColorSpaceFromDepth (depth)]
5075                 && ![NSCalibratedWhiteColorSpace isEqualToString:
5076                                                  NSColorSpaceFromDepth (depth)];
5077     dpyinfo->n_planes = NSBitsPerPixelFromDepth (depth);
5078     dpyinfo->color_table = xmalloc (sizeof *dpyinfo->color_table);
5079     dpyinfo->color_table->colors = NULL;
5080     dpyinfo->root_window = 42; /* a placeholder.. */
5081     dpyinfo->x_highlight_frame = dpyinfo->x_focus_frame = NULL;
5082     dpyinfo->n_fonts = 0;
5083     dpyinfo->smallest_font_height = 1;
5084     dpyinfo->smallest_char_width = 1;
5086     reset_mouse_highlight (&dpyinfo->mouse_highlight);
5090 /* This and next define (many of the) public functions in this file. */
5091 /* x_... are generic versions in xdisp.c that we, and other terms, get away
5092          with using despite presence in the "system dependent" redisplay
5093          interface.  In addition, many of the ns_ methods have code that is
5094          shared with all terms, indicating need for further refactoring. */
5095 extern frame_parm_handler ns_frame_parm_handlers[];
5096 static struct redisplay_interface ns_redisplay_interface =
5098   ns_frame_parm_handlers,
5099   x_produce_glyphs,
5100   x_write_glyphs,
5101   x_insert_glyphs,
5102   x_clear_end_of_line,
5103   ns_scroll_run,
5104   ns_after_update_window_line,
5105   ns_update_window_begin,
5106   ns_update_window_end,
5107   0, /* flush_display */
5108   x_clear_window_mouse_face,
5109   x_get_glyph_overhangs,
5110   x_fix_overlapping_area,
5111   ns_draw_fringe_bitmap,
5112   0, /* define_fringe_bitmap */ /* FIXME: simplify ns_draw_fringe_bitmap */
5113   0, /* destroy_fringe_bitmap */
5114   ns_compute_glyph_string_overhangs,
5115   ns_draw_glyph_string,
5116   ns_define_frame_cursor,
5117   ns_clear_frame_area,
5118   ns_draw_window_cursor,
5119   ns_draw_vertical_window_border,
5120   ns_draw_window_divider,
5121   ns_shift_glyphs_for_insert,
5122   ns_show_hourglass,
5123   ns_hide_hourglass
5127 static void
5128 ns_delete_display (struct ns_display_info *dpyinfo)
5130   /* TODO... */
5134 /* This function is called when the last frame on a display is deleted. */
5135 static void
5136 ns_delete_terminal (struct terminal *terminal)
5138   struct ns_display_info *dpyinfo = terminal->display_info.ns;
5140   NSTRACE ("ns_delete_terminal");
5142   /* Protect against recursive calls.  delete_frame in
5143      delete_terminal calls us back when it deletes our last frame.  */
5144   if (!terminal->name)
5145     return;
5147   block_input ();
5149   x_destroy_all_bitmaps (dpyinfo);
5150   ns_delete_display (dpyinfo);
5151   unblock_input ();
5155 static struct terminal *
5156 ns_create_terminal (struct ns_display_info *dpyinfo)
5157 /* --------------------------------------------------------------------------
5158       Set up use of NS before we make the first connection.
5159    -------------------------------------------------------------------------- */
5161   struct terminal *terminal;
5163   NSTRACE ("ns_create_terminal");
5165   terminal = create_terminal (output_ns, &ns_redisplay_interface);
5167   terminal->display_info.ns = dpyinfo;
5168   dpyinfo->terminal = terminal;
5170   terminal->clear_frame_hook = ns_clear_frame;
5171   terminal->ring_bell_hook = ns_ring_bell;
5172   terminal->update_begin_hook = ns_update_begin;
5173   terminal->update_end_hook = ns_update_end;
5174   terminal->read_socket_hook = ns_read_socket;
5175   terminal->frame_up_to_date_hook = ns_frame_up_to_date;
5176   terminal->mouse_position_hook = ns_mouse_position;
5177   terminal->frame_rehighlight_hook = ns_frame_rehighlight;
5178   terminal->frame_raise_lower_hook = ns_frame_raise_lower;
5179   terminal->fullscreen_hook = ns_fullscreen_hook;
5180   terminal->menu_show_hook = ns_menu_show;
5181   terminal->popup_dialog_hook = ns_popup_dialog;
5182   terminal->set_vertical_scroll_bar_hook = ns_set_vertical_scroll_bar;
5183   terminal->set_horizontal_scroll_bar_hook = ns_set_horizontal_scroll_bar;
5184   terminal->condemn_scroll_bars_hook = ns_condemn_scroll_bars;
5185   terminal->redeem_scroll_bar_hook = ns_redeem_scroll_bar;
5186   terminal->judge_scroll_bars_hook = ns_judge_scroll_bars;
5187   terminal->delete_frame_hook = x_destroy_window;
5188   terminal->delete_terminal_hook = ns_delete_terminal;
5189   /* Other hooks are NULL by default.  */
5191   return terminal;
5195 struct ns_display_info *
5196 ns_term_init (Lisp_Object display_name)
5197 /* --------------------------------------------------------------------------
5198      Start the Application and get things rolling.
5199    -------------------------------------------------------------------------- */
5201   struct terminal *terminal;
5202   struct ns_display_info *dpyinfo;
5203   static int ns_initialized = 0;
5204   Lisp_Object tmp;
5206   if (ns_initialized) return x_display_list;
5207   ns_initialized = 1;
5209   block_input ();
5211   NSTRACE ("ns_term_init");
5213   [outerpool release];
5214   outerpool = [[NSAutoreleasePool alloc] init];
5216   /* count object allocs (About, click icon); on macOS use ObjectAlloc tool */
5217   /*GSDebugAllocationActive (YES); */
5218   block_input ();
5220   baud_rate = 38400;
5221   Fset_input_interrupt_mode (Qnil);
5223   if (selfds[0] == -1)
5224     {
5225       if (emacs_pipe (selfds) != 0)
5226         {
5227           fprintf (stderr, "Failed to create pipe: %s\n",
5228                    emacs_strerror (errno));
5229           emacs_abort ();
5230         }
5232       fcntl (selfds[0], F_SETFL, O_NONBLOCK|fcntl (selfds[0], F_GETFL));
5233       FD_ZERO (&select_readfds);
5234       FD_ZERO (&select_writefds);
5235       pthread_mutex_init (&select_mutex, NULL);
5236     }
5238   ns_pending_files = [[NSMutableArray alloc] init];
5239   ns_pending_service_names = [[NSMutableArray alloc] init];
5240   ns_pending_service_args = [[NSMutableArray alloc] init];
5242 /* Start app and create the main menu, window, view.
5243      Needs to be here because ns_initialize_display_info () uses AppKit classes.
5244      The view will then ask the NSApp to stop and return to Emacs. */
5245   [EmacsApp sharedApplication];
5246   if (NSApp == nil)
5247     return NULL;
5248   [NSApp setDelegate: NSApp];
5250   /* Start the select thread.  */
5251   [NSThread detachNewThreadSelector:@selector (fd_handler:)
5252                            toTarget:NSApp
5253                          withObject:nil];
5255   /* debugging: log all notifications */
5256   /*   [[NSNotificationCenter defaultCenter] addObserver: NSApp
5257                                          selector: @selector (logNotification:)
5258                                              name: nil object: nil]; */
5260   dpyinfo = xzalloc (sizeof *dpyinfo);
5262   ns_initialize_display_info (dpyinfo);
5263   terminal = ns_create_terminal (dpyinfo);
5265   terminal->kboard = allocate_kboard (Qns);
5266   /* Don't let the initial kboard remain current longer than necessary.
5267      That would cause problems if a file loaded on startup tries to
5268      prompt in the mini-buffer.  */
5269   if (current_kboard == initial_kboard)
5270     current_kboard = terminal->kboard;
5271   terminal->kboard->reference_count++;
5273   dpyinfo->next = x_display_list;
5274   x_display_list = dpyinfo;
5276   dpyinfo->name_list_element = Fcons (display_name, Qnil);
5278   terminal->name = xlispstrdup (display_name);
5280   unblock_input ();
5282   if (!inhibit_x_resources)
5283     {
5284       ns_default ("GSFontAntiAlias", &ns_antialias_text,
5285                  Qt, Qnil, NO, NO);
5286       tmp = Qnil;
5287       /* this is a standard variable */
5288       ns_default ("AppleAntiAliasingThreshold", &tmp,
5289                  make_float (10.0), make_float (6.0), YES, NO);
5290       ns_antialias_threshold = NILP (tmp) ? 10.0 : extract_float (tmp);
5291     }
5293   NSTRACE_MSG ("Colors");
5295   {
5296     NSColorList *cl = [NSColorList colorListNamed: @"Emacs"];
5298     if ( cl == nil )
5299       {
5300         Lisp_Object color_file, color_map, color;
5301         unsigned long c;
5302         char *name;
5304         color_file = Fexpand_file_name (build_string ("rgb.txt"),
5305                          Fsymbol_value (intern ("data-directory")));
5307         color_map = Fx_load_color_file (color_file);
5308         if (NILP (color_map))
5309           fatal ("Could not read %s.\n", SDATA (color_file));
5311         cl = [[NSColorList alloc] initWithName: @"Emacs"];
5312         for ( ; CONSP (color_map); color_map = XCDR (color_map))
5313           {
5314             color = XCAR (color_map);
5315             name = SSDATA (XCAR (color));
5316             c = XINT (XCDR (color));
5317             [cl setColor:
5318                   [NSColor colorForEmacsRed: RED_FROM_ULONG (c) / 255.0
5319                                       green: GREEN_FROM_ULONG (c) / 255.0
5320                                        blue: BLUE_FROM_ULONG (c) / 255.0
5321                                       alpha: 1.0]
5322                   forKey: [NSString stringWithUTF8String: name]];
5323           }
5324         [cl writeToFile: nil];
5325       }
5326   }
5328   NSTRACE_MSG ("Versions");
5330   {
5331 #ifdef NS_IMPL_GNUSTEP
5332     Vwindow_system_version = build_string (gnustep_base_version);
5333 #else
5334     /*PSnextrelease (128, c); */
5335     char c[DBL_BUFSIZE_BOUND];
5336     int len = dtoastr (c, sizeof c, 0, 0, NSAppKitVersionNumber);
5337     Vwindow_system_version = make_unibyte_string (c, len);
5338 #endif
5339   }
5341   delete_keyboard_wait_descriptor (0);
5343   ns_app_name = [[NSProcessInfo processInfo] processName];
5345   /* Set up macOS app menu */
5347   NSTRACE_MSG ("Menu init");
5349 #ifdef NS_IMPL_COCOA
5350   {
5351     NSMenu *appMenu;
5352     NSMenuItem *item;
5353     /* set up the application menu */
5354     svcsMenu = [[EmacsMenu alloc] initWithTitle: @"Services"];
5355     [svcsMenu setAutoenablesItems: NO];
5356     appMenu = [[EmacsMenu alloc] initWithTitle: @"Emacs"];
5357     [appMenu setAutoenablesItems: NO];
5358     mainMenu = [[EmacsMenu alloc] initWithTitle: @""];
5359     dockMenu = [[EmacsMenu alloc] initWithTitle: @""];
5361     [appMenu insertItemWithTitle: @"About Emacs"
5362                           action: @selector (orderFrontStandardAboutPanel:)
5363                    keyEquivalent: @""
5364                          atIndex: 0];
5365     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 1];
5366     [appMenu insertItemWithTitle: @"Preferences..."
5367                           action: @selector (showPreferencesWindow:)
5368                    keyEquivalent: @","
5369                          atIndex: 2];
5370     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 3];
5371     item = [appMenu insertItemWithTitle: @"Services"
5372                                  action: @selector (menuDown:)
5373                           keyEquivalent: @""
5374                                 atIndex: 4];
5375     [appMenu setSubmenu: svcsMenu forItem: item];
5376     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 5];
5377     [appMenu insertItemWithTitle: @"Hide Emacs"
5378                           action: @selector (hide:)
5379                    keyEquivalent: @"h"
5380                          atIndex: 6];
5381     item =  [appMenu insertItemWithTitle: @"Hide Others"
5382                           action: @selector (hideOtherApplications:)
5383                    keyEquivalent: @"h"
5384                          atIndex: 7];
5385     [item setKeyEquivalentModifierMask: NSEventModifierFlagCommand | NSEventModifierFlagOption];
5386     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 8];
5387     [appMenu insertItemWithTitle: @"Quit Emacs"
5388                           action: @selector (terminate:)
5389                    keyEquivalent: @"q"
5390                          atIndex: 9];
5392     item = [mainMenu insertItemWithTitle: ns_app_name
5393                                   action: @selector (menuDown:)
5394                            keyEquivalent: @""
5395                                  atIndex: 0];
5396     [mainMenu setSubmenu: appMenu forItem: item];
5397     [dockMenu insertItemWithTitle: @"New Frame"
5398                            action: @selector (newFrame:)
5399                     keyEquivalent: @""
5400                           atIndex: 0];
5402     [NSApp setMainMenu: mainMenu];
5403     [NSApp setAppleMenu: appMenu];
5404     [NSApp setServicesMenu: svcsMenu];
5405     /* Needed at least on Cocoa, to get dock menu to show windows */
5406     [NSApp setWindowsMenu: [[NSMenu alloc] init]];
5408     [[NSNotificationCenter defaultCenter]
5409       addObserver: mainMenu
5410          selector: @selector (trackingNotification:)
5411              name: NSMenuDidBeginTrackingNotification object: mainMenu];
5412     [[NSNotificationCenter defaultCenter]
5413       addObserver: mainMenu
5414          selector: @selector (trackingNotification:)
5415              name: NSMenuDidEndTrackingNotification object: mainMenu];
5416   }
5417 #endif /* macOS menu setup */
5419   /* Register our external input/output types, used for determining
5420      applicable services and also drag/drop eligibility. */
5422   NSTRACE_MSG ("Input/output types");
5424   ns_send_types = [[NSArray arrayWithObjects: NSStringPboardType, nil] retain];
5425   ns_return_types = [[NSArray arrayWithObjects: NSStringPboardType, nil]
5426                       retain];
5427   ns_drag_types = [[NSArray arrayWithObjects:
5428                             NSStringPboardType,
5429                             NSTabularTextPboardType,
5430                             NSFilenamesPboardType,
5431                             NSURLPboardType, nil] retain];
5433   /* If fullscreen is in init/default-frame-alist, focus isn't set
5434      right for fullscreen windows, so set this.  */
5435   [NSApp activateIgnoringOtherApps:YES];
5437   NSTRACE_MSG ("Call NSApp run");
5439   [NSApp run];
5440   ns_do_open_file = YES;
5442 #ifdef NS_IMPL_GNUSTEP
5443   /* GNUstep steals SIGCHLD for use in NSTask, but we don't use NSTask.
5444      We must re-catch it so subprocess works.  */
5445   catch_child_signal ();
5446 #endif
5448   NSTRACE_MSG ("ns_term_init done");
5450   unblock_input ();
5452   return dpyinfo;
5456 void
5457 ns_term_shutdown (int sig)
5459   [[NSUserDefaults standardUserDefaults] synchronize];
5461   /* code not reached in emacs.c after this is called by shut_down_emacs: */
5462   if (STRINGP (Vauto_save_list_file_name))
5463     unlink (SSDATA (Vauto_save_list_file_name));
5465   if (sig == 0 || sig == SIGTERM)
5466     {
5467       [NSApp terminate: NSApp];
5468     }
5469   else // force a stack trace to happen
5470     {
5471       emacs_abort ();
5472     }
5476 /* ==========================================================================
5478     EmacsApp implementation
5480    ========================================================================== */
5483 @implementation EmacsApp
5485 - (id)init
5487   NSTRACE ("[EmacsApp init]");
5489   if ((self = [super init]))
5490     {
5491 #ifdef NS_IMPL_COCOA
5492       self->isFirst = YES;
5493 #endif
5494 #ifdef NS_IMPL_GNUSTEP
5495       self->applicationDidFinishLaunchingCalled = NO;
5496 #endif
5497     }
5499   return self;
5502 #ifdef NS_IMPL_COCOA
5503 - (void)run
5505   NSTRACE ("[EmacsApp run]");
5507 #ifndef NSAppKitVersionNumber10_9
5508 #define NSAppKitVersionNumber10_9 1265
5509 #endif
5511     if ((int)NSAppKitVersionNumber != NSAppKitVersionNumber10_9)
5512       {
5513         [super run];
5514         return;
5515       }
5517   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5519   if (isFirst) [self finishLaunching];
5520   isFirst = NO;
5522   shouldKeepRunning = YES;
5523   do
5524     {
5525       [pool release];
5526       pool = [[NSAutoreleasePool alloc] init];
5528       NSEvent *event =
5529         [self nextEventMatchingMask:NSEventMaskAny
5530                           untilDate:[NSDate distantFuture]
5531                              inMode:NSDefaultRunLoopMode
5532                             dequeue:YES];
5534       [self sendEvent:event];
5535       [self updateWindows];
5536     } while (shouldKeepRunning);
5538   [pool release];
5541 - (void)stop: (id)sender
5543   NSTRACE ("[EmacsApp stop:]");
5545     shouldKeepRunning = NO;
5546     // Stop possible dialog also.  Noop if no dialog present.
5547     // The file dialog still leaks 7k - 10k on 10.9 though.
5548     [super stop:sender];
5550 #endif /* NS_IMPL_COCOA */
5552 - (void)logNotification: (NSNotification *)notification
5554   NSTRACE ("[EmacsApp logNotification:]");
5556   const char *name = [[notification name] UTF8String];
5557   if (!strstr (name, "Update") && !strstr (name, "NSMenu")
5558       && !strstr (name, "WindowNumber"))
5559     NSLog (@"notification: '%@'", [notification name]);
5563 - (void)sendEvent: (NSEvent *)theEvent
5564 /* --------------------------------------------------------------------------
5565      Called when NSApp is running for each event received.  Used to stop
5566      the loop when we choose, since there's no way to just run one iteration.
5567    -------------------------------------------------------------------------- */
5569   int type = [theEvent type];
5570   NSWindow *window = [theEvent window];
5572   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsApp sendEvent:]");
5573   NSTRACE_MSG ("Type: %d", type);
5575 #ifdef NS_IMPL_GNUSTEP
5576   // Keyboard events aren't propagated to file dialogs for some reason.
5577   if ([NSApp modalWindow] != nil &&
5578       (type == NSEventTypeKeyDown || type == NSEventTypeKeyUp || type == NSEventTypeFlagsChanged))
5579     {
5580       [[NSApp modalWindow] sendEvent: theEvent];
5581       return;
5582     }
5583 #endif
5585   if (represented_filename != nil && represented_frame)
5586     {
5587       NSString *fstr = represented_filename;
5588       NSView *view = FRAME_NS_VIEW (represented_frame);
5589 #ifdef NS_IMPL_COCOA
5590       /* work around a bug observed on 10.3 and later where
5591          setTitleWithRepresentedFilename does not clear out previous state
5592          if given filename does not exist */
5593       if (! [[NSFileManager defaultManager] fileExistsAtPath: fstr])
5594         [[view window] setRepresentedFilename: @""];
5595 #endif
5596       [[view window] setRepresentedFilename: fstr];
5597       [represented_filename release];
5598       represented_filename = nil;
5599       represented_frame = NULL;
5600     }
5602   if (type == NSEventTypeApplicationDefined)
5603     {
5604       switch ([theEvent data2])
5605         {
5606 #ifdef NS_IMPL_COCOA
5607         case NSAPP_DATA2_RUNASSCRIPT:
5608           ns_run_ascript ();
5609           [self stop: self];
5610           return;
5611 #endif
5612         case NSAPP_DATA2_RUNFILEDIALOG:
5613           ns_run_file_dialog ();
5614           [self stop: self];
5615           return;
5616         }
5617     }
5619   if (type == NSEventTypeCursorUpdate && window == nil)
5620     {
5621       fprintf (stderr, "Dropping external cursor update event.\n");
5622       return;
5623     }
5625   if (type == NSEventTypeApplicationDefined)
5626     {
5627       /* Events posted by ns_send_appdefined interrupt the run loop here.
5628          But, if a modal window is up, an appdefined can still come through,
5629          (e.g., from a makeKeyWindow event) but stopping self also stops the
5630          modal loop. Just defer it until later. */
5631       if ([NSApp modalWindow] == nil)
5632         {
5633           last_appdefined_event_data = [theEvent data1];
5634           [self stop: self];
5635         }
5636       else
5637         {
5638           send_appdefined = YES;
5639         }
5640     }
5643 #ifdef NS_IMPL_COCOA
5644   /* If no dialog and none of our frames have focus and it is a move, skip it.
5645      It is a mouse move in an auxiliary menu, i.e. on the top right on macOS,
5646      such as Wifi, sound, date or similar.
5647      This prevents "spooky" highlighting in the frame under the menu.  */
5648   if (type == NSEventTypeMouseMoved && [NSApp modalWindow] == nil)
5649     {
5650       struct ns_display_info *di;
5651       BOOL has_focus = NO;
5652       for (di = x_display_list; ! has_focus && di; di = di->next)
5653         has_focus = di->x_focus_frame != 0;
5654       if (! has_focus)
5655         return;
5656     }
5657 #endif
5659   NSTRACE_UNSILENCE();
5661   [super sendEvent: theEvent];
5665 - (void)showPreferencesWindow: (id)sender
5667   struct frame *emacsframe = SELECTED_FRAME ();
5668   NSEvent *theEvent = [NSApp currentEvent];
5670   if (!emacs_event)
5671     return;
5672   emacs_event->kind = NS_NONKEY_EVENT;
5673   emacs_event->code = KEY_NS_SHOW_PREFS;
5674   emacs_event->modifiers = 0;
5675   EV_TRAILER (theEvent);
5679 - (void)newFrame: (id)sender
5681   NSTRACE ("[EmacsApp newFrame:]");
5683   struct frame *emacsframe = SELECTED_FRAME ();
5684   NSEvent *theEvent = [NSApp currentEvent];
5686   if (!emacs_event)
5687     return;
5688   emacs_event->kind = NS_NONKEY_EVENT;
5689   emacs_event->code = KEY_NS_NEW_FRAME;
5690   emacs_event->modifiers = 0;
5691   EV_TRAILER (theEvent);
5695 /* Open a file (used by below, after going into queue read by ns_read_socket) */
5696 - (BOOL) openFile: (NSString *)fileName
5698   NSTRACE ("[EmacsApp openFile:]");
5700   struct frame *emacsframe = SELECTED_FRAME ();
5701   NSEvent *theEvent = [NSApp currentEvent];
5703   if (!emacs_event)
5704     return NO;
5706   emacs_event->kind = NS_NONKEY_EVENT;
5707   emacs_event->code = KEY_NS_OPEN_FILE_LINE;
5708   ns_input_file = append2 (ns_input_file, build_string ([fileName UTF8String]));
5709   ns_input_line = Qnil; /* can be start or cons start,end */
5710   emacs_event->modifiers =0;
5711   EV_TRAILER (theEvent);
5713   return YES;
5717 /* **************************************************************************
5719       EmacsApp delegate implementation
5721    ************************************************************************** */
5723 - (void)applicationDidFinishLaunching: (NSNotification *)notification
5724 /* --------------------------------------------------------------------------
5725      When application is loaded, terminate event loop in ns_term_init
5726    -------------------------------------------------------------------------- */
5728   NSTRACE ("[EmacsApp applicationDidFinishLaunching:]");
5730 #ifdef NS_IMPL_GNUSTEP
5731   ((EmacsApp *)self)->applicationDidFinishLaunchingCalled = YES;
5732 #endif
5733   [NSApp setServicesProvider: NSApp];
5735   [self antialiasThresholdDidChange:nil];
5736 #ifdef NS_IMPL_COCOA
5737   [[NSNotificationCenter defaultCenter]
5738     addObserver:self
5739        selector:@selector(antialiasThresholdDidChange:)
5740            name:NSAntialiasThresholdChangedNotification
5741          object:nil];
5742 #endif
5744 #ifdef NS_IMPL_COCOA
5745   if ([NSApp activationPolicy] == NSApplicationActivationPolicyProhibited) {
5746     /* Set the app's activation policy to regular when we run outside
5747        of a bundle.  This is already done for us by Info.plist when we
5748        run inside a bundle. */
5749     [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
5750     [NSApp setApplicationIconImage:
5751              [EmacsImage
5752                allocInitFromFile:
5753                  build_string("icons/hicolor/128x128/apps/emacs.png")]];
5754   }
5755 #endif
5757   ns_send_appdefined (-2);
5760 - (void)antialiasThresholdDidChange:(NSNotification *)notification
5762 #ifdef NS_IMPL_COCOA
5763   macfont_update_antialias_threshold ();
5764 #endif
5768 /* Termination sequences:
5769     C-x C-c:
5770     Cmd-Q:
5771     MenuBar | File | Exit:
5772     Select Quit from App menubar:
5773         -terminate
5774         KEY_NS_POWER_OFF, (save-buffers-kill-emacs)
5775         ns_term_shutdown()
5777     Select Quit from Dock menu:
5778     Logout attempt:
5779         -appShouldTerminate
5780           Cancel -> Nothing else
5781           Accept ->
5783           -terminate
5784           KEY_NS_POWER_OFF, (save-buffers-kill-emacs)
5785           ns_term_shutdown()
5789 - (void) terminate: (id)sender
5791   NSTRACE ("[EmacsApp terminate:]");
5793   struct frame *emacsframe = SELECTED_FRAME ();
5795   if (!emacs_event)
5796     return;
5798   emacs_event->kind = NS_NONKEY_EVENT;
5799   emacs_event->code = KEY_NS_POWER_OFF;
5800   emacs_event->arg = Qt; /* mark as non-key event */
5801   EV_TRAILER ((id)nil);
5804 static bool
5805 runAlertPanel(NSString *title,
5806               NSString *msgFormat,
5807               NSString *defaultButton,
5808               NSString *alternateButton)
5810 #ifdef NS_IMPL_GNUSTEP
5811   return NSRunAlertPanel(title, msgFormat, defaultButton, alternateButton, nil)
5812     == NSAlertDefaultReturn;
5813 #else
5814   NSAlert *alert = [[NSAlert alloc] init];
5815   [alert setAlertStyle: NSAlertStyleCritical];
5816   [alert setMessageText: msgFormat];
5817   [alert addButtonWithTitle: defaultButton];
5818   [alert addButtonWithTitle: alternateButton];
5819   NSInteger ret = [alert runModal];
5820   [alert release];
5821   return ret == NSAlertFirstButtonReturn;
5822 #endif
5826 - (NSApplicationTerminateReply)applicationShouldTerminate: (id)sender
5828   NSTRACE ("[EmacsApp applicationShouldTerminate:]");
5830   bool ret;
5832   if (NILP (ns_confirm_quit)) //   || ns_shutdown_properly  --> TO DO
5833     return NSTerminateNow;
5835   ret = runAlertPanel(ns_app_name,
5836                       @"Exit requested.  Would you like to Save Buffers and Exit, or Cancel the request?",
5837                       @"Save Buffers and Exit", @"Cancel");
5839   return ret ? NSTerminateNow : NSTerminateCancel;
5842 static int
5843 not_in_argv (NSString *arg)
5845   int k;
5846   const char *a = [arg UTF8String];
5847   for (k = 1; k < initial_argc; ++k)
5848     if (strcmp (a, initial_argv[k]) == 0) return 0;
5849   return 1;
5852 /*   Notification from the Workspace to open a file */
5853 - (BOOL)application: sender openFile: (NSString *)file
5855   if (ns_do_open_file || not_in_argv (file))
5856     [ns_pending_files addObject: file];
5857   return YES;
5861 /*   Open a file as a temporary file */
5862 - (BOOL)application: sender openTempFile: (NSString *)file
5864   if (ns_do_open_file || not_in_argv (file))
5865     [ns_pending_files addObject: file];
5866   return YES;
5870 /*   Notification from the Workspace to open a file noninteractively (?) */
5871 - (BOOL)application: sender openFileWithoutUI: (NSString *)file
5873   if (ns_do_open_file || not_in_argv (file))
5874     [ns_pending_files addObject: file];
5875   return YES;
5878 /*   Notification from the Workspace to open multiple files */
5879 - (void)application: sender openFiles: (NSArray *)fileList
5881   NSEnumerator *files = [fileList objectEnumerator];
5882   NSString *file;
5883   /* Don't open files from the command line unconditionally,
5884      Cocoa parses the command line wrong, --option value tries to open value
5885      if --option is the last option.  */
5886   while ((file = [files nextObject]) != nil)
5887     if (ns_do_open_file || not_in_argv (file))
5888       [ns_pending_files addObject: file];
5890   [self replyToOpenOrPrint: NSApplicationDelegateReplySuccess];
5895 /* Handle dock menu requests.  */
5896 - (NSMenu *)applicationDockMenu: (NSApplication *) sender
5898   return dockMenu;
5902 /* TODO: these may help w/IO switching btwn terminal and NSApp */
5903 - (void)applicationWillBecomeActive: (NSNotification *)notification
5905   NSTRACE ("[EmacsApp applicationWillBecomeActive:]");
5906   //ns_app_active=YES;
5909 - (void)applicationDidBecomeActive: (NSNotification *)notification
5911   NSTRACE ("[EmacsApp applicationDidBecomeActive:]");
5913 #ifdef NS_IMPL_GNUSTEP
5914   if (! applicationDidFinishLaunchingCalled)
5915     [self applicationDidFinishLaunching:notification];
5916 #endif
5917   //ns_app_active=YES;
5919   ns_update_auto_hide_menu_bar ();
5920   // No constraining takes place when the application is not active.
5921   ns_constrain_all_frames ();
5923 - (void)applicationDidResignActive: (NSNotification *)notification
5925   NSTRACE ("[EmacsApp applicationDidResignActive:]");
5927   //ns_app_active=NO;
5928   ns_send_appdefined (-1);
5933 /* ==========================================================================
5935     EmacsApp aux handlers for managing event loop
5937    ========================================================================== */
5940 - (void)timeout_handler: (NSTimer *)timedEntry
5941 /* --------------------------------------------------------------------------
5942      The timeout specified to ns_select has passed.
5943    -------------------------------------------------------------------------- */
5945   /*NSTRACE ("timeout_handler"); */
5946   ns_send_appdefined (-2);
5949 - (void)sendFromMainThread:(id)unused
5951   ns_send_appdefined (nextappdefined);
5954 - (void)fd_handler:(id)unused
5955 /* --------------------------------------------------------------------------
5956      Check data waiting on file descriptors and terminate if so
5957    -------------------------------------------------------------------------- */
5959   int result;
5960   int waiting = 1, nfds;
5961   char c;
5963   fd_set readfds, writefds, *wfds;
5964   struct timespec timeout, *tmo;
5965   NSAutoreleasePool *pool = nil;
5967   /* NSTRACE ("fd_handler"); */
5969   for (;;)
5970     {
5971       [pool release];
5972       pool = [[NSAutoreleasePool alloc] init];
5974       if (waiting)
5975         {
5976           fd_set fds;
5977           FD_ZERO (&fds);
5978           FD_SET (selfds[0], &fds);
5979           result = select (selfds[0]+1, &fds, NULL, NULL, NULL);
5980           if (result > 0 && read (selfds[0], &c, 1) == 1 && c == 'g')
5981             waiting = 0;
5982         }
5983       else
5984         {
5985           pthread_mutex_lock (&select_mutex);
5986           nfds = select_nfds;
5988           if (select_valid & SELECT_HAVE_READ)
5989             readfds = select_readfds;
5990           else
5991             FD_ZERO (&readfds);
5993           if (select_valid & SELECT_HAVE_WRITE)
5994             {
5995               writefds = select_writefds;
5996               wfds = &writefds;
5997             }
5998           else
5999             wfds = NULL;
6000           if (select_valid & SELECT_HAVE_TMO)
6001             {
6002               timeout = select_timeout;
6003               tmo = &timeout;
6004             }
6005           else
6006             tmo = NULL;
6008           pthread_mutex_unlock (&select_mutex);
6010           FD_SET (selfds[0], &readfds);
6011           if (selfds[0] >= nfds) nfds = selfds[0]+1;
6013           result = pselect (nfds, &readfds, wfds, NULL, tmo, NULL);
6015           if (result == 0)
6016             ns_send_appdefined (-2);
6017           else if (result > 0)
6018             {
6019               if (FD_ISSET (selfds[0], &readfds))
6020                 {
6021                   if (read (selfds[0], &c, 1) == 1 && c == 's')
6022                     waiting = 1;
6023                 }
6024               else
6025                 {
6026                   pthread_mutex_lock (&select_mutex);
6027                   if (select_valid & SELECT_HAVE_READ)
6028                     select_readfds = readfds;
6029                   if (select_valid & SELECT_HAVE_WRITE)
6030                     select_writefds = writefds;
6031                   if (select_valid & SELECT_HAVE_TMO)
6032                     select_timeout = timeout;
6033                   pthread_mutex_unlock (&select_mutex);
6035                   ns_send_appdefined (result);
6036                 }
6037             }
6038           waiting = 1;
6039         }
6040     }
6045 /* ==========================================================================
6047     Service provision
6049    ========================================================================== */
6051 /* called from system: queue for next pass through event loop */
6052 - (void)requestService: (NSPasteboard *)pboard
6053               userData: (NSString *)userData
6054                  error: (NSString **)error
6056   [ns_pending_service_names addObject: userData];
6057   [ns_pending_service_args addObject: [NSString stringWithUTF8String:
6058       SSDATA (ns_string_from_pasteboard (pboard))]];
6062 /* called from ns_read_socket to clear queue */
6063 - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
6065   struct frame *emacsframe = SELECTED_FRAME ();
6066   NSEvent *theEvent = [NSApp currentEvent];
6068   NSTRACE ("[EmacsApp fulfillService:withArg:]");
6070   if (!emacs_event)
6071     return NO;
6073   emacs_event->kind = NS_NONKEY_EVENT;
6074   emacs_event->code = KEY_NS_SPI_SERVICE_CALL;
6075   ns_input_spi_name = build_string ([name UTF8String]);
6076   ns_input_spi_arg = build_string ([arg UTF8String]);
6077   emacs_event->modifiers = EV_MODIFIERS (theEvent);
6078   EV_TRAILER (theEvent);
6080   return YES;
6084 @end  /* EmacsApp */
6087 /* ==========================================================================
6089     EmacsView implementation
6091    ========================================================================== */
6094 @implementation EmacsView
6096 /* needed to inform when window closed from LISP */
6097 - (void) setWindowClosing: (BOOL)closing
6099   NSTRACE ("[EmacsView setWindowClosing:%d]", closing);
6101   windowClosing = closing;
6105 - (void)dealloc
6107   NSTRACE ("[EmacsView dealloc]");
6108   [toolbar release];
6109   if (fs_state == FULLSCREEN_BOTH)
6110     [nonfs_window release];
6111   [super dealloc];
6115 /* called on font panel selection */
6116 - (void)changeFont: (id)sender
6118   NSEvent *e = [[self window] currentEvent];
6119   struct face *face = FACE_FROM_ID (emacsframe, DEFAULT_FACE_ID);
6120   struct font *font = face->font;
6121   id newFont;
6122   CGFloat size;
6123   NSFont *nsfont;
6125   NSTRACE ("[EmacsView changeFont:]");
6127   if (!emacs_event)
6128     return;
6130 #ifdef NS_IMPL_GNUSTEP
6131   nsfont = ((struct nsfont_info *)font)->nsfont;
6132 #endif
6133 #ifdef NS_IMPL_COCOA
6134   nsfont = (NSFont *) macfont_get_nsctfont (font);
6135 #endif
6137   if ((newFont = [sender convertFont: nsfont]))
6138     {
6139       SET_FRAME_GARBAGED (emacsframe); /* now needed as of 2008/10 */
6141       emacs_event->kind = NS_NONKEY_EVENT;
6142       emacs_event->modifiers = 0;
6143       emacs_event->code = KEY_NS_CHANGE_FONT;
6145       size = [newFont pointSize];
6146       ns_input_fontsize = make_number (lrint (size));
6147       ns_input_font = build_string ([[newFont familyName] UTF8String]);
6148       EV_TRAILER (e);
6149     }
6153 - (BOOL)acceptsFirstResponder
6155   NSTRACE ("[EmacsView acceptsFirstResponder]");
6156   return YES;
6160 - (void)resetCursorRects
6162   NSRect visible = [self visibleRect];
6163   NSCursor *currentCursor = FRAME_POINTER_TYPE (emacsframe);
6164   NSTRACE ("[EmacsView resetCursorRects]");
6166   if (currentCursor == nil)
6167     currentCursor = [NSCursor arrowCursor];
6169   if (!NSIsEmptyRect (visible))
6170     [self addCursorRect: visible cursor: currentCursor];
6172 #if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 101300
6173 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
6174   if ([currentCursor respondsToSelector: @selector(setOnMouseEntered)])
6175 #endif
6176     [currentCursor setOnMouseEntered: YES];
6177 #endif
6182 /*****************************************************************************/
6183 /* Keyboard handling. */
6184 #define NS_KEYLOG 0
6186 - (void)keyDown: (NSEvent *)theEvent
6188   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
6189   int code;
6190   unsigned fnKeysym = 0;
6191   static NSMutableArray *nsEvArray;
6192   unsigned int flags = [theEvent modifierFlags];
6194   NSTRACE ("[EmacsView keyDown:]");
6196   /* Rhapsody and macOS give up and down events for the arrow keys */
6197   if (ns_fake_keydown == YES)
6198     ns_fake_keydown = NO;
6199   else if ([theEvent type] != NSEventTypeKeyDown)
6200     return;
6202   if (!emacs_event)
6203     return;
6205  if (![[self window] isKeyWindow]
6206      && [[theEvent window] isKindOfClass: [EmacsWindow class]]
6207      /* we must avoid an infinite loop here. */
6208      && (EmacsView *)[[theEvent window] delegate] != self)
6209    {
6210      /* XXX: There is an occasional condition in which, when Emacs display
6211          updates a different frame from the current one, and temporarily
6212          selects it, then processes some interrupt-driven input
6213          (dispnew.c:3878), OS will send the event to the correct NSWindow, but
6214          for some reason that window has its first responder set to the NSView
6215          most recently updated (I guess), which is not the correct one. */
6216      [(EmacsView *)[[theEvent window] delegate] keyDown: theEvent];
6217      return;
6218    }
6220   if (nsEvArray == nil)
6221     nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1];
6223   [NSCursor setHiddenUntilMouseMoves: YES];
6225   if (hlinfo->mouse_face_hidden && INTEGERP (Vmouse_highlight))
6226     {
6227       clear_mouse_face (hlinfo);
6228       hlinfo->mouse_face_hidden = 1;
6229     }
6231   if (!processingCompose)
6232     {
6233       /* FIXME: What should happen for key sequences with more than
6234          one character?  */
6235       code = ([[theEvent charactersIgnoringModifiers] length] == 0) ?
6236         0 : [[theEvent charactersIgnoringModifiers] characterAtIndex: 0];
6238       /* is it a "function key"? */
6239       /* Note: Sometimes a plain key will have the NSEventModifierFlagNumericPad
6240          flag set (this is probably a bug in the OS).
6241       */
6242       if (code < 0x00ff && (flags&NSEventModifierFlagNumericPad))
6243         {
6244           fnKeysym = ns_convert_key ([theEvent keyCode] | NSEventModifierFlagNumericPad);
6245         }
6246       if (fnKeysym == 0)
6247         {
6248           fnKeysym = ns_convert_key (code);
6249         }
6251       if (fnKeysym)
6252         {
6253           /* COUNTERHACK: map 'Delete' on upper-right main KB to 'Backspace',
6254              because Emacs treats Delete and KP-Delete same (in simple.el). */
6255           if ((fnKeysym == 0xFFFF && [theEvent keyCode] == 0x33)
6256 #ifdef NS_IMPL_GNUSTEP
6257               /*  GNUstep uses incompatible keycodes, even for those that are
6258                   supposed to be hardware independent.  Just check for delete.
6259                   Keypad delete does not have keysym 0xFFFF.
6260                   See https://savannah.gnu.org/bugs/?25395
6261               */
6262               || (fnKeysym == 0xFFFF && code == 127)
6263 #endif
6264             )
6265             code = 0xFF08; /* backspace */
6266           else
6267             code = fnKeysym;
6268         }
6270       /* The âŒ˜ and âŒ¥ modifiers can be either shift-like (for alternate
6271          character input) or control-like (as command prefix).  If we
6272          have only shift-like modifiers, then we should use the
6273          translated characters (returned by the characters method); if
6274          we have only control-like modifiers, then we should use the
6275          untranslated characters (returned by the
6276          charactersIgnoringModifiers method).  An annoyance happens if
6277          we have both shift-like and control-like modifiers because
6278          the NSEvent API doesn’t let us ignore only some modifiers.
6279          In that case we use UCKeyTranslate (ns_get_shifted_character)
6280          to look up the correct character.  */
6282       /* EV_MODIFIERS2 uses parse_solitary_modifier on all known
6283          modifier keys, which returns 0 for shift-like modifiers.
6284          Therefore its return value is the set of control-like
6285          modifiers.  */
6286       emacs_event->modifiers = EV_MODIFIERS2 (flags);
6288       /* Function keys (such as the F-keys, arrow keys, etc.) set
6289          modifiers as though the fn key has been pressed when it
6290          hasn't.  Also some combinations of fn and a function key
6291          return a different key than was pressed (e.g. fn-<left> gives
6292          <home>).  We need to unset the fn modifier in these cases.
6293          FIXME: Can we avoid setting it in the first place.  */
6294       if (fnKeysym && (flags & NS_FUNCTION_KEY_MASK))
6295         emacs_event->modifiers ^= parse_solitary_modifier (ns_function_modifier);
6297       if (NS_KEYLOG)
6298         fprintf (stderr, "keyDown: code =%x\tfnKey =%x\tflags = %x\tmods = %x\n",
6299                  code, fnKeysym, flags, emacs_event->modifiers);
6301       /* If it was a function key or had control-like modifiers, pass
6302          it directly to Emacs.  */
6303       if (fnKeysym || (emacs_event->modifiers
6304                        && (emacs_event->modifiers != shift_modifier)
6305                        && [[theEvent charactersIgnoringModifiers] length] > 0))
6306         {
6307           emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
6308           /* FIXME: What are the next four lines supposed to do?  */
6309           if (code < 0x20)
6310             code |= (1<<28)|(3<<16);
6311           else if (code == 0x7f)
6312             code |= (1<<28)|(3<<16);
6313           else if (!fnKeysym)
6314             {
6315 #ifdef NS_IMPL_COCOA
6316               /* We potentially have both shift- and control-like
6317                  modifiers in use, so find the correct character
6318                  ignoring any control-like ones.  */
6319               code = ns_get_shifted_character (theEvent);
6320 #endif
6322               /* FIXME: This seems wrong, characters in the range
6323                  [0x80, 0xFF] are not ASCII characters.  Can’t we just
6324                  use MULTIBYTE_CHAR_KEYSTROKE_EVENT here for all kinds
6325                  of characters?  */
6326               emacs_event->kind = code > 0xFF
6327                 ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT;
6328             }
6330           emacs_event->code = code;
6331           EV_TRAILER (theEvent);
6332           processingCompose = NO;
6333           return;
6334         }
6335     }
6337   /* If we get here, a non-function key without control-like modifiers
6338      was hit.  Use interpretKeyEvents, which in turn will call
6339      insertText; see
6340      https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/EventOverview/HandlingKeyEvents/HandlingKeyEvents.html.  */
6342   if (NS_KEYLOG && !processingCompose)
6343     fprintf (stderr, "keyDown: Begin compose sequence.\n");
6345   /* FIXME: interpretKeyEvents doesn’t seem to send insertText if âŒ˜ is
6346      used as shift-like modifier, at least on El Capitan.  Mask it
6347      out.  This shouldn’t be needed though; we should figure out what
6348      the correct way of handling âŒ˜ is.  */
6349   if ([theEvent modifierFlags] & NSEventModifierFlagCommand)
6350     theEvent = [NSEvent keyEventWithType:[theEvent type]
6351                                 location:[theEvent locationInWindow]
6352                            modifierFlags:[theEvent modifierFlags] & ~NSEventModifierFlagCommand
6353                                timestamp:[theEvent timestamp]
6354                             windowNumber:[theEvent windowNumber]
6355                                  context:nil
6356                               characters:[theEvent characters]
6357                         charactersIgnoringModifiers:[theEvent charactersIgnoringModifiers]
6358                                isARepeat:[theEvent isARepeat]
6359                                  keyCode:[theEvent keyCode]];
6361   processingCompose = YES;
6362   /* FIXME: Use [NSArray arrayWithObject:theEvent]?  */
6363   [nsEvArray addObject: theEvent];
6364   [self interpretKeyEvents: nsEvArray];
6365   [nsEvArray removeObject: theEvent];
6369 /* <NSTextInput> implementation (called through super interpretKeyEvents:]). */
6372 /* <NSTextInput>: called when done composing;
6373    NOTE: also called when we delete over working text, followed immed.
6374          by doCommandBySelector: deleteBackward: */
6375 - (void)insertText: (id)aString
6377   NSString *s;
6378   NSUInteger len;
6380   NSTRACE ("[EmacsView insertText:]");
6382   if ([aString isKindOfClass:[NSAttributedString class]])
6383     s = [aString string];
6384   else
6385     s = aString;
6387   len = [s length];
6389   if (NS_KEYLOG)
6390     NSLog (@"insertText '%@'\tlen = %lu", aString, (unsigned long) len);
6391   processingCompose = NO;
6393   if (!emacs_event)
6394     return;
6396   /* first, clear any working text */
6397   if (workingText != nil)
6398     [self deleteWorkingText];
6400   /* It might be preferable to use getCharacters:range: below,
6401      cf. https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CocoaPerformance/Articles/StringDrawing.html#//apple_ref/doc/uid/TP40001445-112378.
6402      However, we probably can't use SAFE_NALLOCA here because it might
6403      exit nonlocally.  */
6405   /* now insert the string as keystrokes */
6406   for (NSUInteger i = 0; i < len; i++)
6407     {
6408       NSUInteger code = [s characterAtIndex:i];
6409       if (UTF_16_HIGH_SURROGATE_P (code) && i < len - 1)
6410         {
6411           unichar low = [s characterAtIndex:i + 1];
6412           if (UTF_16_LOW_SURROGATE_P (low))
6413             {
6414               code = surrogates_to_codepoint (low, code);
6415               ++i;
6416             }
6417         }
6418       /* TODO: still need this? */
6419       if (code == 0x2DC)
6420         code = '~'; /* 0x7E */
6421       if (code != 32) /* Space */
6422         emacs_event->modifiers = 0;
6423       emacs_event->kind
6424         = code > 0xFF ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT;
6425       emacs_event->code = code;
6426       EV_TRAILER ((id)nil);
6427     }
6431 /* <NSTextInput>: inserts display of composing characters */
6432 - (void)setMarkedText: (id)aString selectedRange: (NSRange)selRange
6434   NSString *str = [aString respondsToSelector: @selector (string)] ?
6435     [aString string] : aString;
6437   NSTRACE ("[EmacsView setMarkedText:selectedRange:]");
6439   if (NS_KEYLOG)
6440     NSLog (@"setMarkedText '%@' len =%lu range %lu from %lu",
6441            str, (unsigned long)[str length],
6442            (unsigned long)selRange.length,
6443            (unsigned long)selRange.location);
6445   if (workingText != nil)
6446     [self deleteWorkingText];
6447   if ([str length] == 0)
6448     return;
6450   if (!emacs_event)
6451     return;
6453   processingCompose = YES;
6454   workingText = [str copy];
6455   ns_working_text = build_string ([workingText UTF8String]);
6457   emacs_event->kind = NS_TEXT_EVENT;
6458   emacs_event->code = KEY_NS_PUT_WORKING_TEXT;
6459   EV_TRAILER ((id)nil);
6463 /* delete display of composing characters [not in <NSTextInput>] */
6464 - (void)deleteWorkingText
6466   NSTRACE ("[EmacsView deleteWorkingText]");
6468   if (workingText == nil)
6469     return;
6470   if (NS_KEYLOG)
6471     NSLog(@"deleteWorkingText len =%lu\n", (unsigned long)[workingText length]);
6472   [workingText release];
6473   workingText = nil;
6474   processingCompose = NO;
6476   if (!emacs_event)
6477     return;
6479   emacs_event->kind = NS_TEXT_EVENT;
6480   emacs_event->code = KEY_NS_UNPUT_WORKING_TEXT;
6481   EV_TRAILER ((id)nil);
6485 - (BOOL)hasMarkedText
6487   NSTRACE ("[EmacsView hasMarkedText]");
6489   return workingText != nil;
6493 - (NSRange)markedRange
6495   NSTRACE ("[EmacsView markedRange]");
6497   NSRange rng = workingText != nil
6498     ? NSMakeRange (0, [workingText length]) : NSMakeRange (NSNotFound, 0);
6499   if (NS_KEYLOG)
6500     NSLog (@"markedRange request");
6501   return rng;
6505 - (void)unmarkText
6507   NSTRACE ("[EmacsView unmarkText]");
6509   if (NS_KEYLOG)
6510     NSLog (@"unmark (accept) text");
6511   [self deleteWorkingText];
6512   processingCompose = NO;
6516 /* used to position char selection windows, etc. */
6517 - (NSRect)firstRectForCharacterRange: (NSRange)theRange
6519   NSRect rect;
6520   NSPoint pt;
6521   struct window *win = XWINDOW (FRAME_SELECTED_WINDOW (emacsframe));
6523   NSTRACE ("[EmacsView firstRectForCharacterRange:]");
6525   if (NS_KEYLOG)
6526     NSLog (@"firstRectForCharRange request");
6528   rect.size.width = theRange.length * FRAME_COLUMN_WIDTH (emacsframe);
6529   rect.size.height = FRAME_LINE_HEIGHT (emacsframe);
6530   pt.x = WINDOW_TEXT_TO_FRAME_PIXEL_X (win, win->phys_cursor.x);
6531   pt.y = WINDOW_TO_FRAME_PIXEL_Y (win, win->phys_cursor.y
6532                                        +FRAME_LINE_HEIGHT (emacsframe));
6534   pt = [self convertPoint: pt toView: nil];
6536 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
6537 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6538   if ([[self window] respondsToSelector: @selector(convertRectToScreen:)])
6539     {
6540 #endif
6541       rect.origin = pt;
6542       rect = [(EmacsWindow *) [self window] convertRectToScreen: rect];
6543 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6544     }
6545   else
6546 #endif
6547 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
6548 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 \
6549   || defined (NS_IMPL_GNUSTEP)
6550     {
6551       pt = [[self window] convertBaseToScreen: pt];
6552       rect.origin = pt;
6553     }
6554 #endif
6556   return rect;
6560 - (NSInteger)conversationIdentifier
6562   return (NSInteger)self;
6566 - (void)doCommandBySelector: (SEL)aSelector
6568   NSTRACE ("[EmacsView doCommandBySelector:]");
6570   if (NS_KEYLOG)
6571     NSLog (@"doCommandBySelector: %@", NSStringFromSelector (aSelector));
6573   processingCompose = NO;
6574   if (aSelector == @selector (deleteBackward:))
6575     {
6576       /* happens when user backspaces over an ongoing composition:
6577          throw a 'delete' into the event queue */
6578       if (!emacs_event)
6579         return;
6580       emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
6581       emacs_event->code = 0xFF08;
6582       EV_TRAILER ((id)nil);
6583     }
6586 - (NSArray *)validAttributesForMarkedText
6588   static NSArray *arr = nil;
6589   if (arr == nil) arr = [NSArray new];
6590  /* [[NSArray arrayWithObject: NSUnderlineStyleAttributeName] retain]; */
6591   return arr;
6594 - (NSRange)selectedRange
6596   if (NS_KEYLOG)
6597     NSLog (@"selectedRange request");
6598   return NSMakeRange (NSNotFound, 0);
6601 #if defined (NS_IMPL_COCOA) || GNUSTEP_GUI_MAJOR_VERSION > 0 || \
6602     GNUSTEP_GUI_MINOR_VERSION > 22
6603 - (NSUInteger)characterIndexForPoint: (NSPoint)thePoint
6604 #else
6605 - (unsigned int)characterIndexForPoint: (NSPoint)thePoint
6606 #endif
6608   if (NS_KEYLOG)
6609     NSLog (@"characterIndexForPoint request");
6610   return 0;
6613 - (NSAttributedString *)attributedSubstringFromRange: (NSRange)theRange
6615   static NSAttributedString *str = nil;
6616   if (str == nil) str = [NSAttributedString new];
6617   if (NS_KEYLOG)
6618     NSLog (@"attributedSubstringFromRange request");
6619   return str;
6622 /* End <NSTextInput> impl. */
6623 /*****************************************************************************/
6626 /* This is what happens when the user presses a mouse button.  */
6627 - (void)mouseDown: (NSEvent *)theEvent
6629   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6630   NSPoint p = [self convertPoint: [theEvent locationInWindow] fromView: nil];
6632   NSTRACE ("[EmacsView mouseDown:]");
6634   [self deleteWorkingText];
6636   if (!emacs_event)
6637     return;
6639   dpyinfo->last_mouse_frame = emacsframe;
6640   /* appears to be needed to prevent spurious movement events generated on
6641      button clicks */
6642   emacsframe->mouse_moved = 0;
6644   if ([theEvent type] == NSEventTypeScrollWheel)
6645     {
6646 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
6647 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6648       if ([theEvent respondsToSelector:@selector(hasPreciseScrollingDeltas)])
6649         {
6650 #endif
6651           /* If the input device is a touchpad or similar, use precise
6652            * scrolling deltas.  These are measured in pixels, so we
6653            * have to add them up until they exceed one line height,
6654            * then we can send a scroll wheel event.
6655            *
6656            * If the device only has coarse scrolling deltas, like a
6657            * real mousewheel, the deltas represent a ratio of whole
6658            * lines, so round up the number of lines.  This means we
6659            * always send one scroll event per click, but can still
6660            * scroll more than one line if the OS tells us to.
6661            */
6662           bool horizontal;
6663           int lines = 0;
6664           int scrollUp = NO;
6666           /* FIXME: At the top or bottom of the buffer we should
6667            * ignore momentum-phase events.  */
6668           if (! ns_use_mwheel_momentum
6669               && [theEvent momentumPhase] != NSEventPhaseNone)
6670             return;
6672           if ([theEvent hasPreciseScrollingDeltas])
6673             {
6674               static int totalDeltaX, totalDeltaY;
6675               int lineHeight;
6677               if (NUMBERP (ns_mwheel_line_height))
6678                 lineHeight = XINT (ns_mwheel_line_height);
6679               else
6680                 {
6681                   /* FIXME: Use actual line height instead of the default.  */
6682                   lineHeight = default_line_pixel_height
6683                     (XWINDOW (FRAME_SELECTED_WINDOW (emacsframe)));
6684                 }
6686               if ([theEvent phase] == NSEventPhaseBegan)
6687                 {
6688                   totalDeltaX = 0;
6689                   totalDeltaY = 0;
6690                 }
6692               totalDeltaX += [theEvent scrollingDeltaX];
6693               totalDeltaY += [theEvent scrollingDeltaY];
6695               /* Calculate the number of lines, if any, to scroll, and
6696                * reset the total delta for the direction we're NOT
6697                * scrolling so that small movements don't add up.  */
6698               if (abs (totalDeltaX) > abs (totalDeltaY)
6699                   && abs (totalDeltaX) > lineHeight)
6700                 {
6701                   horizontal = YES;
6702                   scrollUp = totalDeltaX > 0;
6704                   lines = abs (totalDeltaX / lineHeight);
6705                   totalDeltaX = totalDeltaX % lineHeight;
6706                   totalDeltaY = 0;
6707                 }
6708               else if (abs (totalDeltaY) >= abs (totalDeltaX)
6709                        && abs (totalDeltaY) > lineHeight)
6710                 {
6711                   horizontal = NO;
6712                   scrollUp = totalDeltaY > 0;
6714                   lines = abs (totalDeltaY / lineHeight);
6715                   totalDeltaY = totalDeltaY % lineHeight;
6716                   totalDeltaX = 0;
6717                 }
6719               if (lines > 1 && ! ns_use_mwheel_acceleration)
6720                 lines = 1;
6721             }
6722           else
6723             {
6724               CGFloat delta;
6726               if ([theEvent scrollingDeltaY] == 0)
6727                 {
6728                   horizontal = YES;
6729                   delta = [theEvent scrollingDeltaX];
6730                 }
6731               else
6732                 {
6733                   horizontal = NO;
6734                   delta = [theEvent scrollingDeltaY];
6735                 }
6737               lines = (ns_use_mwheel_acceleration)
6738                 ? ceil (fabs (delta)) : 1;
6740               scrollUp = delta > 0;
6741             }
6743           if (lines == 0)
6744             return;
6746           emacs_event->kind = horizontal ? HORIZ_WHEEL_EVENT : WHEEL_EVENT;
6747           emacs_event->arg = (make_number (lines));
6749           emacs_event->code = 0;
6750           emacs_event->modifiers = EV_MODIFIERS (theEvent) |
6751             (scrollUp ? up_modifier : down_modifier);
6752 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6753         }
6754       else
6755 #endif
6756 #endif /* defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
6757 #if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6758         {
6759           CGFloat delta = [theEvent deltaY];
6760           /* Mac notebooks send wheel events w/delta =0 when trackpad scrolling */
6761           if (delta == 0)
6762             {
6763               delta = [theEvent deltaX];
6764               if (delta == 0)
6765                 {
6766                   NSTRACE_MSG ("deltaIsZero");
6767                   return;
6768                 }
6769               emacs_event->kind = HORIZ_WHEEL_EVENT;
6770             }
6771           else
6772             emacs_event->kind = WHEEL_EVENT;
6774           emacs_event->code = 0;
6775           emacs_event->modifiers = EV_MODIFIERS (theEvent) |
6776             ((delta > 0) ? up_modifier : down_modifier);
6777         }
6778 #endif
6779     }
6780   else
6781     {
6782       emacs_event->kind = MOUSE_CLICK_EVENT;
6783       emacs_event->code = EV_BUTTON (theEvent);
6784       emacs_event->modifiers = EV_MODIFIERS (theEvent)
6785                              | EV_UDMODIFIERS (theEvent);
6786     }
6788   XSETINT (emacs_event->x, lrint (p.x));
6789   XSETINT (emacs_event->y, lrint (p.y));
6790   EV_TRAILER (theEvent);
6791   return;
6795 - (void)rightMouseDown: (NSEvent *)theEvent
6797   NSTRACE ("[EmacsView rightMouseDown:]");
6798   [self mouseDown: theEvent];
6802 - (void)otherMouseDown: (NSEvent *)theEvent
6804   NSTRACE ("[EmacsView otherMouseDown:]");
6805   [self mouseDown: theEvent];
6809 - (void)mouseUp: (NSEvent *)theEvent
6811   NSTRACE ("[EmacsView mouseUp:]");
6812   [self mouseDown: theEvent];
6816 - (void)rightMouseUp: (NSEvent *)theEvent
6818   NSTRACE ("[EmacsView rightMouseUp:]");
6819   [self mouseDown: theEvent];
6823 - (void)otherMouseUp: (NSEvent *)theEvent
6825   NSTRACE ("[EmacsView otherMouseUp:]");
6826   [self mouseDown: theEvent];
6830 - (void) scrollWheel: (NSEvent *)theEvent
6832   NSTRACE ("[EmacsView scrollWheel:]");
6833   [self mouseDown: theEvent];
6837 /* Tell emacs the mouse has moved. */
6838 - (void)mouseMoved: (NSEvent *)e
6840   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
6841   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6842   Lisp_Object frame;
6843   NSPoint pt;
6845   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsView mouseMoved:]");
6847   dpyinfo->last_mouse_movement_time = EV_TIMESTAMP (e);
6848   pt = [self convertPoint: [e locationInWindow] fromView: nil];
6849   dpyinfo->last_mouse_motion_x = pt.x;
6850   dpyinfo->last_mouse_motion_y = pt.y;
6852   /* update any mouse face */
6853   if (hlinfo->mouse_face_hidden)
6854     {
6855       hlinfo->mouse_face_hidden = 0;
6856       clear_mouse_face (hlinfo);
6857     }
6859   /* tooltip handling */
6860   previous_help_echo_string = help_echo_string;
6861   help_echo_string = Qnil;
6863   if (!NILP (Vmouse_autoselect_window))
6864     {
6865       NSTRACE_MSG ("mouse_autoselect_window");
6866       static Lisp_Object last_mouse_window;
6867       Lisp_Object window
6868         = window_from_coordinates (emacsframe, pt.x, pt.y, 0, 0);
6870       if (WINDOWP (window)
6871           && !EQ (window, last_mouse_window)
6872           && !EQ (window, selected_window)
6873           && (!NILP (focus_follows_mouse)
6874               || (EQ (XWINDOW (window)->frame,
6875                       XWINDOW (selected_window)->frame))))
6876         {
6877           NSTRACE_MSG ("in_window");
6878           emacs_event->kind = SELECT_WINDOW_EVENT;
6879           emacs_event->frame_or_window = window;
6880           EV_TRAILER2 (e);
6881         }
6882       /* Remember the last window where we saw the mouse.  */
6883       last_mouse_window = window;
6884     }
6886   if (!note_mouse_movement (emacsframe, pt.x, pt.y))
6887     help_echo_string = previous_help_echo_string;
6889   XSETFRAME (frame, emacsframe);
6890   if (!NILP (help_echo_string) || !NILP (previous_help_echo_string))
6891     {
6892       /* NOTE: help_echo_{window,pos,object} are set in xdisp.c
6893          (note_mouse_highlight), which is called through the
6894          note_mouse_movement () call above */
6895       any_help_event_p = YES;
6896       gen_help_event (help_echo_string, frame, help_echo_window,
6897                       help_echo_object, help_echo_pos);
6898     }
6900   if (emacsframe->mouse_moved && send_appdefined)
6901     ns_send_appdefined (-1);
6905 - (void)mouseDragged: (NSEvent *)e
6907   NSTRACE ("[EmacsView mouseDragged:]");
6908   [self mouseMoved: e];
6912 - (void)rightMouseDragged: (NSEvent *)e
6914   NSTRACE ("[EmacsView rightMouseDragged:]");
6915   [self mouseMoved: e];
6919 - (void)otherMouseDragged: (NSEvent *)e
6921   NSTRACE ("[EmacsView otherMouseDragged:]");
6922   [self mouseMoved: e];
6926 - (BOOL)windowShouldClose: (id)sender
6928   NSEvent *e =[[self window] currentEvent];
6930   NSTRACE ("[EmacsView windowShouldClose:]");
6931   windowClosing = YES;
6932   if (!emacs_event)
6933     return NO;
6934   emacs_event->kind = DELETE_WINDOW_EVENT;
6935   emacs_event->modifiers = 0;
6936   emacs_event->code = 0;
6937   EV_TRAILER (e);
6938   /* Don't close this window, let this be done from lisp code.  */
6939   return NO;
6942 - (void) updateFrameSize: (BOOL) delay
6944   NSWindow *window = [self window];
6945   NSRect wr = [window frame];
6946   int extra = 0;
6947   int oldc = cols, oldr = rows;
6948   int oldw = FRAME_PIXEL_WIDTH (emacsframe);
6949   int oldh = FRAME_PIXEL_HEIGHT (emacsframe);
6950   int neww, newh;
6952   NSTRACE ("[EmacsView updateFrameSize:]");
6953   NSTRACE_SIZE ("Original size", NSMakeSize (oldw, oldh));
6954   NSTRACE_RECT ("Original frame", wr);
6955   NSTRACE_MSG  ("Original columns: %d", cols);
6956   NSTRACE_MSG  ("Original rows: %d", rows);
6958   if (! [self isFullscreen])
6959     {
6960       int toolbar_height;
6961 #ifdef NS_IMPL_GNUSTEP
6962       // GNUstep does not always update the tool bar height.  Force it.
6963       if (toolbar && [toolbar isVisible])
6964           update_frame_tool_bar (emacsframe);
6965 #endif
6967       toolbar_height = FRAME_TOOLBAR_HEIGHT (emacsframe);
6968       if (toolbar_height < 0)
6969         toolbar_height = 35;
6971       extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
6972         + toolbar_height;
6973     }
6975   if (wait_for_tool_bar)
6976     {
6977       /* The toolbar height is always 0 in fullscreen and undecorated
6978          frames, so don't wait for it to become available. */
6979       if (FRAME_TOOLBAR_HEIGHT (emacsframe) == 0
6980           && FRAME_UNDECORATED (emacsframe) == false
6981           && ! [self isFullscreen])
6982         {
6983           NSTRACE_MSG ("Waiting for toolbar");
6984           return;
6985         }
6986       wait_for_tool_bar = NO;
6987     }
6989   neww = (int)wr.size.width - emacsframe->border_width;
6990   newh = (int)wr.size.height - extra;
6992   NSTRACE_SIZE ("New size", NSMakeSize (neww, newh));
6993   NSTRACE_MSG ("FRAME_TOOLBAR_HEIGHT: %d", FRAME_TOOLBAR_HEIGHT (emacsframe));
6994   NSTRACE_MSG ("FRAME_NS_TITLEBAR_HEIGHT: %d", FRAME_NS_TITLEBAR_HEIGHT (emacsframe));
6996   cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, neww);
6997   rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe, newh);
6999   if (cols < MINWIDTH)
7000     cols = MINWIDTH;
7002   if (rows < MINHEIGHT)
7003     rows = MINHEIGHT;
7005   NSTRACE_MSG ("New columns: %d", cols);
7006   NSTRACE_MSG ("New rows: %d", rows);
7008   if (oldr != rows || oldc != cols || neww != oldw || newh != oldh)
7009     {
7010       NSView *view = FRAME_NS_VIEW (emacsframe);
7012       change_frame_size (emacsframe,
7013                          FRAME_PIXEL_TO_TEXT_WIDTH (emacsframe, neww),
7014                          FRAME_PIXEL_TO_TEXT_HEIGHT (emacsframe, newh),
7015                          0, delay, 0, 1);
7016       SET_FRAME_GARBAGED (emacsframe);
7017       cancel_mouse_face (emacsframe);
7019       /* The next two lines set the frame to the same size as we've
7020          already set above.  We need to do this when we switch back
7021          from non-native fullscreen, in other circumstances it appears
7022          to be a noop.  (bug#28872) */
7023       wr = NSMakeRect (0, 0, neww, newh);
7024       [view setFrame: wr];
7026       // to do: consider using [NSNotificationCenter postNotificationName:].
7027       [self windowDidMove: // Update top/left.
7028               [NSNotification notificationWithName:NSWindowDidMoveNotification
7029                                             object:[view window]]];
7030     }
7031   else
7032     {
7033       NSTRACE_MSG ("No change");
7034     }
7037 - (NSSize)windowWillResize: (NSWindow *)sender toSize: (NSSize)frameSize
7038 /* normalize frame to gridded text size */
7040   int extra = 0;
7042   NSTRACE ("[EmacsView windowWillResize:toSize: " NSTRACE_FMT_SIZE "]",
7043            NSTRACE_ARG_SIZE (frameSize));
7044   NSTRACE_RECT   ("[sender frame]", [sender frame]);
7045   NSTRACE_FSTYPE ("fs_state", fs_state);
7047   if (!FRAME_LIVE_P (emacsframe))
7048     return frameSize;
7050   if (fs_state == FULLSCREEN_MAXIMIZED
7051       && (maximized_width != (int)frameSize.width
7052           || maximized_height != (int)frameSize.height))
7053     [self setFSValue: FULLSCREEN_NONE];
7054   else if (fs_state == FULLSCREEN_WIDTH
7055            && maximized_width != (int)frameSize.width)
7056     [self setFSValue: FULLSCREEN_NONE];
7057   else if (fs_state == FULLSCREEN_HEIGHT
7058            && maximized_height != (int)frameSize.height)
7059     [self setFSValue: FULLSCREEN_NONE];
7061   if (fs_state == FULLSCREEN_NONE)
7062     maximized_width = maximized_height = -1;
7064   if (! [self isFullscreen])
7065     {
7066       extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
7067         + FRAME_TOOLBAR_HEIGHT (emacsframe);
7068     }
7070   cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, frameSize.width);
7071   if (cols < MINWIDTH)
7072     cols = MINWIDTH;
7074   rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe,
7075                                            frameSize.height - extra);
7076   if (rows < MINHEIGHT)
7077     rows = MINHEIGHT;
7078 #ifdef NS_IMPL_COCOA
7079   {
7080     /* this sets window title to have size in it; the wm does this under GS */
7081     NSRect r = [[self window] frame];
7082     if (r.size.height == frameSize.height && r.size.width == frameSize.width)
7083       {
7084         if (old_title != 0)
7085           {
7086             xfree (old_title);
7087             old_title = 0;
7088           }
7089       }
7090     else if (fs_state == FULLSCREEN_NONE && ! maximizing_resize
7091              && [[self window] title] != NULL)
7092       {
7093         char *size_title;
7094         NSWindow *window = [self window];
7095         if (old_title == 0)
7096           {
7097             char *t = strdup ([[[self window] title] UTF8String]);
7098             char *pos = strstr (t, "  â€”  ");
7099             if (pos)
7100               *pos = '\0';
7101             old_title = t;
7102           }
7103         size_title = xmalloc (strlen (old_title) + 40);
7104         esprintf (size_title, "%s  â€”  (%d x %d)", old_title, cols, rows);
7105         [window setTitle: [NSString stringWithUTF8String: size_title]];
7106         [window display];
7107         xfree (size_title);
7108       }
7109   }
7110 #endif /* NS_IMPL_COCOA */
7112   NSTRACE_MSG ("cols: %d  rows: %d", cols, rows);
7114   /* Restrict the new size to the text gird.
7116      Don't restrict the width if the user only adjusted the height, and
7117      vice versa.  (Without this, the frame would shrink, and move
7118      slightly, if the window was resized by dragging one of its
7119      borders.) */
7120   if (!frame_resize_pixelwise)
7121     {
7122       NSRect r = [[self window] frame];
7124       if (r.size.width != frameSize.width)
7125         {
7126           frameSize.width =
7127             FRAME_TEXT_COLS_TO_PIXEL_WIDTH  (emacsframe, cols);
7128         }
7130       if (r.size.height != frameSize.height)
7131         {
7132           frameSize.height =
7133             FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (emacsframe, rows) + extra;
7134         }
7135     }
7137   NSTRACE_RETURN_SIZE (frameSize);
7139   return frameSize;
7143 - (void)windowDidResize: (NSNotification *)notification
7145   NSTRACE ("[EmacsView windowDidResize:]");
7146   if (!FRAME_LIVE_P (emacsframe))
7147     {
7148       NSTRACE_MSG ("Ignored (frame dead)");
7149       return;
7150     }
7151   if (emacsframe->output_data.ns->in_animation)
7152     {
7153       NSTRACE_MSG ("Ignored (in animation)");
7154       return;
7155     }
7157   if (! [self fsIsNative])
7158     {
7159       NSWindow *theWindow = [notification object];
7160       /* We can get notification on the non-FS window when in
7161          fullscreen mode.  */
7162       if ([self window] != theWindow) return;
7163     }
7165   NSTRACE_RECT ("frame", [[notification object] frame]);
7167 #ifdef NS_IMPL_GNUSTEP
7168   NSWindow *theWindow = [notification object];
7170    /* In GNUstep, at least currently, it's possible to get a didResize
7171       without getting a willResize.. therefore we need to act as if we got
7172       the willResize now */
7173   NSSize sz = [theWindow frame].size;
7174   sz = [self windowWillResize: theWindow toSize: sz];
7175 #endif /* NS_IMPL_GNUSTEP */
7177   if (cols > 0 && rows > 0)
7178     {
7179       [self updateFrameSize: YES];
7180     }
7182   ns_send_appdefined (-1);
7185 #ifdef NS_IMPL_COCOA
7186 - (void)viewDidEndLiveResize
7188   NSTRACE ("[EmacsView viewDidEndLiveResize]");
7190   [super viewDidEndLiveResize];
7191   if (old_title != 0)
7192     {
7193       [[self window] setTitle: [NSString stringWithUTF8String: old_title]];
7194       xfree (old_title);
7195       old_title = 0;
7196     }
7197   maximizing_resize = NO;
7199 #endif /* NS_IMPL_COCOA */
7202 - (void)windowDidBecomeKey: (NSNotification *)notification
7203 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
7205   [self windowDidBecomeKey];
7209 - (void)windowDidBecomeKey      /* for direct calls */
7211   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
7212   struct frame *old_focus = dpyinfo->x_focus_frame;
7214   NSTRACE ("[EmacsView windowDidBecomeKey]");
7216   if (emacsframe != old_focus)
7217     dpyinfo->x_focus_frame = emacsframe;
7219   ns_frame_rehighlight (emacsframe);
7221   if (emacs_event)
7222     {
7223       emacs_event->kind = FOCUS_IN_EVENT;
7224       EV_TRAILER ((id)nil);
7225     }
7229 - (void)windowDidResignKey: (NSNotification *)notification
7230 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
7232   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
7233   BOOL is_focus_frame = dpyinfo->x_focus_frame == emacsframe;
7234   NSTRACE ("[EmacsView windowDidResignKey:]");
7236   if (is_focus_frame)
7237     dpyinfo->x_focus_frame = 0;
7239   emacsframe->mouse_moved = 0;
7240   ns_frame_rehighlight (emacsframe);
7242   /* FIXME: for some reason needed on second and subsequent clicks away
7243             from sole-frame Emacs to get hollow box to show */
7244   if (!windowClosing && [[self window] isVisible] == YES)
7245     {
7246       x_update_cursor (emacsframe, 1);
7247       x_set_frame_alpha (emacsframe);
7248     }
7250   if (any_help_event_p)
7251     {
7252       Lisp_Object frame;
7253       XSETFRAME (frame, emacsframe);
7254       help_echo_string = Qnil;
7255       gen_help_event (Qnil, frame, Qnil, Qnil, 0);
7256     }
7258   if (emacs_event && is_focus_frame)
7259     {
7260       [self deleteWorkingText];
7261       emacs_event->kind = FOCUS_OUT_EVENT;
7262       EV_TRAILER ((id)nil);
7263     }
7267 - (void)windowWillMiniaturize: sender
7269   NSTRACE ("[EmacsView windowWillMiniaturize:]");
7273 - (void)setFrame:(NSRect)frameRect
7275   NSTRACE ("[EmacsView setFrame:" NSTRACE_FMT_RECT "]",
7276            NSTRACE_ARG_RECT (frameRect));
7278   [super setFrame:(NSRect)frameRect];
7282 - (BOOL)isFlipped
7284   return YES;
7288 - (BOOL)isOpaque
7290   return NO;
7294 - (void)createToolbar: (struct frame *)f
7296   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
7297   NSWindow *window = [view window];
7299   toolbar = [[EmacsToolbar alloc] initForView: self withIdentifier:
7300                    [NSString stringWithFormat: @"Emacs Frame %d",
7301                              ns_window_num]];
7302   [toolbar setVisible: NO];
7303   [window setToolbar: toolbar];
7305   /* Don't set frame garbaged until tool bar is up to date?
7306      This avoids an extra clear and redraw (flicker) at frame creation.  */
7307   if (FRAME_EXTERNAL_TOOL_BAR (f)) wait_for_tool_bar = YES;
7308   else wait_for_tool_bar = NO;
7311 #ifdef NS_IMPL_COCOA
7312   {
7313     NSButton *toggleButton;
7314     toggleButton = [window standardWindowButton: NSWindowToolbarButton];
7315     [toggleButton setTarget: self];
7316     [toggleButton setAction: @selector (toggleToolbar: )];
7317   }
7318 #endif
7322 - (instancetype) initFrameFromEmacs: (struct frame *)f
7324   NSRect r, wr;
7325   Lisp_Object tem;
7326   NSWindow *win;
7327   NSColor *col;
7328   NSString *name;
7330   NSTRACE ("[EmacsView initFrameFromEmacs:]");
7331   NSTRACE_MSG ("cols:%d lines:%d", f->text_cols, f->text_lines);
7333   windowClosing = NO;
7334   processingCompose = NO;
7335   scrollbarsNeedingUpdate = 0;
7336   fs_state = FULLSCREEN_NONE;
7337   fs_before_fs = next_maximized = -1;
7339   fs_is_native = NO;
7340 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7341 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7342   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7343 #endif
7344     fs_is_native = ns_use_native_fullscreen;
7345 #endif
7347   maximized_width = maximized_height = -1;
7348   nonfs_window = nil;
7350   ns_userRect = NSMakeRect (0, 0, 0, 0);
7351   r = NSMakeRect (0, 0, FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, f->text_cols),
7352                  FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, f->text_lines));
7353   [self initWithFrame: r];
7354   [self setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
7356   FRAME_NS_VIEW (f) = self;
7357   emacsframe = f;
7358 #ifdef NS_IMPL_COCOA
7359   old_title = 0;
7360   maximizing_resize = NO;
7361 #endif
7363   win = [[EmacsWindow alloc]
7364             initWithContentRect: r
7365                       styleMask: (FRAME_UNDECORATED (f)
7366                                   ? FRAME_UNDECORATED_FLAGS
7367                                   : FRAME_DECORATED_FLAGS)
7368                         backing: NSBackingStoreBuffered
7369                           defer: YES];
7371 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7372 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7373   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7374 #endif
7375     [win setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
7376 #endif
7378   wr = [win frame];
7379   bwidth = f->border_width = wr.size.width - r.size.width;
7381   [win setAcceptsMouseMovedEvents: YES];
7382   [win setDelegate: self];
7383 #if !defined (NS_IMPL_COCOA) || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7384 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7385   if ([win respondsToSelector: @selector(useOptimizedDrawing:)])
7386 #endif
7387     [win useOptimizedDrawing: YES];
7388 #endif
7390   [[win contentView] addSubview: self];
7392   if (ns_drag_types)
7393     [self registerForDraggedTypes: ns_drag_types];
7395   tem = f->name;
7396   name = [NSString stringWithUTF8String:
7397                    NILP (tem) ? "Emacs" : SSDATA (tem)];
7398   [win setTitle: name];
7400   /* toolbar support */
7401   if (! FRAME_UNDECORATED (f))
7402     [self createToolbar: f];
7404 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
7405 #ifndef NSAppKitVersionNumber10_10
7406 #define NSAppKitVersionNumber10_10 1343
7407 #endif
7409   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_10
7410       && FRAME_NS_APPEARANCE (f) != ns_appearance_aqua)
7411     win.appearance = [NSAppearance
7412                           appearanceNamed: NSAppearanceNameVibrantDark];
7413 #endif
7415 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
7416   if ([win respondsToSelector: @selector(titlebarAppearsTransparent)])
7417     win.titlebarAppearsTransparent = FRAME_NS_TRANSPARENT_TITLEBAR (f);
7418 #endif
7420   tem = f->icon_name;
7421   if (!NILP (tem))
7422     [win setMiniwindowTitle:
7423            [NSString stringWithUTF8String: SSDATA (tem)]];
7425   if (FRAME_PARENT_FRAME (f) != NULL)
7426     {
7427       NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
7428       [parent addChildWindow: win
7429                      ordered: NSWindowAbove];
7430     }
7432   if (FRAME_Z_GROUP (f) != z_group_none)
7433       win.level = NSNormalWindowLevel
7434         + (FRAME_Z_GROUP_BELOW (f) ? -1 : 1);
7436   {
7437     NSScreen *screen = [win screen];
7439     if (screen != 0)
7440       {
7441         NSPoint pt = NSMakePoint
7442           (IN_BOUND (-SCREENMAX, f->left_pos
7443                      + NS_PARENT_WINDOW_LEFT_POS (f), SCREENMAX),
7444            IN_BOUND (-SCREENMAX,
7445                      NS_PARENT_WINDOW_TOP_POS (f) - f->top_pos,
7446                      SCREENMAX));
7448         [win setFrameTopLeftPoint: pt];
7450         NSTRACE_RECT ("new frame", [win frame]);
7451       }
7452   }
7454   [win makeFirstResponder: self];
7456   col = ns_lookup_indexed_color (NS_FACE_BACKGROUND
7457                                  (FACE_FROM_ID (emacsframe, DEFAULT_FACE_ID)),
7458                                  emacsframe);
7459   [win setBackgroundColor: col];
7460   if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7461     [win setOpaque: NO];
7463 #if !defined (NS_IMPL_COCOA) \
7464   || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7465 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7466   if ([self respondsToSelector: @selector(allocateGState)])
7467 #endif
7468     [self allocateGState];
7469 #endif
7470   [NSApp registerServicesMenuSendTypes: ns_send_types
7471                            returnTypes: [NSArray array]];
7473   /* macOS Sierra automatically enables tabbed windows.  We can't
7474      allow this to be enabled until it's available on a Free system.
7475      Currently it only happens by accident and is buggy anyway. */
7476 #if defined (NS_IMPL_COCOA) \
7477   && MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
7478 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101200
7479   if ([win respondsToSelector: @selector(setTabbingMode:)])
7480 #endif
7481     [win setTabbingMode: NSWindowTabbingModeDisallowed];
7482 #endif
7484   ns_window_num++;
7485   return self;
7489 - (void)windowDidMove: sender
7491   NSWindow *win = [self window];
7492   NSRect r = [win frame];
7493   NSArray *screens = [NSScreen screens];
7494   NSScreen *screen = [screens objectAtIndex: 0];
7496   NSTRACE ("[EmacsView windowDidMove:]");
7498   if (!emacsframe->output_data.ns)
7499     return;
7500   if (screen != nil)
7501     {
7502       emacsframe->left_pos = r.origin.x - NS_PARENT_WINDOW_LEFT_POS (emacsframe);
7503       emacsframe->top_pos =
7504         NS_PARENT_WINDOW_TOP_POS (emacsframe) - (r.origin.y + r.size.height);
7506       if (emacs_event)
7507         {
7508           emacs_event->kind = MOVE_FRAME_EVENT;
7509           EV_TRAILER ((id)nil);
7510         }
7511     }
7515 /* Called AFTER method below, but before our windowWillResize call there leads
7516    to windowDidResize -> x_set_window_size.  Update emacs' notion of frame
7517    location so set_window_size moves the frame. */
7518 - (BOOL)windowShouldZoom: (NSWindow *)sender toFrame: (NSRect)newFrame
7520   NSTRACE (("[EmacsView windowShouldZoom:toFrame:" NSTRACE_FMT_RECT "]"
7521             NSTRACE_FMT_RETURN "YES"),
7522            NSTRACE_ARG_RECT (newFrame));
7524   emacsframe->output_data.ns->zooming = 1;
7525   return YES;
7529 /* Override to do something slightly nonstandard, but nice.  First click on
7530    zoom button will zoom vertically.  Second will zoom completely.  Third
7531    returns to original. */
7532 - (NSRect)windowWillUseStandardFrame:(NSWindow *)sender
7533                         defaultFrame:(NSRect)defaultFrame
7535   // TODO: Rename to "currentFrame" and assign "result" properly in
7536   // all paths.
7537   NSRect result = [sender frame];
7539   NSTRACE (("[EmacsView windowWillUseStandardFrame:defaultFrame:"
7540             NSTRACE_FMT_RECT "]"),
7541            NSTRACE_ARG_RECT (defaultFrame));
7542   NSTRACE_FSTYPE ("fs_state", fs_state);
7543   NSTRACE_FSTYPE ("fs_before_fs", fs_before_fs);
7544   NSTRACE_FSTYPE ("next_maximized", next_maximized);
7545   NSTRACE_RECT   ("ns_userRect", ns_userRect);
7546   NSTRACE_RECT   ("[sender frame]", [sender frame]);
7548   if (fs_before_fs != -1) /* Entering fullscreen */
7549     {
7550       NSTRACE_MSG ("Entering fullscreen");
7551       result = defaultFrame;
7552     }
7553   else
7554     {
7555       // Save the window size and position (frame) before the resize.
7556       if (fs_state != FULLSCREEN_MAXIMIZED
7557           && fs_state != FULLSCREEN_WIDTH)
7558         {
7559           ns_userRect.size.width = result.size.width;
7560           ns_userRect.origin.x   = result.origin.x;
7561         }
7563       if (fs_state != FULLSCREEN_MAXIMIZED
7564           && fs_state != FULLSCREEN_HEIGHT)
7565         {
7566           ns_userRect.size.height = result.size.height;
7567           ns_userRect.origin.y    = result.origin.y;
7568         }
7570       NSTRACE_RECT ("ns_userRect (2)", ns_userRect);
7572       if (next_maximized == FULLSCREEN_HEIGHT
7573           || (next_maximized == -1
7574               && abs ((int)(defaultFrame.size.height - result.size.height))
7575               > FRAME_LINE_HEIGHT (emacsframe)))
7576         {
7577           /* first click */
7578           NSTRACE_MSG ("FULLSCREEN_HEIGHT");
7579           maximized_height = result.size.height = defaultFrame.size.height;
7580           maximized_width = -1;
7581           result.origin.y = defaultFrame.origin.y;
7582           if (ns_userRect.size.height != 0)
7583             {
7584               result.origin.x = ns_userRect.origin.x;
7585               result.size.width = ns_userRect.size.width;
7586             }
7587           [self setFSValue: FULLSCREEN_HEIGHT];
7588 #ifdef NS_IMPL_COCOA
7589           maximizing_resize = YES;
7590 #endif
7591         }
7592       else if (next_maximized == FULLSCREEN_WIDTH)
7593         {
7594           NSTRACE_MSG ("FULLSCREEN_WIDTH");
7595           maximized_width = result.size.width = defaultFrame.size.width;
7596           maximized_height = -1;
7597           result.origin.x = defaultFrame.origin.x;
7598           if (ns_userRect.size.width != 0)
7599             {
7600               result.origin.y = ns_userRect.origin.y;
7601               result.size.height = ns_userRect.size.height;
7602             }
7603           [self setFSValue: FULLSCREEN_WIDTH];
7604         }
7605       else if (next_maximized == FULLSCREEN_MAXIMIZED
7606                || (next_maximized == -1
7607                    && abs ((int)(defaultFrame.size.width - result.size.width))
7608                    > FRAME_COLUMN_WIDTH (emacsframe)))
7609         {
7610           NSTRACE_MSG ("FULLSCREEN_MAXIMIZED");
7612           result = defaultFrame;  /* second click */
7613           maximized_width = result.size.width;
7614           maximized_height = result.size.height;
7615           [self setFSValue: FULLSCREEN_MAXIMIZED];
7616 #ifdef NS_IMPL_COCOA
7617           maximizing_resize = YES;
7618 #endif
7619         }
7620       else
7621         {
7622           /* restore */
7623           NSTRACE_MSG ("Restore");
7624           result = ns_userRect.size.height ? ns_userRect : result;
7625           NSTRACE_RECT ("restore (2)", result);
7626           ns_userRect = NSMakeRect (0, 0, 0, 0);
7627 #ifdef NS_IMPL_COCOA
7628           maximizing_resize = fs_state != FULLSCREEN_NONE;
7629 #endif
7630           [self setFSValue: FULLSCREEN_NONE];
7631           maximized_width = maximized_height = -1;
7632         }
7633     }
7635   if (fs_before_fs == -1) next_maximized = -1;
7637   NSTRACE_RECT   ("Final ns_userRect", ns_userRect);
7638   NSTRACE_MSG    ("Final maximized_width: %d", maximized_width);
7639   NSTRACE_MSG    ("Final maximized_height: %d", maximized_height);
7640   NSTRACE_FSTYPE ("Final next_maximized", next_maximized);
7642   [self windowWillResize: sender toSize: result.size];
7644   NSTRACE_RETURN_RECT (result);
7646   return result;
7650 - (void)windowDidDeminiaturize: sender
7652   NSTRACE ("[EmacsView windowDidDeminiaturize:]");
7653   if (!emacsframe->output_data.ns)
7654     return;
7656   SET_FRAME_ICONIFIED (emacsframe, 0);
7657   SET_FRAME_VISIBLE (emacsframe, 1);
7658   windows_or_buffers_changed = 63;
7660   if (emacs_event)
7661     {
7662       emacs_event->kind = DEICONIFY_EVENT;
7663       EV_TRAILER ((id)nil);
7664     }
7668 - (void)windowDidExpose: sender
7670   NSTRACE ("[EmacsView windowDidExpose:]");
7671   if (!emacsframe->output_data.ns)
7672     return;
7674   SET_FRAME_VISIBLE (emacsframe, 1);
7675   SET_FRAME_GARBAGED (emacsframe);
7677   if (send_appdefined)
7678     ns_send_appdefined (-1);
7682 - (void)windowDidMiniaturize: sender
7684   NSTRACE ("[EmacsView windowDidMiniaturize:]");
7685   if (!emacsframe->output_data.ns)
7686     return;
7688   SET_FRAME_ICONIFIED (emacsframe, 1);
7689   SET_FRAME_VISIBLE (emacsframe, 0);
7691   if (emacs_event)
7692     {
7693       emacs_event->kind = ICONIFY_EVENT;
7694       EV_TRAILER ((id)nil);
7695     }
7698 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7699 - (NSApplicationPresentationOptions)window:(NSWindow *)window
7700       willUseFullScreenPresentationOptions:
7701   (NSApplicationPresentationOptions)proposedOptions
7703   return proposedOptions|NSApplicationPresentationAutoHideToolbar;
7705 #endif
7707 - (void)windowWillEnterFullScreen:(NSNotification *)notification
7709   NSTRACE ("[EmacsView windowWillEnterFullScreen:]");
7710   [self windowWillEnterFullScreen];
7712 - (void)windowWillEnterFullScreen /* provided for direct calls */
7714   NSTRACE ("[EmacsView windowWillEnterFullScreen]");
7715   fs_before_fs = fs_state;
7718 - (void)windowDidEnterFullScreen:(NSNotification *)notification
7720   NSTRACE ("[EmacsView windowDidEnterFullScreen:]");
7721   [self windowDidEnterFullScreen];
7724 - (void)windowDidEnterFullScreen /* provided for direct calls */
7726   NSTRACE ("[EmacsView windowDidEnterFullScreen]");
7727   [self setFSValue: FULLSCREEN_BOTH];
7728   if (! [self fsIsNative])
7729     {
7730       [self windowDidBecomeKey];
7731       [nonfs_window orderOut:self];
7732     }
7733   else
7734     {
7735       BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (emacsframe) ? YES : NO;
7736 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 \
7737   && MAC_OS_X_VERSION_MIN_REQUIRED <= 1070
7738       unsigned val = (unsigned)[NSApp presentationOptions];
7740       // Mac OS X 10.7 bug fix, the menu won't appear without this.
7741       // val is non-zero on other macOS versions.
7742       if (val == 0)
7743         {
7744           NSApplicationPresentationOptions options
7745             = NSApplicationPresentationAutoHideDock
7746             | NSApplicationPresentationAutoHideMenuBar
7747             | NSApplicationPresentationFullScreen
7748             | NSApplicationPresentationAutoHideToolbar;
7750           [NSApp setPresentationOptions: options];
7751         }
7752 #endif
7753       [toolbar setVisible:tbar_visible];
7754     }
7757 - (void)windowWillExitFullScreen:(NSNotification *)notification
7759   NSTRACE ("[EmacsView windowWillExitFullScreen:]");
7760   [self windowWillExitFullScreen];
7763 - (void)windowWillExitFullScreen /* provided for direct calls */
7765   NSTRACE ("[EmacsView windowWillExitFullScreen]");
7766   if (!FRAME_LIVE_P (emacsframe))
7767     {
7768       NSTRACE_MSG ("Ignored (frame dead)");
7769       return;
7770     }
7771   if (next_maximized != -1)
7772     fs_before_fs = next_maximized;
7775 - (void)windowDidExitFullScreen:(NSNotification *)notification
7777   NSTRACE ("[EmacsView windowDidExitFullScreen:]");
7778   [self windowDidExitFullScreen];
7781 - (void)windowDidExitFullScreen /* provided for direct calls */
7783   NSTRACE ("[EmacsView windowDidExitFullScreen]");
7784   if (!FRAME_LIVE_P (emacsframe))
7785     {
7786       NSTRACE_MSG ("Ignored (frame dead)");
7787       return;
7788     }
7789   [self setFSValue: fs_before_fs];
7790   fs_before_fs = -1;
7791 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7792   [self updateCollectionBehavior];
7793 #endif
7794   if (FRAME_EXTERNAL_TOOL_BAR (emacsframe))
7795     {
7796       [toolbar setVisible:YES];
7797       update_frame_tool_bar (emacsframe);
7798       [self updateFrameSize:YES];
7799       [[self window] display];
7800     }
7801   else
7802     [toolbar setVisible:NO];
7804   if (next_maximized != -1)
7805     [[self window] performZoom:self];
7808 - (BOOL)fsIsNative
7810   return fs_is_native;
7813 - (BOOL)isFullscreen
7815   BOOL res;
7817   if (! fs_is_native)
7818     {
7819       res = (nonfs_window != nil);
7820     }
7821   else
7822     {
7823 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7824       res = (([[self window] styleMask] & NSWindowStyleMaskFullScreen) != 0);
7825 #else
7826       res = NO;
7827 #endif
7828     }
7830   NSTRACE ("[EmacsView isFullscreen] " NSTRACE_FMT_RETURN " %d",
7831            (int) res);
7833   return res;
7836 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7837 - (void)updateCollectionBehavior
7839   NSTRACE ("[EmacsView updateCollectionBehavior]");
7841   if (! [self isFullscreen])
7842     {
7843       NSWindow *win = [self window];
7844       NSWindowCollectionBehavior b = [win collectionBehavior];
7845       if (ns_use_native_fullscreen)
7846         b |= NSWindowCollectionBehaviorFullScreenPrimary;
7847       else
7848         b &= ~NSWindowCollectionBehaviorFullScreenPrimary;
7850       [win setCollectionBehavior: b];
7851 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7852       if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7853 #endif
7854         fs_is_native = ns_use_native_fullscreen;
7855     }
7857 #endif
7859 - (void)toggleFullScreen: (id)sender
7861   NSWindow *w, *fw;
7862   BOOL onFirstScreen;
7863   struct frame *f;
7864   NSRect r, wr;
7865   NSColor *col;
7867   NSTRACE ("[EmacsView toggleFullScreen:]");
7869   if (fs_is_native)
7870     {
7871 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7872 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7873       if ([[self window] respondsToSelector: @selector(toggleFullScreen:)])
7874 #endif
7875         [[self window] toggleFullScreen:sender];
7876 #endif
7877       return;
7878     }
7880   w = [self window];
7881   onFirstScreen = [[w screen] isEqual:[[NSScreen screens] objectAtIndex:0]];
7882   f = emacsframe;
7883   wr = [w frame];
7884   col = ns_lookup_indexed_color (NS_FACE_BACKGROUND
7885                                  (FACE_FROM_ID (f, DEFAULT_FACE_ID)),
7886                                  f);
7888   if (fs_state != FULLSCREEN_BOTH)
7889     {
7890       NSScreen *screen = [w screen];
7892 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
7893       /* Hide ghost menu bar on secondary monitor? */
7894       if (! onFirstScreen
7895 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
7896           && [NSScreen respondsToSelector: @selector(screensHaveSeparateSpaces)]
7897 #endif
7898           )
7899         onFirstScreen = [NSScreen screensHaveSeparateSpaces];
7900 #endif
7901       /* Hide dock and menubar if we are on the primary screen.  */
7902       if (onFirstScreen)
7903         {
7904 #ifdef NS_IMPL_COCOA
7905           NSApplicationPresentationOptions options
7906             = NSApplicationPresentationAutoHideDock
7907             | NSApplicationPresentationAutoHideMenuBar;
7909           [NSApp setPresentationOptions: options];
7910 #else
7911           [NSMenu setMenuBarVisible:NO];
7912 #endif
7913         }
7915       fw = [[EmacsFSWindow alloc]
7916                        initWithContentRect:[w contentRectForFrameRect:wr]
7917                                  styleMask:NSWindowStyleMaskBorderless
7918                                    backing:NSBackingStoreBuffered
7919                                      defer:YES
7920                                     screen:screen];
7922       [fw setContentView:[w contentView]];
7923       [fw setTitle:[w title]];
7924       [fw setDelegate:self];
7925       [fw setAcceptsMouseMovedEvents: YES];
7926 #if !defined (NS_IMPL_COCOA) \
7927   || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7928 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7929       if ([fw respondsToSelector: @selector(useOptimizedDrawing:)])
7930 #endif
7931         [fw useOptimizedDrawing: YES];
7932 #endif
7933       [fw setBackgroundColor: col];
7934       if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7935         [fw setOpaque: NO];
7937       f->border_width = 0;
7939       nonfs_window = w;
7941       [self windowWillEnterFullScreen];
7942       [fw makeKeyAndOrderFront:NSApp];
7943       [fw makeFirstResponder:self];
7944       [w orderOut:self];
7945       r = [fw frameRectForContentRect:[screen frame]];
7946       [fw setFrame: r display:YES animate:ns_use_fullscreen_animation];
7947       [self windowDidEnterFullScreen];
7948       [fw display];
7949     }
7950   else
7951     {
7952       fw = w;
7953       w = nonfs_window;
7954       nonfs_window = nil;
7956       if (onFirstScreen)
7957         {
7958 #ifdef NS_IMPL_COCOA
7959           [NSApp setPresentationOptions: NSApplicationPresentationDefault];
7960 #else
7961           [NSMenu setMenuBarVisible:YES];
7962 #endif
7963         }
7965       [w setContentView:[fw contentView]];
7966       [w setBackgroundColor: col];
7967       if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7968         [w setOpaque: NO];
7970       f->border_width = bwidth;
7972       // to do: consider using [NSNotificationCenter postNotificationName:] to send notifications.
7974       [self windowWillExitFullScreen];
7975       [fw setFrame: [w frame] display:YES animate:ns_use_fullscreen_animation];
7976       [fw close];
7977       [w makeKeyAndOrderFront:NSApp];
7978       [self windowDidExitFullScreen];
7979       [self updateFrameSize:YES];
7980     }
7983 - (void)handleFS
7985   NSTRACE ("[EmacsView handleFS]");
7987   if (fs_state != emacsframe->want_fullscreen)
7988     {
7989       if (fs_state == FULLSCREEN_BOTH)
7990         {
7991           NSTRACE_MSG ("fs_state == FULLSCREEN_BOTH");
7992           [self toggleFullScreen:self];
7993         }
7995       switch (emacsframe->want_fullscreen)
7996         {
7997         case FULLSCREEN_BOTH:
7998           NSTRACE_MSG ("FULLSCREEN_BOTH");
7999           [self toggleFullScreen:self];
8000           break;
8001         case FULLSCREEN_WIDTH:
8002           NSTRACE_MSG ("FULLSCREEN_WIDTH");
8003           next_maximized = FULLSCREEN_WIDTH;
8004           if (fs_state != FULLSCREEN_BOTH)
8005             [[self window] performZoom:self];
8006           break;
8007         case FULLSCREEN_HEIGHT:
8008           NSTRACE_MSG ("FULLSCREEN_HEIGHT");
8009           next_maximized = FULLSCREEN_HEIGHT;
8010           if (fs_state != FULLSCREEN_BOTH)
8011             [[self window] performZoom:self];
8012           break;
8013         case FULLSCREEN_MAXIMIZED:
8014           NSTRACE_MSG ("FULLSCREEN_MAXIMIZED");
8015           next_maximized = FULLSCREEN_MAXIMIZED;
8016           if (fs_state != FULLSCREEN_BOTH)
8017             [[self window] performZoom:self];
8018           break;
8019         case FULLSCREEN_NONE:
8020           NSTRACE_MSG ("FULLSCREEN_NONE");
8021           if (fs_state != FULLSCREEN_BOTH)
8022             {
8023               next_maximized = FULLSCREEN_NONE;
8024               [[self window] performZoom:self];
8025             }
8026           break;
8027         }
8029       emacsframe->want_fullscreen = FULLSCREEN_NONE;
8030     }
8034 - (void) setFSValue: (int)value
8036   NSTRACE ("[EmacsView setFSValue:" NSTRACE_FMT_FSTYPE "]",
8037            NSTRACE_ARG_FSTYPE(value));
8039   Lisp_Object lval = Qnil;
8040   switch (value)
8041     {
8042     case FULLSCREEN_BOTH:
8043       lval = Qfullboth;
8044       break;
8045     case FULLSCREEN_WIDTH:
8046       lval = Qfullwidth;
8047       break;
8048     case FULLSCREEN_HEIGHT:
8049       lval = Qfullheight;
8050       break;
8051     case FULLSCREEN_MAXIMIZED:
8052       lval = Qmaximized;
8053       break;
8054     }
8055   store_frame_param (emacsframe, Qfullscreen, lval);
8056   fs_state = value;
8059 - (void)mouseEntered: (NSEvent *)theEvent
8061   NSTRACE ("[EmacsView mouseEntered:]");
8062   if (emacsframe)
8063     FRAME_DISPLAY_INFO (emacsframe)->last_mouse_movement_time
8064       = EV_TIMESTAMP (theEvent);
8068 - (void)mouseExited: (NSEvent *)theEvent
8070   Mouse_HLInfo *hlinfo = emacsframe ? MOUSE_HL_INFO (emacsframe) : NULL;
8072   NSTRACE ("[EmacsView mouseExited:]");
8074   if (!hlinfo)
8075     return;
8077   FRAME_DISPLAY_INFO (emacsframe)->last_mouse_movement_time
8078     = EV_TIMESTAMP (theEvent);
8080   if (emacsframe == hlinfo->mouse_face_mouse_frame)
8081     {
8082       clear_mouse_face (hlinfo);
8083       hlinfo->mouse_face_mouse_frame = 0;
8084     }
8088 - (instancetype)menuDown: sender
8090   NSTRACE ("[EmacsView menuDown:]");
8091   if (context_menu_value == -1)
8092     context_menu_value = [sender tag];
8093   else
8094     {
8095       NSInteger tag = [sender tag];
8096       find_and_call_menu_selection (emacsframe, emacsframe->menu_bar_items_used,
8097                                     emacsframe->menu_bar_vector,
8098                                     (void *)tag);
8099     }
8101   ns_send_appdefined (-1);
8102   return self;
8106 - (EmacsToolbar *)toolbar
8108   return toolbar;
8112 /* this gets called on toolbar button click */
8113 - (instancetype)toolbarClicked: (id)item
8115   NSEvent *theEvent;
8116   int idx = [item tag] * TOOL_BAR_ITEM_NSLOTS;
8118   NSTRACE ("[EmacsView toolbarClicked:]");
8120   if (!emacs_event)
8121     return self;
8123   /* send first event (for some reason two needed) */
8124   theEvent = [[self window] currentEvent];
8125   emacs_event->kind = TOOL_BAR_EVENT;
8126   XSETFRAME (emacs_event->arg, emacsframe);
8127   EV_TRAILER (theEvent);
8129   emacs_event->kind = TOOL_BAR_EVENT;
8130 /*   XSETINT (emacs_event->code, 0); */
8131   emacs_event->arg = AREF (emacsframe->tool_bar_items,
8132                            idx + TOOL_BAR_ITEM_KEY);
8133   emacs_event->modifiers = EV_MODIFIERS (theEvent);
8134   EV_TRAILER (theEvent);
8135   return self;
8139 - (instancetype)toggleToolbar: (id)sender
8141   NSTRACE ("[EmacsView toggleToolbar:]");
8143   if (!emacs_event)
8144     return self;
8146   emacs_event->kind = NS_NONKEY_EVENT;
8147   emacs_event->code = KEY_NS_TOGGLE_TOOLBAR;
8148   EV_TRAILER ((id)nil);
8149   return self;
8153 - (void)drawRect: (NSRect)rect
8155   int x = NSMinX (rect), y = NSMinY (rect);
8156   int width = NSWidth (rect), height = NSHeight (rect);
8158   NSTRACE ("[EmacsView drawRect:" NSTRACE_FMT_RECT "]",
8159            NSTRACE_ARG_RECT(rect));
8161   if (!emacsframe || !emacsframe->output_data.ns)
8162     return;
8164   ns_clear_frame_area (emacsframe, x, y, width, height);
8165   block_input ();
8166   expose_frame (emacsframe, x, y, width, height);
8167   unblock_input ();
8169   /*
8170     drawRect: may be called (at least in Mac OS X 10.5) for invisible
8171     views as well for some reason.  Thus, do not infer visibility
8172     here.
8174     emacsframe->async_visible = 1;
8175     emacsframe->async_iconified = 0;
8176   */
8180 /* NSDraggingDestination protocol methods.  Actually this is not really a
8181    protocol, but a category of Object.  O well...  */
8183 -(NSDragOperation) draggingEntered: (id <NSDraggingInfo>) sender
8185   NSTRACE ("[EmacsView draggingEntered:]");
8186   return NSDragOperationGeneric;
8190 -(BOOL)prepareForDragOperation: (id <NSDraggingInfo>) sender
8192   return YES;
8196 -(BOOL)performDragOperation: (id <NSDraggingInfo>) sender
8198   id pb;
8199   int x, y;
8200   NSString *type;
8201   NSEvent *theEvent = [[self window] currentEvent];
8202   NSPoint position;
8203   NSDragOperation op = [sender draggingSourceOperationMask];
8204   int modifiers = 0;
8206   NSTRACE ("[EmacsView performDragOperation:]");
8208   if (!emacs_event)
8209     return NO;
8211   position = [self convertPoint: [sender draggingLocation] fromView: nil];
8212   x = lrint (position.x);  y = lrint (position.y);
8214   pb = [sender draggingPasteboard];
8215   type = [pb availableTypeFromArray: ns_drag_types];
8217   if (! (op & (NSDragOperationMove|NSDragOperationDelete)) &&
8218       // URL drags contain all operations (0xf), don't allow all to be set.
8219       (op & 0xf) != 0xf)
8220     {
8221       if (op & NSDragOperationLink)
8222         modifiers |= NSEventModifierFlagControl;
8223       if (op & NSDragOperationCopy)
8224         modifiers |= NSEventModifierFlagOption;
8225       if (op & NSDragOperationGeneric)
8226         modifiers |= NSEventModifierFlagCommand;
8227     }
8229   modifiers = EV_MODIFIERS2 (modifiers);
8230   if (type == 0)
8231     {
8232       return NO;
8233     }
8234   else if ([type isEqualToString: NSFilenamesPboardType])
8235     {
8236       NSArray *files;
8237       NSEnumerator *fenum;
8238       NSString *file;
8240       if (!(files = [pb propertyListForType: type]))
8241         return NO;
8243       fenum = [files objectEnumerator];
8244       while ( (file = [fenum nextObject]) )
8245         {
8246           emacs_event->kind = DRAG_N_DROP_EVENT;
8247           XSETINT (emacs_event->x, x);
8248           XSETINT (emacs_event->y, y);
8249           emacs_event->modifiers = modifiers;
8250           emacs_event->arg =  list2 (Qfile, build_string ([file UTF8String]));
8251           EV_TRAILER (theEvent);
8252         }
8253       return YES;
8254     }
8255   else if ([type isEqualToString: NSURLPboardType])
8256     {
8257       NSURL *url = [NSURL URLFromPasteboard: pb];
8258       if (url == nil) return NO;
8260       emacs_event->kind = DRAG_N_DROP_EVENT;
8261       XSETINT (emacs_event->x, x);
8262       XSETINT (emacs_event->y, y);
8263       emacs_event->modifiers = modifiers;
8264       emacs_event->arg =  list2 (Qurl,
8265                                  build_string ([[url absoluteString]
8266                                                  UTF8String]));
8267       EV_TRAILER (theEvent);
8269       if ([url isFileURL] != NO)
8270         {
8271           NSString *file = [url path];
8272           ns_input_file = append2 (ns_input_file,
8273                                    build_string ([file UTF8String]));
8274         }
8275       return YES;
8276     }
8277   else if ([type isEqualToString: NSStringPboardType]
8278            || [type isEqualToString: NSTabularTextPboardType])
8279     {
8280       NSString *data;
8282       if (! (data = [pb stringForType: type]))
8283         return NO;
8285       emacs_event->kind = DRAG_N_DROP_EVENT;
8286       XSETINT (emacs_event->x, x);
8287       XSETINT (emacs_event->y, y);
8288       emacs_event->modifiers = modifiers;
8289       emacs_event->arg =  list2 (Qnil, build_string ([data UTF8String]));
8290       EV_TRAILER (theEvent);
8291       return YES;
8292     }
8293   else
8294     {
8295       fprintf (stderr, "Invalid data type in dragging pasteboard");
8296       return NO;
8297     }
8301 - (id) validRequestorForSendType: (NSString *)typeSent
8302                       returnType: (NSString *)typeReturned
8304   NSTRACE ("[EmacsView validRequestorForSendType:returnType:]");
8305   if (typeSent != nil && [ns_send_types indexOfObject: typeSent] != NSNotFound
8306       && typeReturned == nil)
8307     {
8308       if (! NILP (ns_get_local_selection (QPRIMARY, QUTF8_STRING)))
8309         return self;
8310     }
8312   return [super validRequestorForSendType: typeSent
8313                                returnType: typeReturned];
8317 /* The next two methods are part of NSServicesRequests informal protocol,
8318    supposedly called when a services menu item is chosen from this app.
8319    But this should not happen because we override the services menu with our
8320    own entries which call ns-perform-service.
8321    Nonetheless, it appeared to happen (under strange circumstances): bug#1435.
8322    So let's at least stub them out until further investigation can be done. */
8324 - (BOOL) readSelectionFromPasteboard: (NSPasteboard *)pb
8326   /* we could call ns_string_from_pasteboard(pboard) here but then it should
8327      be written into the buffer in place of the existing selection..
8328      ordinary service calls go through functions defined in ns-win.el */
8329   return NO;
8332 - (BOOL) writeSelectionToPasteboard: (NSPasteboard *)pb types: (NSArray *)types
8334   NSArray *typesDeclared;
8335   Lisp_Object val;
8337   NSTRACE ("[EmacsView writeSelectionToPasteboard:types:]");
8339   /* We only support NSStringPboardType */
8340   if ([types containsObject:NSStringPboardType] == NO) {
8341     return NO;
8342   }
8344   val = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8345   if (CONSP (val) && SYMBOLP (XCAR (val)))
8346     {
8347       val = XCDR (val);
8348       if (CONSP (val) && NILP (XCDR (val)))
8349         val = XCAR (val);
8350     }
8351   if (! STRINGP (val))
8352     return NO;
8354   typesDeclared = [NSArray arrayWithObject:NSStringPboardType];
8355   [pb declareTypes:typesDeclared owner:nil];
8356   ns_string_to_pasteboard (pb, val);
8357   return YES;
8361 /* setMini =YES means set from internal (gives a finder icon), NO means set nil
8362    (gives a miniaturized version of the window); currently we use the latter for
8363    frames whose active buffer doesn't correspond to any file
8364    (e.g., '*scratch*') */
8365 - (instancetype)setMiniwindowImage: (BOOL) setMini
8367   id image = [[self window] miniwindowImage];
8368   NSTRACE ("[EmacsView setMiniwindowImage:%d]", setMini);
8370   /* NOTE: under Cocoa miniwindowImage always returns nil, documentation
8371      about "AppleDockIconEnabled" notwithstanding, however the set message
8372      below has its effect nonetheless. */
8373   if (image != emacsframe->output_data.ns->miniimage)
8374     {
8375       if (image && [image isKindOfClass: [EmacsImage class]])
8376         [image release];
8377       [[self window] setMiniwindowImage:
8378                        setMini ? emacsframe->output_data.ns->miniimage : nil];
8379     }
8381   return self;
8385 - (void) setRows: (int) r andColumns: (int) c
8387   NSTRACE ("[EmacsView setRows:%d andColumns:%d]", r, c);
8388   rows = r;
8389   cols = c;
8392 - (int) fullscreenState
8394   return fs_state;
8397 @end  /* EmacsView */
8401 /* ==========================================================================
8403     EmacsWindow implementation
8405    ========================================================================== */
8407 @implementation EmacsWindow
8409 #ifdef NS_IMPL_COCOA
8410 - (id)accessibilityAttributeValue:(NSString *)attribute
8412   Lisp_Object str = Qnil;
8413   struct frame *f = SELECTED_FRAME ();
8414   struct buffer *curbuf = XBUFFER (XWINDOW (f->selected_window)->contents);
8416   NSTRACE ("[EmacsWindow accessibilityAttributeValue:]");
8418   if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
8419     return NSAccessibilityTextFieldRole;
8421   if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]
8422       && curbuf && ! NILP (BVAR (curbuf, mark_active)))
8423     {
8424       str = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8425     }
8426   else if (curbuf && [attribute isEqualToString:NSAccessibilityValueAttribute])
8427     {
8428       if (! NILP (BVAR (curbuf, mark_active)))
8429           str = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8431       if (NILP (str))
8432         {
8433           ptrdiff_t start_byte = BUF_BEGV_BYTE (curbuf);
8434           ptrdiff_t byte_range = BUF_ZV_BYTE (curbuf) - start_byte;
8435           ptrdiff_t range = BUF_ZV (curbuf) - BUF_BEGV (curbuf);
8437           if (! NILP (BVAR (curbuf, enable_multibyte_characters)))
8438             str = make_uninit_multibyte_string (range, byte_range);
8439           else
8440             str = make_uninit_string (range);
8441           /* To check: This returns emacs-utf-8, which is a superset of utf-8.
8442              Is this a problem?  */
8443           memcpy (SDATA (str), BYTE_POS_ADDR (start_byte), byte_range);
8444         }
8445     }
8448   if (! NILP (str))
8449     {
8450       if (CONSP (str) && SYMBOLP (XCAR (str)))
8451         {
8452           str = XCDR (str);
8453           if (CONSP (str) && NILP (XCDR (str)))
8454             str = XCAR (str);
8455         }
8456       if (STRINGP (str))
8457         {
8458           const char *utfStr = SSDATA (str);
8459           NSString *nsStr = [NSString stringWithUTF8String: utfStr];
8460           return nsStr;
8461         }
8462     }
8464   return [super accessibilityAttributeValue:attribute];
8466 #endif /* NS_IMPL_COCOA */
8468 /* Constrain size and placement of a frame.
8470    By returning the original "frameRect", the frame is not
8471    constrained. This can lead to unwanted situations where, for
8472    example, the menu bar covers the frame.
8474    The default implementation (accessed using "super") constrains the
8475    frame to the visible area of SCREEN, minus the menu bar (if
8476    present) and the Dock.  Note that default implementation also calls
8477    windowWillResize, with the frame it thinks should have.  (This can
8478    make the frame exit maximized mode.)
8480    Note that this should work in situations where multiple monitors
8481    are present.  Common configurations are side-by-side monitors and a
8482    monitor on top of another (e.g. when a laptop is placed under a
8483    large screen). */
8484 - (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
8486   NSTRACE ("[EmacsWindow constrainFrameRect:" NSTRACE_FMT_RECT " toScreen:]",
8487              NSTRACE_ARG_RECT (frameRect));
8489 #ifdef NS_IMPL_COCOA
8490 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
8491   // If separate spaces is on, it is like each screen is independent.  There is
8492   // no spanning of frames across screens.
8493   if (
8494 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
8495       [NSScreen respondsToSelector: @selector(screensHaveSeparateSpaces)] &&
8496 #endif
8497       [NSScreen screensHaveSeparateSpaces])
8498     {
8499       NSTRACE_MSG ("Screens have separate spaces");
8500       frameRect = [super constrainFrameRect:frameRect toScreen:screen];
8501       NSTRACE_RETURN_RECT (frameRect);
8502       return frameRect;
8503     }
8504   else
8505 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 */
8507     // Check that the proposed frameRect is visible in at least one
8508     // screen.  If it is not, ask the system to reposition it (only
8509     // for non-child windows).
8511     if (!FRAME_PARENT_FRAME (((EmacsView *)[self delegate])->emacsframe))
8512     {
8513       NSArray *screens = [NSScreen screens];
8514       NSUInteger nr_screens = [screens count];
8516       int i;
8517       BOOL frame_on_screen = NO;
8519       for (i = 0; i < nr_screens; ++i)
8520         {
8521           NSScreen *s = [screens objectAtIndex: i];
8522           NSRect scrRect = [s frame];
8524           if (NSIntersectsRect(frameRect, scrRect))
8525             {
8526               frame_on_screen = YES;
8527               break;
8528             }
8529         }
8531       if (!frame_on_screen)
8532         {
8533           NSTRACE_MSG ("Frame outside screens; constraining");
8534           frameRect = [super constrainFrameRect:frameRect toScreen:screen];
8535           NSTRACE_RETURN_RECT (frameRect);
8536           return frameRect;
8537         }
8538     }
8539 #endif
8541   return constrain_frame_rect(frameRect,
8542                               [(EmacsView *)[self delegate] isFullscreen]);
8546 - (void)performZoom:(id)sender
8548   NSTRACE ("[EmacsWindow performZoom:]");
8550   return [super performZoom:sender];
8553 - (void)zoom:(id)sender
8555   NSTRACE ("[EmacsWindow zoom:]");
8557   ns_update_auto_hide_menu_bar();
8559   // Below are three zoom implementations.  In the final commit, the
8560   // idea is that the last should be included.
8562 #if 0
8563   // Native zoom done using the standard zoom animation.  Size of the
8564   // resulting frame reduced to accommodate the Dock and, if present,
8565   // the menu-bar.
8566   [super zoom:sender];
8568 #elif 0
8569   // Native zoom done using the standard zoom animation, plus an
8570   // explicit resize to cover the full screen, except the menu-bar and
8571   // dock, if present.
8572   [super zoom:sender];
8574   // After the native zoom, resize the resulting frame to fill the
8575   // entire screen, except the menu-bar.
8576   //
8577   // This works for all practical purposes.  (The only minor oddity is
8578   // when transiting from full-height frame to a maximized, the
8579   // animation reduces the height of the frame slightly (to the 4
8580   // pixels needed to accommodate the Doc) before it snaps back into
8581   // full height.  The user would need a very trained eye to spot
8582   // this.)
8583   NSScreen * screen = [self screen];
8584   if (screen != nil)
8585     {
8586       int fs_state = [(EmacsView *)[self delegate] fullscreenState];
8588       NSTRACE_FSTYPE ("fullscreenState", fs_state);
8590       NSRect sr = [screen frame];
8591       struct EmacsMargins margins
8592         = ns_screen_margins_ignoring_hidden_dock(screen);
8594       NSRect wr = [self frame];
8595       NSTRACE_RECT ("Rect after zoom", wr);
8597       NSRect newWr = wr;
8599       if (fs_state == FULLSCREEN_MAXIMIZED
8600           || fs_state == FULLSCREEN_HEIGHT)
8601         {
8602           newWr.origin.y = sr.origin.y + margins.bottom;
8603           newWr.size.height = sr.size.height - margins.top - margins.bottom;
8604         }
8606       if (fs_state == FULLSCREEN_MAXIMIZED
8607           || fs_state == FULLSCREEN_WIDTH)
8608         {
8609           newWr.origin.x = sr.origin.x + margins.left;
8610           newWr.size.width = sr.size.width - margins.right - margins.left;
8611         }
8613       if (newWr.size.width     != wr.size.width
8614           || newWr.size.height != wr.size.height
8615           || newWr.origin.x    != wr.origin.x
8616           || newWr.origin.y    != wr.origin.y)
8617         {
8618           NSTRACE_MSG ("New frame different");
8619           [self setFrame: newWr display: NO];
8620         }
8621     }
8622 #else
8623   // Non-native zoom which is done instantaneously.  The resulting
8624   // frame covers the entire screen, except the menu-bar and dock, if
8625   // present.
8626   NSScreen * screen = [self screen];
8627   if (screen != nil)
8628     {
8629       NSRect sr = [screen frame];
8630       struct EmacsMargins margins
8631         = ns_screen_margins_ignoring_hidden_dock(screen);
8633       sr.size.height -= (margins.top + margins.bottom);
8634       sr.size.width  -= (margins.left + margins.right);
8635       sr.origin.x += margins.left;
8636       sr.origin.y += margins.bottom;
8638       sr = [[self delegate] windowWillUseStandardFrame:self
8639                                           defaultFrame:sr];
8640       [self setFrame: sr display: NO];
8641     }
8642 #endif
8645 - (void)setFrame:(NSRect)windowFrame
8646          display:(BOOL)displayViews
8648   NSTRACE ("[EmacsWindow setFrame:" NSTRACE_FMT_RECT " display:%d]",
8649            NSTRACE_ARG_RECT (windowFrame), displayViews);
8651   [super setFrame:windowFrame display:displayViews];
8654 - (void)setFrame:(NSRect)windowFrame
8655          display:(BOOL)displayViews
8656          animate:(BOOL)performAnimation
8658   NSTRACE ("[EmacsWindow setFrame:" NSTRACE_FMT_RECT
8659            " display:%d performAnimation:%d]",
8660            NSTRACE_ARG_RECT (windowFrame), displayViews, performAnimation);
8662   [super setFrame:windowFrame display:displayViews animate:performAnimation];
8665 - (void)setFrameTopLeftPoint:(NSPoint)point
8667   NSTRACE ("[EmacsWindow setFrameTopLeftPoint:" NSTRACE_FMT_POINT "]",
8668            NSTRACE_ARG_POINT (point));
8670   [super setFrameTopLeftPoint:point];
8673 - (BOOL)canBecomeKeyWindow
8675   return !FRAME_NO_ACCEPT_FOCUS (((EmacsView *)[self delegate])->emacsframe);
8677 @end /* EmacsWindow */
8680 @implementation EmacsFSWindow
8682 - (BOOL)canBecomeKeyWindow
8684   return YES;
8687 - (BOOL)canBecomeMainWindow
8689   return YES;
8692 @end
8694 /* ==========================================================================
8696     EmacsScroller implementation
8698    ========================================================================== */
8701 @implementation EmacsScroller
8703 /* for repeat button push */
8704 #define SCROLL_BAR_FIRST_DELAY 0.5
8705 #define SCROLL_BAR_CONTINUOUS_DELAY (1.0 / 15)
8707 + (CGFloat) scrollerWidth
8709   /* TODO: if we want to allow variable widths, this is the place to do it,
8710            however neither GNUstep nor Cocoa support it very well */
8711   CGFloat r;
8712 #if defined (NS_IMPL_COCOA) \
8713   && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
8714 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
8715   if ([NSScroller respondsToSelector:
8716                     @selector(scrollerWidthForControlSize:scrollerStyle:)])
8717 #endif
8718     r = [NSScroller scrollerWidthForControlSize: NSControlSizeRegular
8719                                   scrollerStyle: NSScrollerStyleLegacy];
8720 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
8721   else
8722 #endif
8723 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
8724 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 \
8725   || defined (NS_IMPL_GNUSTEP)
8726     r = [NSScroller scrollerWidth];
8727 #endif
8728   return r;
8731 - (instancetype)initFrame: (NSRect )r window: (Lisp_Object)nwin
8733   NSTRACE ("[EmacsScroller initFrame: window:]");
8735   if (r.size.width > r.size.height)
8736       horizontal = YES;
8737   else
8738       horizontal = NO;
8740   [super initWithFrame: r/*NSMakeRect (0, 0, 0, 0)*/];
8741   [self setContinuous: YES];
8742   [self setEnabled: YES];
8744   /* Ensure auto resizing of scrollbars occurs within the emacs frame's view
8745      locked against the top and bottom edges, and right edge on macOS, where
8746      scrollers are on right. */
8747 #ifdef NS_IMPL_GNUSTEP
8748   [self setAutoresizingMask: NSViewMaxXMargin | NSViewHeightSizable];
8749 #else
8750   [self setAutoresizingMask: NSViewMinXMargin | NSViewHeightSizable];
8751 #endif
8753   window = XWINDOW (nwin);
8754   condemned = NO;
8755   if (horizontal)
8756     pixel_length = NSWidth (r);
8757   else
8758     pixel_length = NSHeight (r);
8759   if (pixel_length == 0) pixel_length = 1;
8760   min_portion = 20 / pixel_length;
8762   frame = XFRAME (window->frame);
8763   if (FRAME_LIVE_P (frame))
8764     {
8765       int i;
8766       EmacsView *view = FRAME_NS_VIEW (frame);
8767       NSView *sview = [[view window] contentView];
8768       NSArray *subs = [sview subviews];
8770       /* disable optimization stopping redraw of other scrollbars */
8771       view->scrollbarsNeedingUpdate = 0;
8772       for (i =[subs count]-1; i >= 0; i--)
8773         if ([[subs objectAtIndex: i] isKindOfClass: [EmacsScroller class]])
8774           view->scrollbarsNeedingUpdate++;
8775       [sview addSubview: self];
8776     }
8778 /*  [self setFrame: r]; */
8780   return self;
8784 - (void)setFrame: (NSRect)newRect
8786   NSTRACE ("[EmacsScroller setFrame:]");
8788 /*  block_input (); */
8789   if (horizontal)
8790     pixel_length = NSWidth (newRect);
8791   else
8792     pixel_length = NSHeight (newRect);
8793   if (pixel_length == 0) pixel_length = 1;
8794   min_portion = 20 / pixel_length;
8795   [super setFrame: newRect];
8796 /*  unblock_input (); */
8800 - (void)dealloc
8802   NSTRACE ("[EmacsScroller dealloc]");
8803   if (window)
8804     {
8805       if (horizontal)
8806         wset_horizontal_scroll_bar (window, Qnil);
8807       else
8808         wset_vertical_scroll_bar (window, Qnil);
8809     }
8810   window = 0;
8811   [super dealloc];
8815 - (instancetype)condemn
8817   NSTRACE ("[EmacsScroller condemn]");
8818   condemned =YES;
8819   return self;
8823 - (instancetype)reprieve
8825   NSTRACE ("[EmacsScroller reprieve]");
8826   condemned =NO;
8827   return self;
8831 -(bool)judge
8833   NSTRACE ("[EmacsScroller judge]");
8834   bool ret = condemned;
8835   if (condemned)
8836     {
8837       EmacsView *view;
8838       block_input ();
8839       /* ensure other scrollbar updates after deletion */
8840       view = (EmacsView *)FRAME_NS_VIEW (frame);
8841       if (view != nil)
8842         view->scrollbarsNeedingUpdate++;
8843       if (window)
8844         {
8845           if (horizontal)
8846             wset_horizontal_scroll_bar (window, Qnil);
8847           else
8848             wset_vertical_scroll_bar (window, Qnil);
8849         }
8850       window = 0;
8851       [self removeFromSuperview];
8852       [self release];
8853       unblock_input ();
8854     }
8855   return ret;
8859 - (void)resetCursorRects
8861   NSRect visible = [self visibleRect];
8862   NSTRACE ("[EmacsScroller resetCursorRects]");
8864   if (!NSIsEmptyRect (visible))
8865     [self addCursorRect: visible cursor: [NSCursor arrowCursor]];
8867 #if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 101300
8868 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
8869   if ([[NSCursor arrowCursor] respondsToSelector:
8870                                 @selector(setOnMouseEntered)])
8871 #endif
8872     [[NSCursor arrowCursor] setOnMouseEntered: YES];
8873 #endif
8877 - (int) checkSamePosition: (int) position portion: (int) portion
8878                     whole: (int) whole
8880   return em_position ==position && em_portion ==portion && em_whole ==whole
8881     && portion != whole; /* needed for resize empty buf */
8885 - (instancetype)setPosition: (int)position portion: (int)portion whole: (int)whole
8887   NSTRACE ("[EmacsScroller setPosition:portion:whole:]");
8889   em_position = position;
8890   em_portion = portion;
8891   em_whole = whole;
8893   if (portion >= whole)
8894     {
8895 #ifdef NS_IMPL_COCOA
8896       [self setKnobProportion: 1.0];
8897       [self setDoubleValue: 1.0];
8898 #else
8899       [self setFloatValue: 0.0 knobProportion: 1.0];
8900 #endif
8901     }
8902   else
8903     {
8904       float pos;
8905       CGFloat por;
8906       portion = max ((float)whole*min_portion/pixel_length, portion);
8907       pos = (float)position / (whole - portion);
8908       por = (CGFloat)portion/whole;
8909 #ifdef NS_IMPL_COCOA
8910       [self setKnobProportion: por];
8911       [self setDoubleValue: pos];
8912 #else
8913       [self setFloatValue: pos knobProportion: por];
8914 #endif
8915     }
8917   return self;
8920 /* set up emacs_event */
8921 - (void) sendScrollEventAtLoc: (float)loc fromEvent: (NSEvent *)e
8923   Lisp_Object win;
8925   NSTRACE ("[EmacsScroller sendScrollEventAtLoc:fromEvent:]");
8927   if (!emacs_event)
8928     return;
8930   emacs_event->part = last_hit_part;
8931   emacs_event->code = 0;
8932   emacs_event->modifiers = EV_MODIFIERS (e) | down_modifier;
8933   XSETWINDOW (win, window);
8934   emacs_event->frame_or_window = win;
8935   emacs_event->timestamp = EV_TIMESTAMP (e);
8936   emacs_event->arg = Qnil;
8938   if (horizontal)
8939     {
8940       emacs_event->kind = HORIZONTAL_SCROLL_BAR_CLICK_EVENT;
8941       XSETINT (emacs_event->x, em_whole * loc / pixel_length);
8942       XSETINT (emacs_event->y, em_whole);
8943     }
8944   else
8945     {
8946       emacs_event->kind = SCROLL_BAR_CLICK_EVENT;
8947       XSETINT (emacs_event->x, loc);
8948       XSETINT (emacs_event->y, pixel_length-20);
8949     }
8951   if (q_event_ptr)
8952     {
8953       n_emacs_events_pending++;
8954       kbd_buffer_store_event_hold (emacs_event, q_event_ptr);
8955     }
8956   else
8957     hold_event (emacs_event);
8958   EVENT_INIT (*emacs_event);
8959   ns_send_appdefined (-1);
8963 /* called manually thru timer to implement repeated button action w/hold-down */
8964 - (instancetype)repeatScroll: (NSTimer *)scrollEntry
8966   NSEvent *e = [[self window] currentEvent];
8967   NSPoint p =  [[self window] mouseLocationOutsideOfEventStream];
8968   BOOL inKnob = [self testPart: p] == NSScrollerKnob;
8970   NSTRACE ("[EmacsScroller repeatScroll:]");
8972   /* clear timer if need be */
8973   if (inKnob || [scroll_repeat_entry timeInterval] == SCROLL_BAR_FIRST_DELAY)
8974     {
8975         [scroll_repeat_entry invalidate];
8976         [scroll_repeat_entry release];
8977         scroll_repeat_entry = nil;
8979         if (inKnob)
8980           return self;
8982         scroll_repeat_entry
8983           = [[NSTimer scheduledTimerWithTimeInterval:
8984                         SCROLL_BAR_CONTINUOUS_DELAY
8985                                             target: self
8986                                           selector: @selector (repeatScroll:)
8987                                           userInfo: 0
8988                                            repeats: YES]
8989               retain];
8990     }
8992   [self sendScrollEventAtLoc: 0 fromEvent: e];
8993   return self;
8997 /* Asynchronous mouse tracking for scroller.  This allows us to dispatch
8998    mouseDragged events without going into a modal loop. */
8999 - (void)mouseDown: (NSEvent *)e
9001   NSRect sr, kr;
9002   /* hitPart is only updated AFTER event is passed on */
9003   NSScrollerPart part = [self testPart: [e locationInWindow]];
9004   CGFloat loc, kloc, pos UNINIT;
9005   int edge = 0;
9007   NSTRACE ("[EmacsScroller mouseDown:]");
9009   switch (part)
9010     {
9011     case NSScrollerDecrementPage:
9012       last_hit_part = horizontal ? scroll_bar_before_handle : scroll_bar_above_handle; break;
9013     case NSScrollerIncrementPage:
9014       last_hit_part = horizontal ? scroll_bar_after_handle : scroll_bar_below_handle; break;
9015     case NSScrollerDecrementLine:
9016       last_hit_part = horizontal ? scroll_bar_left_arrow : scroll_bar_up_arrow; break;
9017     case NSScrollerIncrementLine:
9018       last_hit_part = horizontal ? scroll_bar_right_arrow : scroll_bar_down_arrow; break;
9019     case NSScrollerKnob:
9020       last_hit_part = horizontal ? scroll_bar_horizontal_handle : scroll_bar_handle; break;
9021     case NSScrollerKnobSlot:  /* GNUstep-only */
9022       last_hit_part = scroll_bar_move_ratio; break;
9023     default:  /* NSScrollerNoPart? */
9024       fprintf (stderr, "EmacsScroller-mouseDown: unexpected part %ld\n",
9025                (long) part);
9026       return;
9027     }
9029   if (part == NSScrollerKnob || part == NSScrollerKnobSlot)
9030     {
9031       /* handle, or on GNUstep possibly slot */
9032       NSEvent *fake_event;
9033       int length;
9035       /* compute float loc in slot and mouse offset on knob */
9036       sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot]
9037                       toView: nil];
9038       if (horizontal)
9039         {
9040           length = NSWidth (sr);
9041           loc = ([e locationInWindow].x - NSMinX (sr));
9042         }
9043       else
9044         {
9045           length = NSHeight (sr);
9046           loc = length - ([e locationInWindow].y - NSMinY (sr));
9047         }
9049       if (loc <= 0.0)
9050         {
9051           loc = 0.0;
9052           edge = -1;
9053         }
9054       else if (loc >= length)
9055         {
9056           loc = length;
9057           edge = 1;
9058         }
9060       if (edge)
9061         kloc = 0.5 * edge;
9062       else
9063         {
9064           kr = [self convertRect: [self rectForPart: NSScrollerKnob]
9065                           toView: nil];
9066           if (horizontal)
9067             kloc = ([e locationInWindow].x - NSMinX (kr));
9068           else
9069             kloc = NSHeight (kr) - ([e locationInWindow].y - NSMinY (kr));
9070         }
9071       last_mouse_offset = kloc;
9073       /* if knob, tell emacs a location offset by knob pos
9074          (to indicate top of handle) */
9075       if (part == NSScrollerKnob)
9076         pos = (loc - last_mouse_offset);
9077       else
9078         /* else this is a slot click on GNUstep: go straight there */
9079         pos = loc;
9081       /* If there are buttons in the scroller area, we need to
9082          recalculate pos as emacs expects the scroller slot to take up
9083          the entire available length.  */
9084       if (length != pixel_length)
9085         pos = pos * pixel_length / length;
9087       /* send a fake mouse-up to super to preempt modal -trackKnob: mode */
9088       fake_event = [NSEvent mouseEventWithType: NSEventTypeLeftMouseUp
9089                                       location: [e locationInWindow]
9090                                  modifierFlags: [e modifierFlags]
9091                                      timestamp: [e timestamp]
9092                                   windowNumber: [e windowNumber]
9093                                        context: nil
9094                                    eventNumber: [e eventNumber]
9095                                     clickCount: [e clickCount]
9096                                       pressure: [e pressure]];
9097       [super mouseUp: fake_event];
9098     }
9099   else
9100     {
9101       pos = 0;      /* ignored */
9103       /* set a timer to repeat, as we can't let superclass do this modally */
9104       scroll_repeat_entry
9105         = [[NSTimer scheduledTimerWithTimeInterval: SCROLL_BAR_FIRST_DELAY
9106                                             target: self
9107                                           selector: @selector (repeatScroll:)
9108                                           userInfo: 0
9109                                            repeats: YES]
9110             retain];
9111     }
9113   if (part != NSScrollerKnob)
9114     [self sendScrollEventAtLoc: pos fromEvent: e];
9118 /* Called as we manually track scroller drags, rather than superclass. */
9119 - (void)mouseDragged: (NSEvent *)e
9121     NSRect sr;
9122     double loc, pos;
9123     int length;
9125     NSTRACE ("[EmacsScroller mouseDragged:]");
9127       sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot]
9128                       toView: nil];
9130       if (horizontal)
9131         {
9132           length = NSWidth (sr);
9133           loc = ([e locationInWindow].x - NSMinX (sr));
9134         }
9135       else
9136         {
9137           length = NSHeight (sr);
9138           loc = length - ([e locationInWindow].y - NSMinY (sr));
9139         }
9141       if (loc <= 0.0)
9142         {
9143           loc = 0.0;
9144         }
9145       else if (loc >= length + last_mouse_offset)
9146         {
9147           loc = length + last_mouse_offset;
9148         }
9150       pos = (loc - last_mouse_offset);
9152       /* If there are buttons in the scroller area, we need to
9153          recalculate pos as emacs expects the scroller slot to take up
9154          the entire available length.  */
9155       if (length != pixel_length)
9156         pos = pos * pixel_length / length;
9158       [self sendScrollEventAtLoc: pos fromEvent: e];
9162 - (void)mouseUp: (NSEvent *)e
9164   NSTRACE ("[EmacsScroller mouseUp:]");
9166   if (scroll_repeat_entry)
9167     {
9168       [scroll_repeat_entry invalidate];
9169       [scroll_repeat_entry release];
9170       scroll_repeat_entry = nil;
9171     }
9172   last_hit_part = scroll_bar_above_handle;
9176 /* treat scrollwheel events in the bar as though they were in the main window */
9177 - (void) scrollWheel: (NSEvent *)theEvent
9179   NSTRACE ("[EmacsScroller scrollWheel:]");
9181   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (frame);
9182   [view mouseDown: theEvent];
9185 @end  /* EmacsScroller */
9188 #ifdef NS_IMPL_GNUSTEP
9189 /* Dummy class to get rid of startup warnings.  */
9190 @implementation EmacsDocument
9192 @end
9193 #endif
9196 /* ==========================================================================
9198    Font-related functions; these used to be in nsfaces.m
9200    ========================================================================== */
9203 Lisp_Object
9204 x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
9206   struct font *font = XFONT_OBJECT (font_object);
9207   EmacsView *view = FRAME_NS_VIEW (f);
9208   int font_ascent, font_descent;
9210   if (fontset < 0)
9211     fontset = fontset_from_font (font_object);
9212   FRAME_FONTSET (f) = fontset;
9214   if (FRAME_FONT (f) == font)
9215     /* This font is already set in frame F.  There's nothing more to
9216        do.  */
9217     return font_object;
9219   FRAME_FONT (f) = font;
9221   FRAME_BASELINE_OFFSET (f) = font->baseline_offset;
9222   FRAME_COLUMN_WIDTH (f) = font->average_width;
9223   get_font_ascent_descent (font, &font_ascent, &font_descent);
9224   FRAME_LINE_HEIGHT (f) = font_ascent + font_descent;
9226   /* Compute the scroll bar width in character columns.  */
9227   if (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) > 0)
9228     {
9229       int wid = FRAME_COLUMN_WIDTH (f);
9230       FRAME_CONFIG_SCROLL_BAR_COLS (f)
9231         = (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) + wid - 1) / wid;
9232     }
9233   else
9234     {
9235       int wid = FRAME_COLUMN_WIDTH (f);
9236       FRAME_CONFIG_SCROLL_BAR_COLS (f) = (14 + wid - 1) / wid;
9237     }
9239   /* Compute the scroll bar height in character lines.  */
9240   if (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) > 0)
9241     {
9242       int height = FRAME_LINE_HEIGHT (f);
9243       FRAME_CONFIG_SCROLL_BAR_LINES (f)
9244         = (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) + height - 1) / height;
9245     }
9246   else
9247     {
9248       int height = FRAME_LINE_HEIGHT (f);
9249       FRAME_CONFIG_SCROLL_BAR_LINES (f) = (14 + height - 1) / height;
9250     }
9252   /* Now make the frame display the given font.  */
9253   if (FRAME_NS_WINDOW (f) != 0 && ! [view isFullscreen])
9254     adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
9255                        FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3,
9256                        false, Qfont);
9258   return font_object;
9262 /* XLFD: -foundry-family-weight-slant-swidth-adstyle-pxlsz-ptSz-resx-resy-spc-avgWidth-rgstry-encoding */
9263 /* Note: ns_font_to_xlfd and ns_fontname_to_xlfd no longer needed, removed
9264          in 1.43. */
9266 const char *
9267 ns_xlfd_to_fontname (const char *xlfd)
9268 /* --------------------------------------------------------------------------
9269     Convert an X font name (XLFD) to an NS font name.
9270     Only family is used.
9271     The string returned is temporarily allocated.
9272    -------------------------------------------------------------------------- */
9274   char *name = xmalloc (180);
9275   int i, len;
9276   const char *ret;
9278   if (!strncmp (xlfd, "--", 2))
9279     sscanf (xlfd, "--%*[^-]-%179[^-]-", name);
9280   else
9281     sscanf (xlfd, "-%*[^-]-%179[^-]-", name);
9283   /* stopgap for malformed XLFD input */
9284   if (strlen (name) == 0)
9285     strcpy (name, "Monaco");
9287   /* undo hack in ns_fontname_to_xlfd, converting '$' to '-', '_' to ' '
9288      also uppercase after '-' or ' ' */
9289   name[0] = c_toupper (name[0]);
9290   for (len =strlen (name), i =0; i<len; i++)
9291     {
9292       if (name[i] == '$')
9293         {
9294           name[i] = '-';
9295           if (i+1<len)
9296             name[i+1] = c_toupper (name[i+1]);
9297         }
9298       else if (name[i] == '_')
9299         {
9300           name[i] = ' ';
9301           if (i+1<len)
9302             name[i+1] = c_toupper (name[i+1]);
9303         }
9304     }
9305 /*fprintf (stderr, "converted '%s' to '%s'\n",xlfd,name);  */
9306   ret = [[NSString stringWithUTF8String: name] UTF8String];
9307   xfree (name);
9308   return ret;
9312 void
9313 syms_of_nsterm (void)
9315   NSTRACE ("syms_of_nsterm");
9317   ns_antialias_threshold = 10.0;
9319   /* from 23+ we need to tell emacs what modifiers there are.. */
9320   DEFSYM (Qmodifier_value, "modifier-value");
9321   DEFSYM (Qalt, "alt");
9322   DEFSYM (Qhyper, "hyper");
9323   DEFSYM (Qmeta, "meta");
9324   DEFSYM (Qsuper, "super");
9325   DEFSYM (Qcontrol, "control");
9326   DEFSYM (QUTF8_STRING, "UTF8_STRING");
9328   DEFSYM (Qfile, "file");
9329   DEFSYM (Qurl, "url");
9331   Fput (Qalt, Qmodifier_value, make_number (alt_modifier));
9332   Fput (Qhyper, Qmodifier_value, make_number (hyper_modifier));
9333   Fput (Qmeta, Qmodifier_value, make_number (meta_modifier));
9334   Fput (Qsuper, Qmodifier_value, make_number (super_modifier));
9335   Fput (Qcontrol, Qmodifier_value, make_number (ctrl_modifier));
9337   DEFVAR_LISP ("ns-input-file", ns_input_file,
9338               "The file specified in the last NS event.");
9339   ns_input_file =Qnil;
9341   DEFVAR_LISP ("ns-working-text", ns_working_text,
9342               "String for visualizing working composition sequence.");
9343   ns_working_text =Qnil;
9345   DEFVAR_LISP ("ns-input-font", ns_input_font,
9346               "The font specified in the last NS event.");
9347   ns_input_font =Qnil;
9349   DEFVAR_LISP ("ns-input-fontsize", ns_input_fontsize,
9350               "The fontsize specified in the last NS event.");
9351   ns_input_fontsize =Qnil;
9353   DEFVAR_LISP ("ns-input-line", ns_input_line,
9354                "The line specified in the last NS event.");
9355   ns_input_line =Qnil;
9357   DEFVAR_LISP ("ns-input-spi-name", ns_input_spi_name,
9358                "The service name specified in the last NS event.");
9359   ns_input_spi_name =Qnil;
9361   DEFVAR_LISP ("ns-input-spi-arg", ns_input_spi_arg,
9362                "The service argument specified in the last NS event.");
9363   ns_input_spi_arg =Qnil;
9365   DEFVAR_LISP ("ns-alternate-modifier", ns_alternate_modifier,
9366                "This variable describes the behavior of the alternate or option key.\n\
9367 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9368 that key.\n\
9369 Set to none means that the alternate / option key is not interpreted by Emacs\n\
9370 at all, allowing it to be used at a lower level for accented character entry.");
9371   ns_alternate_modifier = Qmeta;
9373   DEFVAR_LISP ("ns-right-alternate-modifier", ns_right_alternate_modifier,
9374                "This variable describes the behavior of the right alternate or option key.\n\
9375 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9376 that key.\n\
9377 Set to left means be the same key as `ns-alternate-modifier'.\n\
9378 Set to none means that the alternate / option key is not interpreted by Emacs\n\
9379 at all, allowing it to be used at a lower level for accented character entry.");
9380   ns_right_alternate_modifier = Qleft;
9382   DEFVAR_LISP ("ns-command-modifier", ns_command_modifier,
9383                "This variable describes the behavior of the command key.\n\
9384 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9385 that key.");
9386   ns_command_modifier = Qsuper;
9388   DEFVAR_LISP ("ns-right-command-modifier", ns_right_command_modifier,
9389                "This variable describes the behavior of the right command key.\n\
9390 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9391 that key.\n\
9392 Set to left means be the same key as `ns-command-modifier'.\n\
9393 Set to none means that the command / option key is not interpreted by Emacs\n\
9394 at all, allowing it to be used at a lower level for accented character entry.");
9395   ns_right_command_modifier = Qleft;
9397   DEFVAR_LISP ("ns-control-modifier", ns_control_modifier,
9398                "This variable describes the behavior of the control key.\n\
9399 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9400 that key.");
9401   ns_control_modifier = Qcontrol;
9403   DEFVAR_LISP ("ns-right-control-modifier", ns_right_control_modifier,
9404                "This variable describes the behavior of the right control key.\n\
9405 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9406 that key.\n\
9407 Set to left means be the same key as `ns-control-modifier'.\n\
9408 Set to none means that the control / option key is not interpreted by Emacs\n\
9409 at all, allowing it to be used at a lower level for accented character entry.");
9410   ns_right_control_modifier = Qleft;
9412   DEFVAR_LISP ("ns-function-modifier", ns_function_modifier,
9413                "This variable describes the behavior of the function key (on laptops).\n\
9414 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9415 that key.\n\
9416 Set to none means that the function key is not interpreted by Emacs at all,\n\
9417 allowing it to be used at a lower level for accented character entry.");
9418   ns_function_modifier = Qnone;
9420   DEFVAR_LISP ("ns-antialias-text", ns_antialias_text,
9421                "Non-nil (the default) means to render text antialiased.");
9422   ns_antialias_text = Qt;
9424   DEFVAR_LISP ("ns-use-thin-smoothing", ns_use_thin_smoothing,
9425                "Non-nil turns on a font smoothing method that produces thinner strokes.");
9426   ns_use_thin_smoothing = Qnil;
9428   DEFVAR_LISP ("ns-confirm-quit", ns_confirm_quit,
9429                "Whether to confirm application quit using dialog.");
9430   ns_confirm_quit = Qnil;
9432   DEFVAR_LISP ("ns-auto-hide-menu-bar", ns_auto_hide_menu_bar,
9433                doc: /* Non-nil means that the menu bar is hidden, but appears when the mouse is near.
9434 Only works on Mac OS X 10.6 or later.  */);
9435   ns_auto_hide_menu_bar = Qnil;
9437   DEFVAR_BOOL ("ns-use-native-fullscreen", ns_use_native_fullscreen,
9438      doc: /*Non-nil means to use native fullscreen on Mac OS X 10.7 and later.
9439 Nil means use fullscreen the old (< 10.7) way.  The old way works better with
9440 multiple monitors, but lacks tool bar.  This variable is ignored on
9441 Mac OS X < 10.7.  Default is t.  */);
9442   ns_use_native_fullscreen = YES;
9443   ns_last_use_native_fullscreen = ns_use_native_fullscreen;
9445   DEFVAR_BOOL ("ns-use-fullscreen-animation", ns_use_fullscreen_animation,
9446      doc: /*Non-nil means use animation on non-native fullscreen.
9447 For native fullscreen, this does nothing.
9448 Default is nil.  */);
9449   ns_use_fullscreen_animation = NO;
9451   DEFVAR_BOOL ("ns-use-srgb-colorspace", ns_use_srgb_colorspace,
9452      doc: /*Non-nil means to use sRGB colorspace on Mac OS X 10.7 and later.
9453 Note that this does not apply to images.
9454 This variable is ignored on Mac OS X < 10.7 and GNUstep.  */);
9455   ns_use_srgb_colorspace = YES;
9457   DEFVAR_BOOL ("ns-use-mwheel-acceleration",
9458                ns_use_mwheel_acceleration,
9459      doc: /*Non-nil means use macOS's standard mouse wheel acceleration.
9460 This variable is ignored on macOS < 10.7 and GNUstep.  Default is t.  */);
9461   ns_use_mwheel_acceleration = YES;
9463   DEFVAR_LISP ("ns-mwheel-line-height", ns_mwheel_line_height,
9464                doc: /*The number of pixels touchpad scrolling considers one line.
9465 Nil or a non-number means use the default frame line height.
9466 This variable is ignored on macOS < 10.7 and GNUstep.  Default is nil.  */);
9467   ns_mwheel_line_height = Qnil;
9469   DEFVAR_BOOL ("ns-use-mwheel-momentum", ns_use_mwheel_momentum,
9470                doc: /*Non-nil means mouse wheel scrolling uses momentum.
9471 This variable is ignored on macOS < 10.7 and GNUstep.  Default is t.  */);
9472   ns_use_mwheel_momentum = YES;
9474   /* TODO: move to common code */
9475   DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars,
9476                doc: /* SKIP: real doc in xterm.c.  */);
9477   Vx_toolkit_scroll_bars = Qt;
9479   DEFVAR_BOOL ("x-use-underline-position-properties",
9480                x_use_underline_position_properties,
9481      doc: /* SKIP: real doc in xterm.c.  */);
9482   x_use_underline_position_properties = 0;
9483   DEFSYM (Qx_use_underline_position_properties,
9484           "x-use-underline-position-properties");
9486   DEFVAR_BOOL ("x-underline-at-descent-line",
9487                x_underline_at_descent_line,
9488      doc: /* SKIP: real doc in xterm.c.  */);
9489   x_underline_at_descent_line = 0;
9490   DEFSYM (Qx_underline_at_descent_line, "x-underline-at-descent-line");
9492   /* Tell Emacs about this window system.  */
9493   Fprovide (Qns, Qnil);
9495   DEFSYM (Qcocoa, "cocoa");
9496   DEFSYM (Qgnustep, "gnustep");
9498 #ifdef NS_IMPL_COCOA
9499   Fprovide (Qcocoa, Qnil);
9500   syms_of_macfont ();
9501 #else
9502   Fprovide (Qgnustep, Qnil);
9503   syms_of_nsfont ();
9504 #endif