; doc/emacs/misc.texi (Network Security): Fix typo.
[emacs.git] / src / nsterm.m
bloba15684d3bf2e9df82d9d5fe986a93438e5754740
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;
292 /* The number of times NSDisableScreenUpdates has been called.  */
293 static int disable_screen_updates_count = 0;
294 #endif
295 /* static int debug_lock = 0; */
297 /* event loop */
298 static BOOL send_appdefined = YES;
299 #define NO_APPDEFINED_DATA (-8)
300 static int last_appdefined_event_data = NO_APPDEFINED_DATA;
301 static NSTimer *timed_entry = 0;
302 static NSTimer *scroll_repeat_entry = nil;
303 static fd_set select_readfds, select_writefds;
304 enum { SELECT_HAVE_READ = 1, SELECT_HAVE_WRITE = 2, SELECT_HAVE_TMO = 4 };
305 static int select_nfds = 0, select_valid = 0;
306 static struct timespec select_timeout = { 0, 0 };
307 static int selfds[2] = { -1, -1 };
308 static pthread_mutex_t select_mutex;
309 static NSAutoreleasePool *outerpool;
310 static struct input_event *emacs_event = NULL;
311 static struct input_event *q_event_ptr = NULL;
312 static int n_emacs_events_pending = 0;
313 static NSMutableArray *ns_pending_files, *ns_pending_service_names,
314   *ns_pending_service_args;
315 static BOOL ns_do_open_file = NO;
316 static BOOL ns_last_use_native_fullscreen;
318 /* Non-zero means that a HELP_EVENT has been generated since Emacs
319    start.  */
321 static BOOL any_help_event_p = NO;
323 static struct {
324   struct input_event *q;
325   int nr, cap;
326 } hold_event_q = {
327   NULL, 0, 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_init_events (struct input_event *ev)
475   EVENT_INIT (*ev);
476   emacs_event = ev;
479 void
480 ns_finish_events (void)
482   emacs_event = NULL;
485 static void
486 hold_event (struct input_event *event)
488   if (hold_event_q.nr == hold_event_q.cap)
489     {
490       if (hold_event_q.cap == 0) hold_event_q.cap = 10;
491       else hold_event_q.cap *= 2;
492       hold_event_q.q =
493         xrealloc (hold_event_q.q, hold_event_q.cap * sizeof *hold_event_q.q);
494     }
496   hold_event_q.q[hold_event_q.nr++] = *event;
497   /* Make sure ns_read_socket is called, i.e. we have input.  */
498   raise (SIGIO);
499   send_appdefined = YES;
502 static Lisp_Object
503 append2 (Lisp_Object list, Lisp_Object item)
504 /* --------------------------------------------------------------------------
505    Utility to append to a list
506    -------------------------------------------------------------------------- */
508   return CALLN (Fnconc, list, list1 (item));
512 const char *
513 ns_etc_directory (void)
514 /* If running as a self-contained app bundle, return as a string the
515    filename of the etc directory, if present; else nil.  */
517   NSBundle *bundle = [NSBundle mainBundle];
518   NSString *resourceDir = [bundle resourcePath];
519   NSString *resourcePath;
520   NSFileManager *fileManager = [NSFileManager defaultManager];
521   BOOL isDir;
523   resourcePath = [resourceDir stringByAppendingPathComponent: @"etc"];
524   if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
525     {
526       if (isDir) return [resourcePath UTF8String];
527     }
528   return NULL;
532 const char *
533 ns_exec_path (void)
534 /* If running as a self-contained app bundle, return as a path string
535    the filenames of the libexec and bin directories, ie libexec:bin.
536    Otherwise, return nil.
537    Normally, Emacs does not add its own bin/ directory to the PATH.
538    However, a self-contained NS build has a different layout, with
539    bin/ and libexec/ subdirectories in the directory that contains
540    Emacs.app itself.
541    We put libexec first, because init_callproc_1 uses the first
542    element to initialize exec-directory.  An alternative would be
543    for init_callproc to check for invocation-directory/libexec.
546   NSBundle *bundle = [NSBundle mainBundle];
547   NSString *resourceDir = [bundle resourcePath];
548   NSString *binDir = [bundle bundlePath];
549   NSString *resourcePath, *resourcePaths;
550   NSRange range;
551   NSString *pathSeparator = [NSString stringWithFormat: @"%c", SEPCHAR];
552   NSFileManager *fileManager = [NSFileManager defaultManager];
553   NSArray *paths;
554   NSEnumerator *pathEnum;
555   BOOL isDir;
557   range = [resourceDir rangeOfString: @"Contents"];
558   if (range.location != NSNotFound)
559     {
560       binDir = [binDir stringByAppendingPathComponent: @"Contents"];
561 #ifdef NS_IMPL_COCOA
562       binDir = [binDir stringByAppendingPathComponent: @"MacOS"];
563 #endif
564     }
566   paths = [binDir stringsByAppendingPaths:
567                 [NSArray arrayWithObjects: @"libexec", @"bin", nil]];
568   pathEnum = [paths objectEnumerator];
569   resourcePaths = @"";
571   while ((resourcePath = [pathEnum nextObject]))
572     {
573       if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
574         if (isDir)
575           {
576             if ([resourcePaths length] > 0)
577               resourcePaths
578                 = [resourcePaths stringByAppendingString: pathSeparator];
579             resourcePaths
580               = [resourcePaths stringByAppendingString: resourcePath];
581           }
582     }
583   if ([resourcePaths length] > 0) return [resourcePaths UTF8String];
585   return NULL;
589 const char *
590 ns_load_path (void)
591 /* If running as a self-contained app bundle, return as a path string
592    the filenames of the site-lisp and lisp directories.
593    Ie, site-lisp:lisp.  Otherwise, return nil.  */
595   NSBundle *bundle = [NSBundle mainBundle];
596   NSString *resourceDir = [bundle resourcePath];
597   NSString *resourcePath, *resourcePaths;
598   NSString *pathSeparator = [NSString stringWithFormat: @"%c", SEPCHAR];
599   NSFileManager *fileManager = [NSFileManager defaultManager];
600   BOOL isDir;
601   NSArray *paths = [resourceDir stringsByAppendingPaths:
602                               [NSArray arrayWithObjects:
603                                          @"site-lisp", @"lisp", nil]];
604   NSEnumerator *pathEnum = [paths objectEnumerator];
605   resourcePaths = @"";
607   /* Hack to skip site-lisp.  */
608   if (no_site_lisp) resourcePath = [pathEnum nextObject];
610   while ((resourcePath = [pathEnum nextObject]))
611     {
612       if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
613         if (isDir)
614           {
615             if ([resourcePaths length] > 0)
616               resourcePaths
617                 = [resourcePaths stringByAppendingString: pathSeparator];
618             resourcePaths
619               = [resourcePaths stringByAppendingString: resourcePath];
620           }
621     }
622   if ([resourcePaths length] > 0) return [resourcePaths UTF8String];
624   return NULL;
628 void
629 ns_init_locale (void)
630 /* macOS doesn't set any environment variables for the locale when run
631    from the GUI. Get the locale from the OS and set LANG.  */
633   NSLocale *locale = [NSLocale currentLocale];
635   NSTRACE ("ns_init_locale");
637   @try
638     {
639       /* It seems macOS should probably use UTF-8 everywhere.
640          'localeIdentifier' does not specify the encoding, and I can't
641          find any way to get the OS to tell us which encoding to use,
642          so hard-code '.UTF-8'.  */
643       NSString *localeID = [NSString stringWithFormat:@"%@.UTF-8",
644                                      [locale localeIdentifier]];
646       /* Set LANG to locale, but not if LANG is already set.  */
647       setenv("LANG", [localeID UTF8String], 0);
648     }
649   @catch (NSException *e)
650     {
651       NSLog (@"Locale detection failed: %@: %@", [e name], [e reason]);
652     }
656 void
657 ns_release_object (void *obj)
658 /* --------------------------------------------------------------------------
659     Release an object (callable from C)
660    -------------------------------------------------------------------------- */
662     [(id)obj release];
666 void
667 ns_retain_object (void *obj)
668 /* --------------------------------------------------------------------------
669      Retain an object (callable from C)
670    -------------------------------------------------------------------------- */
672     [(id)obj retain];
676 void *
677 ns_alloc_autorelease_pool (void)
678 /* --------------------------------------------------------------------------
679      Allocate a pool for temporary objects (callable from C)
680    -------------------------------------------------------------------------- */
682   return [[NSAutoreleasePool alloc] init];
686 void
687 ns_release_autorelease_pool (void *pool)
688 /* --------------------------------------------------------------------------
689      Free a pool and temporary objects it refers to (callable from C)
690    -------------------------------------------------------------------------- */
692   ns_release_object (pool);
696 #ifdef NS_IMPL_COCOA
697 /* Disabling screen updates can be used to make several actions appear
698    "atomic" to the end user.  It seems some actions can still update
699    the display, though.
701    When we re-enable screen updates the number of calls to
702    NSEnableScreenUpdates should match the number to
703    NSDisableScreenUpdates.
705    We use these functions to prevent the user seeing a blank frame
706    after it has been resized.  x_set_window_size disables updates and
707    when redisplay completes unwind_redisplay enables them again
708    (bug#30699).  */
710 static void
711 ns_disable_screen_updates (void)
713   NSDisableScreenUpdates ();
714   disable_screen_updates_count++;
717 void
718 ns_enable_screen_updates (void)
719 /* Re-enable screen updates.  Called from unwind_redisplay.  */
721   while (disable_screen_updates_count > 0)
722     {
723       NSEnableScreenUpdates ();
724       disable_screen_updates_count--;
725     }
727 #endif
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 a 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 bells.
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 #ifdef NS_IMPL_COCOA
1881   /* To prevent showing the user a blank frame, stop updates being
1882      flushed to the screen until after redisplay has completed.  This
1883      breaks live resize (resizing with a mouse), so don't do it if
1884      we're in a live resize loop.  */
1885   if (![view inLiveResize])
1886     ns_disable_screen_updates ();
1887 #endif
1889   if (pixelwise)
1890     {
1891       pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
1892       pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
1893     }
1894   else
1895     {
1896       pixelwidth =  FRAME_TEXT_COLS_TO_PIXEL_WIDTH   (f, width);
1897       pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, height);
1898     }
1900   wr.size.width = pixelwidth + f->border_width;
1901   wr.size.height = pixelheight;
1902   if (! [view isFullscreen])
1903     wr.size.height += FRAME_NS_TITLEBAR_HEIGHT (f)
1904       + FRAME_TOOLBAR_HEIGHT (f);
1906   /* Do not try to constrain to this screen.  We may have multiple
1907      screens, and want Emacs to span those.  Constraining to screen
1908      prevents that, and that is not nice to the user.  */
1909  if (f->output_data.ns->zooming)
1910    f->output_data.ns->zooming = 0;
1911  else
1912    wr.origin.y += orig_height - wr.size.height;
1914  frame_size_history_add
1915    (f, Qx_set_window_size_1, width, height,
1916     list5 (Fcons (make_number (pixelwidth), make_number (pixelheight)),
1917            Fcons (make_number (wr.size.width), make_number (wr.size.height)),
1918            make_number (f->border_width),
1919            make_number (FRAME_NS_TITLEBAR_HEIGHT (f)),
1920            make_number (FRAME_TOOLBAR_HEIGHT (f))));
1922   [window setFrame: wr display: YES];
1924   [view updateFrameSize: NO];
1925   unblock_input ();
1928 #ifdef NS_IMPL_COCOA
1929 void
1930 x_set_undecorated (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1931 /* --------------------------------------------------------------------------
1932      Set frame F's `undecorated' parameter.  If non-nil, F's window-system
1933      window is drawn without decorations, title, minimize/maximize boxes
1934      and external borders.  This usually means that the window cannot be
1935      dragged, resized, iconified, maximized or deleted with the mouse.  If
1936      nil, draw the frame with all the elements listed above unless these
1937      have been suspended via window manager settings.
1939      GNUStep cannot change an existing window's style.
1940    -------------------------------------------------------------------------- */
1942   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
1943   NSWindow *window = [view window];
1945   NSTRACE ("x_set_undecorated");
1947   if (!EQ (new_value, old_value))
1948     {
1949       block_input ();
1951       if (NILP (new_value))
1952         {
1953           FRAME_UNDECORATED (f) = false;
1954           [window setStyleMask: ((window.styleMask | FRAME_DECORATED_FLAGS)
1955                                   ^ FRAME_UNDECORATED_FLAGS)];
1957           [view createToolbar: f];
1958         }
1959       else
1960         {
1961           [window setToolbar: nil];
1962           /* Do I need to release the toolbar here?  */
1964           FRAME_UNDECORATED (f) = true;
1965           [window setStyleMask: ((window.styleMask | FRAME_UNDECORATED_FLAGS)
1966                                  ^ FRAME_DECORATED_FLAGS)];
1967         }
1969       /* At this point it seems we don't have an active NSResponder,
1970          so some key presses (TAB) are swallowed by the system.  */
1971       [window makeFirstResponder: view];
1973       [view updateFrameSize: NO];
1974       unblock_input ();
1975     }
1977 #endif /* NS_IMPL_COCOA */
1979 void
1980 x_set_parent_frame (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1981 /* --------------------------------------------------------------------------
1982      Set frame F's `parent-frame' parameter.  If non-nil, make F a child
1983      frame of the frame specified by that parameter.  Technically, this
1984      makes F's window-system window a child window of the parent frame's
1985      window-system window.  If nil, make F's window-system window a
1986      top-level window--a child of its display's root window.
1988      A child frame's `left' and `top' parameters specify positions
1989      relative to the top-left corner of its parent frame's native
1990      rectangle.  On macOS moving a parent frame moves all its child
1991      frames too, keeping their position relative to the parent
1992      unaltered.  When a parent frame is iconified or made invisible, its
1993      child frames are made invisible.  When a parent frame is deleted,
1994      its child frames are deleted too.
1996      Whether a child frame has a tool bar may be window-system or window
1997      manager dependent.  It's advisable to disable it via the frame
1998      parameter settings.
2000      Some window managers may not honor this parameter.
2001    -------------------------------------------------------------------------- */
2003   struct frame *p = NULL;
2004   NSWindow *parent, *child;
2006   NSTRACE ("x_set_parent_frame");
2008   if (!NILP (new_value)
2009       && (!FRAMEP (new_value)
2010           || !FRAME_LIVE_P (p = XFRAME (new_value))
2011           || !FRAME_NS_P (p)))
2012     {
2013       store_frame_param (f, Qparent_frame, old_value);
2014       error ("Invalid specification of `parent-frame'");
2015     }
2017   if (p != FRAME_PARENT_FRAME (f))
2018     {
2019       block_input ();
2020       child = [FRAME_NS_VIEW (f) window];
2022       if ([child parentWindow] != nil)
2023         {
2024           [[child parentWindow] removeChildWindow:child];
2025 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
2026 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101000
2027           if ([child respondsToSelector:@selector(setAccessibilitySubrole:)]
2028 #endif
2029               [child setAccessibilitySubrole:NSAccessibilityStandardWindowSubrole];
2030 #endif
2031         }
2033       if (!NILP (new_value))
2034         {
2035           parent = [FRAME_NS_VIEW (p) window];
2037           [parent addChildWindow: child
2038                          ordered: NSWindowAbove];
2039 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
2040 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101000
2041           if ([child respondsToSelector:@selector(setAccessibilitySubrole:)]
2042 #endif
2043               [child setAccessibilitySubrole:NSAccessibilityFloatingWindowSubrole];
2044 #endif
2045         }
2047       unblock_input ();
2049       fset_parent_frame (f, new_value);
2050     }
2053 void
2054 x_set_no_focus_on_map (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
2055 /* Set frame F's `no-focus-on-map' parameter which, if non-nil, means
2056  * that F's window-system window does not want to receive input focus
2057  * when it is mapped.  (A frame's window is mapped when the frame is
2058  * displayed for the first time and when the frame changes its state
2059  * from `iconified' or `invisible' to `visible'.)
2061  * Some window managers may not honor this parameter.  */
2063   NSTRACE ("x_set_no_focus_on_map");
2065   if (!EQ (new_value, old_value))
2066     {
2067       FRAME_NO_FOCUS_ON_MAP (f) = !NILP (new_value);
2068     }
2071 void
2072 x_set_no_accept_focus (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
2073 /*  Set frame F's `no-accept-focus' parameter which, if non-nil, hints
2074  * that F's window-system window does not want to receive input focus
2075  * via mouse clicks or by moving the mouse into it.
2077  * If non-nil, this may have the unwanted side-effect that a user cannot
2078  * scroll a non-selected frame with the mouse.
2080  * Some window managers may not honor this parameter.  */
2082   NSTRACE ("x_set_no_accept_focus");
2084   if (!EQ (new_value, old_value))
2085     FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value);
2088 void
2089 x_set_z_group (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
2090 /* Set frame F's `z-group' parameter.  If `above', F's window-system
2091    window is displayed above all windows that do not have the `above'
2092    property set.  If nil, F's window is shown below all windows that
2093    have the `above' property set and above all windows that have the
2094    `below' property set.  If `below', F's window is displayed below
2095    all windows that do.
2097    Some window managers may not honor this parameter.  */
2099   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2100   NSWindow *window = [view window];
2102   NSTRACE ("x_set_z_group");
2104   if (NILP (new_value))
2105     {
2106       window.level = NSNormalWindowLevel;
2107       FRAME_Z_GROUP (f) = z_group_none;
2108     }
2109   else if (EQ (new_value, Qabove))
2110     {
2111       window.level = NSNormalWindowLevel + 1;
2112       FRAME_Z_GROUP (f) = z_group_above;
2113     }
2114   else if (EQ (new_value, Qabove_suspended))
2115     {
2116       /* Not sure what level this should be.  */
2117       window.level = NSNormalWindowLevel + 1;
2118       FRAME_Z_GROUP (f) = z_group_above_suspended;
2119     }
2120   else if (EQ (new_value, Qbelow))
2121     {
2122       window.level = NSNormalWindowLevel - 1;
2123       FRAME_Z_GROUP (f) = z_group_below;
2124     }
2125   else
2126     error ("Invalid z-group specification");
2129 #ifdef NS_IMPL_COCOA
2130 void
2131 ns_set_appearance (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
2133 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
2134   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2135   NSWindow *window = [view window];
2137   NSTRACE ("ns_set_appearance");
2139 #ifndef NSAppKitVersionNumber10_10
2140 #define NSAppKitVersionNumber10_10 1343
2141 #endif
2143   if (NSAppKitVersionNumber < NSAppKitVersionNumber10_10)
2144     return;
2146   if (EQ (new_value, Qdark))
2147     {
2148       window.appearance = [NSAppearance
2149                             appearanceNamed: NSAppearanceNameVibrantDark];
2150       FRAME_NS_APPEARANCE (f) = ns_appearance_vibrant_dark;
2151     }
2152   else
2153     {
2154       window.appearance = [NSAppearance
2155                             appearanceNamed: NSAppearanceNameAqua];
2156       FRAME_NS_APPEARANCE (f) = ns_appearance_aqua;
2157     }
2158 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 */
2161 void
2162 ns_set_transparent_titlebar (struct frame *f, Lisp_Object new_value,
2163                              Lisp_Object old_value)
2165 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
2166   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2167   NSWindow *window = [view window];
2169   NSTRACE ("ns_set_transparent_titlebar");
2171   if ([window respondsToSelector: @selector(titlebarAppearsTransparent)]
2172       && !EQ (new_value, old_value))
2173     {
2174       window.titlebarAppearsTransparent = !NILP (new_value);
2175       FRAME_NS_TRANSPARENT_TITLEBAR (f) = !NILP (new_value);
2176     }
2177 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 */
2179 #endif /* NS_IMPL_COCOA */
2181 static void
2182 ns_fullscreen_hook (struct frame *f)
2184   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2186   NSTRACE ("ns_fullscreen_hook");
2188   if (!FRAME_VISIBLE_P (f))
2189     return;
2191    if (! [view fsIsNative] && f->want_fullscreen == FULLSCREEN_BOTH)
2192     {
2193       /* Old style fs don't initiate correctly if created from
2194          init/default-frame alist, so use a timer (not nice...).  */
2195       [NSTimer scheduledTimerWithTimeInterval: 0.5 target: view
2196                                      selector: @selector (handleFS)
2197                                      userInfo: nil repeats: NO];
2198       return;
2199     }
2201   block_input ();
2202   [view handleFS];
2203   unblock_input ();
2206 /* ==========================================================================
2208     Color management
2210    ========================================================================== */
2213 NSColor *
2214 ns_lookup_indexed_color (unsigned long idx, struct frame *f)
2216   struct ns_color_table *color_table = FRAME_DISPLAY_INFO (f)->color_table;
2217   if (idx < 1 || idx >= color_table->avail)
2218     return nil;
2219   return color_table->colors[idx];
2223 unsigned long
2224 ns_index_color (NSColor *color, struct frame *f)
2226   struct ns_color_table *color_table = FRAME_DISPLAY_INFO (f)->color_table;
2227   ptrdiff_t idx;
2228   ptrdiff_t i;
2230   if (!color_table->colors)
2231     {
2232       color_table->size = NS_COLOR_CAPACITY;
2233       color_table->avail = 1; /* skip idx=0 as marker */
2234       color_table->colors = xmalloc (color_table->size * sizeof (NSColor *));
2235       color_table->colors[0] = nil;
2236       color_table->empty_indices = [[NSMutableSet alloc] init];
2237     }
2239   /* Do we already have this color?  */
2240   for (i = 1; i < color_table->avail; i++)
2241     if (color_table->colors[i] && [color_table->colors[i] isEqual: color])
2242       return i;
2244   if ([color_table->empty_indices count] > 0)
2245     {
2246       NSNumber *index = [color_table->empty_indices anyObject];
2247       [color_table->empty_indices removeObject: index];
2248       idx = [index unsignedLongValue];
2249     }
2250   else
2251     {
2252       if (color_table->avail == color_table->size)
2253         color_table->colors =
2254           xpalloc (color_table->colors, &color_table->size, 1,
2255                    min (ULONG_MAX, PTRDIFF_MAX), sizeof *color_table->colors);
2256       idx = color_table->avail++;
2257     }
2259   color_table->colors[idx] = color;
2260   [color retain];
2261   /* fprintf(stderr, "color_table: allocated %d\n",idx); */
2262   return idx;
2266 static int
2267 ns_get_color (const char *name, NSColor **col)
2268 /* --------------------------------------------------------------------------
2269      Parse a color name
2270    -------------------------------------------------------------------------- */
2271 /* On *Step, we attempt to mimic the X11 platform here, down to installing an
2272    X11 rgb.txt-compatible color list in Emacs.clr (see ns_term_init()).
2273    See https://lists.gnu.org/r/emacs-devel/2009-07/msg01203.html.  */
2275   NSColor *new = nil;
2276   static char hex[20];
2277   int scaling = 0;
2278   float r = -1.0, g, b;
2279   NSString *nsname = [NSString stringWithUTF8String: name];
2281   NSTRACE ("ns_get_color(%s, **)", name);
2283   block_input ();
2285   if ([nsname isEqualToString: @"ns_selection_bg_color"])
2286     {
2287 #ifdef NS_IMPL_COCOA
2288       NSString *defname = [[NSUserDefaults standardUserDefaults]
2289                             stringForKey: @"AppleHighlightColor"];
2290       if (defname != nil)
2291         nsname = defname;
2292       else
2293 #endif
2294       if ((new = [NSColor selectedTextBackgroundColor]) != nil)
2295         {
2296           *col = [new colorUsingDefaultColorSpace];
2297           unblock_input ();
2298           return 0;
2299         }
2300       else
2301         nsname = NS_SELECTION_BG_COLOR_DEFAULT;
2303       name = [nsname UTF8String];
2304     }
2305   else if ([nsname isEqualToString: @"ns_selection_fg_color"])
2306     {
2307       /* NOTE: macOS applications normally don't set foreground
2308          selection, but text may be unreadable if we don't.  */
2309       if ((new = [NSColor selectedTextColor]) != nil)
2310         {
2311           *col = [new colorUsingDefaultColorSpace];
2312           unblock_input ();
2313           return 0;
2314         }
2316       nsname = NS_SELECTION_FG_COLOR_DEFAULT;
2317       name = [nsname UTF8String];
2318     }
2320   /* First, check for some sort of numeric specification.  */
2321   hex[0] = '\0';
2323   if (name[0] == '0' || name[0] == '1' || name[0] == '.')  /* RGB decimal */
2324     {
2325       NSScanner *scanner = [NSScanner scannerWithString: nsname];
2326       [scanner scanFloat: &r];
2327       [scanner scanFloat: &g];
2328       [scanner scanFloat: &b];
2329     }
2330   else if (!strncmp(name, "rgb:", 4))  /* A newer X11 format -- rgb:r/g/b */
2331     scaling = (snprintf (hex, sizeof hex, "%s", name + 4) - 2) / 3;
2332   else if (name[0] == '#')        /* An old X11 format; convert to newer */
2333     {
2334       int len = (strlen(name) - 1);
2335       int start = (len % 3 == 0) ? 1 : len / 4 + 1;
2336       int i;
2337       scaling = strlen(name+start) / 3;
2338       for (i = 0; i < 3; i++)
2339         sprintf (hex + i * (scaling + 1), "%.*s/", scaling,
2340                  name + start + i * scaling);
2341       hex[3 * (scaling + 1) - 1] = '\0';
2342     }
2344   if (hex[0])
2345     {
2346       unsigned int rr, gg, bb;
2347       float fscale = scaling == 4 ? 65535.0 : (scaling == 2 ? 255.0 : 15.0);
2348       if (sscanf (hex, "%x/%x/%x", &rr, &gg, &bb))
2349         {
2350           r = rr / fscale;
2351           g = gg / fscale;
2352           b = bb / fscale;
2353         }
2354     }
2356   if (r >= 0.0F)
2357     {
2358       *col = [NSColor colorForEmacsRed: r green: g blue: b alpha: 1.0];
2359       unblock_input ();
2360       return 0;
2361     }
2363   /* Otherwise, color is expected to be from a list */
2364   {
2365     NSEnumerator *lenum, *cenum;
2366     NSString *name;
2367     NSColorList *clist;
2369 #ifdef NS_IMPL_GNUSTEP
2370     /* XXX: who is wrong, the requestor or the implementation?  */
2371     if ([nsname compare: @"Highlight" options: NSCaseInsensitiveSearch]
2372         == NSOrderedSame)
2373       nsname = @"highlightColor";
2374 #endif
2376     lenum = [[NSColorList availableColorLists] objectEnumerator];
2377     while ( (clist = [lenum nextObject]) && new == nil)
2378       {
2379         cenum = [[clist allKeys] objectEnumerator];
2380         while ( (name = [cenum nextObject]) && new == nil )
2381           {
2382             if ([name compare: nsname
2383                       options: NSCaseInsensitiveSearch] == NSOrderedSame )
2384               new = [clist colorWithKey: name];
2385           }
2386       }
2387   }
2389   if (new)
2390     *col = [new colorUsingDefaultColorSpace];
2391   unblock_input ();
2392   return new ? 0 : 1;
2397 ns_lisp_to_color (Lisp_Object color, NSColor **col)
2398 /* --------------------------------------------------------------------------
2399      Convert a Lisp string object to a NS color.
2400    -------------------------------------------------------------------------- */
2402   NSTRACE ("ns_lisp_to_color");
2403   if (STRINGP (color))
2404     return ns_get_color (SSDATA (color), col);
2405   else if (SYMBOLP (color))
2406     return ns_get_color (SSDATA (SYMBOL_NAME (color)), col);
2407   return 1;
2411 void
2412 ns_query_color(void *col, XColor *color_def, int setPixel)
2413 /* --------------------------------------------------------------------------
2414          Get ARGB values out of NSColor col and put them into color_def.
2415          If setPixel, set the pixel to a concatenated version.
2416          and set color_def pixel to the resulting index.
2417    -------------------------------------------------------------------------- */
2419   EmacsCGFloat r, g, b, a;
2421   [((NSColor *)col) getRed: &r green: &g blue: &b alpha: &a];
2422   color_def->red   = r * 65535;
2423   color_def->green = g * 65535;
2424   color_def->blue  = b * 65535;
2426   if (setPixel == YES)
2427     color_def->pixel
2428       = ARGB_TO_ULONG((int)(a*255),
2429                       (int)(r*255), (int)(g*255), (int)(b*255));
2433 bool
2434 ns_defined_color (struct frame *f,
2435                   const char *name,
2436                   XColor *color_def,
2437                   bool alloc,
2438                   bool makeIndex)
2439 /* --------------------------------------------------------------------------
2440          Return true if named color found, and set color_def rgb accordingly.
2441          If makeIndex and alloc are nonzero put the color in the color_table,
2442          and set color_def pixel to the resulting index.
2443          If makeIndex is zero, set color_def pixel to ARGB.
2444          Return false if not found.
2445    -------------------------------------------------------------------------- */
2447   NSColor *col;
2448   NSTRACE_WHEN (NSTRACE_GROUP_COLOR, "ns_defined_color");
2450   block_input ();
2451   if (ns_get_color (name, &col) != 0) /* Color not found  */
2452     {
2453       unblock_input ();
2454       return 0;
2455     }
2456   if (makeIndex && alloc)
2457     color_def->pixel = ns_index_color (col, f);
2458   ns_query_color (col, color_def, !makeIndex);
2459   unblock_input ();
2460   return 1;
2464 void
2465 x_set_frame_alpha (struct frame *f)
2466 /* --------------------------------------------------------------------------
2467      change the entire-frame transparency
2468    -------------------------------------------------------------------------- */
2470   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
2471   double alpha = 1.0;
2472   double alpha_min = 1.0;
2474   NSTRACE ("x_set_frame_alpha");
2476   if (dpyinfo->x_highlight_frame == f)
2477     alpha = f->alpha[0];
2478   else
2479     alpha = f->alpha[1];
2481   if (FLOATP (Vframe_alpha_lower_limit))
2482     alpha_min = XFLOAT_DATA (Vframe_alpha_lower_limit);
2483   else if (INTEGERP (Vframe_alpha_lower_limit))
2484     alpha_min = (XINT (Vframe_alpha_lower_limit)) / 100.0;
2486   if (alpha < 0.0)
2487     return;
2488   else if (1.0 < alpha)
2489     alpha = 1.0;
2490   else if (0.0 <= alpha && alpha < alpha_min && alpha_min <= 1.0)
2491     alpha = alpha_min;
2493 #ifdef NS_IMPL_COCOA
2494   {
2495     EmacsView *view = FRAME_NS_VIEW (f);
2496   [[view window] setAlphaValue: alpha];
2497   }
2498 #endif
2502 /* ==========================================================================
2504     Mouse handling
2506    ========================================================================== */
2509 void
2510 frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y)
2511 /* --------------------------------------------------------------------------
2512      Programmatically reposition mouse pointer in pixel coordinates
2513    -------------------------------------------------------------------------- */
2515   NSTRACE ("frame_set_mouse_pixel_position");
2517   /* FIXME: what about GNUstep?  */
2518 #ifdef NS_IMPL_COCOA
2519   CGPoint mouse_pos =
2520     CGPointMake(f->left_pos + pix_x,
2521                 f->top_pos + pix_y +
2522                 FRAME_NS_TITLEBAR_HEIGHT(f) + FRAME_TOOLBAR_HEIGHT(f));
2523   CGWarpMouseCursorPosition (mouse_pos);
2524 #endif
2527 static int
2528 note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y)
2529 /*   ------------------------------------------------------------------------
2530      Called by EmacsView on mouseMovement events.  Passes on
2531      to emacs mainstream code if we moved off of a rect of interest
2532      known as last_mouse_glyph.
2533      ------------------------------------------------------------------------ */
2535   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
2536   NSRect *r;
2538   // NSTRACE ("note_mouse_movement");
2540   dpyinfo->last_mouse_motion_frame = frame;
2541   r = &dpyinfo->last_mouse_glyph;
2543   /* Note, this doesn't get called for enter/leave, since we don't have a
2544      position.  Those are taken care of in the corresponding NSView methods.  */
2546   /* Has movement gone beyond last rect we were tracking?  */
2547   if (x < r->origin.x || x >= r->origin.x + r->size.width
2548       || y < r->origin.y || y >= r->origin.y + r->size.height)
2549     {
2550       ns_update_begin (frame);
2551       frame->mouse_moved = 1;
2552       note_mouse_highlight (frame, x, y);
2553       remember_mouse_glyph (frame, x, y, r);
2554       ns_update_end (frame);
2555       return 1;
2556     }
2558   return 0;
2562 static void
2563 ns_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window,
2564                    enum scroll_bar_part *part, Lisp_Object *x, Lisp_Object *y,
2565                    Time *time)
2566 /* --------------------------------------------------------------------------
2567     External (hook): inform emacs about mouse position and hit parts.
2568     If a scrollbar is being dragged, set bar_window, part, x, y, time.
2569     x & y should be position in the scrollbar (the whole bar, not the handle)
2570     and length of scrollbar respectively.
2571    -------------------------------------------------------------------------- */
2573   id view;
2574   NSPoint position;
2575   Lisp_Object frame, tail;
2576   struct frame *f;
2577   struct ns_display_info *dpyinfo;
2579   NSTRACE ("ns_mouse_position");
2581   if (*fp == NULL)
2582     {
2583       fprintf (stderr, "Warning: ns_mouse_position () called with null *fp.\n");
2584       return;
2585     }
2587   dpyinfo = FRAME_DISPLAY_INFO (*fp);
2589   block_input ();
2591   /* Clear the mouse-moved flag for every frame on this display.  */
2592   FOR_EACH_FRAME (tail, frame)
2593     if (FRAME_NS_P (XFRAME (frame))
2594         && FRAME_NS_DISPLAY (XFRAME (frame)) == FRAME_NS_DISPLAY (*fp))
2595       XFRAME (frame)->mouse_moved = 0;
2597   dpyinfo->last_mouse_scroll_bar = nil;
2598   if (dpyinfo->last_mouse_frame
2599       && FRAME_LIVE_P (dpyinfo->last_mouse_frame))
2600     f = dpyinfo->last_mouse_frame;
2601   else
2602     f = dpyinfo->x_focus_frame ? dpyinfo->x_focus_frame : SELECTED_FRAME ();
2604   if (f && FRAME_NS_P (f))
2605     {
2606       view = FRAME_NS_VIEW (f);
2608       position = [[view window] mouseLocationOutsideOfEventStream];
2609       position = [view convertPoint: position fromView: nil];
2610       remember_mouse_glyph (f, position.x, position.y,
2611                             &dpyinfo->last_mouse_glyph);
2612       NSTRACE_POINT ("position", position);
2614       if (bar_window) *bar_window = Qnil;
2615       if (part) *part = scroll_bar_above_handle;
2617       if (x) XSETINT (*x, lrint (position.x));
2618       if (y) XSETINT (*y, lrint (position.y));
2619       if (time)
2620         *time = dpyinfo->last_mouse_movement_time;
2621       *fp = f;
2622     }
2624   unblock_input ();
2628 static void
2629 ns_frame_up_to_date (struct frame *f)
2630 /* --------------------------------------------------------------------------
2631     External (hook): Fix up mouse highlighting right after a full update.
2632     Can't use FRAME_MOUSE_UPDATE due to ns_frame_begin and ns_frame_end calls.
2633    -------------------------------------------------------------------------- */
2635   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_frame_up_to_date");
2637   if (FRAME_NS_P (f))
2638     {
2639       Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
2640       if (f == hlinfo->mouse_face_mouse_frame)
2641         {
2642           block_input ();
2643           ns_update_begin(f);
2644           note_mouse_highlight (hlinfo->mouse_face_mouse_frame,
2645                                 hlinfo->mouse_face_mouse_x,
2646                                 hlinfo->mouse_face_mouse_y);
2647           ns_update_end(f);
2648           unblock_input ();
2649         }
2650     }
2654 static void
2655 ns_define_frame_cursor (struct frame *f, Cursor cursor)
2656 /* --------------------------------------------------------------------------
2657     External (RIF): set frame mouse pointer type.
2658    -------------------------------------------------------------------------- */
2660   NSTRACE ("ns_define_frame_cursor");
2661   if (FRAME_POINTER_TYPE (f) != cursor)
2662     {
2663       EmacsView *view = FRAME_NS_VIEW (f);
2664       FRAME_POINTER_TYPE (f) = cursor;
2665       [[view window] invalidateCursorRectsForView: view];
2666       /* Redisplay assumes this function also draws the changed frame
2667          cursor, but this function doesn't, so do it explicitly.  */
2668       x_update_cursor (f, 1);
2669     }
2674 /* ==========================================================================
2676     Keyboard handling
2678    ========================================================================== */
2681 static unsigned
2682 ns_convert_key (unsigned code)
2683 /* --------------------------------------------------------------------------
2684     Internal call used by NSView-keyDown.
2685    -------------------------------------------------------------------------- */
2687   const unsigned last_keysym = ARRAYELTS (convert_ns_to_X_keysym);
2688   unsigned keysym;
2689   /* An array would be faster, but less easy to read.  */
2690   for (keysym = 0; keysym < last_keysym; keysym += 2)
2691     if (code == convert_ns_to_X_keysym[keysym])
2692       return 0xFF00 | convert_ns_to_X_keysym[keysym+1];
2693   return 0;
2694 /* if decide to use keyCode and Carbon table, use this line:
2695      return code > 0xff ? 0 : 0xFF00 | ns_keycode_to_xkeysym_table[code]; */
2699 char *
2700 x_get_keysym_name (int keysym)
2701 /* --------------------------------------------------------------------------
2702     Called by keyboard.c.  Not sure if the return val is important, except
2703     that it be unique.
2704    -------------------------------------------------------------------------- */
2706   static char value[16];
2707   NSTRACE ("x_get_keysym_name");
2708   sprintf (value, "%d", keysym);
2709   return value;
2712 #ifdef NS_IMPL_COCOA
2713 static UniChar
2714 ns_get_shifted_character (NSEvent *event)
2715 /* Look up the character corresponding to the key pressed on the
2716    current keyboard layout and the currently configured shift-like
2717    modifiers.  This ignores the control-like modifiers that cause
2718    [event characters] to give us the wrong result.
2720    Although UCKeyTranslate doesn't require the Carbon framework, some
2721    of the surrounding paraphernalia does, so this function makes
2722    Carbon a requirement.  */
2724   static UInt32 dead_key_state;
2726   /* UCKeyTranslate may return up to 255 characters.  If the buffer
2727      isn't large enough then it produces an error.  What kind of
2728      keyboard inputs 255 characters in a single keypress?  */
2729   UniChar buf[255];
2730   UniCharCount max_string_length = 255;
2731   UniCharCount actual_string_length = 0;
2732   OSStatus result;
2734   CFDataRef layout_ref = (CFDataRef) TISGetInputSourceProperty
2735     (TISCopyCurrentKeyboardLayoutInputSource (), kTISPropertyUnicodeKeyLayoutData);
2736   UCKeyboardLayout* layout = (UCKeyboardLayout*) CFDataGetBytePtr (layout_ref);
2738   UInt32 flags = [event modifierFlags];
2739   UInt32 modifiers = (flags & NSEventModifierFlagShift) ? shiftKey : 0;
2741   NSTRACE ("ns_get_shifted_character");
2743   if ((flags & NSRightAlternateKeyMask) == NSRightAlternateKeyMask
2744       && (EQ (ns_right_alternate_modifier, Qnone)
2745           || (EQ (ns_right_alternate_modifier, Qleft)
2746               && EQ (ns_alternate_modifier, Qnone))))
2747     modifiers |= rightOptionKey;
2749   if ((flags & NSLeftAlternateKeyMask) == NSLeftAlternateKeyMask
2750       && EQ (ns_alternate_modifier, Qnone))
2751     modifiers |= optionKey;
2753   if ((flags & NSRightCommandKeyMask) == NSRightCommandKeyMask
2754       && (EQ (ns_right_command_modifier, Qnone)
2755           || (EQ (ns_right_command_modifier, Qleft)
2756               && EQ (ns_command_modifier, Qnone))))
2757     /* Carbon doesn't differentiate between left and right command
2758        keys.  */
2759     modifiers |= cmdKey;
2761   if ((flags & NSLeftCommandKeyMask) == NSLeftCommandKeyMask
2762       && EQ (ns_command_modifier, Qnone))
2763     modifiers |= cmdKey;
2765   result = UCKeyTranslate (layout, [event keyCode], kUCKeyActionDown,
2766                            (modifiers >> 8) & 0xFF, LMGetKbdType (),
2767                            kUCKeyTranslateNoDeadKeysBit, &dead_key_state,
2768                            max_string_length, &actual_string_length, buf);
2770   if (result != 0)
2771     {
2772       NSLog(@"Failed to translate character '%@' with modifiers %x",
2773             [event characters], modifiers);
2774       return 0;
2775     }
2777   /* FIXME: What do we do if more than one code unit is returned?  */
2778   if (actual_string_length > 0)
2779     return buf[0];
2781   return 0;
2783 #endif /* NS_IMPL_COCOA */
2785 /* ==========================================================================
2787     Block drawing operations
2789    ========================================================================== */
2792 static void
2793 ns_redraw_scroll_bars (struct frame *f)
2795   int i;
2796   id view;
2797   NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews];
2798   NSTRACE ("ns_redraw_scroll_bars");
2799   for (i =[subviews count]-1; i >= 0; i--)
2800     {
2801       view = [subviews objectAtIndex: i];
2802       if (![view isKindOfClass: [EmacsScroller class]]) continue;
2803       [view display];
2804     }
2808 void
2809 ns_clear_frame (struct frame *f)
2810 /* --------------------------------------------------------------------------
2811       External (hook): Erase the entire frame
2812    -------------------------------------------------------------------------- */
2814   NSView *view = FRAME_NS_VIEW (f);
2815   NSRect r;
2817   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_clear_frame");
2819  /* comes on initial frame because we have
2820     after-make-frame-functions = select-frame */
2821  if (!FRAME_DEFAULT_FACE (f))
2822    return;
2824   mark_window_cursors_off (XWINDOW (FRAME_ROOT_WINDOW (f)));
2826   r = [view bounds];
2828   block_input ();
2829   ns_focus (f, &r, 1);
2830   [ns_lookup_indexed_color (NS_FACE_BACKGROUND
2831                             (FACE_FROM_ID (f, DEFAULT_FACE_ID)), f) set];
2832   NSRectFill (r);
2833   ns_unfocus (f);
2835   /* as of 2006/11 or so this is now needed */
2836   ns_redraw_scroll_bars (f);
2837   unblock_input ();
2841 static void
2842 ns_clear_frame_area (struct frame *f, int x, int y, int width, int height)
2843 /* --------------------------------------------------------------------------
2844     External (RIF):  Clear section of frame
2845    -------------------------------------------------------------------------- */
2847   NSRect r = NSMakeRect (x, y, width, height);
2848   NSView *view = FRAME_NS_VIEW (f);
2849   struct face *face = FRAME_DEFAULT_FACE (f);
2851   if (!view || !face)
2852     return;
2854   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_clear_frame_area");
2856   r = NSIntersectionRect (r, [view frame]);
2857   ns_focus (f, &r, 1);
2858   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), f) set];
2860   NSRectFill (r);
2862   ns_unfocus (f);
2863   return;
2866 static void
2867 ns_copy_bits (struct frame *f, NSRect src, NSRect dest)
2869   NSTRACE ("ns_copy_bits");
2871   if (FRAME_NS_VIEW (f))
2872     {
2873       hide_bell();              // Ensure the bell image isn't scrolled.
2875       ns_focus (f, &dest, 1);
2876       [FRAME_NS_VIEW (f) scrollRect: src
2877                                  by: NSMakeSize (dest.origin.x - src.origin.x,
2878                                                  dest.origin.y - src.origin.y)];
2879       ns_unfocus (f);
2880     }
2883 static void
2884 ns_scroll_run (struct window *w, struct run *run)
2885 /* --------------------------------------------------------------------------
2886     External (RIF):  Insert or delete n lines at line vpos.
2887    -------------------------------------------------------------------------- */
2889   struct frame *f = XFRAME (w->frame);
2890   int x, y, width, height, from_y, to_y, bottom_y;
2892   NSTRACE ("ns_scroll_run");
2894   /* begin copy from other terms */
2895   /* Get frame-relative bounding box of the text display area of W,
2896      without mode lines.  Include in this box the left and right
2897      fringe of W.  */
2898   window_box (w, ANY_AREA, &x, &y, &width, &height);
2900   from_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->current_y);
2901   to_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->desired_y);
2902   bottom_y = y + height;
2904   if (to_y < from_y)
2905     {
2906       /* Scrolling up.  Make sure we don't copy part of the mode
2907          line at the bottom.  */
2908       if (from_y + run->height > bottom_y)
2909         height = bottom_y - from_y;
2910       else
2911         height = run->height;
2912     }
2913   else
2914     {
2915       /* Scrolling down.  Make sure we don't copy over the mode line.
2916          at the bottom.  */
2917       if (to_y + run->height > bottom_y)
2918         height = bottom_y - to_y;
2919       else
2920         height = run->height;
2921     }
2922   /* end copy from other terms */
2924   if (height == 0)
2925       return;
2927   block_input ();
2929   x_clear_cursor (w);
2931   {
2932     NSRect srcRect = NSMakeRect (x, from_y, width, height);
2933     NSRect dstRect = NSMakeRect (x, to_y, width, height);
2935     ns_copy_bits (f, srcRect , dstRect);
2936   }
2938   unblock_input ();
2942 static void
2943 ns_after_update_window_line (struct window *w, struct glyph_row *desired_row)
2944 /* --------------------------------------------------------------------------
2945     External (RIF): preparatory to fringe update after text was updated
2946    -------------------------------------------------------------------------- */
2948   struct frame *f;
2949   int width, height;
2951   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_after_update_window_line");
2953   /* begin copy from other terms */
2954   eassert (w);
2956   if (!desired_row->mode_line_p && !w->pseudo_window_p)
2957     desired_row->redraw_fringe_bitmaps_p = 1;
2959   /* When a window has disappeared, make sure that no rest of
2960      full-width rows stays visible in the internal border.  */
2961   if (windows_or_buffers_changed
2962       && desired_row->full_width_p
2963       && (f = XFRAME (w->frame),
2964           width = FRAME_INTERNAL_BORDER_WIDTH (f),
2965           width != 0)
2966       && (height = desired_row->visible_height,
2967           height > 0))
2968     {
2969       int y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, desired_row->y));
2971       block_input ();
2972       ns_clear_frame_area (f, 0, y, width, height);
2973       ns_clear_frame_area (f,
2974                            FRAME_PIXEL_WIDTH (f) - width,
2975                            y, width, height);
2976       unblock_input ();
2977     }
2981 static void
2982 ns_shift_glyphs_for_insert (struct frame *f,
2983                            int x, int y, int width, int height,
2984                            int shift_by)
2985 /* --------------------------------------------------------------------------
2986     External (RIF): copy an area horizontally, don't worry about clearing src
2987    -------------------------------------------------------------------------- */
2989   NSRect srcRect = NSMakeRect (x, y, width, height);
2990   NSRect dstRect = NSMakeRect (x+shift_by, y, width, height);
2992   NSTRACE ("ns_shift_glyphs_for_insert");
2994   ns_copy_bits (f, srcRect, dstRect);
2999 /* ==========================================================================
3001     Character encoding and metrics
3003    ========================================================================== */
3006 static void
3007 ns_compute_glyph_string_overhangs (struct glyph_string *s)
3008 /* --------------------------------------------------------------------------
3009      External (RIF); compute left/right overhang of whole string and set in s
3010    -------------------------------------------------------------------------- */
3012   struct font *font = s->font;
3014   if (s->char2b)
3015     {
3016       struct font_metrics metrics;
3017       unsigned int codes[2];
3018       codes[0] = *(s->char2b);
3019       codes[1] = *(s->char2b + s->nchars - 1);
3021       font->driver->text_extents (font, codes, 2, &metrics);
3022       s->left_overhang = -metrics.lbearing;
3023       s->right_overhang
3024         = metrics.rbearing > metrics.width
3025         ? metrics.rbearing - metrics.width : 0;
3026     }
3027   else
3028     {
3029       s->left_overhang = 0;
3030       if (EQ (font->driver->type, Qns))
3031         s->right_overhang = ((struct nsfont_info *)font)->ital ?
3032           FONT_HEIGHT (font) * 0.2 : 0;
3033       else
3034         s->right_overhang = 0;
3035     }
3040 /* ==========================================================================
3042     Fringe and cursor drawing
3044    ========================================================================== */
3047 extern int max_used_fringe_bitmap;
3048 static void
3049 ns_draw_fringe_bitmap (struct window *w, struct glyph_row *row,
3050                       struct draw_fringe_bitmap_params *p)
3051 /* --------------------------------------------------------------------------
3052     External (RIF); fringe-related
3053    -------------------------------------------------------------------------- */
3055   /* Fringe bitmaps comes in two variants, normal and periodic.  A
3056      periodic bitmap is used to create a continuous pattern.  Since a
3057      bitmap is rendered one text line at a time, the start offset (dh)
3058      of the bitmap varies.  Concretely, this is used for the empty
3059      line indicator.
3061      For a bitmap, "h + dh" is the full height and is always
3062      invariant.  For a normal bitmap "dh" is zero.
3064      For example, when the period is three and the full height is 72
3065      the following combinations exists:
3067        h=72 dh=0
3068        h=71 dh=1
3069        h=70 dh=2 */
3071   struct frame *f = XFRAME (WINDOW_FRAME (w));
3072   struct face *face = p->face;
3073   static EmacsImage **bimgs = NULL;
3074   static int nBimgs = 0;
3076   NSTRACE_WHEN (NSTRACE_GROUP_FRINGE, "ns_draw_fringe_bitmap");
3077   NSTRACE_MSG ("which:%d cursor:%d overlay:%d width:%d height:%d period:%d",
3078                p->which, p->cursor_p, p->overlay_p, p->wd, p->h, p->dh);
3080   /* grow bimgs if needed */
3081   if (nBimgs < max_used_fringe_bitmap)
3082     {
3083       bimgs = xrealloc (bimgs, max_used_fringe_bitmap * sizeof *bimgs);
3084       memset (bimgs + nBimgs, 0,
3085               (max_used_fringe_bitmap - nBimgs) * sizeof *bimgs);
3086       nBimgs = max_used_fringe_bitmap;
3087     }
3089   /* Must clip because of partially visible lines.  */
3090   ns_clip_to_row (w, row, ANY_AREA, YES);
3092   if (!p->overlay_p)
3093     {
3094       int bx = p->bx, by = p->by, nx = p->nx, ny = p->ny;
3096       if (bx >= 0 && nx > 0)
3097         {
3098           NSRect r = NSMakeRect (bx, by, nx, ny);
3099           NSRectClip (r);
3100           [ns_lookup_indexed_color (face->background, f) set];
3101           NSRectFill (r);
3102         }
3103     }
3105   if (p->which)
3106     {
3107       NSRect r = NSMakeRect (p->x, p->y, p->wd, p->h);
3108       EmacsImage *img = bimgs[p->which - 1];
3110       if (!img)
3111         {
3112           // Note: For "periodic" images, allocate one EmacsImage for
3113           // the base image, and use it for all dh:s.
3114           unsigned short *bits = p->bits;
3115           int full_height = p->h + p->dh;
3116           int i;
3117           unsigned char *cbits = xmalloc (full_height);
3119           for (i = 0; i < full_height; i++)
3120             cbits[i] = bits[i];
3121           img = [[EmacsImage alloc] initFromXBM: cbits width: 8
3122                                          height: full_height
3123                                              fg: 0 bg: 0];
3124           bimgs[p->which - 1] = img;
3125           xfree (cbits);
3126         }
3128       NSTRACE_RECT ("r", r);
3130       NSRectClip (r);
3131       /* Since we composite the bitmap instead of just blitting it, we need
3132          to erase the whole background.  */
3133       [ns_lookup_indexed_color(face->background, f) set];
3134       NSRectFill (r);
3136       {
3137         NSColor *bm_color;
3138         if (!p->cursor_p)
3139           bm_color = ns_lookup_indexed_color(face->foreground, f);
3140         else if (p->overlay_p)
3141           bm_color = ns_lookup_indexed_color(face->background, f);
3142         else
3143           bm_color = f->output_data.ns->cursor_color;
3144         [img setXBMColor: bm_color];
3145       }
3147 #ifdef NS_IMPL_COCOA
3148       // Note: For periodic images, the full image height is "h + hd".
3149       // By using the height h, a suitable part of the image is used.
3150       NSRect fromRect = NSMakeRect(0, 0, p->wd, p->h);
3152       NSTRACE_RECT ("fromRect", fromRect);
3154       [img drawInRect: r
3155               fromRect: fromRect
3156              operation: NSCompositingOperationSourceOver
3157               fraction: 1.0
3158            respectFlipped: YES
3159                 hints: nil];
3160 #else
3161       {
3162         NSPoint pt = r.origin;
3163         pt.y += p->h;
3164         [img compositeToPoint: pt operation: NSCompositingOperationSourceOver];
3165       }
3166 #endif
3167     }
3168   ns_unfocus (f);
3172 static void
3173 ns_draw_window_cursor (struct window *w, struct glyph_row *glyph_row,
3174                        int x, int y, enum text_cursor_kinds cursor_type,
3175                        int cursor_width, bool on_p, bool active_p)
3176 /* --------------------------------------------------------------------------
3177      External call (RIF): draw cursor.
3178      Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
3179    -------------------------------------------------------------------------- */
3181   NSRect r, s;
3182   int fx, fy, h, cursor_height;
3183   struct frame *f = WINDOW_XFRAME (w);
3184   struct glyph *phys_cursor_glyph;
3185   struct glyph *cursor_glyph;
3186   struct face *face;
3187   NSColor *hollow_color = FRAME_BACKGROUND_COLOR (f);
3189   /* If cursor is out of bounds, don't draw garbage.  This can happen
3190      in mini-buffer windows when switching between echo area glyphs
3191      and mini-buffer.  */
3193   NSTRACE ("ns_draw_window_cursor");
3195   if (!on_p)
3196     return;
3198   w->phys_cursor_type = cursor_type;
3199   w->phys_cursor_on_p = on_p;
3201   if (cursor_type == NO_CURSOR)
3202     {
3203       w->phys_cursor_width = 0;
3204       return;
3205     }
3207   if ((phys_cursor_glyph = get_phys_cursor_glyph (w)) == NULL)
3208     {
3209       if (glyph_row->exact_window_width_line_p
3210           && w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])
3211         {
3212           glyph_row->cursor_in_fringe_p = 1;
3213           draw_fringe_bitmap (w, glyph_row, 0);
3214         }
3215       return;
3216     }
3218   /* We draw the cursor (with NSRectFill), then draw the glyph on top
3219      (other terminals do it the other way round).  We must set
3220      w->phys_cursor_width to the cursor width.  For bar cursors, that
3221      is CURSOR_WIDTH; for box cursors, it is the glyph width.  */
3222   get_phys_cursor_geometry (w, glyph_row, phys_cursor_glyph, &fx, &fy, &h);
3224   /* The above get_phys_cursor_geometry call set w->phys_cursor_width
3225      to the glyph width; replace with CURSOR_WIDTH for (V)BAR cursors.  */
3226   if (cursor_type == BAR_CURSOR)
3227     {
3228       if (cursor_width < 1)
3229         cursor_width = max (FRAME_CURSOR_WIDTH (f), 1);
3231       /* The bar cursor should never be wider than the glyph.  */
3232       if (cursor_width < w->phys_cursor_width)
3233         w->phys_cursor_width = cursor_width;
3234     }
3235   /* If we have an HBAR, "cursor_width" MAY specify height.  */
3236   else if (cursor_type == HBAR_CURSOR)
3237     {
3238       cursor_height = (cursor_width < 1) ? lrint (0.25 * h) : cursor_width;
3239       if (cursor_height > glyph_row->height)
3240         cursor_height = glyph_row->height;
3241       if (h > cursor_height) // Cursor smaller than line height, move down
3242         fy += h - cursor_height;
3243       h = cursor_height;
3244     }
3246   r.origin.x = fx, r.origin.y = fy;
3247   r.size.height = h;
3248   r.size.width = w->phys_cursor_width;
3250   /* Prevent the cursor from being drawn outside the text area.  */
3251   ns_clip_to_row (w, glyph_row, TEXT_AREA, NO); /* do ns_focus(f, &r, 1); if remove */
3254   face = FACE_FROM_ID_OR_NULL (f, phys_cursor_glyph->face_id);
3255   if (face && NS_FACE_BACKGROUND (face)
3256       == ns_index_color (FRAME_CURSOR_COLOR (f), f))
3257     {
3258       [ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), f) set];
3259       hollow_color = FRAME_CURSOR_COLOR (f);
3260     }
3261   else
3262     [FRAME_CURSOR_COLOR (f) set];
3264 #ifdef NS_IMPL_COCOA
3265   /* TODO: This makes drawing of cursor plus that of phys_cursor_glyph
3266            atomic.  Cleaner ways of doing this should be investigated.
3267            One way would be to set a global variable DRAWING_CURSOR
3268            when making the call to draw_phys..(), don't focus in that
3269            case, then move the ns_unfocus() here after that call.  */
3270   NSDisableScreenUpdates ();
3271 #endif
3273   switch (cursor_type)
3274     {
3275     case DEFAULT_CURSOR:
3276     case NO_CURSOR:
3277       break;
3278     case FILLED_BOX_CURSOR:
3279       NSRectFill (r);
3280       break;
3281     case HOLLOW_BOX_CURSOR:
3282       NSRectFill (r);
3283       [hollow_color set];
3284       NSRectFill (NSInsetRect (r, 1, 1));
3285       [FRAME_CURSOR_COLOR (f) set];
3286       break;
3287     case HBAR_CURSOR:
3288       NSRectFill (r);
3289       break;
3290     case BAR_CURSOR:
3291       s = r;
3292       /* If the character under cursor is R2L, draw the bar cursor
3293          on the right of its glyph, rather than on the left.  */
3294       cursor_glyph = get_phys_cursor_glyph (w);
3295       if ((cursor_glyph->resolved_level & 1) != 0)
3296         s.origin.x += cursor_glyph->pixel_width - s.size.width;
3298       NSRectFill (s);
3299       break;
3300     }
3301   ns_unfocus (f);
3303   /* draw the character under the cursor */
3304   if (cursor_type != NO_CURSOR)
3305     draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
3307 #ifdef NS_IMPL_COCOA
3308   NSEnableScreenUpdates ();
3309 #endif
3314 static void
3315 ns_draw_vertical_window_border (struct window *w, int x, int y0, int y1)
3316 /* --------------------------------------------------------------------------
3317      External (RIF): Draw a vertical line.
3318    -------------------------------------------------------------------------- */
3320   struct frame *f = XFRAME (WINDOW_FRAME (w));
3321   struct face *face;
3322   NSRect r = NSMakeRect (x, y0, 1, y1-y0);
3324   NSTRACE ("ns_draw_vertical_window_border");
3326   face = FACE_FROM_ID_OR_NULL (f, VERTICAL_BORDER_FACE_ID);
3328   ns_focus (f, &r, 1);
3329   if (face)
3330     [ns_lookup_indexed_color(face->foreground, f) set];
3332   NSRectFill(r);
3333   ns_unfocus (f);
3337 static void
3338 ns_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1)
3339 /* --------------------------------------------------------------------------
3340      External (RIF): Draw a window divider.
3341    -------------------------------------------------------------------------- */
3343   struct frame *f = XFRAME (WINDOW_FRAME (w));
3344   struct face *face = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FACE_ID);
3345   struct face *face_first
3346     = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FIRST_PIXEL_FACE_ID);
3347   struct face *face_last
3348     = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_LAST_PIXEL_FACE_ID);
3349   unsigned long color = face ? face->foreground : FRAME_FOREGROUND_PIXEL (f);
3350   unsigned long color_first = (face_first
3351                                ? face_first->foreground
3352                                : FRAME_FOREGROUND_PIXEL (f));
3353   unsigned long color_last = (face_last
3354                               ? face_last->foreground
3355                               : FRAME_FOREGROUND_PIXEL (f));
3356   NSRect divider = NSMakeRect (x0, y0, x1-x0, y1-y0);
3358   NSTRACE ("ns_draw_window_divider");
3360   ns_focus (f, &divider, 1);
3362   if ((y1 - y0 > x1 - x0) && (x1 - x0 >= 3))
3363     /* A vertical divider, at least three pixels wide: Draw first and
3364        last pixels differently.  */
3365     {
3366       [ns_lookup_indexed_color(color_first, f) set];
3367       NSRectFill(NSMakeRect (x0, y0, 1, y1 - y0));
3368       [ns_lookup_indexed_color(color, f) set];
3369       NSRectFill(NSMakeRect (x0 + 1, y0, x1 - x0 - 2, y1 - y0));
3370       [ns_lookup_indexed_color(color_last, f) set];
3371       NSRectFill(NSMakeRect (x1 - 1, y0, 1, y1 - y0));
3372     }
3373   else if ((x1 - x0 > y1 - y0) && (y1 - y0 >= 3))
3374     /* A horizontal divider, at least three pixels high: Draw first and
3375        last pixels differently.  */
3376     {
3377       [ns_lookup_indexed_color(color_first, f) set];
3378       NSRectFill(NSMakeRect (x0, y0, x1 - x0, 1));
3379       [ns_lookup_indexed_color(color, f) set];
3380       NSRectFill(NSMakeRect (x0, y0 + 1, x1 - x0, y1 - y0 - 2));
3381       [ns_lookup_indexed_color(color_last, f) set];
3382       NSRectFill(NSMakeRect (x0, y1 - 1, x1 - x0, 1));
3383     }
3384   else
3385     {
3386       /* In any other case do not draw the first and last pixels
3387          differently.  */
3388       [ns_lookup_indexed_color(color, f) set];
3389       NSRectFill(divider);
3390     }
3392   ns_unfocus (f);
3395 static void
3396 ns_show_hourglass (struct frame *f)
3398   /* TODO: add NSProgressIndicator to all frames.  */
3401 static void
3402 ns_hide_hourglass (struct frame *f)
3404   /* TODO: remove NSProgressIndicator from all frames.  */
3407 /* ==========================================================================
3409     Glyph drawing operations
3411    ========================================================================== */
3413 static int
3414 ns_get_glyph_string_clip_rect (struct glyph_string *s, NativeRectangle *nr)
3415 /* --------------------------------------------------------------------------
3416     Wrapper utility to account for internal border width on full-width lines,
3417     and allow top full-width rows to hit the frame top.  nr should be pointer
3418     to two successive NSRects.  Number of rects actually used is returned.
3419    -------------------------------------------------------------------------- */
3421   int n = get_glyph_string_clip_rects (s, nr, 2);
3422   return n;
3425 /* --------------------------------------------------------------------
3426    Draw a wavy line under glyph string s. The wave fills wave_height
3427    pixels from y.
3429                     x          wave_length = 2
3430                                  --
3431                 y    *   *   *   *   *
3432                      |* * * * * * * * *
3433     wave_height = 3  | *   *   *   *
3434   --------------------------------------------------------------------- */
3436 static void
3437 ns_draw_underwave (struct glyph_string *s, EmacsCGFloat width, EmacsCGFloat x)
3439   int wave_height = 3, wave_length = 2;
3440   int y, dx, dy, odd, xmax;
3441   NSPoint a, b;
3442   NSRect waveClip;
3444   dx = wave_length;
3445   dy = wave_height - 1;
3446   y =  s->ybase - wave_height + 3;
3447   xmax = x + width;
3449   /* Find and set clipping rectangle */
3450   waveClip = NSMakeRect (x, y, width, wave_height);
3451   [[NSGraphicsContext currentContext] saveGraphicsState];
3452   NSRectClip (waveClip);
3454   /* Draw the waves */
3455   a.x = x - ((int)(x) % dx) + (EmacsCGFloat) 0.5;
3456   b.x = a.x + dx;
3457   odd = (int)(a.x/dx) % 2;
3458   a.y = b.y = y + 0.5;
3460   if (odd)
3461     a.y += dy;
3462   else
3463     b.y += dy;
3465   while (a.x <= xmax)
3466     {
3467       [NSBezierPath strokeLineFromPoint:a toPoint:b];
3468       a.x = b.x, a.y = b.y;
3469       b.x += dx, b.y = y + 0.5 + odd*dy;
3470       odd = !odd;
3471     }
3473   /* Restore previous clipping rectangle(s) */
3474   [[NSGraphicsContext currentContext] restoreGraphicsState];
3479 static void
3480 ns_draw_text_decoration (struct glyph_string *s, struct face *face,
3481                          NSColor *defaultCol, CGFloat width, CGFloat x)
3482 /* --------------------------------------------------------------------------
3483    Draw underline, overline, and strike-through on glyph string s.
3484    -------------------------------------------------------------------------- */
3486   if (s->for_overlaps)
3487     return;
3489   /* Do underline.  */
3490   if (face->underline_p)
3491     {
3492       if (s->face->underline_type == FACE_UNDER_WAVE)
3493         {
3494           if (face->underline_defaulted_p)
3495             [defaultCol set];
3496           else
3497             [ns_lookup_indexed_color (face->underline_color, s->f) set];
3499           ns_draw_underwave (s, width, x);
3500         }
3501       else if (s->face->underline_type == FACE_UNDER_LINE)
3502         {
3504           NSRect r;
3505           unsigned long thickness, position;
3507           /* If the prev was underlined, match its appearance.  */
3508           if (s->prev && s->prev->face->underline_p
3509               && s->prev->face->underline_type == FACE_UNDER_LINE
3510               && s->prev->underline_thickness > 0)
3511             {
3512               thickness = s->prev->underline_thickness;
3513               position = s->prev->underline_position;
3514             }
3515           else
3516             {
3517               struct font *font = font_for_underline_metrics (s);
3518               unsigned long descent = s->y + s->height - s->ybase;
3519               unsigned long minimum_offset;
3520               BOOL underline_at_descent_line, use_underline_position_properties;
3521               Lisp_Object val = buffer_local_value (Qunderline_minimum_offset,
3522                                                     s->w->contents);
3523               if (INTEGERP (val))
3524                 minimum_offset = XFASTINT (val);
3525               else
3526                 minimum_offset = 1;
3527               val = buffer_local_value (Qx_underline_at_descent_line,
3528                                         s->w->contents);
3529               underline_at_descent_line = !(NILP (val) || EQ (val, Qunbound));
3530               val = buffer_local_value (Qx_use_underline_position_properties,
3531                                         s->w->contents);
3532               use_underline_position_properties =
3533                 !(NILP (val) || EQ (val, Qunbound));
3535               /* Use underline thickness of font, defaulting to 1.  */
3536               thickness = (font && font->underline_thickness > 0)
3537                 ? font->underline_thickness : 1;
3539               /* Determine the offset of underlining from the baseline.  */
3540               if (underline_at_descent_line)
3541                 position = descent - thickness;
3542               else if (use_underline_position_properties
3543                        && font && font->underline_position >= 0)
3544                 position = font->underline_position;
3545               else if (font)
3546                 position = lround (font->descent / 2);
3547               else
3548                 position = minimum_offset;
3550               position = max (position, minimum_offset);
3552               /* Ensure underlining is not cropped.  */
3553               if (descent <= position)
3554                 {
3555                   position = descent - 1;
3556                   thickness = 1;
3557                 }
3558               else if (descent < position + thickness)
3559                 thickness = 1;
3560             }
3562           s->underline_thickness = thickness;
3563           s->underline_position = position;
3565           r = NSMakeRect (x, s->ybase + position, width, thickness);
3567           if (face->underline_defaulted_p)
3568             [defaultCol set];
3569           else
3570             [ns_lookup_indexed_color (face->underline_color, s->f) set];
3571           NSRectFill (r);
3572         }
3573     }
3574   /* Do overline. We follow other terms in using a thickness of 1
3575      and ignoring overline_margin.  */
3576   if (face->overline_p)
3577     {
3578       NSRect r;
3579       r = NSMakeRect (x, s->y, width, 1);
3581       if (face->overline_color_defaulted_p)
3582         [defaultCol set];
3583       else
3584         [ns_lookup_indexed_color (face->overline_color, s->f) set];
3585       NSRectFill (r);
3586     }
3588   /* Do strike-through.  We follow other terms for thickness and
3589      vertical position.  */
3590   if (face->strike_through_p)
3591     {
3592       NSRect r;
3593       /* Y-coordinate and height of the glyph string's first glyph.
3594          We cannot use s->y and s->height because those could be
3595          larger if there are taller display elements (e.g., characters
3596          displayed with a larger font) in the same glyph row.  */
3597       int glyph_y = s->ybase - s->first_glyph->ascent;
3598       int glyph_height = s->first_glyph->ascent + s->first_glyph->descent;
3599       /* Strike-through width and offset from the glyph string's
3600          top edge.  */
3601       unsigned long h = 1;
3602       unsigned long dy;
3604       dy = lrint ((glyph_height - h) / 2);
3605       r = NSMakeRect (x, glyph_y + dy, width, 1);
3607       if (face->strike_through_color_defaulted_p)
3608         [defaultCol set];
3609       else
3610         [ns_lookup_indexed_color (face->strike_through_color, s->f) set];
3611       NSRectFill (r);
3612     }
3615 static void
3616 ns_draw_box (NSRect r, CGFloat thickness, NSColor *col,
3617              char left_p, char right_p)
3618 /* --------------------------------------------------------------------------
3619     Draw an unfilled rect inside r, optionally leaving left and/or right open.
3620     Note we can't just use an NSDrawRect command, because of the possibility
3621     of some sides not being drawn, and because the rect will be filled.
3622    -------------------------------------------------------------------------- */
3624   NSRect s = r;
3625   [col set];
3627   /* top, bottom */
3628   s.size.height = thickness;
3629   NSRectFill (s);
3630   s.origin.y += r.size.height - thickness;
3631   NSRectFill (s);
3633   s.size.height = r.size.height;
3634   s.origin.y = r.origin.y;
3636   /* left, right (optional) */
3637   s.size.width = thickness;
3638   if (left_p)
3639     NSRectFill (s);
3640   if (right_p)
3641     {
3642       s.origin.x += r.size.width - thickness;
3643       NSRectFill (s);
3644     }
3648 static void
3649 ns_draw_relief (NSRect r, int thickness, char raised_p,
3650                char top_p, char bottom_p, char left_p, char right_p,
3651                struct glyph_string *s)
3652 /* --------------------------------------------------------------------------
3653     Draw a relief rect inside r, optionally leaving some sides open.
3654     Note we can't just use an NSDrawBezel command, because of the possibility
3655     of some sides not being drawn, and because the rect will be filled.
3656    -------------------------------------------------------------------------- */
3658   static NSColor *baseCol = nil, *lightCol = nil, *darkCol = nil;
3659   NSColor *newBaseCol = nil;
3660   NSRect sr = r;
3662   NSTRACE ("ns_draw_relief");
3664   /* set up colors */
3666   if (s->face->use_box_color_for_shadows_p)
3667     {
3668       newBaseCol = ns_lookup_indexed_color (s->face->box_color, s->f);
3669     }
3670 /*     else if (s->first_glyph->type == IMAGE_GLYPH
3671            && s->img->pixmap
3672            && !IMAGE_BACKGROUND_TRANSPARENT (s->img, s->f, 0))
3673        {
3674          newBaseCol = IMAGE_BACKGROUND  (s->img, s->f, 0);
3675        } */
3676   else
3677     {
3678       newBaseCol = ns_lookup_indexed_color (s->face->background, s->f);
3679     }
3681   if (newBaseCol == nil)
3682     newBaseCol = [NSColor grayColor];
3684   if (newBaseCol != baseCol)  /* TODO: better check */
3685     {
3686       [baseCol release];
3687       baseCol = [newBaseCol retain];
3688       [lightCol release];
3689       lightCol = [[baseCol highlightWithLevel: 0.2] retain];
3690       [darkCol release];
3691       darkCol = [[baseCol shadowWithLevel: 0.3] retain];
3692     }
3694   [(raised_p ? lightCol : darkCol) set];
3696   /* TODO: mitering. Using NSBezierPath doesn't work because of color switch.  */
3698   /* top */
3699   sr.size.height = thickness;
3700   if (top_p) NSRectFill (sr);
3702   /* left */
3703   sr.size.height = r.size.height;
3704   sr.size.width = thickness;
3705   if (left_p) NSRectFill (sr);
3707   [(raised_p ? darkCol : lightCol) set];
3709   /* bottom */
3710   sr.size.width = r.size.width;
3711   sr.size.height = thickness;
3712   sr.origin.y += r.size.height - thickness;
3713   if (bottom_p) NSRectFill (sr);
3715   /* right */
3716   sr.size.height = r.size.height;
3717   sr.origin.y = r.origin.y;
3718   sr.size.width = thickness;
3719   sr.origin.x += r.size.width - thickness;
3720   if (right_p) NSRectFill (sr);
3724 static void
3725 ns_dumpglyphs_box_or_relief (struct glyph_string *s)
3726 /* --------------------------------------------------------------------------
3727       Function modeled after x_draw_glyph_string_box ().
3728       Sets up parameters for drawing.
3729    -------------------------------------------------------------------------- */
3731   int right_x, last_x;
3732   char left_p, right_p;
3733   struct glyph *last_glyph;
3734   NSRect r;
3735   int thickness;
3736   struct face *face;
3738   if (s->hl == DRAW_MOUSE_FACE)
3739     {
3740       face = FACE_FROM_ID_OR_NULL (s->f,
3741                                    MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3742       if (!face)
3743         face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3744     }
3745   else
3746     face = s->face;
3748   thickness = face->box_line_width;
3750   NSTRACE ("ns_dumpglyphs_box_or_relief");
3752   last_x = ((s->row->full_width_p && !s->w->pseudo_window_p)
3753             ? WINDOW_RIGHT_EDGE_X (s->w)
3754             : window_box_right (s->w, s->area));
3755   last_glyph = (s->cmp || s->img
3756                 ? s->first_glyph : s->first_glyph + s->nchars-1);
3758   right_x = ((s->row->full_width_p && s->extends_to_end_of_line_p
3759               ? last_x - 1 : min (last_x, s->x + s->background_width) - 1));
3761   left_p = (s->first_glyph->left_box_line_p
3762             || (s->hl == DRAW_MOUSE_FACE
3763                 && (s->prev == NULL || s->prev->hl != s->hl)));
3764   right_p = (last_glyph->right_box_line_p
3765              || (s->hl == DRAW_MOUSE_FACE
3766                  && (s->next == NULL || s->next->hl != s->hl)));
3768   r = NSMakeRect (s->x, s->y, right_x - s->x + 1, s->height);
3770   /* TODO: Sometimes box_color is 0 and this seems wrong; should investigate.  */
3771   if (s->face->box == FACE_SIMPLE_BOX && s->face->box_color)
3772     {
3773       ns_draw_box (r, abs (thickness),
3774                    ns_lookup_indexed_color (face->box_color, s->f),
3775                   left_p, right_p);
3776     }
3777   else
3778     {
3779       ns_draw_relief (r, abs (thickness), s->face->box == FACE_RAISED_BOX,
3780                      1, 1, left_p, right_p, s);
3781     }
3785 static void
3786 ns_maybe_dumpglyphs_background (struct glyph_string *s, char force_p)
3787 /* --------------------------------------------------------------------------
3788       Modeled after x_draw_glyph_string_background, which draws BG in
3789       certain cases.  Others are left to the text rendering routine.
3790    -------------------------------------------------------------------------- */
3792   NSTRACE ("ns_maybe_dumpglyphs_background");
3794   if (!s->background_filled_p/* || s->hl == DRAW_MOUSE_FACE*/)
3795     {
3796       int box_line_width = max (s->face->box_line_width, 0);
3797       if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width
3798           /* When xdisp.c ignores FONT_HEIGHT, we cannot trust font
3799              dimensions, since the actual glyphs might be much
3800              smaller.  So in that case we always clear the rectangle
3801              with background color.  */
3802           || FONT_TOO_HIGH (s->font)
3803           || s->font_not_found_p || s->extends_to_end_of_line_p || force_p)
3804         {
3805           struct face *face;
3806           if (s->hl == DRAW_MOUSE_FACE)
3807             {
3808               face
3809                 = FACE_FROM_ID_OR_NULL (s->f,
3810                                         MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3811               if (!face)
3812                 face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3813             }
3814           else
3815             face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3816           if (!face->stipple)
3817             [(NS_FACE_BACKGROUND (face) != 0
3818               ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
3819               : FRAME_BACKGROUND_COLOR (s->f)) set];
3820           else
3821             {
3822               struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
3823               [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set];
3824             }
3826           if (s->hl != DRAW_CURSOR)
3827             {
3828               NSRect r = NSMakeRect (s->x, s->y + box_line_width,
3829                                     s->background_width,
3830                                     s->height-2*box_line_width);
3831               NSRectFill (r);
3832             }
3834           s->background_filled_p = 1;
3835         }
3836     }
3840 static void
3841 ns_dumpglyphs_image (struct glyph_string *s, NSRect r)
3842 /* --------------------------------------------------------------------------
3843       Renders an image and associated borders.
3844    -------------------------------------------------------------------------- */
3846   EmacsImage *img = s->img->pixmap;
3847   int box_line_vwidth = max (s->face->box_line_width, 0);
3848   int x = s->x, y = s->ybase - image_ascent (s->img, s->face, &s->slice);
3849   int bg_x, bg_y, bg_height;
3850   int th;
3851   char raised_p;
3852   NSRect br;
3853   struct face *face;
3854   NSColor *tdCol;
3856   NSTRACE ("ns_dumpglyphs_image");
3858   if (s->face->box != FACE_NO_BOX
3859       && s->first_glyph->left_box_line_p && s->slice.x == 0)
3860     x += abs (s->face->box_line_width);
3862   bg_x = x;
3863   bg_y =  s->slice.y == 0 ? s->y : s->y + box_line_vwidth;
3864   bg_height = s->height;
3865   /* other terms have this, but was causing problems w/tabbar mode */
3866   /* - 2 * box_line_vwidth; */
3868   if (s->slice.x == 0) x += s->img->hmargin;
3869   if (s->slice.y == 0) y += s->img->vmargin;
3871   /* Draw BG: if we need larger area than image itself cleared, do that,
3872      otherwise, since we composite the image under NS (instead of mucking
3873      with its background color), we must clear just the image area.  */
3874   if (s->hl == DRAW_MOUSE_FACE)
3875     {
3876       face = FACE_FROM_ID_OR_NULL (s->f,
3877                                    MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3878       if (!face)
3879        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3880     }
3881   else
3882     face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3884   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f) set];
3886   if (bg_height > s->slice.height || s->img->hmargin || s->img->vmargin
3887       || s->img->mask || s->img->pixmap == 0 || s->width != s->background_width)
3888     {
3889       br = NSMakeRect (bg_x, bg_y, s->background_width, bg_height);
3890       s->background_filled_p = 1;
3891     }
3892   else
3893     {
3894       br = NSMakeRect (x, y, s->slice.width, s->slice.height);
3895     }
3897   NSRectFill (br);
3899   /* Draw the image... do we need to draw placeholder if img == nil?  */
3900   if (img != nil)
3901     {
3902 #ifdef NS_IMPL_COCOA
3903       NSRect dr = NSMakeRect (x, y, s->slice.width, s->slice.height);
3904       NSRect ir = NSMakeRect (s->slice.x,
3905                               s->img->height - s->slice.y - s->slice.height,
3906                               s->slice.width, s->slice.height);
3907       [img drawInRect: dr
3908              fromRect: ir
3909              operation: NSCompositingOperationSourceOver
3910               fraction: 1.0
3911            respectFlipped: YES
3912                 hints: nil];
3913 #else
3914       [img compositeToPoint: NSMakePoint (x, y + s->slice.height)
3915                   operation: NSCompositingOperationSourceOver];
3916 #endif
3917     }
3919   if (s->hl == DRAW_CURSOR)
3920     {
3921     [FRAME_CURSOR_COLOR (s->f) set];
3922     if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
3923       tdCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
3924     else
3925       /* Currently on NS img->mask is always 0.  Since
3926          get_window_cursor_type specifies a hollow box cursor when on
3927          a non-masked image we never reach this clause.  But we put it
3928          in, in anticipation of better support for image masks on
3929          NS.  */
3930       tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3931     }
3932   else
3933     {
3934       tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3935     }
3937   /* Draw underline, overline, strike-through.  */
3938   ns_draw_text_decoration (s, face, tdCol, br.size.width, br.origin.x);
3940   /* Draw relief, if requested */
3941   if (s->img->relief || s->hl ==DRAW_IMAGE_RAISED || s->hl ==DRAW_IMAGE_SUNKEN)
3942     {
3943       if (s->hl == DRAW_IMAGE_SUNKEN || s->hl == DRAW_IMAGE_RAISED)
3944         {
3945           th = tool_bar_button_relief >= 0 ?
3946             tool_bar_button_relief : DEFAULT_TOOL_BAR_BUTTON_RELIEF;
3947           raised_p = (s->hl == DRAW_IMAGE_RAISED);
3948         }
3949       else
3950         {
3951           th = abs (s->img->relief);
3952           raised_p = (s->img->relief > 0);
3953         }
3955       r.origin.x = x - th;
3956       r.origin.y = y - th;
3957       r.size.width = s->slice.width + 2*th-1;
3958       r.size.height = s->slice.height + 2*th-1;
3959       ns_draw_relief (r, th, raised_p,
3960                       s->slice.y == 0,
3961                       s->slice.y + s->slice.height == s->img->height,
3962                       s->slice.x == 0,
3963                       s->slice.x + s->slice.width == s->img->width, s);
3964     }
3966   /* If there is no mask, the background won't be seen,
3967      so draw a rectangle on the image for the cursor.
3968      Do this for all images, getting transparency right is not reliable.  */
3969   if (s->hl == DRAW_CURSOR)
3970     {
3971       int thickness = abs (s->img->relief);
3972       if (thickness == 0) thickness = 1;
3973       ns_draw_box (br, thickness, FRAME_CURSOR_COLOR (s->f), 1, 1);
3974     }
3978 static void
3979 ns_dumpglyphs_stretch (struct glyph_string *s)
3981   NSRect r[2];
3982   int n, i;
3983   struct face *face;
3984   NSColor *fgCol, *bgCol;
3986   if (!s->background_filled_p)
3987     {
3988       n = ns_get_glyph_string_clip_rect (s, r);
3989       *r = NSMakeRect (s->x, s->y, s->background_width, s->height);
3991       ns_focus (s->f, r, n);
3993       if (s->hl == DRAW_MOUSE_FACE)
3994        {
3995          face = FACE_FROM_ID_OR_NULL (s->f,
3996                                       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3997          if (!face)
3998            face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3999        }
4000       else
4001        face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
4003       bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
4004       fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
4006       for (i = 0; i < n; ++i)
4007         {
4008           if (!s->row->full_width_p)
4009             {
4010               int overrun, leftoverrun;
4012               /* truncate to avoid overwriting fringe and/or scrollbar */
4013               overrun = max (0, (s->x + s->background_width)
4014                              - (WINDOW_BOX_RIGHT_EDGE_X (s->w)
4015                                 - WINDOW_RIGHT_FRINGE_WIDTH (s->w)));
4016               r[i].size.width -= overrun;
4018               /* truncate to avoid overwriting to left of the window box */
4019               leftoverrun = (WINDOW_BOX_LEFT_EDGE_X (s->w)
4020                              + WINDOW_LEFT_FRINGE_WIDTH (s->w)) - s->x;
4022               if (leftoverrun > 0)
4023                 {
4024                   r[i].origin.x += leftoverrun;
4025                   r[i].size.width -= leftoverrun;
4026                 }
4028               /* XXX: Try to work between problem where a stretch glyph on
4029                  a partially-visible bottom row will clear part of the
4030                  modeline, and another where list-buffers headers and similar
4031                  rows erroneously have visible_height set to 0.  Not sure
4032                  where this is coming from as other terms seem not to show.  */
4033               r[i].size.height = min (s->height, s->row->visible_height);
4034             }
4036           [bgCol set];
4038           /* NOTE: under NS this is NOT used to draw cursors, but we must avoid
4039              overwriting cursor (usually when cursor on a tab).  */
4040           if (s->hl == DRAW_CURSOR)
4041             {
4042               CGFloat x, width;
4044               x = r[i].origin.x;
4045               width = s->w->phys_cursor_width;
4046               r[i].size.width -= width;
4047               r[i].origin.x += width;
4049               NSRectFill (r[i]);
4051               /* Draw overlining, etc. on the cursor.  */
4052               if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
4053                 ns_draw_text_decoration (s, face, bgCol, width, x);
4054               else
4055                 ns_draw_text_decoration (s, face, fgCol, width, x);
4056             }
4057           else
4058             {
4059               NSRectFill (r[i]);
4060             }
4062           /* Draw overlining, etc. on the stretch glyph (or the part
4063              of the stretch glyph after the cursor).  */
4064           ns_draw_text_decoration (s, face, fgCol, r[i].size.width,
4065                                    r[i].origin.x);
4066         }
4067       ns_unfocus (s->f);
4068       s->background_filled_p = 1;
4069     }
4073 static void
4074 ns_draw_glyph_string_foreground (struct glyph_string *s)
4076   int x, flags;
4077   struct font *font = s->font;
4079   /* If first glyph of S has a left box line, start drawing the text
4080      of S to the right of that box line.  */
4081   if (s->face && s->face->box != FACE_NO_BOX
4082       && s->first_glyph->left_box_line_p)
4083     x = s->x + eabs (s->face->box_line_width);
4084   else
4085     x = s->x;
4087   flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
4088     (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
4089      (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
4090       NS_DUMPGLYPH_NORMAL));
4092   font->driver->draw
4093     (s, s->cmp_from, s->nchars, x, s->ybase,
4094      (flags == NS_DUMPGLYPH_NORMAL && !s->background_filled_p)
4095      || flags == NS_DUMPGLYPH_MOUSEFACE);
4099 static void
4100 ns_draw_composite_glyph_string_foreground (struct glyph_string *s)
4102   int i, j, x;
4103   struct font *font = s->font;
4105   /* If first glyph of S has a left box line, start drawing the text
4106      of S to the right of that box line.  */
4107   if (s->face && s->face->box != FACE_NO_BOX
4108       && s->first_glyph->left_box_line_p)
4109     x = s->x + eabs (s->face->box_line_width);
4110   else
4111     x = s->x;
4113   /* S is a glyph string for a composition.  S->cmp_from is the index
4114      of the first character drawn for glyphs of this composition.
4115      S->cmp_from == 0 means we are drawing the very first character of
4116      this composition.  */
4118   /* Draw a rectangle for the composition if the font for the very
4119      first character of the composition could not be loaded.  */
4120   if (s->font_not_found_p)
4121     {
4122       if (s->cmp_from == 0)
4123         {
4124           NSRect r = NSMakeRect (s->x, s->y, s->width-1, s->height -1);
4125           ns_draw_box (r, 1, FRAME_CURSOR_COLOR (s->f), 1, 1);
4126         }
4127     }
4128   else if (! s->first_glyph->u.cmp.automatic)
4129     {
4130       int y = s->ybase;
4132       for (i = 0, j = s->cmp_from; i < s->nchars; i++, j++)
4133         /* TAB in a composition means display glyphs with padding
4134            space on the left or right.  */
4135         if (COMPOSITION_GLYPH (s->cmp, j) != '\t')
4136           {
4137             int xx = x + s->cmp->offsets[j * 2];
4138             int yy = y - s->cmp->offsets[j * 2 + 1];
4140             font->driver->draw (s, j, j + 1, xx, yy, false);
4141             if (s->face->overstrike)
4142               font->driver->draw (s, j, j + 1, xx + 1, yy, false);
4143           }
4144     }
4145   else
4146     {
4147       Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
4148       Lisp_Object glyph;
4149       int y = s->ybase;
4150       int width = 0;
4152       for (i = j = s->cmp_from; i < s->cmp_to; i++)
4153         {
4154           glyph = LGSTRING_GLYPH (gstring, i);
4155           if (NILP (LGLYPH_ADJUSTMENT (glyph)))
4156             width += LGLYPH_WIDTH (glyph);
4157           else
4158             {
4159               int xoff, yoff, wadjust;
4161               if (j < i)
4162                 {
4163                   font->driver->draw (s, j, i, x, y, false);
4164                   if (s->face->overstrike)
4165                     font->driver->draw (s, j, i, x + 1, y, false);
4166                   x += width;
4167                 }
4168               xoff = LGLYPH_XOFF (glyph);
4169               yoff = LGLYPH_YOFF (glyph);
4170               wadjust = LGLYPH_WADJUST (glyph);
4171               font->driver->draw (s, i, i + 1, x + xoff, y + yoff, false);
4172               if (s->face->overstrike)
4173                 font->driver->draw (s, i, i + 1, x + xoff + 1, y + yoff,
4174                                     false);
4175               x += wadjust;
4176               j = i + 1;
4177               width = 0;
4178             }
4179         }
4180       if (j < i)
4181         {
4182           font->driver->draw (s, j, i, x, y, false);
4183           if (s->face->overstrike)
4184             font->driver->draw (s, j, i, x + 1, y, false);
4185         }
4186     }
4189 static void
4190 ns_draw_glyph_string (struct glyph_string *s)
4191 /* --------------------------------------------------------------------------
4192       External (RIF): Main draw-text call.
4193    -------------------------------------------------------------------------- */
4195   /* TODO (optimize): focus for box and contents draw */
4196   NSRect r[2];
4197   int n;
4198   char box_drawn_p = 0;
4199   struct font *font = s->face->font;
4200   if (! font) font = FRAME_FONT (s->f);
4202   NSTRACE_WHEN (NSTRACE_GROUP_GLYPHS, "ns_draw_glyph_string");
4204   if (s->next && s->right_overhang && !s->for_overlaps/*&&s->hl!=DRAW_CURSOR*/)
4205     {
4206       int width;
4207       struct glyph_string *next;
4209       for (width = 0, next = s->next;
4210            next && width < s->right_overhang;
4211            width += next->width, next = next->next)
4212         if (next->first_glyph->type != IMAGE_GLYPH)
4213           {
4214             if (next->first_glyph->type != STRETCH_GLYPH)
4215               {
4216                 n = ns_get_glyph_string_clip_rect (s->next, r);
4217                 ns_focus (s->f, r, n);
4218                 ns_maybe_dumpglyphs_background (s->next, 1);
4219                 ns_unfocus (s->f);
4220               }
4221             else
4222               {
4223                 ns_dumpglyphs_stretch (s->next);
4224               }
4225             next->num_clips = 0;
4226           }
4227     }
4229   if (!s->for_overlaps && s->face->box != FACE_NO_BOX
4230         && (s->first_glyph->type == CHAR_GLYPH
4231             || s->first_glyph->type == COMPOSITE_GLYPH))
4232     {
4233       n = ns_get_glyph_string_clip_rect (s, r);
4234       ns_focus (s->f, r, n);
4235       ns_maybe_dumpglyphs_background (s, 1);
4236       ns_dumpglyphs_box_or_relief (s);
4237       ns_unfocus (s->f);
4238       box_drawn_p = 1;
4239     }
4241   switch (s->first_glyph->type)
4242     {
4244     case IMAGE_GLYPH:
4245       n = ns_get_glyph_string_clip_rect (s, r);
4246       ns_focus (s->f, r, n);
4247       ns_dumpglyphs_image (s, r[0]);
4248       ns_unfocus (s->f);
4249       break;
4251     case STRETCH_GLYPH:
4252       ns_dumpglyphs_stretch (s);
4253       break;
4255     case CHAR_GLYPH:
4256     case COMPOSITE_GLYPH:
4257       n = ns_get_glyph_string_clip_rect (s, r);
4258       ns_focus (s->f, r, n);
4260       if (s->for_overlaps || (s->cmp_from > 0
4261                               && ! s->first_glyph->u.cmp.automatic))
4262         s->background_filled_p = 1;
4263       else
4264         ns_maybe_dumpglyphs_background
4265           (s, s->first_glyph->type == COMPOSITE_GLYPH);
4267       if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
4268         {
4269           unsigned long tmp = NS_FACE_BACKGROUND (s->face);
4270           NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
4271           NS_FACE_FOREGROUND (s->face) = tmp;
4272         }
4274       {
4275         BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
4277         if (isComposite)
4278           ns_draw_composite_glyph_string_foreground (s);
4279         else
4280           ns_draw_glyph_string_foreground (s);
4281       }
4283       {
4284         NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
4285                         ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face),
4286                                                    s->f)
4287                         : FRAME_FOREGROUND_COLOR (s->f));
4288         [col set];
4290         /* Draw underline, overline, strike-through.  */
4291         ns_draw_text_decoration (s, s->face, col, s->width, s->x);
4292       }
4294       if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
4295         {
4296           unsigned long tmp = NS_FACE_BACKGROUND (s->face);
4297           NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
4298           NS_FACE_FOREGROUND (s->face) = tmp;
4299         }
4301       ns_unfocus (s->f);
4302       break;
4304     case GLYPHLESS_GLYPH:
4305       n = ns_get_glyph_string_clip_rect (s, r);
4306       ns_focus (s->f, r, n);
4308       if (s->for_overlaps || (s->cmp_from > 0
4309                               && ! s->first_glyph->u.cmp.automatic))
4310         s->background_filled_p = 1;
4311       else
4312         ns_maybe_dumpglyphs_background
4313           (s, s->first_glyph->type == COMPOSITE_GLYPH);
4314       /* ... */
4315       /* Not yet implemented.  */
4316       /* ... */
4317       ns_unfocus (s->f);
4318       break;
4320     default:
4321       emacs_abort ();
4322     }
4324   /* Draw box if not done already.  */
4325   if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX)
4326     {
4327       n = ns_get_glyph_string_clip_rect (s, r);
4328       ns_focus (s->f, r, n);
4329       ns_dumpglyphs_box_or_relief (s);
4330       ns_unfocus (s->f);
4331     }
4333   s->num_clips = 0;
4338 /* ==========================================================================
4340     Event loop
4342    ========================================================================== */
4345 static void
4346 ns_send_appdefined (int value)
4347 /* --------------------------------------------------------------------------
4348     Internal: post an appdefined event which EmacsApp-sendEvent will
4349               recognize and take as a command to halt the event loop.
4350    -------------------------------------------------------------------------- */
4352   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_send_appdefined(%d)", value);
4354   // GNUstep needs postEvent to happen on the main thread.
4355   // Cocoa needs nextEventMatchingMask to happen on the main thread too.
4356   if (! [[NSThread currentThread] isMainThread])
4357     {
4358       EmacsApp *app = (EmacsApp *)NSApp;
4359       app->nextappdefined = value;
4360       [app performSelectorOnMainThread:@selector (sendFromMainThread:)
4361                             withObject:nil
4362                          waitUntilDone:NO];
4363       return;
4364     }
4366   /* Only post this event if we haven't already posted one.  This will end
4367      the [NXApp run] main loop after having processed all events queued at
4368      this moment.  */
4370 #ifdef NS_IMPL_COCOA
4371   if (! send_appdefined)
4372     {
4373       /* OS X 10.10.1 swallows the AppDefined event we are sending ourselves
4374          in certain situations (rapid incoming events).
4375          So check if we have one, if not add one.  */
4376       NSEvent *appev = [NSApp nextEventMatchingMask:NSEventMaskApplicationDefined
4377                                           untilDate:[NSDate distantPast]
4378                                              inMode:NSDefaultRunLoopMode
4379                                             dequeue:NO];
4380       if (! appev) send_appdefined = YES;
4381     }
4382 #endif
4384   if (send_appdefined)
4385     {
4386       NSEvent *nxev;
4388       /* We only need one NX_APPDEFINED event to stop NXApp from running.  */
4389       send_appdefined = NO;
4391       /* Don't need wakeup timer any more.  */
4392       if (timed_entry)
4393         {
4394           [timed_entry invalidate];
4395           [timed_entry release];
4396           timed_entry = nil;
4397         }
4399       nxev = [NSEvent otherEventWithType: NSEventTypeApplicationDefined
4400                                 location: NSMakePoint (0, 0)
4401                            modifierFlags: 0
4402                                timestamp: 0
4403                             windowNumber: [[NSApp mainWindow] windowNumber]
4404                                  context: [NSApp context]
4405                                  subtype: 0
4406                                    data1: value
4407                                    data2: 0];
4409       /* Post an application defined event on the event queue.  When this is
4410          received the [NXApp run] will return, thus having processed all
4411          events which are currently queued.  */
4412       [NSApp postEvent: nxev atStart: NO];
4413     }
4416 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4417 static void
4418 check_native_fs ()
4420   Lisp_Object frame, tail;
4422   if (ns_last_use_native_fullscreen == ns_use_native_fullscreen)
4423     return;
4425   ns_last_use_native_fullscreen = ns_use_native_fullscreen;
4427   FOR_EACH_FRAME (tail, frame)
4428     {
4429       struct frame *f = XFRAME (frame);
4430       if (FRAME_NS_P (f))
4431         {
4432           EmacsView *view = FRAME_NS_VIEW (f);
4433           [view updateCollectionBehavior];
4434         }
4435     }
4437 #endif
4439 /* GNUstep does not have cancelTracking.  */
4440 #ifdef NS_IMPL_COCOA
4441 /* Check if menu open should be canceled or continued as normal.  */
4442 void
4443 ns_check_menu_open (NSMenu *menu)
4445   /* Click in menu bar?  */
4446   NSArray *a = [[NSApp mainMenu] itemArray];
4447   int i;
4448   BOOL found = NO;
4450   if (menu == nil) // Menu tracking ended.
4451     {
4452       if (menu_will_open_state == MENU_OPENING)
4453         menu_will_open_state = MENU_NONE;
4454       return;
4455     }
4457   for (i = 0; ! found && i < [a count]; i++)
4458     found = menu == [[a objectAtIndex:i] submenu];
4459   if (found)
4460     {
4461       if (menu_will_open_state == MENU_NONE && emacs_event)
4462         {
4463           NSEvent *theEvent = [NSApp currentEvent];
4464           struct frame *emacsframe = SELECTED_FRAME ();
4466           [menu cancelTracking];
4467           menu_will_open_state = MENU_PENDING;
4468           emacs_event->kind = MENU_BAR_ACTIVATE_EVENT;
4469           EV_TRAILER (theEvent);
4471           CGEventRef ourEvent = CGEventCreate (NULL);
4472           menu_mouse_point = CGEventGetLocation (ourEvent);
4473           CFRelease (ourEvent);
4474         }
4475       else if (menu_will_open_state == MENU_OPENING)
4476         {
4477           menu_will_open_state = MENU_NONE;
4478         }
4479     }
4482 /* Redo saved menu click if state is MENU_PENDING.  */
4483 void
4484 ns_check_pending_open_menu ()
4486   if (menu_will_open_state == MENU_PENDING)
4487     {
4488       CGEventSourceRef source
4489         = CGEventSourceCreate (kCGEventSourceStateHIDSystemState);
4491       CGEventRef event = CGEventCreateMouseEvent (source,
4492                                                   kCGEventLeftMouseDown,
4493                                                   menu_mouse_point,
4494                                                   kCGMouseButtonLeft);
4495       CGEventSetType (event, kCGEventLeftMouseDown);
4496       CGEventPost (kCGHIDEventTap, event);
4497       CFRelease (event);
4498       CFRelease (source);
4500       menu_will_open_state = MENU_OPENING;
4501     }
4503 #endif /* NS_IMPL_COCOA */
4505 static int
4506 ns_read_socket (struct terminal *terminal, struct input_event *hold_quit)
4507 /* --------------------------------------------------------------------------
4508      External (hook): Post an event to ourself and keep reading events until
4509      we read it back again.  In effect process all events which were waiting.
4510      From 21+ we have to manage the event buffer ourselves.
4511    -------------------------------------------------------------------------- */
4513   struct input_event ev;
4514   int nevents;
4516   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_read_socket");
4518 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4519   check_native_fs ();
4520 #endif
4522   if ([NSApp modalWindow] != nil)
4523     return -1;
4525   if (hold_event_q.nr > 0)
4526     {
4527       int i;
4528       for (i = 0; i < hold_event_q.nr; ++i)
4529         kbd_buffer_store_event_hold (&hold_event_q.q[i], hold_quit);
4530       hold_event_q.nr = 0;
4531       return i;
4532     }
4534   if ([NSThread isMainThread])
4535     {
4536       block_input ();
4537       n_emacs_events_pending = 0;
4538       ns_init_events (&ev);
4539       q_event_ptr = hold_quit;
4541       /* We manage autorelease pools by allocate/reallocate each time around
4542          the loop; strict nesting is occasionally violated but seems not to
4543          matter... earlier methods using full nesting caused major memory leaks.  */
4544       [outerpool release];
4545       outerpool = [[NSAutoreleasePool alloc] init];
4547       /* If have pending open-file requests, attend to the next one of those.  */
4548       if (ns_pending_files && [ns_pending_files count] != 0
4549           && [(EmacsApp *)NSApp openFile: [ns_pending_files objectAtIndex: 0]])
4550         {
4551           [ns_pending_files removeObjectAtIndex: 0];
4552         }
4553       /* Deal with pending service requests.  */
4554       else if (ns_pending_service_names && [ns_pending_service_names count] != 0
4555                && [(EmacsApp *)
4556                     NSApp fulfillService: [ns_pending_service_names objectAtIndex: 0]
4557                                  withArg: [ns_pending_service_args objectAtIndex: 0]])
4558         {
4559           [ns_pending_service_names removeObjectAtIndex: 0];
4560           [ns_pending_service_args removeObjectAtIndex: 0];
4561         }
4562       else
4563         {
4564           /* Run and wait for events.  We must always send one NX_APPDEFINED event
4565              to ourself, otherwise [NXApp run] will never exit.  */
4566           send_appdefined = YES;
4567           ns_send_appdefined (-1);
4569           [NSApp run];
4570         }
4572       nevents = n_emacs_events_pending;
4573       n_emacs_events_pending = 0;
4574       ns_finish_events ();
4575       q_event_ptr = NULL;
4576       unblock_input ();
4577     }
4578   else
4579     return -1;
4581   return nevents;
4586 ns_select (int nfds, fd_set *readfds, fd_set *writefds,
4587            fd_set *exceptfds, struct timespec *timeout,
4588            sigset_t *sigmask)
4589 /* --------------------------------------------------------------------------
4590      Replacement for select, checking for events
4591    -------------------------------------------------------------------------- */
4593   int result;
4594   int t, k, nr = 0;
4595   struct input_event event;
4596   char c;
4598   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_select");
4600 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4601   check_native_fs ();
4602 #endif
4604   if (hold_event_q.nr > 0)
4605     {
4606       /* We already have events pending.  */
4607       raise (SIGIO);
4608       errno = EINTR;
4609       return -1;
4610     }
4612   for (k = 0; k < nfds+1; k++)
4613     {
4614       if (readfds && FD_ISSET(k, readfds)) ++nr;
4615       if (writefds && FD_ISSET(k, writefds)) ++nr;
4616     }
4618   if (NSApp == nil
4619       || ![NSThread isMainThread]
4620       || (timeout && timeout->tv_sec == 0 && timeout->tv_nsec == 0))
4621     return thread_select(pselect, nfds, readfds, writefds,
4622                          exceptfds, timeout, sigmask);
4623   else
4624     {
4625       struct timespec t = {0, 0};
4626       thread_select(pselect, 0, NULL, NULL, NULL, &t, sigmask);
4627     }
4629   [outerpool release];
4630   outerpool = [[NSAutoreleasePool alloc] init];
4633   send_appdefined = YES;
4634   if (nr > 0)
4635     {
4636       pthread_mutex_lock (&select_mutex);
4637       select_nfds = nfds;
4638       select_valid = 0;
4639       if (readfds)
4640         {
4641           select_readfds = *readfds;
4642           select_valid += SELECT_HAVE_READ;
4643         }
4644       if (writefds)
4645         {
4646           select_writefds = *writefds;
4647           select_valid += SELECT_HAVE_WRITE;
4648         }
4650       if (timeout)
4651         {
4652           select_timeout = *timeout;
4653           select_valid += SELECT_HAVE_TMO;
4654         }
4656       pthread_mutex_unlock (&select_mutex);
4658       /* Inform fd_handler that select should be called.  */
4659       c = 'g';
4660       emacs_write_sig (selfds[1], &c, 1);
4661     }
4662   else if (nr == 0 && timeout)
4663     {
4664       /* No file descriptor, just a timeout, no need to wake fd_handler.  */
4665       double time = timespectod (*timeout);
4666       timed_entry = [[NSTimer scheduledTimerWithTimeInterval: time
4667                                                       target: NSApp
4668                                                     selector:
4669                                   @selector (timeout_handler:)
4670                                                     userInfo: 0
4671                                                      repeats: NO]
4672                       retain];
4673     }
4674   else /* No timeout and no file descriptors, can this happen?  */
4675     {
4676       /* Send appdefined so we exit from the loop.  */
4677       ns_send_appdefined (-1);
4678     }
4680   block_input ();
4681   ns_init_events (&event);
4683   [NSApp run];
4685   ns_finish_events ();
4686   if (nr > 0 && readfds)
4687     {
4688       c = 's';
4689       emacs_write_sig (selfds[1], &c, 1);
4690     }
4691   unblock_input ();
4693   t = last_appdefined_event_data;
4695   if (t != NO_APPDEFINED_DATA)
4696     {
4697       last_appdefined_event_data = NO_APPDEFINED_DATA;
4699       if (t == -2)
4700         {
4701           /* The NX_APPDEFINED event we received was a timeout.  */
4702           result = 0;
4703         }
4704       else if (t == -1)
4705         {
4706           /* The NX_APPDEFINED event we received was the result of
4707              at least one real input event arriving.  */
4708           errno = EINTR;
4709           result = -1;
4710         }
4711       else
4712         {
4713           /* Received back from select () in fd_handler; copy the results.  */
4714           pthread_mutex_lock (&select_mutex);
4715           if (readfds) *readfds = select_readfds;
4716           if (writefds) *writefds = select_writefds;
4717           pthread_mutex_unlock (&select_mutex);
4718           result = t;
4719         }
4720     }
4721   else
4722     {
4723       errno = EINTR;
4724       result = -1;
4725     }
4727   return result;
4730 #ifdef HAVE_PTHREAD
4731 void
4732 ns_run_loop_break ()
4733 /* Break out of the NS run loop in ns_select or ns_read_socket.  */
4735   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_run_loop_break");
4737   /* If we don't have a GUI, don't send the event.  */
4738   if (NSApp != NULL)
4739     ns_send_appdefined(-1);
4741 #endif
4744 /* ==========================================================================
4746     Scrollbar handling
4748    ========================================================================== */
4751 static void
4752 ns_set_vertical_scroll_bar (struct window *window,
4753                            int portion, int whole, int position)
4754 /* --------------------------------------------------------------------------
4755       External (hook): Update or add scrollbar
4756    -------------------------------------------------------------------------- */
4758   Lisp_Object win;
4759   NSRect r, v;
4760   struct frame *f = XFRAME (WINDOW_FRAME (window));
4761   EmacsView *view = FRAME_NS_VIEW (f);
4762   EmacsScroller *bar;
4763   int window_y, window_height;
4764   int top, left, height, width;
4765   BOOL update_p = YES;
4767   /* Optimization; display engine sends WAY too many of these.  */
4768   if (!NILP (window->vertical_scroll_bar))
4769     {
4770       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4771       if ([bar checkSamePosition: position portion: portion whole: whole])
4772         {
4773           if (view->scrollbarsNeedingUpdate == 0)
4774             {
4775               if (!windows_or_buffers_changed)
4776                   return;
4777             }
4778           else
4779             view->scrollbarsNeedingUpdate--;
4780           update_p = NO;
4781         }
4782     }
4784   NSTRACE ("ns_set_vertical_scroll_bar");
4786   /* Get dimensions.  */
4787   window_box (window, ANY_AREA, 0, &window_y, 0, &window_height);
4788   top = window_y;
4789   height = window_height;
4790   width = NS_SCROLL_BAR_WIDTH (f);
4791   left = WINDOW_SCROLL_BAR_AREA_X (window);
4793   r = NSMakeRect (left, top, width, height);
4794   /* The parent view is flipped, so we need to flip y value.  */
4795   v = [view frame];
4796   r.origin.y = (v.size.height - r.size.height - r.origin.y);
4798   XSETWINDOW (win, window);
4799   block_input ();
4801   /* We want at least 5 lines to display a scrollbar.  */
4802   if (WINDOW_TOTAL_LINES (window) < 5)
4803     {
4804       if (!NILP (window->vertical_scroll_bar))
4805         {
4806           bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4807           [bar removeFromSuperview];
4808           wset_vertical_scroll_bar (window, Qnil);
4809           [bar release];
4810         }
4811       ns_clear_frame_area (f, left, top, width, height);
4812       unblock_input ();
4813       return;
4814     }
4816   if (NILP (window->vertical_scroll_bar))
4817     {
4818       if (width > 0 && height > 0)
4819         ns_clear_frame_area (f, left, top, width, height);
4821       bar = [[EmacsScroller alloc] initFrame: r window: win];
4822       wset_vertical_scroll_bar (window, make_mint_ptr (bar));
4823       update_p = YES;
4824     }
4825   else
4826     {
4827       NSRect oldRect;
4828       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4829       oldRect = [bar frame];
4830       r.size.width = oldRect.size.width;
4831       if (FRAME_LIVE_P (f) && !NSEqualRects (oldRect, r))
4832         {
4833           if (oldRect.origin.x != r.origin.x)
4834               ns_clear_frame_area (f, left, top, width, height);
4835           [bar setFrame: r];
4836         }
4837     }
4839   if (update_p)
4840     [bar setPosition: position portion: portion whole: whole];
4841   unblock_input ();
4845 static void
4846 ns_set_horizontal_scroll_bar (struct window *window,
4847                               int portion, int whole, int position)
4848 /* --------------------------------------------------------------------------
4849       External (hook): Update or add scrollbar.
4850    -------------------------------------------------------------------------- */
4852   Lisp_Object win;
4853   NSRect r, v;
4854   struct frame *f = XFRAME (WINDOW_FRAME (window));
4855   EmacsView *view = FRAME_NS_VIEW (f);
4856   EmacsScroller *bar;
4857   int top, height, left, width;
4858   int window_x, window_width;
4859   BOOL update_p = YES;
4861   /* Optimization; display engine sends WAY too many of these.  */
4862   if (!NILP (window->horizontal_scroll_bar))
4863     {
4864       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4865       if ([bar checkSamePosition: position portion: portion whole: whole])
4866         {
4867           if (view->scrollbarsNeedingUpdate == 0)
4868             {
4869               if (!windows_or_buffers_changed)
4870                   return;
4871             }
4872           else
4873             view->scrollbarsNeedingUpdate--;
4874           update_p = NO;
4875         }
4876     }
4878   NSTRACE ("ns_set_horizontal_scroll_bar");
4880   /* Get dimensions.  */
4881   window_box (window, ANY_AREA, &window_x, 0, &window_width, 0);
4882   left = window_x;
4883   width = window_width;
4884   height = NS_SCROLL_BAR_HEIGHT (f);
4885   top = WINDOW_SCROLL_BAR_AREA_Y (window);
4887   r = NSMakeRect (left, top, width, height);
4888   /* The parent view is flipped, so we need to flip y value.  */
4889   v = [view frame];
4890   r.origin.y = (v.size.height - r.size.height - r.origin.y);
4892   XSETWINDOW (win, window);
4893   block_input ();
4895   if (NILP (window->horizontal_scroll_bar))
4896     {
4897       if (width > 0 && height > 0)
4898         ns_clear_frame_area (f, left, top, width, height);
4900       bar = [[EmacsScroller alloc] initFrame: r window: win];
4901       wset_horizontal_scroll_bar (window, make_mint_ptr (bar));
4902       update_p = YES;
4903     }
4904   else
4905     {
4906       NSRect oldRect;
4907       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4908       oldRect = [bar frame];
4909       if (FRAME_LIVE_P (f) && !NSEqualRects (oldRect, r))
4910         {
4911           if (oldRect.origin.y != r.origin.y)
4912             ns_clear_frame_area (f, left, top, width, height);
4913           [bar setFrame: r];
4914           update_p = YES;
4915         }
4916     }
4918   /* If there are both horizontal and vertical scroll-bars they leave
4919      a square that belongs to neither. We need to clear it otherwise
4920      it fills with junk.  */
4921   if (!NILP (window->vertical_scroll_bar))
4922     ns_clear_frame_area (f, WINDOW_SCROLL_BAR_AREA_X (window), top,
4923                          NS_SCROLL_BAR_HEIGHT (f), height);
4925   if (update_p)
4926     [bar setPosition: position portion: portion whole: whole];
4927   unblock_input ();
4931 static void
4932 ns_condemn_scroll_bars (struct frame *f)
4933 /* --------------------------------------------------------------------------
4934      External (hook): arrange for all frame's scrollbars to be removed
4935      at next call to judge_scroll_bars, except for those redeemed.
4936    -------------------------------------------------------------------------- */
4938   int i;
4939   id view;
4940   NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews];
4942   NSTRACE ("ns_condemn_scroll_bars");
4944   for (i =[subviews count]-1; i >= 0; i--)
4945     {
4946       view = [subviews objectAtIndex: i];
4947       if ([view isKindOfClass: [EmacsScroller class]])
4948         [view condemn];
4949     }
4953 static void
4954 ns_redeem_scroll_bar (struct window *window)
4955 /* --------------------------------------------------------------------------
4956      External (hook): arrange to spare this window's scrollbar
4957      at next call to judge_scroll_bars.
4958    -------------------------------------------------------------------------- */
4960   id bar;
4961   NSTRACE ("ns_redeem_scroll_bar");
4962   if (!NILP (window->vertical_scroll_bar)
4963       && WINDOW_HAS_VERTICAL_SCROLL_BAR (window))
4964     {
4965       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4966       [bar reprieve];
4967     }
4969   if (!NILP (window->horizontal_scroll_bar)
4970       && WINDOW_HAS_HORIZONTAL_SCROLL_BAR (window))
4971     {
4972       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4973       [bar reprieve];
4974     }
4978 static void
4979 ns_judge_scroll_bars (struct frame *f)
4980 /* --------------------------------------------------------------------------
4981      External (hook): destroy all scrollbars on frame that weren't
4982      redeemed after call to condemn_scroll_bars.
4983    -------------------------------------------------------------------------- */
4985   int i;
4986   id view;
4987   EmacsView *eview = FRAME_NS_VIEW (f);
4988   NSArray *subviews = [[eview superview] subviews];
4989   BOOL removed = NO;
4991   NSTRACE ("ns_judge_scroll_bars");
4992   for (i = [subviews count]-1; i >= 0; --i)
4993     {
4994       view = [subviews objectAtIndex: i];
4995       if (![view isKindOfClass: [EmacsScroller class]]) continue;
4996       if ([view judge])
4997         removed = YES;
4998     }
5000   if (removed)
5001     [eview updateFrameSize: NO];
5004 /* ==========================================================================
5006     Initialization
5008    ========================================================================== */
5011 x_display_pixel_height (struct ns_display_info *dpyinfo)
5013   NSArray *screens = [NSScreen screens];
5014   NSEnumerator *enumerator = [screens objectEnumerator];
5015   NSScreen *screen;
5016   NSRect frame;
5018   frame = NSZeroRect;
5019   while ((screen = [enumerator nextObject]) != nil)
5020     frame = NSUnionRect (frame, [screen frame]);
5022   return NSHeight (frame);
5026 x_display_pixel_width (struct ns_display_info *dpyinfo)
5028   NSArray *screens = [NSScreen screens];
5029   NSEnumerator *enumerator = [screens objectEnumerator];
5030   NSScreen *screen;
5031   NSRect frame;
5033   frame = NSZeroRect;
5034   while ((screen = [enumerator nextObject]) != nil)
5035     frame = NSUnionRect (frame, [screen frame]);
5037   return NSWidth (frame);
5041 static Lisp_Object ns_string_to_lispmod (const char *s)
5042 /* --------------------------------------------------------------------------
5043      Convert modifier name to lisp symbol.
5044    -------------------------------------------------------------------------- */
5046   if (!strncmp (SSDATA (SYMBOL_NAME (Qmeta)), s, 10))
5047     return Qmeta;
5048   else if (!strncmp (SSDATA (SYMBOL_NAME (Qsuper)), s, 10))
5049     return Qsuper;
5050   else if (!strncmp (SSDATA (SYMBOL_NAME (Qcontrol)), s, 10))
5051     return Qcontrol;
5052   else if (!strncmp (SSDATA (SYMBOL_NAME (Qalt)), s, 10))
5053     return Qalt;
5054   else if (!strncmp (SSDATA (SYMBOL_NAME (Qhyper)), s, 10))
5055     return Qhyper;
5056   else if (!strncmp (SSDATA (SYMBOL_NAME (Qnone)), s, 10))
5057     return Qnone;
5058   else
5059     return Qnil;
5063 static void
5064 ns_default (const char *parameter, Lisp_Object *result,
5065            Lisp_Object yesval, Lisp_Object noval,
5066            BOOL is_float, BOOL is_modstring)
5067 /* --------------------------------------------------------------------------
5068       Check a parameter value in user's preferences.
5069    -------------------------------------------------------------------------- */
5071   const char *value = ns_get_defaults_value (parameter);
5073   if (value)
5074     {
5075       double f;
5076       char *pos;
5077       if (c_strcasecmp (value, "YES") == 0)
5078         *result = yesval;
5079       else if (c_strcasecmp (value, "NO") == 0)
5080         *result = noval;
5081       else if (is_float && (f = strtod (value, &pos), pos != value))
5082         *result = make_float (f);
5083       else if (is_modstring && value)
5084         *result = ns_string_to_lispmod (value);
5085       else fprintf (stderr,
5086                    "Bad value for default \"%s\": \"%s\"\n", parameter, value);
5087     }
5091 static void
5092 ns_initialize_display_info (struct ns_display_info *dpyinfo)
5093 /* --------------------------------------------------------------------------
5094       Initialize global info and storage for display.
5095    -------------------------------------------------------------------------- */
5097     NSScreen *screen = [NSScreen mainScreen];
5098     NSWindowDepth depth = [screen depth];
5100     dpyinfo->resx = 72.27; /* used 75.0, but this makes pt == pixel, expected */
5101     dpyinfo->resy = 72.27;
5102     dpyinfo->color_p = ![NSDeviceWhiteColorSpace isEqualToString:
5103                                                   NSColorSpaceFromDepth (depth)]
5104                 && ![NSCalibratedWhiteColorSpace isEqualToString:
5105                                                  NSColorSpaceFromDepth (depth)];
5106     dpyinfo->n_planes = NSBitsPerPixelFromDepth (depth);
5107     dpyinfo->color_table = xmalloc (sizeof *dpyinfo->color_table);
5108     dpyinfo->color_table->colors = NULL;
5109     dpyinfo->root_window = 42; /* A placeholder.  */
5110     dpyinfo->x_highlight_frame = dpyinfo->x_focus_frame = NULL;
5111     dpyinfo->n_fonts = 0;
5112     dpyinfo->smallest_font_height = 1;
5113     dpyinfo->smallest_char_width = 1;
5115     reset_mouse_highlight (&dpyinfo->mouse_highlight);
5119 /* This and next define (many of the) public functions in this file.  */
5120 /* x_... are generic versions in xdisp.c that we, and other terms, get away
5121          with using despite presence in the "system dependent" redisplay
5122          interface.  In addition, many of the ns_ methods have code that is
5123          shared with all terms, indicating need for further refactoring.  */
5124 extern frame_parm_handler ns_frame_parm_handlers[];
5125 static struct redisplay_interface ns_redisplay_interface =
5127   ns_frame_parm_handlers,
5128   x_produce_glyphs,
5129   x_write_glyphs,
5130   x_insert_glyphs,
5131   x_clear_end_of_line,
5132   ns_scroll_run,
5133   ns_after_update_window_line,
5134   ns_update_window_begin,
5135   ns_update_window_end,
5136   0, /* flush_display */
5137   x_clear_window_mouse_face,
5138   x_get_glyph_overhangs,
5139   x_fix_overlapping_area,
5140   ns_draw_fringe_bitmap,
5141   0, /* define_fringe_bitmap */ /* FIXME: simplify ns_draw_fringe_bitmap */
5142   0, /* destroy_fringe_bitmap */
5143   ns_compute_glyph_string_overhangs,
5144   ns_draw_glyph_string,
5145   ns_define_frame_cursor,
5146   ns_clear_frame_area,
5147   ns_draw_window_cursor,
5148   ns_draw_vertical_window_border,
5149   ns_draw_window_divider,
5150   ns_shift_glyphs_for_insert,
5151   ns_show_hourglass,
5152   ns_hide_hourglass
5156 static void
5157 ns_delete_display (struct ns_display_info *dpyinfo)
5159   /* TODO...  */
5163 /* This function is called when the last frame on a display is deleted.  */
5164 static void
5165 ns_delete_terminal (struct terminal *terminal)
5167   struct ns_display_info *dpyinfo = terminal->display_info.ns;
5169   NSTRACE ("ns_delete_terminal");
5171   /* Protect against recursive calls.  delete_frame in
5172      delete_terminal calls us back when it deletes our last frame.  */
5173   if (!terminal->name)
5174     return;
5176   block_input ();
5178   x_destroy_all_bitmaps (dpyinfo);
5179   ns_delete_display (dpyinfo);
5180   unblock_input ();
5184 static struct terminal *
5185 ns_create_terminal (struct ns_display_info *dpyinfo)
5186 /* --------------------------------------------------------------------------
5187       Set up use of NS before we make the first connection.
5188    -------------------------------------------------------------------------- */
5190   struct terminal *terminal;
5192   NSTRACE ("ns_create_terminal");
5194   terminal = create_terminal (output_ns, &ns_redisplay_interface);
5196   terminal->display_info.ns = dpyinfo;
5197   dpyinfo->terminal = terminal;
5199   terminal->clear_frame_hook = ns_clear_frame;
5200   terminal->ring_bell_hook = ns_ring_bell;
5201   terminal->update_begin_hook = ns_update_begin;
5202   terminal->update_end_hook = ns_update_end;
5203   terminal->read_socket_hook = ns_read_socket;
5204   terminal->frame_up_to_date_hook = ns_frame_up_to_date;
5205   terminal->mouse_position_hook = ns_mouse_position;
5206   terminal->frame_rehighlight_hook = ns_frame_rehighlight;
5207   terminal->frame_raise_lower_hook = ns_frame_raise_lower;
5208   terminal->fullscreen_hook = ns_fullscreen_hook;
5209   terminal->menu_show_hook = ns_menu_show;
5210   terminal->popup_dialog_hook = ns_popup_dialog;
5211   terminal->set_vertical_scroll_bar_hook = ns_set_vertical_scroll_bar;
5212   terminal->set_horizontal_scroll_bar_hook = ns_set_horizontal_scroll_bar;
5213   terminal->condemn_scroll_bars_hook = ns_condemn_scroll_bars;
5214   terminal->redeem_scroll_bar_hook = ns_redeem_scroll_bar;
5215   terminal->judge_scroll_bars_hook = ns_judge_scroll_bars;
5216   terminal->delete_frame_hook = x_destroy_window;
5217   terminal->delete_terminal_hook = ns_delete_terminal;
5218   /* Other hooks are NULL by default.  */
5220   return terminal;
5224 struct ns_display_info *
5225 ns_term_init (Lisp_Object display_name)
5226 /* --------------------------------------------------------------------------
5227      Start the Application and get things rolling.
5228    -------------------------------------------------------------------------- */
5230   struct terminal *terminal;
5231   struct ns_display_info *dpyinfo;
5232   static int ns_initialized = 0;
5233   Lisp_Object tmp;
5235   if (ns_initialized) return x_display_list;
5236   ns_initialized = 1;
5238   block_input ();
5240   NSTRACE ("ns_term_init");
5242   [outerpool release];
5243   outerpool = [[NSAutoreleasePool alloc] init];
5245   /* count object allocs (About, click icon); on macOS use ObjectAlloc tool */
5246   /*GSDebugAllocationActive (YES); */
5247   block_input ();
5249   baud_rate = 38400;
5250   Fset_input_interrupt_mode (Qnil);
5252   if (selfds[0] == -1)
5253     {
5254       if (emacs_pipe (selfds) != 0)
5255         {
5256           fprintf (stderr, "Failed to create pipe: %s\n",
5257                    emacs_strerror (errno));
5258           emacs_abort ();
5259         }
5261       fcntl (selfds[0], F_SETFL, O_NONBLOCK|fcntl (selfds[0], F_GETFL));
5262       FD_ZERO (&select_readfds);
5263       FD_ZERO (&select_writefds);
5264       pthread_mutex_init (&select_mutex, NULL);
5265     }
5267   ns_pending_files = [[NSMutableArray alloc] init];
5268   ns_pending_service_names = [[NSMutableArray alloc] init];
5269   ns_pending_service_args = [[NSMutableArray alloc] init];
5271   /* Start app and create the main menu, window, view.
5272      Needs to be here because ns_initialize_display_info () uses AppKit classes.
5273      The view will then ask the NSApp to stop and return to Emacs.  */
5274   [EmacsApp sharedApplication];
5275   if (NSApp == nil)
5276     return NULL;
5277   [NSApp setDelegate: NSApp];
5279   /* Start the select thread.  */
5280   [NSThread detachNewThreadSelector:@selector (fd_handler:)
5281                            toTarget:NSApp
5282                          withObject:nil];
5284   /* debugging: log all notifications */
5285   /*   [[NSNotificationCenter defaultCenter] addObserver: NSApp
5286                                          selector: @selector (logNotification:)
5287                                              name: nil object: nil]; */
5289   dpyinfo = xzalloc (sizeof *dpyinfo);
5291   ns_initialize_display_info (dpyinfo);
5292   terminal = ns_create_terminal (dpyinfo);
5294   terminal->kboard = allocate_kboard (Qns);
5295   /* Don't let the initial kboard remain current longer than necessary.
5296      That would cause problems if a file loaded on startup tries to
5297      prompt in the mini-buffer.  */
5298   if (current_kboard == initial_kboard)
5299     current_kboard = terminal->kboard;
5300   terminal->kboard->reference_count++;
5302   dpyinfo->next = x_display_list;
5303   x_display_list = dpyinfo;
5305   dpyinfo->name_list_element = Fcons (display_name, Qnil);
5307   terminal->name = xlispstrdup (display_name);
5309   unblock_input ();
5311   if (!inhibit_x_resources)
5312     {
5313       ns_default ("GSFontAntiAlias", &ns_antialias_text,
5314                  Qt, Qnil, NO, NO);
5315       tmp = Qnil;
5316       /* this is a standard variable */
5317       ns_default ("AppleAntiAliasingThreshold", &tmp,
5318                  make_float (10.0), make_float (6.0), YES, NO);
5319       ns_antialias_threshold = NILP (tmp) ? 10.0 : extract_float (tmp);
5320     }
5322   NSTRACE_MSG ("Colors");
5324   {
5325     NSColorList *cl = [NSColorList colorListNamed: @"Emacs"];
5327     if ( cl == nil )
5328       {
5329         Lisp_Object color_file, color_map, color;
5330         unsigned long c;
5331         char *name;
5333         color_file = Fexpand_file_name (build_string ("rgb.txt"),
5334                          Fsymbol_value (intern ("data-directory")));
5336         color_map = Fx_load_color_file (color_file);
5337         if (NILP (color_map))
5338           fatal ("Could not read %s.\n", SDATA (color_file));
5340         cl = [[NSColorList alloc] initWithName: @"Emacs"];
5341         for ( ; CONSP (color_map); color_map = XCDR (color_map))
5342           {
5343             color = XCAR (color_map);
5344             name = SSDATA (XCAR (color));
5345             c = XINT (XCDR (color));
5346             [cl setColor:
5347                   [NSColor colorForEmacsRed: RED_FROM_ULONG (c) / 255.0
5348                                       green: GREEN_FROM_ULONG (c) / 255.0
5349                                        blue: BLUE_FROM_ULONG (c) / 255.0
5350                                       alpha: 1.0]
5351                   forKey: [NSString stringWithUTF8String: name]];
5352           }
5353         [cl writeToFile: nil];
5354       }
5355   }
5357   NSTRACE_MSG ("Versions");
5359   {
5360 #ifdef NS_IMPL_GNUSTEP
5361     Vwindow_system_version = build_string (gnustep_base_version);
5362 #else
5363     /* PSnextrelease (128, c); */
5364     char c[DBL_BUFSIZE_BOUND];
5365     int len = dtoastr (c, sizeof c, 0, 0, NSAppKitVersionNumber);
5366     Vwindow_system_version = make_unibyte_string (c, len);
5367 #endif
5368   }
5370   delete_keyboard_wait_descriptor (0);
5372   ns_app_name = [[NSProcessInfo processInfo] processName];
5374   /* Set up macOS app menu */
5376   NSTRACE_MSG ("Menu init");
5378 #ifdef NS_IMPL_COCOA
5379   {
5380     NSMenu *appMenu;
5381     NSMenuItem *item;
5382     /* set up the application menu */
5383     svcsMenu = [[EmacsMenu alloc] initWithTitle: @"Services"];
5384     [svcsMenu setAutoenablesItems: NO];
5385     appMenu = [[EmacsMenu alloc] initWithTitle: @"Emacs"];
5386     [appMenu setAutoenablesItems: NO];
5387     mainMenu = [[EmacsMenu alloc] initWithTitle: @""];
5388     dockMenu = [[EmacsMenu alloc] initWithTitle: @""];
5390     [appMenu insertItemWithTitle: @"About Emacs"
5391                           action: @selector (orderFrontStandardAboutPanel:)
5392                    keyEquivalent: @""
5393                          atIndex: 0];
5394     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 1];
5395     [appMenu insertItemWithTitle: @"Preferences..."
5396                           action: @selector (showPreferencesWindow:)
5397                    keyEquivalent: @","
5398                          atIndex: 2];
5399     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 3];
5400     item = [appMenu insertItemWithTitle: @"Services"
5401                                  action: @selector (menuDown:)
5402                           keyEquivalent: @""
5403                                 atIndex: 4];
5404     [appMenu setSubmenu: svcsMenu forItem: item];
5405     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 5];
5406     [appMenu insertItemWithTitle: @"Hide Emacs"
5407                           action: @selector (hide:)
5408                    keyEquivalent: @"h"
5409                          atIndex: 6];
5410     item =  [appMenu insertItemWithTitle: @"Hide Others"
5411                           action: @selector (hideOtherApplications:)
5412                    keyEquivalent: @"h"
5413                          atIndex: 7];
5414     [item setKeyEquivalentModifierMask: NSEventModifierFlagCommand | NSEventModifierFlagOption];
5415     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 8];
5416     [appMenu insertItemWithTitle: @"Quit Emacs"
5417                           action: @selector (terminate:)
5418                    keyEquivalent: @"q"
5419                          atIndex: 9];
5421     item = [mainMenu insertItemWithTitle: ns_app_name
5422                                   action: @selector (menuDown:)
5423                            keyEquivalent: @""
5424                                  atIndex: 0];
5425     [mainMenu setSubmenu: appMenu forItem: item];
5426     [dockMenu insertItemWithTitle: @"New Frame"
5427                            action: @selector (newFrame:)
5428                     keyEquivalent: @""
5429                           atIndex: 0];
5431     [NSApp setMainMenu: mainMenu];
5432     [NSApp setAppleMenu: appMenu];
5433     [NSApp setServicesMenu: svcsMenu];
5434     /* Needed at least on Cocoa, to get dock menu to show windows */
5435     [NSApp setWindowsMenu: [[NSMenu alloc] init]];
5437     [[NSNotificationCenter defaultCenter]
5438       addObserver: mainMenu
5439          selector: @selector (trackingNotification:)
5440              name: NSMenuDidBeginTrackingNotification object: mainMenu];
5441     [[NSNotificationCenter defaultCenter]
5442       addObserver: mainMenu
5443          selector: @selector (trackingNotification:)
5444              name: NSMenuDidEndTrackingNotification object: mainMenu];
5445   }
5446 #endif /* macOS menu setup */
5448   /* Register our external input/output types, used for determining
5449      applicable services and also drag/drop eligibility.  */
5451   NSTRACE_MSG ("Input/output types");
5453   ns_send_types = [[NSArray arrayWithObjects: NSStringPboardType, nil] retain];
5454   ns_return_types = [[NSArray arrayWithObjects: NSStringPboardType, nil]
5455                       retain];
5456   ns_drag_types = [[NSArray arrayWithObjects:
5457                             NSStringPboardType,
5458                             NSTabularTextPboardType,
5459                             NSFilenamesPboardType,
5460                             NSURLPboardType, nil] retain];
5462   /* If fullscreen is in init/default-frame-alist, focus isn't set
5463      right for fullscreen windows, so set this.  */
5464   [NSApp activateIgnoringOtherApps:YES];
5466   NSTRACE_MSG ("Call NSApp run");
5468   [NSApp run];
5469   ns_do_open_file = YES;
5471 #ifdef NS_IMPL_GNUSTEP
5472   /* GNUstep steals SIGCHLD for use in NSTask, but we don't use NSTask.
5473      We must re-catch it so subprocess works.  */
5474   catch_child_signal ();
5475 #endif
5477   NSTRACE_MSG ("ns_term_init done");
5479   unblock_input ();
5481   return dpyinfo;
5485 void
5486 ns_term_shutdown (int sig)
5488   [[NSUserDefaults standardUserDefaults] synchronize];
5490   /* code not reached in emacs.c after this is called by shut_down_emacs: */
5491   if (STRINGP (Vauto_save_list_file_name))
5492     unlink (SSDATA (Vauto_save_list_file_name));
5494   if (sig == 0 || sig == SIGTERM)
5495     {
5496       [NSApp terminate: NSApp];
5497     }
5498   else // force a stack trace to happen
5499     {
5500       emacs_abort ();
5501     }
5505 /* ==========================================================================
5507     EmacsApp implementation
5509    ========================================================================== */
5512 @implementation EmacsApp
5514 - (id)init
5516   NSTRACE ("[EmacsApp init]");
5518   if ((self = [super init]))
5519     {
5520 #ifdef NS_IMPL_COCOA
5521       self->isFirst = YES;
5522 #endif
5523 #ifdef NS_IMPL_GNUSTEP
5524       self->applicationDidFinishLaunchingCalled = NO;
5525 #endif
5526     }
5528   return self;
5531 #ifdef NS_IMPL_COCOA
5532 - (void)run
5534   NSTRACE ("[EmacsApp run]");
5536 #ifndef NSAppKitVersionNumber10_9
5537 #define NSAppKitVersionNumber10_9 1265
5538 #endif
5540     if ((int)NSAppKitVersionNumber != NSAppKitVersionNumber10_9)
5541       {
5542         [super run];
5543         return;
5544       }
5546   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5548   if (isFirst) [self finishLaunching];
5549   isFirst = NO;
5551   shouldKeepRunning = YES;
5552   do
5553     {
5554       [pool release];
5555       pool = [[NSAutoreleasePool alloc] init];
5557       NSEvent *event =
5558         [self nextEventMatchingMask:NSEventMaskAny
5559                           untilDate:[NSDate distantFuture]
5560                              inMode:NSDefaultRunLoopMode
5561                             dequeue:YES];
5563       [self sendEvent:event];
5564       [self updateWindows];
5565     } while (shouldKeepRunning);
5567   [pool release];
5570 - (void)stop: (id)sender
5572   NSTRACE ("[EmacsApp stop:]");
5574     shouldKeepRunning = NO;
5575     // Stop possible dialog also.  Noop if no dialog present.
5576     // The file dialog still leaks 7k - 10k on 10.9 though.
5577     [super stop:sender];
5579 #endif /* NS_IMPL_COCOA */
5581 - (void)logNotification: (NSNotification *)notification
5583   NSTRACE ("[EmacsApp logNotification:]");
5585   const char *name = [[notification name] UTF8String];
5586   if (!strstr (name, "Update") && !strstr (name, "NSMenu")
5587       && !strstr (name, "WindowNumber"))
5588     NSLog (@"notification: '%@'", [notification name]);
5592 - (void)sendEvent: (NSEvent *)theEvent
5593 /* --------------------------------------------------------------------------
5594      Called when NSApp is running for each event received.  Used to stop
5595      the loop when we choose, since there's no way to just run one iteration.
5596    -------------------------------------------------------------------------- */
5598   int type = [theEvent type];
5599   NSWindow *window = [theEvent window];
5601   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsApp sendEvent:]");
5602   NSTRACE_MSG ("Type: %d", type);
5604 #ifdef NS_IMPL_GNUSTEP
5605   // Keyboard events aren't propagated to file dialogs for some reason.
5606   if ([NSApp modalWindow] != nil &&
5607       (type == NSEventTypeKeyDown || type == NSEventTypeKeyUp || type == NSEventTypeFlagsChanged))
5608     {
5609       [[NSApp modalWindow] sendEvent: theEvent];
5610       return;
5611     }
5612 #endif
5614   if (type == NSEventTypeApplicationDefined)
5615     {
5616       switch ([theEvent data2])
5617         {
5618 #ifdef NS_IMPL_COCOA
5619         case NSAPP_DATA2_RUNASSCRIPT:
5620           ns_run_ascript ();
5621           [self stop: self];
5622           return;
5623 #endif
5624         case NSAPP_DATA2_RUNFILEDIALOG:
5625           ns_run_file_dialog ();
5626           [self stop: self];
5627           return;
5628         }
5629     }
5631   if (type == NSEventTypeCursorUpdate && window == nil)
5632     {
5633       fprintf (stderr, "Dropping external cursor update event.\n");
5634       return;
5635     }
5637   if (type == NSEventTypeApplicationDefined)
5638     {
5639       /* Events posted by ns_send_appdefined interrupt the run loop here.
5640          But, if a modal window is up, an appdefined can still come through,
5641          (e.g., from a makeKeyWindow event) but stopping self also stops the
5642          modal loop. Just defer it until later.  */
5643       if ([NSApp modalWindow] == nil)
5644         {
5645           last_appdefined_event_data = [theEvent data1];
5646           [self stop: self];
5647         }
5648       else
5649         {
5650           send_appdefined = YES;
5651         }
5652     }
5655 #ifdef NS_IMPL_COCOA
5656   /* If no dialog and none of our frames have focus and it is a move, skip it.
5657      It is a mouse move in an auxiliary menu, i.e. on the top right on macOS,
5658      such as Wifi, sound, date or similar.
5659      This prevents "spooky" highlighting in the frame under the menu.  */
5660   if (type == NSEventTypeMouseMoved && [NSApp modalWindow] == nil)
5661     {
5662       struct ns_display_info *di;
5663       BOOL has_focus = NO;
5664       for (di = x_display_list; ! has_focus && di; di = di->next)
5665         has_focus = di->x_focus_frame != 0;
5666       if (! has_focus)
5667         return;
5668     }
5669 #endif
5671   NSTRACE_UNSILENCE();
5673   [super sendEvent: theEvent];
5677 - (void)showPreferencesWindow: (id)sender
5679   struct frame *emacsframe = SELECTED_FRAME ();
5680   NSEvent *theEvent = [NSApp currentEvent];
5682   if (!emacs_event)
5683     return;
5684   emacs_event->kind = NS_NONKEY_EVENT;
5685   emacs_event->code = KEY_NS_SHOW_PREFS;
5686   emacs_event->modifiers = 0;
5687   EV_TRAILER (theEvent);
5691 - (void)newFrame: (id)sender
5693   NSTRACE ("[EmacsApp newFrame:]");
5695   struct frame *emacsframe = SELECTED_FRAME ();
5696   NSEvent *theEvent = [NSApp currentEvent];
5698   if (!emacs_event)
5699     return;
5700   emacs_event->kind = NS_NONKEY_EVENT;
5701   emacs_event->code = KEY_NS_NEW_FRAME;
5702   emacs_event->modifiers = 0;
5703   EV_TRAILER (theEvent);
5707 /* Open a file (used by below, after going into queue read by ns_read_socket).  */
5708 - (BOOL) openFile: (NSString *)fileName
5710   NSTRACE ("[EmacsApp openFile:]");
5712   struct frame *emacsframe = SELECTED_FRAME ();
5713   NSEvent *theEvent = [NSApp currentEvent];
5715   if (!emacs_event)
5716     return NO;
5718   emacs_event->kind = NS_NONKEY_EVENT;
5719   emacs_event->code = KEY_NS_OPEN_FILE_LINE;
5720   ns_input_file = append2 (ns_input_file, build_string ([fileName UTF8String]));
5721   ns_input_line = Qnil; /* can be start or cons start,end */
5722   emacs_event->modifiers =0;
5723   EV_TRAILER (theEvent);
5725   return YES;
5729 /* **************************************************************************
5731       EmacsApp delegate implementation
5733    ************************************************************************** */
5735 - (void)applicationDidFinishLaunching: (NSNotification *)notification
5736 /* --------------------------------------------------------------------------
5737      When application is loaded, terminate event loop in ns_term_init.
5738    -------------------------------------------------------------------------- */
5740   NSTRACE ("[EmacsApp applicationDidFinishLaunching:]");
5742 #ifdef NS_IMPL_GNUSTEP
5743   ((EmacsApp *)self)->applicationDidFinishLaunchingCalled = YES;
5744 #endif
5745   [NSApp setServicesProvider: NSApp];
5747   [self antialiasThresholdDidChange:nil];
5748 #ifdef NS_IMPL_COCOA
5749   [[NSNotificationCenter defaultCenter]
5750     addObserver:self
5751        selector:@selector(antialiasThresholdDidChange:)
5752            name:NSAntialiasThresholdChangedNotification
5753          object:nil];
5754 #endif
5756 #ifdef NS_IMPL_COCOA
5757   if ([NSApp activationPolicy] == NSApplicationActivationPolicyProhibited) {
5758     /* Set the app's activation policy to regular when we run outside
5759        of a bundle.  This is already done for us by Info.plist when we
5760        run inside a bundle.  */
5761     [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
5762     [NSApp setApplicationIconImage:
5763              [EmacsImage
5764                allocInitFromFile:
5765                  build_string("icons/hicolor/128x128/apps/emacs.png")]];
5766   }
5767 #endif
5769   ns_send_appdefined (-2);
5772 - (void)antialiasThresholdDidChange:(NSNotification *)notification
5774 #ifdef NS_IMPL_COCOA
5775   macfont_update_antialias_threshold ();
5776 #endif
5780 /* Termination sequences:
5781     C-x C-c:
5782     Cmd-Q:
5783     MenuBar | File | Exit:
5784     Select Quit from App menubar:
5785         -terminate
5786         KEY_NS_POWER_OFF, (save-buffers-kill-emacs)
5787         ns_term_shutdown()
5789     Select Quit from Dock menu:
5790     Logout attempt:
5791         -appShouldTerminate
5792           Cancel -> Nothing else
5793           Accept ->
5795           -terminate
5796           KEY_NS_POWER_OFF, (save-buffers-kill-emacs)
5797           ns_term_shutdown()
5801 - (void) terminate: (id)sender
5803   NSTRACE ("[EmacsApp terminate:]");
5805   struct frame *emacsframe = SELECTED_FRAME ();
5807   if (!emacs_event)
5808     return;
5810   emacs_event->kind = NS_NONKEY_EVENT;
5811   emacs_event->code = KEY_NS_POWER_OFF;
5812   emacs_event->arg = Qt; /* mark as non-key event */
5813   EV_TRAILER ((id)nil);
5816 static bool
5817 runAlertPanel(NSString *title,
5818               NSString *msgFormat,
5819               NSString *defaultButton,
5820               NSString *alternateButton)
5822 #ifdef NS_IMPL_GNUSTEP
5823   return NSRunAlertPanel(title, msgFormat, defaultButton, alternateButton, nil)
5824     == NSAlertDefaultReturn;
5825 #else
5826   NSAlert *alert = [[NSAlert alloc] init];
5827   [alert setAlertStyle: NSAlertStyleCritical];
5828   [alert setMessageText: msgFormat];
5829   [alert addButtonWithTitle: defaultButton];
5830   [alert addButtonWithTitle: alternateButton];
5831   NSInteger ret = [alert runModal];
5832   [alert release];
5833   return ret == NSAlertFirstButtonReturn;
5834 #endif
5838 - (NSApplicationTerminateReply)applicationShouldTerminate: (id)sender
5840   NSTRACE ("[EmacsApp applicationShouldTerminate:]");
5842   bool ret;
5844   if (NILP (ns_confirm_quit)) //   || ns_shutdown_properly  --> TO DO
5845     return NSTerminateNow;
5847   ret = runAlertPanel(ns_app_name,
5848                       @"Exit requested.  Would you like to Save Buffers and Exit, or Cancel the request?",
5849                       @"Save Buffers and Exit", @"Cancel");
5851   return ret ? NSTerminateNow : NSTerminateCancel;
5854 static int
5855 not_in_argv (NSString *arg)
5857   int k;
5858   const char *a = [arg UTF8String];
5859   for (k = 1; k < initial_argc; ++k)
5860     if (strcmp (a, initial_argv[k]) == 0) return 0;
5861   return 1;
5864 /* Notification from the Workspace to open a file.  */
5865 - (BOOL)application: sender openFile: (NSString *)file
5867   if (ns_do_open_file || not_in_argv (file))
5868     [ns_pending_files addObject: file];
5869   return YES;
5873 /* Open a file as a temporary file.  */
5874 - (BOOL)application: sender openTempFile: (NSString *)file
5876   if (ns_do_open_file || not_in_argv (file))
5877     [ns_pending_files addObject: file];
5878   return YES;
5882 /* Notification from the Workspace to open a file noninteractively (?).  */
5883 - (BOOL)application: sender openFileWithoutUI: (NSString *)file
5885   if (ns_do_open_file || not_in_argv (file))
5886     [ns_pending_files addObject: file];
5887   return YES;
5890 /* Notification from the Workspace to open multiple files.  */
5891 - (void)application: sender openFiles: (NSArray *)fileList
5893   NSEnumerator *files = [fileList objectEnumerator];
5894   NSString *file;
5895   /* Don't open files from the command line unconditionally,
5896      Cocoa parses the command line wrong, --option value tries to open value
5897      if --option is the last option.  */
5898   while ((file = [files nextObject]) != nil)
5899     if (ns_do_open_file || not_in_argv (file))
5900       [ns_pending_files addObject: file];
5902   [self replyToOpenOrPrint: NSApplicationDelegateReplySuccess];
5907 /* Handle dock menu requests.  */
5908 - (NSMenu *)applicationDockMenu: (NSApplication *) sender
5910   return dockMenu;
5914 /* TODO: these may help w/IO switching between terminal and NSApp.  */
5915 - (void)applicationWillBecomeActive: (NSNotification *)notification
5917   NSTRACE ("[EmacsApp applicationWillBecomeActive:]");
5918   // ns_app_active=YES;
5921 - (void)applicationDidBecomeActive: (NSNotification *)notification
5923   NSTRACE ("[EmacsApp applicationDidBecomeActive:]");
5925 #ifdef NS_IMPL_GNUSTEP
5926   if (! applicationDidFinishLaunchingCalled)
5927     [self applicationDidFinishLaunching:notification];
5928 #endif
5929   // ns_app_active=YES;
5931   ns_update_auto_hide_menu_bar ();
5932   // No constraining takes place when the application is not active.
5933   ns_constrain_all_frames ();
5935 - (void)applicationDidResignActive: (NSNotification *)notification
5937   NSTRACE ("[EmacsApp applicationDidResignActive:]");
5939   // ns_app_active=NO;
5940   ns_send_appdefined (-1);
5945 /* ==========================================================================
5947     EmacsApp aux handlers for managing event loop
5949    ========================================================================== */
5952 - (void)timeout_handler: (NSTimer *)timedEntry
5953 /* --------------------------------------------------------------------------
5954      The timeout specified to ns_select has passed.
5955    -------------------------------------------------------------------------- */
5957   /* NSTRACE ("timeout_handler"); */
5958   ns_send_appdefined (-2);
5961 - (void)sendFromMainThread:(id)unused
5963   ns_send_appdefined (nextappdefined);
5966 - (void)fd_handler:(id)unused
5967 /* --------------------------------------------------------------------------
5968      Check data waiting on file descriptors and terminate if so.
5969    -------------------------------------------------------------------------- */
5971   int result;
5972   int waiting = 1, nfds;
5973   char c;
5975   fd_set readfds, writefds, *wfds;
5976   struct timespec timeout, *tmo;
5977   NSAutoreleasePool *pool = nil;
5979   /* NSTRACE ("fd_handler"); */
5981   for (;;)
5982     {
5983       [pool release];
5984       pool = [[NSAutoreleasePool alloc] init];
5986       if (waiting)
5987         {
5988           fd_set fds;
5989           FD_ZERO (&fds);
5990           FD_SET (selfds[0], &fds);
5991           result = select (selfds[0]+1, &fds, NULL, NULL, NULL);
5992           if (result > 0 && read (selfds[0], &c, 1) == 1 && c == 'g')
5993             waiting = 0;
5994         }
5995       else
5996         {
5997           pthread_mutex_lock (&select_mutex);
5998           nfds = select_nfds;
6000           if (select_valid & SELECT_HAVE_READ)
6001             readfds = select_readfds;
6002           else
6003             FD_ZERO (&readfds);
6005           if (select_valid & SELECT_HAVE_WRITE)
6006             {
6007               writefds = select_writefds;
6008               wfds = &writefds;
6009             }
6010           else
6011             wfds = NULL;
6012           if (select_valid & SELECT_HAVE_TMO)
6013             {
6014               timeout = select_timeout;
6015               tmo = &timeout;
6016             }
6017           else
6018             tmo = NULL;
6020           pthread_mutex_unlock (&select_mutex);
6022           FD_SET (selfds[0], &readfds);
6023           if (selfds[0] >= nfds) nfds = selfds[0]+1;
6025           result = pselect (nfds, &readfds, wfds, NULL, tmo, NULL);
6027           if (result == 0)
6028             ns_send_appdefined (-2);
6029           else if (result > 0)
6030             {
6031               if (FD_ISSET (selfds[0], &readfds))
6032                 {
6033                   if (read (selfds[0], &c, 1) == 1 && c == 's')
6034                     waiting = 1;
6035                 }
6036               else
6037                 {
6038                   pthread_mutex_lock (&select_mutex);
6039                   if (select_valid & SELECT_HAVE_READ)
6040                     select_readfds = readfds;
6041                   if (select_valid & SELECT_HAVE_WRITE)
6042                     select_writefds = writefds;
6043                   if (select_valid & SELECT_HAVE_TMO)
6044                     select_timeout = timeout;
6045                   pthread_mutex_unlock (&select_mutex);
6047                   ns_send_appdefined (result);
6048                 }
6049             }
6050           waiting = 1;
6051         }
6052     }
6057 /* ==========================================================================
6059     Service provision
6061    ========================================================================== */
6063 /* Called from system: queue for next pass through event loop.  */
6064 - (void)requestService: (NSPasteboard *)pboard
6065               userData: (NSString *)userData
6066                  error: (NSString **)error
6068   [ns_pending_service_names addObject: userData];
6069   [ns_pending_service_args addObject: [NSString stringWithUTF8String:
6070       SSDATA (ns_string_from_pasteboard (pboard))]];
6074 /* Called from ns_read_socket to clear queue.  */
6075 - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
6077   struct frame *emacsframe = SELECTED_FRAME ();
6078   NSEvent *theEvent = [NSApp currentEvent];
6080   NSTRACE ("[EmacsApp fulfillService:withArg:]");
6082   if (!emacs_event)
6083     return NO;
6085   emacs_event->kind = NS_NONKEY_EVENT;
6086   emacs_event->code = KEY_NS_SPI_SERVICE_CALL;
6087   ns_input_spi_name = build_string ([name UTF8String]);
6088   ns_input_spi_arg = build_string ([arg UTF8String]);
6089   emacs_event->modifiers = EV_MODIFIERS (theEvent);
6090   EV_TRAILER (theEvent);
6092   return YES;
6096 @end  /* EmacsApp */
6099 /* ==========================================================================
6101     EmacsView implementation
6103    ========================================================================== */
6106 @implementation EmacsView
6108 /* Needed to inform when window closed from lisp.  */
6109 - (void) setWindowClosing: (BOOL)closing
6111   NSTRACE ("[EmacsView setWindowClosing:%d]", closing);
6113   windowClosing = closing;
6117 - (void)dealloc
6119   NSTRACE ("[EmacsView dealloc]");
6120   [toolbar release];
6121   if (fs_state == FULLSCREEN_BOTH)
6122     [nonfs_window release];
6123   [super dealloc];
6127 /* Called on font panel selection.  */
6128 - (void)changeFont: (id)sender
6130   NSEvent *e = [[self window] currentEvent];
6131   struct face *face = FACE_FROM_ID (emacsframe, DEFAULT_FACE_ID);
6132   struct font *font = face->font;
6133   id newFont;
6134   CGFloat size;
6135   NSFont *nsfont;
6137   NSTRACE ("[EmacsView changeFont:]");
6139   if (!emacs_event)
6140     return;
6142 #ifdef NS_IMPL_GNUSTEP
6143   nsfont = ((struct nsfont_info *)font)->nsfont;
6144 #endif
6145 #ifdef NS_IMPL_COCOA
6146   nsfont = (NSFont *) macfont_get_nsctfont (font);
6147 #endif
6149   if ((newFont = [sender convertFont: nsfont]))
6150     {
6151       SET_FRAME_GARBAGED (emacsframe); /* now needed as of 2008/10 */
6153       emacs_event->kind = NS_NONKEY_EVENT;
6154       emacs_event->modifiers = 0;
6155       emacs_event->code = KEY_NS_CHANGE_FONT;
6157       size = [newFont pointSize];
6158       ns_input_fontsize = make_number (lrint (size));
6159       ns_input_font = build_string ([[newFont familyName] UTF8String]);
6160       EV_TRAILER (e);
6161     }
6165 - (BOOL)acceptsFirstResponder
6167   NSTRACE ("[EmacsView acceptsFirstResponder]");
6168   return YES;
6172 - (void)resetCursorRects
6174   NSRect visible = [self visibleRect];
6175   NSCursor *currentCursor = FRAME_POINTER_TYPE (emacsframe);
6176   NSTRACE ("[EmacsView resetCursorRects]");
6178   if (currentCursor == nil)
6179     currentCursor = [NSCursor arrowCursor];
6181   if (!NSIsEmptyRect (visible))
6182     [self addCursorRect: visible cursor: currentCursor];
6184 #if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 101300
6185 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
6186   if ([currentCursor respondsToSelector: @selector(setOnMouseEntered)])
6187 #endif
6188     [currentCursor setOnMouseEntered: YES];
6189 #endif
6194 /*****************************************************************************/
6195 /* Keyboard handling.  */
6196 #define NS_KEYLOG 0
6198 - (void)keyDown: (NSEvent *)theEvent
6200   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
6201   int code;
6202   unsigned fnKeysym = 0;
6203   static NSMutableArray *nsEvArray;
6204   unsigned int flags = [theEvent modifierFlags];
6206   NSTRACE ("[EmacsView keyDown:]");
6208   /* Rhapsody and macOS give up and down events for the arrow keys.  */
6209   if (ns_fake_keydown == YES)
6210     ns_fake_keydown = NO;
6211   else if ([theEvent type] != NSEventTypeKeyDown)
6212     return;
6214   if (!emacs_event)
6215     return;
6217  if (![[self window] isKeyWindow]
6218      && [[theEvent window] isKindOfClass: [EmacsWindow class]]
6219      /* We must avoid an infinite loop here.  */
6220      && (EmacsView *)[[theEvent window] delegate] != self)
6221    {
6222      /* XXX: There is an occasional condition in which, when Emacs display
6223          updates a different frame from the current one, and temporarily
6224          selects it, then processes some interrupt-driven input
6225          (dispnew.c:3878), OS will send the event to the correct NSWindow, but
6226          for some reason that window has its first responder set to the NSView
6227          most recently updated (I guess), which is not the correct one.  */
6228      [(EmacsView *)[[theEvent window] delegate] keyDown: theEvent];
6229      return;
6230    }
6232   if (nsEvArray == nil)
6233     nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1];
6235   [NSCursor setHiddenUntilMouseMoves: YES];
6237   if (hlinfo->mouse_face_hidden && INTEGERP (Vmouse_highlight))
6238     {
6239       clear_mouse_face (hlinfo);
6240       hlinfo->mouse_face_hidden = 1;
6241     }
6243   if (!processingCompose)
6244     {
6245       /* FIXME: What should happen for key sequences with more than
6246          one character?  */
6247       code = ([[theEvent charactersIgnoringModifiers] length] == 0) ?
6248         0 : [[theEvent charactersIgnoringModifiers] characterAtIndex: 0];
6250       /* Is it a "function key"?  */
6251       /* Note: Sometimes a plain key will have the NSEventModifierFlagNumericPad
6252          flag set (this is probably a bug in the OS).  */
6253       if (code < 0x00ff && (flags&NSEventModifierFlagNumericPad))
6254         {
6255           fnKeysym = ns_convert_key ([theEvent keyCode] | NSEventModifierFlagNumericPad);
6256         }
6257       if (fnKeysym == 0)
6258         {
6259           fnKeysym = ns_convert_key (code);
6260         }
6262       if (fnKeysym)
6263         {
6264           /* COUNTERHACK: map 'Delete' on upper-right main KB to 'Backspace',
6265              because Emacs treats Delete and KP-Delete same (in simple.el).  */
6266           if ((fnKeysym == 0xFFFF && [theEvent keyCode] == 0x33)
6267 #ifdef NS_IMPL_GNUSTEP
6268               /*  GNUstep uses incompatible keycodes, even for those that are
6269                   supposed to be hardware independent.  Just check for delete.
6270                   Keypad delete does not have keysym 0xFFFF.
6271                   See https://savannah.gnu.org/bugs/?25395  */
6272               || (fnKeysym == 0xFFFF && code == 127)
6273 #endif
6274             )
6275             code = 0xFF08; /* backspace */
6276           else
6277             code = fnKeysym;
6278         }
6280       /* The âŒ˜ and âŒ¥ modifiers can be either shift-like (for alternate
6281          character input) or control-like (as command prefix).  If we
6282          have only shift-like modifiers, then we should use the
6283          translated characters (returned by the characters method); if
6284          we have only control-like modifiers, then we should use the
6285          untranslated characters (returned by the
6286          charactersIgnoringModifiers method).  An annoyance happens if
6287          we have both shift-like and control-like modifiers because
6288          the NSEvent API doesn’t let us ignore only some modifiers.
6289          In that case we use UCKeyTranslate (ns_get_shifted_character)
6290          to look up the correct character.  */
6292       /* EV_MODIFIERS2 uses parse_solitary_modifier on all known
6293          modifier keys, which returns 0 for shift-like modifiers.
6294          Therefore its return value is the set of control-like
6295          modifiers.  */
6296       emacs_event->modifiers = EV_MODIFIERS2 (flags);
6298       /* Function keys (such as the F-keys, arrow keys, etc.) set
6299          modifiers as though the fn key has been pressed when it
6300          hasn't.  Also some combinations of fn and a function key
6301          return a different key than was pressed (e.g. fn-<left> gives
6302          <home>).  We need to unset the fn modifier in these cases.
6303          FIXME: Can we avoid setting it in the first place?  */
6304       if (fnKeysym && (flags & NS_FUNCTION_KEY_MASK))
6305         emacs_event->modifiers ^= parse_solitary_modifier (ns_function_modifier);
6307       if (NS_KEYLOG)
6308         fprintf (stderr, "keyDown: code =%x\tfnKey =%x\tflags = %x\tmods = %x\n",
6309                  code, fnKeysym, flags, emacs_event->modifiers);
6311       /* If it was a function key or had control-like modifiers, pass
6312          it directly to Emacs.  */
6313       if (fnKeysym || (emacs_event->modifiers
6314                        && (emacs_event->modifiers != shift_modifier)
6315                        && [[theEvent charactersIgnoringModifiers] length] > 0))
6316         {
6317           emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
6318           /* FIXME: What are the next four lines supposed to do?  */
6319           if (code < 0x20)
6320             code |= (1<<28)|(3<<16);
6321           else if (code == 0x7f)
6322             code |= (1<<28)|(3<<16);
6323           else if (!fnKeysym)
6324             {
6325 #ifdef NS_IMPL_COCOA
6326               /* We potentially have both shift- and control-like
6327                  modifiers in use, so find the correct character
6328                  ignoring any control-like ones.  */
6329               code = ns_get_shifted_character (theEvent);
6330 #endif
6332               /* FIXME: This seems wrong, characters in the range
6333                  [0x80, 0xFF] are not ASCII characters.  Can’t we just
6334                  use MULTIBYTE_CHAR_KEYSTROKE_EVENT here for all kinds
6335                  of characters?  */
6336               emacs_event->kind = code > 0xFF
6337                 ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT;
6338             }
6340           emacs_event->code = code;
6341           EV_TRAILER (theEvent);
6342           processingCompose = NO;
6343           return;
6344         }
6345     }
6347   /* If we get here, a non-function key without control-like modifiers
6348      was hit.  Use interpretKeyEvents, which in turn will call
6349      insertText; see
6350      https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/EventOverview/HandlingKeyEvents/HandlingKeyEvents.html.  */
6352   if (NS_KEYLOG && !processingCompose)
6353     fprintf (stderr, "keyDown: Begin compose sequence.\n");
6355   /* FIXME: interpretKeyEvents doesn’t seem to send insertText if âŒ˜ is
6356      used as shift-like modifier, at least on El Capitan.  Mask it
6357      out.  This shouldn’t be needed though; we should figure out what
6358      the correct way of handling âŒ˜ is.  */
6359   if ([theEvent modifierFlags] & NSEventModifierFlagCommand)
6360     theEvent = [NSEvent keyEventWithType:[theEvent type]
6361                                 location:[theEvent locationInWindow]
6362                            modifierFlags:[theEvent modifierFlags] & ~NSEventModifierFlagCommand
6363                                timestamp:[theEvent timestamp]
6364                             windowNumber:[theEvent windowNumber]
6365                                  context:nil
6366                               characters:[theEvent characters]
6367                         charactersIgnoringModifiers:[theEvent charactersIgnoringModifiers]
6368                                isARepeat:[theEvent isARepeat]
6369                                  keyCode:[theEvent keyCode]];
6371   processingCompose = YES;
6372   /* FIXME: Use [NSArray arrayWithObject:theEvent]?  */
6373   [nsEvArray addObject: theEvent];
6374   [self interpretKeyEvents: nsEvArray];
6375   [nsEvArray removeObject: theEvent];
6379 /* <NSTextInput> implementation (called through [super interpretKeyEvents:]).  */
6382 /* <NSTextInput>: called when done composing;
6383    NOTE: also called when we delete over working text, followed
6384    immediately by doCommandBySelector: deleteBackward:  */
6385 - (void)insertText: (id)aString
6387   NSString *s;
6388   NSUInteger len;
6390   NSTRACE ("[EmacsView insertText:]");
6392   if ([aString isKindOfClass:[NSAttributedString class]])
6393     s = [aString string];
6394   else
6395     s = aString;
6397   len = [s length];
6399   if (NS_KEYLOG)
6400     NSLog (@"insertText '%@'\tlen = %lu", aString, (unsigned long) len);
6401   processingCompose = NO;
6403   if (!emacs_event)
6404     return;
6406   /* First, clear any working text.  */
6407   if (workingText != nil)
6408     [self deleteWorkingText];
6410   /* It might be preferable to use getCharacters:range: below,
6411      cf. https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CocoaPerformance/Articles/StringDrawing.html#//apple_ref/doc/uid/TP40001445-112378.
6412      However, we probably can't use SAFE_NALLOCA here because it might
6413      exit nonlocally.  */
6415   /* Now insert the string as keystrokes.  */
6416   for (NSUInteger i = 0; i < len; i++)
6417     {
6418       NSUInteger code = [s characterAtIndex:i];
6419       if (UTF_16_HIGH_SURROGATE_P (code) && i < len - 1)
6420         {
6421           unichar low = [s characterAtIndex:i + 1];
6422           if (UTF_16_LOW_SURROGATE_P (low))
6423             {
6424               code = surrogates_to_codepoint (low, code);
6425               ++i;
6426             }
6427         }
6428       /* TODO: still need this?  */
6429       if (code == 0x2DC)
6430         code = '~'; /* 0x7E */
6431       if (code != 32) /* Space */
6432         emacs_event->modifiers = 0;
6433       emacs_event->kind
6434         = code > 0xFF ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT;
6435       emacs_event->code = code;
6436       EV_TRAILER ((id)nil);
6437     }
6441 /* <NSTextInput>: inserts display of composing characters.  */
6442 - (void)setMarkedText: (id)aString selectedRange: (NSRange)selRange
6444   NSString *str = [aString respondsToSelector: @selector (string)] ?
6445     [aString string] : aString;
6447   NSTRACE ("[EmacsView setMarkedText:selectedRange:]");
6449   if (NS_KEYLOG)
6450     NSLog (@"setMarkedText '%@' len =%lu range %lu from %lu",
6451            str, (unsigned long)[str length],
6452            (unsigned long)selRange.length,
6453            (unsigned long)selRange.location);
6455   if (workingText != nil)
6456     [self deleteWorkingText];
6457   if ([str length] == 0)
6458     return;
6460   if (!emacs_event)
6461     return;
6463   processingCompose = YES;
6464   workingText = [str copy];
6465   ns_working_text = build_string ([workingText UTF8String]);
6467   emacs_event->kind = NS_TEXT_EVENT;
6468   emacs_event->code = KEY_NS_PUT_WORKING_TEXT;
6469   EV_TRAILER ((id)nil);
6473 /* Delete display of composing characters [not in <NSTextInput>].  */
6474 - (void)deleteWorkingText
6476   NSTRACE ("[EmacsView deleteWorkingText]");
6478   if (workingText == nil)
6479     return;
6480   if (NS_KEYLOG)
6481     NSLog(@"deleteWorkingText len =%lu\n", (unsigned long)[workingText length]);
6482   [workingText release];
6483   workingText = nil;
6484   processingCompose = NO;
6486   if (!emacs_event)
6487     return;
6489   emacs_event->kind = NS_TEXT_EVENT;
6490   emacs_event->code = KEY_NS_UNPUT_WORKING_TEXT;
6491   EV_TRAILER ((id)nil);
6495 - (BOOL)hasMarkedText
6497   NSTRACE ("[EmacsView hasMarkedText]");
6499   return workingText != nil;
6503 - (NSRange)markedRange
6505   NSTRACE ("[EmacsView markedRange]");
6507   NSRange rng = workingText != nil
6508     ? NSMakeRange (0, [workingText length]) : NSMakeRange (NSNotFound, 0);
6509   if (NS_KEYLOG)
6510     NSLog (@"markedRange request");
6511   return rng;
6515 - (void)unmarkText
6517   NSTRACE ("[EmacsView unmarkText]");
6519   if (NS_KEYLOG)
6520     NSLog (@"unmark (accept) text");
6521   [self deleteWorkingText];
6522   processingCompose = NO;
6526 /* Used to position char selection windows, etc.  */
6527 - (NSRect)firstRectForCharacterRange: (NSRange)theRange
6529   NSRect rect;
6530   NSPoint pt;
6531   struct window *win = XWINDOW (FRAME_SELECTED_WINDOW (emacsframe));
6533   NSTRACE ("[EmacsView firstRectForCharacterRange:]");
6535   if (NS_KEYLOG)
6536     NSLog (@"firstRectForCharRange request");
6538   rect.size.width = theRange.length * FRAME_COLUMN_WIDTH (emacsframe);
6539   rect.size.height = FRAME_LINE_HEIGHT (emacsframe);
6540   pt.x = WINDOW_TEXT_TO_FRAME_PIXEL_X (win, win->phys_cursor.x);
6541   pt.y = WINDOW_TO_FRAME_PIXEL_Y (win, win->phys_cursor.y
6542                                        +FRAME_LINE_HEIGHT (emacsframe));
6544   pt = [self convertPoint: pt toView: nil];
6546 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
6547 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6548   if ([[self window] respondsToSelector: @selector(convertRectToScreen:)])
6549     {
6550 #endif
6551       rect.origin = pt;
6552       rect = [(EmacsWindow *) [self window] convertRectToScreen: rect];
6553 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6554     }
6555   else
6556 #endif
6557 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
6558 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 \
6559   || defined (NS_IMPL_GNUSTEP)
6560     {
6561       pt = [[self window] convertBaseToScreen: pt];
6562       rect.origin = pt;
6563     }
6564 #endif
6566   return rect;
6570 - (NSInteger)conversationIdentifier
6572   return (NSInteger)self;
6576 - (void)doCommandBySelector: (SEL)aSelector
6578   NSTRACE ("[EmacsView doCommandBySelector:]");
6580   if (NS_KEYLOG)
6581     NSLog (@"doCommandBySelector: %@", NSStringFromSelector (aSelector));
6583   processingCompose = NO;
6584   if (aSelector == @selector (deleteBackward:))
6585     {
6586       /* Happens when user backspaces over an ongoing composition:
6587          throw a 'delete' into the event queue.  */
6588       if (!emacs_event)
6589         return;
6590       emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
6591       emacs_event->code = 0xFF08;
6592       EV_TRAILER ((id)nil);
6593     }
6596 - (NSArray *)validAttributesForMarkedText
6598   static NSArray *arr = nil;
6599   if (arr == nil) arr = [NSArray new];
6600  /* [[NSArray arrayWithObject: NSUnderlineStyleAttributeName] retain]; */
6601   return arr;
6604 - (NSRange)selectedRange
6606   if (NS_KEYLOG)
6607     NSLog (@"selectedRange request");
6608   return NSMakeRange (NSNotFound, 0);
6611 #if defined (NS_IMPL_COCOA) || GNUSTEP_GUI_MAJOR_VERSION > 0 || \
6612     GNUSTEP_GUI_MINOR_VERSION > 22
6613 - (NSUInteger)characterIndexForPoint: (NSPoint)thePoint
6614 #else
6615 - (unsigned int)characterIndexForPoint: (NSPoint)thePoint
6616 #endif
6618   if (NS_KEYLOG)
6619     NSLog (@"characterIndexForPoint request");
6620   return 0;
6623 - (NSAttributedString *)attributedSubstringFromRange: (NSRange)theRange
6625   static NSAttributedString *str = nil;
6626   if (str == nil) str = [NSAttributedString new];
6627   if (NS_KEYLOG)
6628     NSLog (@"attributedSubstringFromRange request");
6629   return str;
6632 /* End <NSTextInput> implementation.  */
6633 /*****************************************************************************/
6636 /* This is what happens when the user presses a mouse button.  */
6637 - (void)mouseDown: (NSEvent *)theEvent
6639   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6640   NSPoint p = [self convertPoint: [theEvent locationInWindow] fromView: nil];
6642   NSTRACE ("[EmacsView mouseDown:]");
6644   [self deleteWorkingText];
6646   if (!emacs_event)
6647     return;
6649   dpyinfo->last_mouse_frame = emacsframe;
6650   /* Appears to be needed to prevent spurious movement events generated on
6651      button clicks.  */
6652   emacsframe->mouse_moved = 0;
6654   if ([theEvent type] == NSEventTypeScrollWheel)
6655     {
6656 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
6657 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6658       if ([theEvent respondsToSelector:@selector(hasPreciseScrollingDeltas)])
6659         {
6660 #endif
6661           /* If the input device is a touchpad or similar, use precise
6662            * scrolling deltas.  These are measured in pixels, so we
6663            * have to add them up until they exceed one line height,
6664            * then we can send a scroll wheel event.
6665            *
6666            * If the device only has coarse scrolling deltas, like a
6667            * real mousewheel, the deltas represent a ratio of whole
6668            * lines, so round up the number of lines.  This means we
6669            * always send one scroll event per click, but can still
6670            * scroll more than one line if the OS tells us to.
6671            */
6672           bool horizontal;
6673           int lines = 0;
6674           int scrollUp = NO;
6676           /* FIXME: At the top or bottom of the buffer we should
6677            * ignore momentum-phase events.  */
6678           if (! ns_use_mwheel_momentum
6679               && [theEvent momentumPhase] != NSEventPhaseNone)
6680             return;
6682           if ([theEvent hasPreciseScrollingDeltas])
6683             {
6684               static int totalDeltaX, totalDeltaY;
6685               int lineHeight;
6687               if (NUMBERP (ns_mwheel_line_height))
6688                 lineHeight = XINT (ns_mwheel_line_height);
6689               else
6690                 {
6691                   /* FIXME: Use actual line height instead of the default.  */
6692                   lineHeight = default_line_pixel_height
6693                     (XWINDOW (FRAME_SELECTED_WINDOW (emacsframe)));
6694                 }
6696               if ([theEvent phase] == NSEventPhaseBegan)
6697                 {
6698                   totalDeltaX = 0;
6699                   totalDeltaY = 0;
6700                 }
6702               totalDeltaX += [theEvent scrollingDeltaX];
6703               totalDeltaY += [theEvent scrollingDeltaY];
6705               /* Calculate the number of lines, if any, to scroll, and
6706                * reset the total delta for the direction we're NOT
6707                * scrolling so that small movements don't add up.  */
6708               if (abs (totalDeltaX) > abs (totalDeltaY)
6709                   && abs (totalDeltaX) > lineHeight)
6710                 {
6711                   horizontal = YES;
6712                   scrollUp = totalDeltaX > 0;
6714                   lines = abs (totalDeltaX / lineHeight);
6715                   totalDeltaX = totalDeltaX % lineHeight;
6716                   totalDeltaY = 0;
6717                 }
6718               else if (abs (totalDeltaY) >= abs (totalDeltaX)
6719                        && abs (totalDeltaY) > lineHeight)
6720                 {
6721                   horizontal = NO;
6722                   scrollUp = totalDeltaY > 0;
6724                   lines = abs (totalDeltaY / lineHeight);
6725                   totalDeltaY = totalDeltaY % lineHeight;
6726                   totalDeltaX = 0;
6727                 }
6729               if (lines > 1 && ! ns_use_mwheel_acceleration)
6730                 lines = 1;
6731             }
6732           else
6733             {
6734               CGFloat delta;
6736               if ([theEvent scrollingDeltaY] == 0)
6737                 {
6738                   horizontal = YES;
6739                   delta = [theEvent scrollingDeltaX];
6740                 }
6741               else
6742                 {
6743                   horizontal = NO;
6744                   delta = [theEvent scrollingDeltaY];
6745                 }
6747               lines = (ns_use_mwheel_acceleration)
6748                 ? ceil (fabs (delta)) : 1;
6750               scrollUp = delta > 0;
6751             }
6753           if (lines == 0)
6754             return;
6756           emacs_event->kind = horizontal ? HORIZ_WHEEL_EVENT : WHEEL_EVENT;
6757           emacs_event->arg = (make_number (lines));
6759           emacs_event->code = 0;
6760           emacs_event->modifiers = EV_MODIFIERS (theEvent) |
6761             (scrollUp ? up_modifier : down_modifier);
6762 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6763         }
6764       else
6765 #endif
6766 #endif /* defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
6767 #if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6768         {
6769           CGFloat delta = [theEvent deltaY];
6770           /* Mac notebooks send wheel events with delta equal to 0
6771              when trackpad scrolling.  */
6772           if (delta == 0)
6773             {
6774               delta = [theEvent deltaX];
6775               if (delta == 0)
6776                 {
6777                   NSTRACE_MSG ("deltaIsZero");
6778                   return;
6779                 }
6780               emacs_event->kind = HORIZ_WHEEL_EVENT;
6781             }
6782           else
6783             emacs_event->kind = WHEEL_EVENT;
6785           emacs_event->code = 0;
6786           emacs_event->modifiers = EV_MODIFIERS (theEvent) |
6787             ((delta > 0) ? up_modifier : down_modifier);
6788         }
6789 #endif
6790     }
6791   else
6792     {
6793       emacs_event->kind = MOUSE_CLICK_EVENT;
6794       emacs_event->code = EV_BUTTON (theEvent);
6795       emacs_event->modifiers = EV_MODIFIERS (theEvent)
6796                              | EV_UDMODIFIERS (theEvent);
6797     }
6799   XSETINT (emacs_event->x, lrint (p.x));
6800   XSETINT (emacs_event->y, lrint (p.y));
6801   EV_TRAILER (theEvent);
6802   return;
6806 - (void)rightMouseDown: (NSEvent *)theEvent
6808   NSTRACE ("[EmacsView rightMouseDown:]");
6809   [self mouseDown: theEvent];
6813 - (void)otherMouseDown: (NSEvent *)theEvent
6815   NSTRACE ("[EmacsView otherMouseDown:]");
6816   [self mouseDown: theEvent];
6820 - (void)mouseUp: (NSEvent *)theEvent
6822   NSTRACE ("[EmacsView mouseUp:]");
6823   [self mouseDown: theEvent];
6827 - (void)rightMouseUp: (NSEvent *)theEvent
6829   NSTRACE ("[EmacsView rightMouseUp:]");
6830   [self mouseDown: theEvent];
6834 - (void)otherMouseUp: (NSEvent *)theEvent
6836   NSTRACE ("[EmacsView otherMouseUp:]");
6837   [self mouseDown: theEvent];
6841 - (void) scrollWheel: (NSEvent *)theEvent
6843   NSTRACE ("[EmacsView scrollWheel:]");
6844   [self mouseDown: theEvent];
6848 /* Tell emacs the mouse has moved.  */
6849 - (void)mouseMoved: (NSEvent *)e
6851   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
6852   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6853   Lisp_Object frame;
6854   NSPoint pt;
6856   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsView mouseMoved:]");
6858   dpyinfo->last_mouse_movement_time = EV_TIMESTAMP (e);
6859   pt = [self convertPoint: [e locationInWindow] fromView: nil];
6860   dpyinfo->last_mouse_motion_x = pt.x;
6861   dpyinfo->last_mouse_motion_y = pt.y;
6863   /* Update any mouse face.  */
6864   if (hlinfo->mouse_face_hidden)
6865     {
6866       hlinfo->mouse_face_hidden = 0;
6867       clear_mouse_face (hlinfo);
6868     }
6870   /* Tooltip handling.  */
6871   previous_help_echo_string = help_echo_string;
6872   help_echo_string = Qnil;
6874   if (!NILP (Vmouse_autoselect_window))
6875     {
6876       NSTRACE_MSG ("mouse_autoselect_window");
6877       static Lisp_Object last_mouse_window;
6878       Lisp_Object window
6879         = window_from_coordinates (emacsframe, pt.x, pt.y, 0, 0);
6881       if (WINDOWP (window)
6882           && !EQ (window, last_mouse_window)
6883           && !EQ (window, selected_window)
6884           && (!NILP (focus_follows_mouse)
6885               || (EQ (XWINDOW (window)->frame,
6886                       XWINDOW (selected_window)->frame))))
6887         {
6888           NSTRACE_MSG ("in_window");
6889           emacs_event->kind = SELECT_WINDOW_EVENT;
6890           emacs_event->frame_or_window = window;
6891           EV_TRAILER2 (e);
6892         }
6893       /* Remember the last window where we saw the mouse.  */
6894       last_mouse_window = window;
6895     }
6897   if (!note_mouse_movement (emacsframe, pt.x, pt.y))
6898     help_echo_string = previous_help_echo_string;
6900   XSETFRAME (frame, emacsframe);
6901   if (!NILP (help_echo_string) || !NILP (previous_help_echo_string))
6902     {
6903       /* NOTE: help_echo_{window,pos,object} are set in xdisp.c
6904          (note_mouse_highlight), which is called through the
6905          note_mouse_movement () call above.  */
6906       any_help_event_p = YES;
6907       gen_help_event (help_echo_string, frame, help_echo_window,
6908                       help_echo_object, help_echo_pos);
6909     }
6911   if (emacsframe->mouse_moved && send_appdefined)
6912     ns_send_appdefined (-1);
6916 - (void)mouseDragged: (NSEvent *)e
6918   NSTRACE ("[EmacsView mouseDragged:]");
6919   [self mouseMoved: e];
6923 - (void)rightMouseDragged: (NSEvent *)e
6925   NSTRACE ("[EmacsView rightMouseDragged:]");
6926   [self mouseMoved: e];
6930 - (void)otherMouseDragged: (NSEvent *)e
6932   NSTRACE ("[EmacsView otherMouseDragged:]");
6933   [self mouseMoved: e];
6937 - (BOOL)windowShouldClose: (id)sender
6939   NSEvent *e =[[self window] currentEvent];
6941   NSTRACE ("[EmacsView windowShouldClose:]");
6942   windowClosing = YES;
6943   if (!emacs_event)
6944     return NO;
6945   emacs_event->kind = DELETE_WINDOW_EVENT;
6946   emacs_event->modifiers = 0;
6947   emacs_event->code = 0;
6948   EV_TRAILER (e);
6949   /* Don't close this window, let this be done from lisp code.  */
6950   return NO;
6953 - (void) updateFrameSize: (BOOL) delay
6955   NSWindow *window = [self window];
6956   NSRect wr = [window frame];
6957   int extra = 0;
6958   int oldc = cols, oldr = rows;
6959   int oldw = FRAME_PIXEL_WIDTH (emacsframe);
6960   int oldh = FRAME_PIXEL_HEIGHT (emacsframe);
6961   int neww, newh;
6963   NSTRACE ("[EmacsView updateFrameSize:]");
6964   NSTRACE_SIZE ("Original size", NSMakeSize (oldw, oldh));
6965   NSTRACE_RECT ("Original frame", wr);
6966   NSTRACE_MSG  ("Original columns: %d", cols);
6967   NSTRACE_MSG  ("Original rows: %d", rows);
6969   if (! [self isFullscreen])
6970     {
6971       int toolbar_height;
6972 #ifdef NS_IMPL_GNUSTEP
6973       // GNUstep does not always update the tool bar height.  Force it.
6974       if (toolbar && [toolbar isVisible])
6975           update_frame_tool_bar (emacsframe);
6976 #endif
6978       toolbar_height = FRAME_TOOLBAR_HEIGHT (emacsframe);
6979       if (toolbar_height < 0)
6980         toolbar_height = 35;
6982       extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
6983         + toolbar_height;
6984     }
6986   if (wait_for_tool_bar)
6987     {
6988       /* The toolbar height is always 0 in fullscreen and undecorated
6989          frames, so don't wait for it to become available.  */
6990       if (FRAME_TOOLBAR_HEIGHT (emacsframe) == 0
6991           && FRAME_UNDECORATED (emacsframe) == false
6992           && ! [self isFullscreen])
6993         {
6994           NSTRACE_MSG ("Waiting for toolbar");
6995           return;
6996         }
6997       wait_for_tool_bar = NO;
6998     }
7000   neww = (int)wr.size.width - emacsframe->border_width;
7001   newh = (int)wr.size.height - extra;
7003   NSTRACE_SIZE ("New size", NSMakeSize (neww, newh));
7004   NSTRACE_MSG ("FRAME_TOOLBAR_HEIGHT: %d", FRAME_TOOLBAR_HEIGHT (emacsframe));
7005   NSTRACE_MSG ("FRAME_NS_TITLEBAR_HEIGHT: %d", FRAME_NS_TITLEBAR_HEIGHT (emacsframe));
7007   cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, neww);
7008   rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe, newh);
7010   if (cols < MINWIDTH)
7011     cols = MINWIDTH;
7013   if (rows < MINHEIGHT)
7014     rows = MINHEIGHT;
7016   NSTRACE_MSG ("New columns: %d", cols);
7017   NSTRACE_MSG ("New rows: %d", rows);
7019   if (oldr != rows || oldc != cols || neww != oldw || newh != oldh)
7020     {
7021       NSView *view = FRAME_NS_VIEW (emacsframe);
7023       change_frame_size (emacsframe,
7024                          FRAME_PIXEL_TO_TEXT_WIDTH (emacsframe, neww),
7025                          FRAME_PIXEL_TO_TEXT_HEIGHT (emacsframe, newh),
7026                          0, delay, 0, 1);
7027       SET_FRAME_GARBAGED (emacsframe);
7028       cancel_mouse_face (emacsframe);
7030       /* The next two lines set the frame to the same size as we've
7031          already set above.  We need to do this when we switch back
7032          from non-native fullscreen, in other circumstances it appears
7033          to be a noop.  (bug#28872) */
7034       wr = NSMakeRect (0, 0, neww, newh);
7035       [view setFrame: wr];
7037       // To do: consider using [NSNotificationCenter postNotificationName:].
7038       [self windowDidMove: // Update top/left.
7039               [NSNotification notificationWithName:NSWindowDidMoveNotification
7040                                             object:[view window]]];
7041     }
7042   else
7043     {
7044       NSTRACE_MSG ("No change");
7045     }
7048 - (NSSize)windowWillResize: (NSWindow *)sender toSize: (NSSize)frameSize
7049 /* Normalize frame to gridded text size.  */
7051   int extra = 0;
7053   NSTRACE ("[EmacsView windowWillResize:toSize: " NSTRACE_FMT_SIZE "]",
7054            NSTRACE_ARG_SIZE (frameSize));
7055   NSTRACE_RECT   ("[sender frame]", [sender frame]);
7056   NSTRACE_FSTYPE ("fs_state", fs_state);
7058   if (!FRAME_LIVE_P (emacsframe))
7059     return frameSize;
7061   if (fs_state == FULLSCREEN_MAXIMIZED
7062       && (maximized_width != (int)frameSize.width
7063           || maximized_height != (int)frameSize.height))
7064     [self setFSValue: FULLSCREEN_NONE];
7065   else if (fs_state == FULLSCREEN_WIDTH
7066            && maximized_width != (int)frameSize.width)
7067     [self setFSValue: FULLSCREEN_NONE];
7068   else if (fs_state == FULLSCREEN_HEIGHT
7069            && maximized_height != (int)frameSize.height)
7070     [self setFSValue: FULLSCREEN_NONE];
7072   if (fs_state == FULLSCREEN_NONE)
7073     maximized_width = maximized_height = -1;
7075   if (! [self isFullscreen])
7076     {
7077       extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
7078         + FRAME_TOOLBAR_HEIGHT (emacsframe);
7079     }
7081   cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, frameSize.width);
7082   if (cols < MINWIDTH)
7083     cols = MINWIDTH;
7085   rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe,
7086                                            frameSize.height - extra);
7087   if (rows < MINHEIGHT)
7088     rows = MINHEIGHT;
7089 #ifdef NS_IMPL_COCOA
7090   {
7091     /* This sets window title to have size in it; the wm does this under GS.  */
7092     NSRect r = [[self window] frame];
7093     if (r.size.height == frameSize.height && r.size.width == frameSize.width)
7094       {
7095         if (old_title != 0)
7096           {
7097             xfree (old_title);
7098             old_title = 0;
7099           }
7100       }
7101     else if (fs_state == FULLSCREEN_NONE && ! maximizing_resize
7102              && [[self window] title] != NULL)
7103       {
7104         char *size_title;
7105         NSWindow *window = [self window];
7106         if (old_title == 0)
7107           {
7108             char *t = strdup ([[[self window] title] UTF8String]);
7109             char *pos = strstr (t, "  â€”  ");
7110             if (pos)
7111               *pos = '\0';
7112             old_title = t;
7113           }
7114         size_title = xmalloc (strlen (old_title) + 40);
7115         esprintf (size_title, "%s  â€”  (%d x %d)", old_title, cols, rows);
7116         [window setTitle: [NSString stringWithUTF8String: size_title]];
7117         [window display];
7118         xfree (size_title);
7119       }
7120   }
7121 #endif /* NS_IMPL_COCOA */
7123   NSTRACE_MSG ("cols: %d  rows: %d", cols, rows);
7125   /* Restrict the new size to the text grid.
7127      Don't restrict the width if the user only adjusted the height, and
7128      vice versa.  (Without this, the frame would shrink, and move
7129      slightly, if the window was resized by dragging one of its
7130      borders.)  */
7131   if (!frame_resize_pixelwise)
7132     {
7133       NSRect r = [[self window] frame];
7135       if (r.size.width != frameSize.width)
7136         {
7137           frameSize.width =
7138             FRAME_TEXT_COLS_TO_PIXEL_WIDTH  (emacsframe, cols);
7139         }
7141       if (r.size.height != frameSize.height)
7142         {
7143           frameSize.height =
7144             FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (emacsframe, rows) + extra;
7145         }
7146     }
7148   NSTRACE_RETURN_SIZE (frameSize);
7150   return frameSize;
7154 - (void)windowDidResize: (NSNotification *)notification
7156   NSTRACE ("[EmacsView windowDidResize:]");
7157   if (!FRAME_LIVE_P (emacsframe))
7158     {
7159       NSTRACE_MSG ("Ignored (frame dead)");
7160       return;
7161     }
7162   if (emacsframe->output_data.ns->in_animation)
7163     {
7164       NSTRACE_MSG ("Ignored (in animation)");
7165       return;
7166     }
7168   if (! [self fsIsNative])
7169     {
7170       NSWindow *theWindow = [notification object];
7171       /* We can get notification on the non-FS window when in
7172          fullscreen mode.  */
7173       if ([self window] != theWindow) return;
7174     }
7176   NSTRACE_RECT ("frame", [[notification object] frame]);
7178 #ifdef NS_IMPL_GNUSTEP
7179   NSWindow *theWindow = [notification object];
7181    /* In GNUstep, at least currently, it's possible to get a didResize
7182       without getting a willResize, therefore we need to act as if we got
7183       the willResize now.  */
7184   NSSize sz = [theWindow frame].size;
7185   sz = [self windowWillResize: theWindow toSize: sz];
7186 #endif /* NS_IMPL_GNUSTEP */
7188   if (cols > 0 && rows > 0)
7189     {
7190       [self updateFrameSize: YES];
7191     }
7193   ns_send_appdefined (-1);
7196 #ifdef NS_IMPL_COCOA
7197 - (void)viewDidEndLiveResize
7199   NSTRACE ("[EmacsView viewDidEndLiveResize]");
7201   [super viewDidEndLiveResize];
7202   if (old_title != 0)
7203     {
7204       [[self window] setTitle: [NSString stringWithUTF8String: old_title]];
7205       xfree (old_title);
7206       old_title = 0;
7207     }
7208   maximizing_resize = NO;
7210 #endif /* NS_IMPL_COCOA */
7213 - (void)windowDidBecomeKey: (NSNotification *)notification
7214 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
7216   [self windowDidBecomeKey];
7220 - (void)windowDidBecomeKey      /* for direct calls */
7222   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
7223   struct frame *old_focus = dpyinfo->x_focus_frame;
7225   NSTRACE ("[EmacsView windowDidBecomeKey]");
7227   if (emacsframe != old_focus)
7228     dpyinfo->x_focus_frame = emacsframe;
7230   ns_frame_rehighlight (emacsframe);
7232   if (emacs_event)
7233     {
7234       emacs_event->kind = FOCUS_IN_EVENT;
7235       EV_TRAILER ((id)nil);
7236     }
7240 - (void)windowDidResignKey: (NSNotification *)notification
7241 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
7243   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
7244   BOOL is_focus_frame = dpyinfo->x_focus_frame == emacsframe;
7245   NSTRACE ("[EmacsView windowDidResignKey:]");
7247   if (is_focus_frame)
7248     dpyinfo->x_focus_frame = 0;
7250   emacsframe->mouse_moved = 0;
7251   ns_frame_rehighlight (emacsframe);
7253   /* FIXME: for some reason needed on second and subsequent clicks away
7254             from sole-frame Emacs to get hollow box to show.  */
7255   if (!windowClosing && [[self window] isVisible] == YES)
7256     {
7257       x_update_cursor (emacsframe, 1);
7258       x_set_frame_alpha (emacsframe);
7259     }
7261   if (any_help_event_p)
7262     {
7263       Lisp_Object frame;
7264       XSETFRAME (frame, emacsframe);
7265       help_echo_string = Qnil;
7266       gen_help_event (Qnil, frame, Qnil, Qnil, 0);
7267     }
7269   if (emacs_event && is_focus_frame)
7270     {
7271       [self deleteWorkingText];
7272       emacs_event->kind = FOCUS_OUT_EVENT;
7273       EV_TRAILER ((id)nil);
7274     }
7278 - (void)windowWillMiniaturize: sender
7280   NSTRACE ("[EmacsView windowWillMiniaturize:]");
7284 - (void)setFrame:(NSRect)frameRect
7286   NSTRACE ("[EmacsView setFrame:" NSTRACE_FMT_RECT "]",
7287            NSTRACE_ARG_RECT (frameRect));
7289   [super setFrame:(NSRect)frameRect];
7293 - (BOOL)isFlipped
7295   return YES;
7299 - (BOOL)isOpaque
7301   return NO;
7305 - (void)createToolbar: (struct frame *)f
7307   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
7308   NSWindow *window = [view window];
7310   toolbar = [[EmacsToolbar alloc] initForView: self withIdentifier:
7311                    [NSString stringWithFormat: @"Emacs Frame %d",
7312                              ns_window_num]];
7313   [toolbar setVisible: NO];
7314   [window setToolbar: toolbar];
7316   /* Don't set frame garbaged until tool bar is up to date?
7317      This avoids an extra clear and redraw (flicker) at frame creation.  */
7318   if (FRAME_EXTERNAL_TOOL_BAR (f)) wait_for_tool_bar = YES;
7319   else wait_for_tool_bar = NO;
7322 #ifdef NS_IMPL_COCOA
7323   {
7324     NSButton *toggleButton;
7325     toggleButton = [window standardWindowButton: NSWindowToolbarButton];
7326     [toggleButton setTarget: self];
7327     [toggleButton setAction: @selector (toggleToolbar: )];
7328   }
7329 #endif
7333 - (instancetype) initFrameFromEmacs: (struct frame *)f
7335   NSRect r, wr;
7336   Lisp_Object tem;
7337   NSWindow *win;
7338   NSColor *col;
7339   NSString *name;
7341   NSTRACE ("[EmacsView initFrameFromEmacs:]");
7342   NSTRACE_MSG ("cols:%d lines:%d", f->text_cols, f->text_lines);
7344   windowClosing = NO;
7345   processingCompose = NO;
7346   scrollbarsNeedingUpdate = 0;
7347   fs_state = FULLSCREEN_NONE;
7348   fs_before_fs = next_maximized = -1;
7350   fs_is_native = NO;
7351 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7352 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7353   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7354 #endif
7355     fs_is_native = ns_use_native_fullscreen;
7356 #endif
7358   maximized_width = maximized_height = -1;
7359   nonfs_window = nil;
7361   ns_userRect = NSMakeRect (0, 0, 0, 0);
7362   r = NSMakeRect (0, 0, FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, f->text_cols),
7363                  FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, f->text_lines));
7364   [self initWithFrame: r];
7365   [self setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
7367   FRAME_NS_VIEW (f) = self;
7368   emacsframe = f;
7369 #ifdef NS_IMPL_COCOA
7370   old_title = 0;
7371   maximizing_resize = NO;
7372 #endif
7374   win = [[EmacsWindow alloc]
7375             initWithContentRect: r
7376                       styleMask: (FRAME_UNDECORATED (f)
7377                                   ? FRAME_UNDECORATED_FLAGS
7378                                   : FRAME_DECORATED_FLAGS)
7379                         backing: NSBackingStoreBuffered
7380                           defer: YES];
7382 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7383 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7384   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7385 #endif
7386     [win setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
7387 #endif
7389   wr = [win frame];
7390   bwidth = f->border_width = wr.size.width - r.size.width;
7392   [win setAcceptsMouseMovedEvents: YES];
7393   [win setDelegate: self];
7394 #if !defined (NS_IMPL_COCOA) || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7395 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7396   if ([win respondsToSelector: @selector(useOptimizedDrawing:)])
7397 #endif
7398     [win useOptimizedDrawing: YES];
7399 #endif
7401   [[win contentView] addSubview: self];
7403   if (ns_drag_types)
7404     [self registerForDraggedTypes: ns_drag_types];
7406   tem = f->name;
7407   name = [NSString stringWithUTF8String:
7408                    NILP (tem) ? "Emacs" : SSDATA (tem)];
7409   [win setTitle: name];
7411   /* toolbar support */
7412   if (! FRAME_UNDECORATED (f))
7413     [self createToolbar: f];
7415 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
7416 #ifndef NSAppKitVersionNumber10_10
7417 #define NSAppKitVersionNumber10_10 1343
7418 #endif
7420   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_10
7421       && FRAME_NS_APPEARANCE (f) != ns_appearance_aqua)
7422     win.appearance = [NSAppearance
7423                           appearanceNamed: NSAppearanceNameVibrantDark];
7424 #endif
7426 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
7427   if ([win respondsToSelector: @selector(titlebarAppearsTransparent)])
7428     win.titlebarAppearsTransparent = FRAME_NS_TRANSPARENT_TITLEBAR (f);
7429 #endif
7431   tem = f->icon_name;
7432   if (!NILP (tem))
7433     [win setMiniwindowTitle:
7434            [NSString stringWithUTF8String: SSDATA (tem)]];
7436   if (FRAME_PARENT_FRAME (f) != NULL)
7437     {
7438       NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
7439       [parent addChildWindow: win
7440                      ordered: NSWindowAbove];
7441     }
7443   if (FRAME_Z_GROUP (f) != z_group_none)
7444       win.level = NSNormalWindowLevel
7445         + (FRAME_Z_GROUP_BELOW (f) ? -1 : 1);
7447   {
7448     NSScreen *screen = [win screen];
7450     if (screen != 0)
7451       {
7452         NSPoint pt = NSMakePoint
7453           (IN_BOUND (-SCREENMAX, f->left_pos
7454                      + NS_PARENT_WINDOW_LEFT_POS (f), SCREENMAX),
7455            IN_BOUND (-SCREENMAX,
7456                      NS_PARENT_WINDOW_TOP_POS (f) - f->top_pos,
7457                      SCREENMAX));
7459         [win setFrameTopLeftPoint: pt];
7461         NSTRACE_RECT ("new frame", [win frame]);
7462       }
7463   }
7465   [win makeFirstResponder: self];
7467   col = ns_lookup_indexed_color (NS_FACE_BACKGROUND
7468                                  (FACE_FROM_ID (emacsframe, DEFAULT_FACE_ID)),
7469                                  emacsframe);
7470   [win setBackgroundColor: col];
7471   if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7472     [win setOpaque: NO];
7474 #if !defined (NS_IMPL_COCOA) \
7475   || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7476 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7477   if ([self respondsToSelector: @selector(allocateGState)])
7478 #endif
7479     [self allocateGState];
7480 #endif
7481   [NSApp registerServicesMenuSendTypes: ns_send_types
7482                            returnTypes: [NSArray array]];
7484   /* macOS Sierra automatically enables tabbed windows.  We can't
7485      allow this to be enabled until it's available on a Free system.
7486      Currently it only happens by accident and is buggy anyway.  */
7487 #if defined (NS_IMPL_COCOA) \
7488   && MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
7489 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101200
7490   if ([win respondsToSelector: @selector(setTabbingMode:)])
7491 #endif
7492     [win setTabbingMode: NSWindowTabbingModeDisallowed];
7493 #endif
7495   ns_window_num++;
7496   return self;
7500 - (void)windowDidMove: sender
7502   NSWindow *win = [self window];
7503   NSRect r = [win frame];
7504   NSArray *screens = [NSScreen screens];
7505   NSScreen *screen = [screens objectAtIndex: 0];
7507   NSTRACE ("[EmacsView windowDidMove:]");
7509   if (!emacsframe->output_data.ns)
7510     return;
7511   if (screen != nil)
7512     {
7513       emacsframe->left_pos = r.origin.x - NS_PARENT_WINDOW_LEFT_POS (emacsframe);
7514       emacsframe->top_pos =
7515         NS_PARENT_WINDOW_TOP_POS (emacsframe) - (r.origin.y + r.size.height);
7517       if (emacs_event)
7518         {
7519           emacs_event->kind = MOVE_FRAME_EVENT;
7520           EV_TRAILER ((id)nil);
7521         }
7522     }
7526 /* Called AFTER method below, but before our windowWillResize call there leads
7527    to windowDidResize -> x_set_window_size.  Update emacs' notion of frame
7528    location so set_window_size moves the frame.  */
7529 - (BOOL)windowShouldZoom: (NSWindow *)sender toFrame: (NSRect)newFrame
7531   NSTRACE (("[EmacsView windowShouldZoom:toFrame:" NSTRACE_FMT_RECT "]"
7532             NSTRACE_FMT_RETURN "YES"),
7533            NSTRACE_ARG_RECT (newFrame));
7535   emacsframe->output_data.ns->zooming = 1;
7536   return YES;
7540 /* Override to do something slightly nonstandard, but nice.  First click on
7541    zoom button will zoom vertically.  Second will zoom completely.  Third
7542    returns to original.  */
7543 - (NSRect)windowWillUseStandardFrame:(NSWindow *)sender
7544                         defaultFrame:(NSRect)defaultFrame
7546   // TODO: Rename to "currentFrame" and assign "result" properly in
7547   // all paths.
7548   NSRect result = [sender frame];
7550   NSTRACE (("[EmacsView windowWillUseStandardFrame:defaultFrame:"
7551             NSTRACE_FMT_RECT "]"),
7552            NSTRACE_ARG_RECT (defaultFrame));
7553   NSTRACE_FSTYPE ("fs_state", fs_state);
7554   NSTRACE_FSTYPE ("fs_before_fs", fs_before_fs);
7555   NSTRACE_FSTYPE ("next_maximized", next_maximized);
7556   NSTRACE_RECT   ("ns_userRect", ns_userRect);
7557   NSTRACE_RECT   ("[sender frame]", [sender frame]);
7559   if (fs_before_fs != -1) /* Entering fullscreen */
7560     {
7561       NSTRACE_MSG ("Entering fullscreen");
7562       result = defaultFrame;
7563     }
7564   else
7565     {
7566       // Save the window size and position (frame) before the resize.
7567       if (fs_state != FULLSCREEN_MAXIMIZED
7568           && fs_state != FULLSCREEN_WIDTH)
7569         {
7570           ns_userRect.size.width = result.size.width;
7571           ns_userRect.origin.x   = result.origin.x;
7572         }
7574       if (fs_state != FULLSCREEN_MAXIMIZED
7575           && fs_state != FULLSCREEN_HEIGHT)
7576         {
7577           ns_userRect.size.height = result.size.height;
7578           ns_userRect.origin.y    = result.origin.y;
7579         }
7581       NSTRACE_RECT ("ns_userRect (2)", ns_userRect);
7583       if (next_maximized == FULLSCREEN_HEIGHT
7584           || (next_maximized == -1
7585               && abs ((int)(defaultFrame.size.height - result.size.height))
7586               > FRAME_LINE_HEIGHT (emacsframe)))
7587         {
7588           /* first click */
7589           NSTRACE_MSG ("FULLSCREEN_HEIGHT");
7590           maximized_height = result.size.height = defaultFrame.size.height;
7591           maximized_width = -1;
7592           result.origin.y = defaultFrame.origin.y;
7593           if (ns_userRect.size.height != 0)
7594             {
7595               result.origin.x = ns_userRect.origin.x;
7596               result.size.width = ns_userRect.size.width;
7597             }
7598           [self setFSValue: FULLSCREEN_HEIGHT];
7599 #ifdef NS_IMPL_COCOA
7600           maximizing_resize = YES;
7601 #endif
7602         }
7603       else if (next_maximized == FULLSCREEN_WIDTH)
7604         {
7605           NSTRACE_MSG ("FULLSCREEN_WIDTH");
7606           maximized_width = result.size.width = defaultFrame.size.width;
7607           maximized_height = -1;
7608           result.origin.x = defaultFrame.origin.x;
7609           if (ns_userRect.size.width != 0)
7610             {
7611               result.origin.y = ns_userRect.origin.y;
7612               result.size.height = ns_userRect.size.height;
7613             }
7614           [self setFSValue: FULLSCREEN_WIDTH];
7615         }
7616       else if (next_maximized == FULLSCREEN_MAXIMIZED
7617                || (next_maximized == -1
7618                    && abs ((int)(defaultFrame.size.width - result.size.width))
7619                    > FRAME_COLUMN_WIDTH (emacsframe)))
7620         {
7621           NSTRACE_MSG ("FULLSCREEN_MAXIMIZED");
7623           result = defaultFrame; /* second click */
7624           maximized_width = result.size.width;
7625           maximized_height = result.size.height;
7626           [self setFSValue: FULLSCREEN_MAXIMIZED];
7627 #ifdef NS_IMPL_COCOA
7628           maximizing_resize = YES;
7629 #endif
7630         }
7631       else
7632         {
7633           /* restore */
7634           NSTRACE_MSG ("Restore");
7635           result = ns_userRect.size.height ? ns_userRect : result;
7636           NSTRACE_RECT ("restore (2)", result);
7637           ns_userRect = NSMakeRect (0, 0, 0, 0);
7638 #ifdef NS_IMPL_COCOA
7639           maximizing_resize = fs_state != FULLSCREEN_NONE;
7640 #endif
7641           [self setFSValue: FULLSCREEN_NONE];
7642           maximized_width = maximized_height = -1;
7643         }
7644     }
7646   if (fs_before_fs == -1) next_maximized = -1;
7648   NSTRACE_RECT   ("Final ns_userRect", ns_userRect);
7649   NSTRACE_MSG    ("Final maximized_width: %d", maximized_width);
7650   NSTRACE_MSG    ("Final maximized_height: %d", maximized_height);
7651   NSTRACE_FSTYPE ("Final next_maximized", next_maximized);
7653   [self windowWillResize: sender toSize: result.size];
7655   NSTRACE_RETURN_RECT (result);
7657   return result;
7661 - (void)windowDidDeminiaturize: sender
7663   NSTRACE ("[EmacsView windowDidDeminiaturize:]");
7664   if (!emacsframe->output_data.ns)
7665     return;
7667   SET_FRAME_ICONIFIED (emacsframe, 0);
7668   SET_FRAME_VISIBLE (emacsframe, 1);
7669   windows_or_buffers_changed = 63;
7671   if (emacs_event)
7672     {
7673       emacs_event->kind = DEICONIFY_EVENT;
7674       EV_TRAILER ((id)nil);
7675     }
7679 - (void)windowDidExpose: sender
7681   NSTRACE ("[EmacsView windowDidExpose:]");
7682   if (!emacsframe->output_data.ns)
7683     return;
7685   SET_FRAME_VISIBLE (emacsframe, 1);
7686   SET_FRAME_GARBAGED (emacsframe);
7688   if (send_appdefined)
7689     ns_send_appdefined (-1);
7693 - (void)windowDidMiniaturize: sender
7695   NSTRACE ("[EmacsView windowDidMiniaturize:]");
7696   if (!emacsframe->output_data.ns)
7697     return;
7699   SET_FRAME_ICONIFIED (emacsframe, 1);
7700   SET_FRAME_VISIBLE (emacsframe, 0);
7702   if (emacs_event)
7703     {
7704       emacs_event->kind = ICONIFY_EVENT;
7705       EV_TRAILER ((id)nil);
7706     }
7709 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7710 - (NSApplicationPresentationOptions)window:(NSWindow *)window
7711       willUseFullScreenPresentationOptions:
7712   (NSApplicationPresentationOptions)proposedOptions
7714   return proposedOptions|NSApplicationPresentationAutoHideToolbar;
7716 #endif
7718 - (void)windowWillEnterFullScreen:(NSNotification *)notification
7720   NSTRACE ("[EmacsView windowWillEnterFullScreen:]");
7721   [self windowWillEnterFullScreen];
7723 - (void)windowWillEnterFullScreen /* provided for direct calls */
7725   NSTRACE ("[EmacsView windowWillEnterFullScreen]");
7726   fs_before_fs = fs_state;
7729 - (void)windowDidEnterFullScreen:(NSNotification *)notification
7731   NSTRACE ("[EmacsView windowDidEnterFullScreen:]");
7732   [self windowDidEnterFullScreen];
7735 - (void)windowDidEnterFullScreen /* provided for direct calls */
7737   NSTRACE ("[EmacsView windowDidEnterFullScreen]");
7738   [self setFSValue: FULLSCREEN_BOTH];
7739   if (! [self fsIsNative])
7740     {
7741       [self windowDidBecomeKey];
7742       [nonfs_window orderOut:self];
7743     }
7744   else
7745     {
7746       BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (emacsframe) ? YES : NO;
7747 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 \
7748   && MAC_OS_X_VERSION_MIN_REQUIRED <= 1070
7749       unsigned val = (unsigned)[NSApp presentationOptions];
7751       // Mac OS X 10.7 bug fix, the menu won't appear without this.
7752       // val is non-zero on other macOS versions.
7753       if (val == 0)
7754         {
7755           NSApplicationPresentationOptions options
7756             = NSApplicationPresentationAutoHideDock
7757             | NSApplicationPresentationAutoHideMenuBar
7758             | NSApplicationPresentationFullScreen
7759             | NSApplicationPresentationAutoHideToolbar;
7761           [NSApp setPresentationOptions: options];
7762         }
7763 #endif
7764       [toolbar setVisible:tbar_visible];
7765     }
7768 - (void)windowWillExitFullScreen:(NSNotification *)notification
7770   NSTRACE ("[EmacsView windowWillExitFullScreen:]");
7771   [self windowWillExitFullScreen];
7774 - (void)windowWillExitFullScreen /* provided for direct calls */
7776   NSTRACE ("[EmacsView windowWillExitFullScreen]");
7777   if (!FRAME_LIVE_P (emacsframe))
7778     {
7779       NSTRACE_MSG ("Ignored (frame dead)");
7780       return;
7781     }
7782   if (next_maximized != -1)
7783     fs_before_fs = next_maximized;
7786 - (void)windowDidExitFullScreen:(NSNotification *)notification
7788   NSTRACE ("[EmacsView windowDidExitFullScreen:]");
7789   [self windowDidExitFullScreen];
7792 - (void)windowDidExitFullScreen /* provided for direct calls */
7794   NSTRACE ("[EmacsView windowDidExitFullScreen]");
7795   if (!FRAME_LIVE_P (emacsframe))
7796     {
7797       NSTRACE_MSG ("Ignored (frame dead)");
7798       return;
7799     }
7800   [self setFSValue: fs_before_fs];
7801   fs_before_fs = -1;
7802 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7803   [self updateCollectionBehavior];
7804 #endif
7805   if (FRAME_EXTERNAL_TOOL_BAR (emacsframe))
7806     {
7807       [toolbar setVisible:YES];
7808       update_frame_tool_bar (emacsframe);
7809       [self updateFrameSize:YES];
7810       [[self window] display];
7811     }
7812   else
7813     [toolbar setVisible:NO];
7815   if (next_maximized != -1)
7816     [[self window] performZoom:self];
7819 - (BOOL)fsIsNative
7821   return fs_is_native;
7824 - (BOOL)isFullscreen
7826   BOOL res;
7828   if (! fs_is_native)
7829     {
7830       res = (nonfs_window != nil);
7831     }
7832   else
7833     {
7834 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7835       res = (([[self window] styleMask] & NSWindowStyleMaskFullScreen) != 0);
7836 #else
7837       res = NO;
7838 #endif
7839     }
7841   NSTRACE ("[EmacsView isFullscreen] " NSTRACE_FMT_RETURN " %d",
7842            (int) res);
7844   return res;
7847 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7848 - (void)updateCollectionBehavior
7850   NSTRACE ("[EmacsView updateCollectionBehavior]");
7852   if (! [self isFullscreen])
7853     {
7854       NSWindow *win = [self window];
7855       NSWindowCollectionBehavior b = [win collectionBehavior];
7856       if (ns_use_native_fullscreen)
7857         b |= NSWindowCollectionBehaviorFullScreenPrimary;
7858       else
7859         b &= ~NSWindowCollectionBehaviorFullScreenPrimary;
7861       [win setCollectionBehavior: b];
7862 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7863       if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7864 #endif
7865         fs_is_native = ns_use_native_fullscreen;
7866     }
7868 #endif
7870 - (void)toggleFullScreen: (id)sender
7872   NSWindow *w, *fw;
7873   BOOL onFirstScreen;
7874   struct frame *f;
7875   NSRect r, wr;
7876   NSColor *col;
7878   NSTRACE ("[EmacsView toggleFullScreen:]");
7880   if (fs_is_native)
7881     {
7882 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7883 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7884       if ([[self window] respondsToSelector: @selector(toggleFullScreen:)])
7885 #endif
7886         [[self window] toggleFullScreen:sender];
7887 #endif
7888       return;
7889     }
7891   w = [self window];
7892   onFirstScreen = [[w screen] isEqual:[[NSScreen screens] objectAtIndex:0]];
7893   f = emacsframe;
7894   wr = [w frame];
7895   col = ns_lookup_indexed_color (NS_FACE_BACKGROUND
7896                                  (FACE_FROM_ID (f, DEFAULT_FACE_ID)),
7897                                  f);
7899   if (fs_state != FULLSCREEN_BOTH)
7900     {
7901       NSScreen *screen = [w screen];
7903 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
7904       /* Hide ghost menu bar on secondary monitor?  */
7905       if (! onFirstScreen
7906 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
7907           && [NSScreen respondsToSelector: @selector(screensHaveSeparateSpaces)]
7908 #endif
7909           )
7910         onFirstScreen = [NSScreen screensHaveSeparateSpaces];
7911 #endif
7912       /* Hide dock and menubar if we are on the primary screen.  */
7913       if (onFirstScreen)
7914         {
7915 #ifdef NS_IMPL_COCOA
7916           NSApplicationPresentationOptions options
7917             = NSApplicationPresentationAutoHideDock
7918             | NSApplicationPresentationAutoHideMenuBar;
7920           [NSApp setPresentationOptions: options];
7921 #else
7922           [NSMenu setMenuBarVisible:NO];
7923 #endif
7924         }
7926       fw = [[EmacsFSWindow alloc]
7927                        initWithContentRect:[w contentRectForFrameRect:wr]
7928                                  styleMask:NSWindowStyleMaskBorderless
7929                                    backing:NSBackingStoreBuffered
7930                                      defer:YES
7931                                     screen:screen];
7933       [fw setContentView:[w contentView]];
7934       [fw setTitle:[w title]];
7935       [fw setDelegate:self];
7936       [fw setAcceptsMouseMovedEvents: YES];
7937 #if !defined (NS_IMPL_COCOA) \
7938   || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7939 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7940       if ([fw respondsToSelector: @selector(useOptimizedDrawing:)])
7941 #endif
7942         [fw useOptimizedDrawing: YES];
7943 #endif
7944       [fw setBackgroundColor: col];
7945       if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7946         [fw setOpaque: NO];
7948       f->border_width = 0;
7950       nonfs_window = w;
7952       [self windowWillEnterFullScreen];
7953       [fw makeKeyAndOrderFront:NSApp];
7954       [fw makeFirstResponder:self];
7955       [w orderOut:self];
7956       r = [fw frameRectForContentRect:[screen frame]];
7957       [fw setFrame: r display:YES animate:ns_use_fullscreen_animation];
7958       [self windowDidEnterFullScreen];
7959       [fw display];
7960     }
7961   else
7962     {
7963       fw = w;
7964       w = nonfs_window;
7965       nonfs_window = nil;
7967       if (onFirstScreen)
7968         {
7969 #ifdef NS_IMPL_COCOA
7970           [NSApp setPresentationOptions: NSApplicationPresentationDefault];
7971 #else
7972           [NSMenu setMenuBarVisible:YES];
7973 #endif
7974         }
7976       [w setContentView:[fw contentView]];
7977       [w setBackgroundColor: col];
7978       if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7979         [w setOpaque: NO];
7981       f->border_width = bwidth;
7983       // To do: consider using [NSNotificationCenter postNotificationName:] to
7984       // send notifications.
7986       [self windowWillExitFullScreen];
7987       [fw setFrame: [w frame] display:YES animate:ns_use_fullscreen_animation];
7988       [fw close];
7989       [w makeKeyAndOrderFront:NSApp];
7990       [self windowDidExitFullScreen];
7991       [self updateFrameSize:YES];
7992     }
7995 - (void)handleFS
7997   NSTRACE ("[EmacsView handleFS]");
7999   if (fs_state != emacsframe->want_fullscreen)
8000     {
8001       if (fs_state == FULLSCREEN_BOTH)
8002         {
8003           NSTRACE_MSG ("fs_state == FULLSCREEN_BOTH");
8004           [self toggleFullScreen:self];
8005         }
8007       switch (emacsframe->want_fullscreen)
8008         {
8009         case FULLSCREEN_BOTH:
8010           NSTRACE_MSG ("FULLSCREEN_BOTH");
8011           [self toggleFullScreen:self];
8012           break;
8013         case FULLSCREEN_WIDTH:
8014           NSTRACE_MSG ("FULLSCREEN_WIDTH");
8015           next_maximized = FULLSCREEN_WIDTH;
8016           if (fs_state != FULLSCREEN_BOTH)
8017             [[self window] performZoom:self];
8018           break;
8019         case FULLSCREEN_HEIGHT:
8020           NSTRACE_MSG ("FULLSCREEN_HEIGHT");
8021           next_maximized = FULLSCREEN_HEIGHT;
8022           if (fs_state != FULLSCREEN_BOTH)
8023             [[self window] performZoom:self];
8024           break;
8025         case FULLSCREEN_MAXIMIZED:
8026           NSTRACE_MSG ("FULLSCREEN_MAXIMIZED");
8027           next_maximized = FULLSCREEN_MAXIMIZED;
8028           if (fs_state != FULLSCREEN_BOTH)
8029             [[self window] performZoom:self];
8030           break;
8031         case FULLSCREEN_NONE:
8032           NSTRACE_MSG ("FULLSCREEN_NONE");
8033           if (fs_state != FULLSCREEN_BOTH)
8034             {
8035               next_maximized = FULLSCREEN_NONE;
8036               [[self window] performZoom:self];
8037             }
8038           break;
8039         }
8041       emacsframe->want_fullscreen = FULLSCREEN_NONE;
8042     }
8046 - (void) setFSValue: (int)value
8048   NSTRACE ("[EmacsView setFSValue:" NSTRACE_FMT_FSTYPE "]",
8049            NSTRACE_ARG_FSTYPE(value));
8051   Lisp_Object lval = Qnil;
8052   switch (value)
8053     {
8054     case FULLSCREEN_BOTH:
8055       lval = Qfullboth;
8056       break;
8057     case FULLSCREEN_WIDTH:
8058       lval = Qfullwidth;
8059       break;
8060     case FULLSCREEN_HEIGHT:
8061       lval = Qfullheight;
8062       break;
8063     case FULLSCREEN_MAXIMIZED:
8064       lval = Qmaximized;
8065       break;
8066     }
8067   store_frame_param (emacsframe, Qfullscreen, lval);
8068   fs_state = value;
8071 - (void)mouseEntered: (NSEvent *)theEvent
8073   NSTRACE ("[EmacsView mouseEntered:]");
8074   if (emacsframe)
8075     FRAME_DISPLAY_INFO (emacsframe)->last_mouse_movement_time
8076       = EV_TIMESTAMP (theEvent);
8080 - (void)mouseExited: (NSEvent *)theEvent
8082   Mouse_HLInfo *hlinfo = emacsframe ? MOUSE_HL_INFO (emacsframe) : NULL;
8084   NSTRACE ("[EmacsView mouseExited:]");
8086   if (!hlinfo)
8087     return;
8089   FRAME_DISPLAY_INFO (emacsframe)->last_mouse_movement_time
8090     = EV_TIMESTAMP (theEvent);
8092   if (emacsframe == hlinfo->mouse_face_mouse_frame)
8093     {
8094       clear_mouse_face (hlinfo);
8095       hlinfo->mouse_face_mouse_frame = 0;
8096     }
8100 - (instancetype)menuDown: sender
8102   NSTRACE ("[EmacsView menuDown:]");
8103   if (context_menu_value == -1)
8104     context_menu_value = [sender tag];
8105   else
8106     {
8107       NSInteger tag = [sender tag];
8108       find_and_call_menu_selection (emacsframe, emacsframe->menu_bar_items_used,
8109                                     emacsframe->menu_bar_vector,
8110                                     (void *)tag);
8111     }
8113   ns_send_appdefined (-1);
8114   return self;
8118 - (EmacsToolbar *)toolbar
8120   return toolbar;
8124 /* This gets called on toolbar button click.  */
8125 - (instancetype)toolbarClicked: (id)item
8127   NSEvent *theEvent;
8128   int idx = [item tag] * TOOL_BAR_ITEM_NSLOTS;
8130   NSTRACE ("[EmacsView toolbarClicked:]");
8132   if (!emacs_event)
8133     return self;
8135   /* Send first event (for some reason two needed).  */
8136   theEvent = [[self window] currentEvent];
8137   emacs_event->kind = TOOL_BAR_EVENT;
8138   XSETFRAME (emacs_event->arg, emacsframe);
8139   EV_TRAILER (theEvent);
8141   emacs_event->kind = TOOL_BAR_EVENT;
8142   /* XSETINT (emacs_event->code, 0); */
8143   emacs_event->arg = AREF (emacsframe->tool_bar_items,
8144                            idx + TOOL_BAR_ITEM_KEY);
8145   emacs_event->modifiers = EV_MODIFIERS (theEvent);
8146   EV_TRAILER (theEvent);
8147   return self;
8151 - (instancetype)toggleToolbar: (id)sender
8153   NSTRACE ("[EmacsView toggleToolbar:]");
8155   if (!emacs_event)
8156     return self;
8158   emacs_event->kind = NS_NONKEY_EVENT;
8159   emacs_event->code = KEY_NS_TOGGLE_TOOLBAR;
8160   EV_TRAILER ((id)nil);
8161   return self;
8165 - (void)drawRect: (NSRect)rect
8167   int x = NSMinX (rect), y = NSMinY (rect);
8168   int width = NSWidth (rect), height = NSHeight (rect);
8170   NSTRACE ("[EmacsView drawRect:" NSTRACE_FMT_RECT "]",
8171            NSTRACE_ARG_RECT(rect));
8173   if (!emacsframe || !emacsframe->output_data.ns)
8174     return;
8176   ns_clear_frame_area (emacsframe, x, y, width, height);
8177   block_input ();
8178   expose_frame (emacsframe, x, y, width, height);
8179   unblock_input ();
8181   /*
8182     drawRect: may be called (at least in Mac OS X 10.5) for invisible
8183     views as well for some reason.  Thus, do not infer visibility
8184     here.
8186     emacsframe->async_visible = 1;
8187     emacsframe->async_iconified = 0;
8188   */
8192 /* NSDraggingDestination protocol methods.  Actually this is not really a
8193    protocol, but a category of Object.  O well...  */
8195 -(NSDragOperation) draggingEntered: (id <NSDraggingInfo>) sender
8197   NSTRACE ("[EmacsView draggingEntered:]");
8198   return NSDragOperationGeneric;
8202 -(BOOL)prepareForDragOperation: (id <NSDraggingInfo>) sender
8204   return YES;
8208 -(BOOL)performDragOperation: (id <NSDraggingInfo>) sender
8210   id pb;
8211   int x, y;
8212   NSString *type;
8213   NSEvent *theEvent = [[self window] currentEvent];
8214   NSPoint position;
8215   NSDragOperation op = [sender draggingSourceOperationMask];
8216   int modifiers = 0;
8218   NSTRACE ("[EmacsView performDragOperation:]");
8220   if (!emacs_event)
8221     return NO;
8223   position = [self convertPoint: [sender draggingLocation] fromView: nil];
8224   x = lrint (position.x);  y = lrint (position.y);
8226   pb = [sender draggingPasteboard];
8227   type = [pb availableTypeFromArray: ns_drag_types];
8229   if (! (op & (NSDragOperationMove|NSDragOperationDelete)) &&
8230       // URL drags contain all operations (0xf), don't allow all to be set.
8231       (op & 0xf) != 0xf)
8232     {
8233       if (op & NSDragOperationLink)
8234         modifiers |= NSEventModifierFlagControl;
8235       if (op & NSDragOperationCopy)
8236         modifiers |= NSEventModifierFlagOption;
8237       if (op & NSDragOperationGeneric)
8238         modifiers |= NSEventModifierFlagCommand;
8239     }
8241   modifiers = EV_MODIFIERS2 (modifiers);
8242   if (type == 0)
8243     {
8244       return NO;
8245     }
8246   else if ([type isEqualToString: NSFilenamesPboardType])
8247     {
8248       NSArray *files;
8249       NSEnumerator *fenum;
8250       NSString *file;
8252       if (!(files = [pb propertyListForType: type]))
8253         return NO;
8255       fenum = [files objectEnumerator];
8256       while ( (file = [fenum nextObject]) )
8257         {
8258           emacs_event->kind = DRAG_N_DROP_EVENT;
8259           XSETINT (emacs_event->x, x);
8260           XSETINT (emacs_event->y, y);
8261           emacs_event->modifiers = modifiers;
8262           emacs_event->arg =  list2 (Qfile, build_string ([file UTF8String]));
8263           EV_TRAILER (theEvent);
8264         }
8265       return YES;
8266     }
8267   else if ([type isEqualToString: NSURLPboardType])
8268     {
8269       NSURL *url = [NSURL URLFromPasteboard: pb];
8270       if (url == nil) return NO;
8272       emacs_event->kind = DRAG_N_DROP_EVENT;
8273       XSETINT (emacs_event->x, x);
8274       XSETINT (emacs_event->y, y);
8275       emacs_event->modifiers = modifiers;
8276       emacs_event->arg =  list2 (Qurl,
8277                                  build_string ([[url absoluteString]
8278                                                  UTF8String]));
8279       EV_TRAILER (theEvent);
8281       if ([url isFileURL] != NO)
8282         {
8283           NSString *file = [url path];
8284           ns_input_file = append2 (ns_input_file,
8285                                    build_string ([file UTF8String]));
8286         }
8287       return YES;
8288     }
8289   else if ([type isEqualToString: NSStringPboardType]
8290            || [type isEqualToString: NSTabularTextPboardType])
8291     {
8292       NSString *data;
8294       if (! (data = [pb stringForType: type]))
8295         return NO;
8297       emacs_event->kind = DRAG_N_DROP_EVENT;
8298       XSETINT (emacs_event->x, x);
8299       XSETINT (emacs_event->y, y);
8300       emacs_event->modifiers = modifiers;
8301       emacs_event->arg =  list2 (Qnil, build_string ([data UTF8String]));
8302       EV_TRAILER (theEvent);
8303       return YES;
8304     }
8305   else
8306     {
8307       fprintf (stderr, "Invalid data type in dragging pasteboard");
8308       return NO;
8309     }
8313 - (id) validRequestorForSendType: (NSString *)typeSent
8314                       returnType: (NSString *)typeReturned
8316   NSTRACE ("[EmacsView validRequestorForSendType:returnType:]");
8317   if (typeSent != nil && [ns_send_types indexOfObject: typeSent] != NSNotFound
8318       && typeReturned == nil)
8319     {
8320       if (! NILP (ns_get_local_selection (QPRIMARY, QUTF8_STRING)))
8321         return self;
8322     }
8324   return [super validRequestorForSendType: typeSent
8325                                returnType: typeReturned];
8329 /* The next two methods are part of NSServicesRequests informal protocol,
8330    supposedly called when a services menu item is chosen from this app.
8331    But this should not happen because we override the services menu with our
8332    own entries which call ns-perform-service.
8333    Nonetheless, it appeared to happen (under strange circumstances): bug#1435.
8334    So let's at least stub them out until further investigation can be done.  */
8336 - (BOOL) readSelectionFromPasteboard: (NSPasteboard *)pb
8338   /* We could call ns_string_from_pasteboard(pboard) here but then it should
8339      be written into the buffer in place of the existing selection.
8340      Ordinary service calls go through functions defined in ns-win.el.  */
8341   return NO;
8344 - (BOOL) writeSelectionToPasteboard: (NSPasteboard *)pb types: (NSArray *)types
8346   NSArray *typesDeclared;
8347   Lisp_Object val;
8349   NSTRACE ("[EmacsView writeSelectionToPasteboard:types:]");
8351   /* We only support NSStringPboardType.  */
8352   if ([types containsObject:NSStringPboardType] == NO) {
8353     return NO;
8354   }
8356   val = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8357   if (CONSP (val) && SYMBOLP (XCAR (val)))
8358     {
8359       val = XCDR (val);
8360       if (CONSP (val) && NILP (XCDR (val)))
8361         val = XCAR (val);
8362     }
8363   if (! STRINGP (val))
8364     return NO;
8366   typesDeclared = [NSArray arrayWithObject:NSStringPboardType];
8367   [pb declareTypes:typesDeclared owner:nil];
8368   ns_string_to_pasteboard (pb, val);
8369   return YES;
8373 /* setMini = YES means set from internal (gives a finder icon), NO means set nil
8374    (gives a miniaturized version of the window); currently we use the latter for
8375    frames whose active buffer doesn't correspond to any file
8376    (e.g., '*scratch*').  */
8377 - (instancetype)setMiniwindowImage: (BOOL) setMini
8379   id image = [[self window] miniwindowImage];
8380   NSTRACE ("[EmacsView setMiniwindowImage:%d]", setMini);
8382   /* NOTE: under Cocoa miniwindowImage always returns nil, documentation
8383      about "AppleDockIconEnabled" notwithstanding, however the set message
8384      below has its effect nonetheless.  */
8385   if (image != emacsframe->output_data.ns->miniimage)
8386     {
8387       if (image && [image isKindOfClass: [EmacsImage class]])
8388         [image release];
8389       [[self window] setMiniwindowImage:
8390                        setMini ? emacsframe->output_data.ns->miniimage : nil];
8391     }
8393   return self;
8397 - (void) setRows: (int) r andColumns: (int) c
8399   NSTRACE ("[EmacsView setRows:%d andColumns:%d]", r, c);
8400   rows = r;
8401   cols = c;
8404 - (int) fullscreenState
8406   return fs_state;
8409 @end  /* EmacsView */
8413 /* ==========================================================================
8415     EmacsWindow implementation
8417    ========================================================================== */
8419 @implementation EmacsWindow
8421 #ifdef NS_IMPL_COCOA
8422 - (id)accessibilityAttributeValue:(NSString *)attribute
8424   Lisp_Object str = Qnil;
8425   struct frame *f = SELECTED_FRAME ();
8426   struct buffer *curbuf = XBUFFER (XWINDOW (f->selected_window)->contents);
8428   NSTRACE ("[EmacsWindow accessibilityAttributeValue:]");
8430   if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
8431     return NSAccessibilityTextFieldRole;
8433   if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]
8434       && curbuf && ! NILP (BVAR (curbuf, mark_active)))
8435     {
8436       str = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8437     }
8438   else if (curbuf && [attribute isEqualToString:NSAccessibilityValueAttribute])
8439     {
8440       if (! NILP (BVAR (curbuf, mark_active)))
8441           str = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8443       if (NILP (str))
8444         {
8445           ptrdiff_t start_byte = BUF_BEGV_BYTE (curbuf);
8446           ptrdiff_t byte_range = BUF_ZV_BYTE (curbuf) - start_byte;
8447           ptrdiff_t range = BUF_ZV (curbuf) - BUF_BEGV (curbuf);
8449           if (! NILP (BVAR (curbuf, enable_multibyte_characters)))
8450             str = make_uninit_multibyte_string (range, byte_range);
8451           else
8452             str = make_uninit_string (range);
8453           /* To check: This returns emacs-utf-8, which is a superset of utf-8.
8454              Is this a problem?  */
8455           memcpy (SDATA (str), BYTE_POS_ADDR (start_byte), byte_range);
8456         }
8457     }
8460   if (! NILP (str))
8461     {
8462       if (CONSP (str) && SYMBOLP (XCAR (str)))
8463         {
8464           str = XCDR (str);
8465           if (CONSP (str) && NILP (XCDR (str)))
8466             str = XCAR (str);
8467         }
8468       if (STRINGP (str))
8469         {
8470           const char *utfStr = SSDATA (str);
8471           NSString *nsStr = [NSString stringWithUTF8String: utfStr];
8472           return nsStr;
8473         }
8474     }
8476   return [super accessibilityAttributeValue:attribute];
8478 #endif /* NS_IMPL_COCOA */
8480 /* Constrain size and placement of a frame.
8482    By returning the original "frameRect", the frame is not
8483    constrained. This can lead to unwanted situations where, for
8484    example, the menu bar covers the frame.
8486    The default implementation (accessed using "super") constrains the
8487    frame to the visible area of SCREEN, minus the menu bar (if
8488    present) and the Dock.  Note that default implementation also calls
8489    windowWillResize, with the frame it thinks should have.  (This can
8490    make the frame exit maximized mode.)
8492    Note that this should work in situations where multiple monitors
8493    are present.  Common configurations are side-by-side monitors and a
8494    monitor on top of another (e.g. when a laptop is placed under a
8495    large screen).  */
8496 - (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
8498   NSTRACE ("[EmacsWindow constrainFrameRect:" NSTRACE_FMT_RECT " toScreen:]",
8499              NSTRACE_ARG_RECT (frameRect));
8501 #ifdef NS_IMPL_COCOA
8502 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
8503   // If separate spaces is on, it is like each screen is independent.  There is
8504   // no spanning of frames across screens.
8505   if (
8506 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
8507       [NSScreen respondsToSelector: @selector(screensHaveSeparateSpaces)] &&
8508 #endif
8509       [NSScreen screensHaveSeparateSpaces])
8510     {
8511       NSTRACE_MSG ("Screens have separate spaces");
8512       frameRect = [super constrainFrameRect:frameRect toScreen:screen];
8513       NSTRACE_RETURN_RECT (frameRect);
8514       return frameRect;
8515     }
8516   else
8517 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 */
8519     // Check that the proposed frameRect is visible in at least one
8520     // screen.  If it is not, ask the system to reposition it (only
8521     // for non-child windows).
8523     if (!FRAME_PARENT_FRAME (((EmacsView *)[self delegate])->emacsframe))
8524     {
8525       NSArray *screens = [NSScreen screens];
8526       NSUInteger nr_screens = [screens count];
8528       int i;
8529       BOOL frame_on_screen = NO;
8531       for (i = 0; i < nr_screens; ++i)
8532         {
8533           NSScreen *s = [screens objectAtIndex: i];
8534           NSRect scrRect = [s frame];
8536           if (NSIntersectsRect(frameRect, scrRect))
8537             {
8538               frame_on_screen = YES;
8539               break;
8540             }
8541         }
8543       if (!frame_on_screen)
8544         {
8545           NSTRACE_MSG ("Frame outside screens; constraining");
8546           frameRect = [super constrainFrameRect:frameRect toScreen:screen];
8547           NSTRACE_RETURN_RECT (frameRect);
8548           return frameRect;
8549         }
8550     }
8551 #endif
8553   return constrain_frame_rect(frameRect,
8554                               [(EmacsView *)[self delegate] isFullscreen]);
8558 - (void)performZoom:(id)sender
8560   NSTRACE ("[EmacsWindow performZoom:]");
8562   return [super performZoom:sender];
8565 - (void)zoom:(id)sender
8567   NSTRACE ("[EmacsWindow zoom:]");
8569   ns_update_auto_hide_menu_bar();
8571   // Below are three zoom implementations.  In the final commit, the
8572   // idea is that the last should be included.
8574 #if 0
8575   // Native zoom done using the standard zoom animation.  Size of the
8576   // resulting frame reduced to accommodate the Dock and, if present,
8577   // the menu-bar.
8578   [super zoom:sender];
8580 #elif 0
8581   // Native zoom done using the standard zoom animation, plus an
8582   // explicit resize to cover the full screen, except the menu-bar and
8583   // dock, if present.
8584   [super zoom:sender];
8586   // After the native zoom, resize the resulting frame to fill the
8587   // entire screen, except the menu-bar.
8588   //
8589   // This works for all practical purposes.  (The only minor oddity is
8590   // when transiting from full-height frame to a maximized, the
8591   // animation reduces the height of the frame slightly (to the 4
8592   // pixels needed to accommodate the Doc) before it snaps back into
8593   // full height.  The user would need a very trained eye to spot
8594   // this.)
8595   NSScreen * screen = [self screen];
8596   if (screen != nil)
8597     {
8598       int fs_state = [(EmacsView *)[self delegate] fullscreenState];
8600       NSTRACE_FSTYPE ("fullscreenState", fs_state);
8602       NSRect sr = [screen frame];
8603       struct EmacsMargins margins
8604         = ns_screen_margins_ignoring_hidden_dock(screen);
8606       NSRect wr = [self frame];
8607       NSTRACE_RECT ("Rect after zoom", wr);
8609       NSRect newWr = wr;
8611       if (fs_state == FULLSCREEN_MAXIMIZED
8612           || fs_state == FULLSCREEN_HEIGHT)
8613         {
8614           newWr.origin.y = sr.origin.y + margins.bottom;
8615           newWr.size.height = sr.size.height - margins.top - margins.bottom;
8616         }
8618       if (fs_state == FULLSCREEN_MAXIMIZED
8619           || fs_state == FULLSCREEN_WIDTH)
8620         {
8621           newWr.origin.x = sr.origin.x + margins.left;
8622           newWr.size.width = sr.size.width - margins.right - margins.left;
8623         }
8625       if (newWr.size.width     != wr.size.width
8626           || newWr.size.height != wr.size.height
8627           || newWr.origin.x    != wr.origin.x
8628           || newWr.origin.y    != wr.origin.y)
8629         {
8630           NSTRACE_MSG ("New frame different");
8631           [self setFrame: newWr display: NO];
8632         }
8633     }
8634 #else
8635   // Non-native zoom which is done instantaneously.  The resulting
8636   // frame covers the entire screen, except the menu-bar and dock, if
8637   // present.
8638   NSScreen * screen = [self screen];
8639   if (screen != nil)
8640     {
8641       NSRect sr = [screen frame];
8642       struct EmacsMargins margins
8643         = ns_screen_margins_ignoring_hidden_dock(screen);
8645       sr.size.height -= (margins.top + margins.bottom);
8646       sr.size.width  -= (margins.left + margins.right);
8647       sr.origin.x += margins.left;
8648       sr.origin.y += margins.bottom;
8650       sr = [[self delegate] windowWillUseStandardFrame:self
8651                                           defaultFrame:sr];
8652       [self setFrame: sr display: NO];
8653     }
8654 #endif
8657 - (void)setFrame:(NSRect)windowFrame
8658          display:(BOOL)displayViews
8660   NSTRACE ("[EmacsWindow setFrame:" NSTRACE_FMT_RECT " display:%d]",
8661            NSTRACE_ARG_RECT (windowFrame), displayViews);
8663   [super setFrame:windowFrame display:displayViews];
8666 - (void)setFrame:(NSRect)windowFrame
8667          display:(BOOL)displayViews
8668          animate:(BOOL)performAnimation
8670   NSTRACE ("[EmacsWindow setFrame:" NSTRACE_FMT_RECT
8671            " display:%d performAnimation:%d]",
8672            NSTRACE_ARG_RECT (windowFrame), displayViews, performAnimation);
8674   [super setFrame:windowFrame display:displayViews animate:performAnimation];
8677 - (void)setFrameTopLeftPoint:(NSPoint)point
8679   NSTRACE ("[EmacsWindow setFrameTopLeftPoint:" NSTRACE_FMT_POINT "]",
8680            NSTRACE_ARG_POINT (point));
8682   [super setFrameTopLeftPoint:point];
8685 - (BOOL)canBecomeKeyWindow
8687   return !FRAME_NO_ACCEPT_FOCUS (((EmacsView *)[self delegate])->emacsframe);
8689 @end /* EmacsWindow */
8692 @implementation EmacsFSWindow
8694 - (BOOL)canBecomeKeyWindow
8696   return YES;
8699 - (BOOL)canBecomeMainWindow
8701   return YES;
8704 @end
8706 /* ==========================================================================
8708     EmacsScroller implementation
8710    ========================================================================== */
8713 @implementation EmacsScroller
8715 /* for repeat button push */
8716 #define SCROLL_BAR_FIRST_DELAY 0.5
8717 #define SCROLL_BAR_CONTINUOUS_DELAY (1.0 / 15)
8719 + (CGFloat) scrollerWidth
8721   /* TODO: if we want to allow variable widths, this is the place to do it,
8722            however neither GNUstep nor Cocoa support it very well.  */
8723   CGFloat r;
8724 #if defined (NS_IMPL_COCOA) \
8725   && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
8726 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
8727   if ([NSScroller respondsToSelector:
8728                     @selector(scrollerWidthForControlSize:scrollerStyle:)])
8729 #endif
8730     r = [NSScroller scrollerWidthForControlSize: NSControlSizeRegular
8731                                   scrollerStyle: NSScrollerStyleLegacy];
8732 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
8733   else
8734 #endif
8735 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
8736 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 \
8737   || defined (NS_IMPL_GNUSTEP)
8738     r = [NSScroller scrollerWidth];
8739 #endif
8740   return r;
8743 - (instancetype)initFrame: (NSRect )r window: (Lisp_Object)nwin
8745   NSTRACE ("[EmacsScroller initFrame: window:]");
8747   if (r.size.width > r.size.height)
8748       horizontal = YES;
8749   else
8750       horizontal = NO;
8752   [super initWithFrame: r/*NSMakeRect (0, 0, 0, 0)*/];
8753   [self setContinuous: YES];
8754   [self setEnabled: YES];
8756   /* Ensure auto resizing of scrollbars occurs within the emacs frame's view
8757      locked against the top and bottom edges, and right edge on macOS, where
8758      scrollers are on right.  */
8759 #ifdef NS_IMPL_GNUSTEP
8760   [self setAutoresizingMask: NSViewMaxXMargin | NSViewHeightSizable];
8761 #else
8762   [self setAutoresizingMask: NSViewMinXMargin | NSViewHeightSizable];
8763 #endif
8765   window = XWINDOW (nwin);
8766   condemned = NO;
8767   if (horizontal)
8768     pixel_length = NSWidth (r);
8769   else
8770     pixel_length = NSHeight (r);
8771   if (pixel_length == 0) pixel_length = 1;
8772   min_portion = 20 / pixel_length;
8774   frame = XFRAME (window->frame);
8775   if (FRAME_LIVE_P (frame))
8776     {
8777       int i;
8778       EmacsView *view = FRAME_NS_VIEW (frame);
8779       NSView *sview = [[view window] contentView];
8780       NSArray *subs = [sview subviews];
8782       /* Disable optimization stopping redraw of other scrollbars.  */
8783       view->scrollbarsNeedingUpdate = 0;
8784       for (i =[subs count]-1; i >= 0; i--)
8785         if ([[subs objectAtIndex: i] isKindOfClass: [EmacsScroller class]])
8786           view->scrollbarsNeedingUpdate++;
8787       [sview addSubview: self];
8788     }
8790   /* [self setFrame: r]; */
8792   return self;
8796 - (void)setFrame: (NSRect)newRect
8798   NSTRACE ("[EmacsScroller setFrame:]");
8800   /* block_input (); */
8801   if (horizontal)
8802     pixel_length = NSWidth (newRect);
8803   else
8804     pixel_length = NSHeight (newRect);
8805   if (pixel_length == 0) pixel_length = 1;
8806   min_portion = 20 / pixel_length;
8807   [super setFrame: newRect];
8808   /* unblock_input (); */
8812 - (void)dealloc
8814   NSTRACE ("[EmacsScroller dealloc]");
8815   if (window)
8816     {
8817       if (horizontal)
8818         wset_horizontal_scroll_bar (window, Qnil);
8819       else
8820         wset_vertical_scroll_bar (window, Qnil);
8821     }
8822   window = 0;
8823   [super dealloc];
8827 - (instancetype)condemn
8829   NSTRACE ("[EmacsScroller condemn]");
8830   condemned =YES;
8831   return self;
8835 - (instancetype)reprieve
8837   NSTRACE ("[EmacsScroller reprieve]");
8838   condemned =NO;
8839   return self;
8843 -(bool)judge
8845   NSTRACE ("[EmacsScroller judge]");
8846   bool ret = condemned;
8847   if (condemned)
8848     {
8849       EmacsView *view;
8850       block_input ();
8851       /* Ensure other scrollbar updates after deletion.  */
8852       view = (EmacsView *)FRAME_NS_VIEW (frame);
8853       if (view != nil)
8854         view->scrollbarsNeedingUpdate++;
8855       if (window)
8856         {
8857           if (horizontal)
8858             wset_horizontal_scroll_bar (window, Qnil);
8859           else
8860             wset_vertical_scroll_bar (window, Qnil);
8861         }
8862       window = 0;
8863       [self removeFromSuperview];
8864       [self release];
8865       unblock_input ();
8866     }
8867   return ret;
8871 - (void)resetCursorRects
8873   NSRect visible = [self visibleRect];
8874   NSTRACE ("[EmacsScroller resetCursorRects]");
8876   if (!NSIsEmptyRect (visible))
8877     [self addCursorRect: visible cursor: [NSCursor arrowCursor]];
8879 #if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 101300
8880 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
8881   if ([[NSCursor arrowCursor] respondsToSelector:
8882                                 @selector(setOnMouseEntered)])
8883 #endif
8884     [[NSCursor arrowCursor] setOnMouseEntered: YES];
8885 #endif
8889 - (int) checkSamePosition: (int) position portion: (int) portion
8890                     whole: (int) whole
8892   return em_position ==position && em_portion ==portion && em_whole ==whole
8893     && portion != whole; /* Needed for resizing empty buffer.  */
8897 - (instancetype)setPosition: (int)position portion: (int)portion whole: (int)whole
8899   NSTRACE ("[EmacsScroller setPosition:portion:whole:]");
8901   em_position = position;
8902   em_portion = portion;
8903   em_whole = whole;
8905   if (portion >= whole)
8906     {
8907 #ifdef NS_IMPL_COCOA
8908       [self setKnobProportion: 1.0];
8909       [self setDoubleValue: 1.0];
8910 #else
8911       [self setFloatValue: 0.0 knobProportion: 1.0];
8912 #endif
8913     }
8914   else
8915     {
8916       float pos;
8917       CGFloat por;
8918       portion = max ((float)whole*min_portion/pixel_length, portion);
8919       pos = (float)position / (whole - portion);
8920       por = (CGFloat)portion/whole;
8921 #ifdef NS_IMPL_COCOA
8922       [self setKnobProportion: por];
8923       [self setDoubleValue: pos];
8924 #else
8925       [self setFloatValue: pos knobProportion: por];
8926 #endif
8927     }
8929   return self;
8932 /* Set up emacs_event.  */
8933 - (void) sendScrollEventAtLoc: (float)loc fromEvent: (NSEvent *)e
8935   Lisp_Object win;
8937   NSTRACE ("[EmacsScroller sendScrollEventAtLoc:fromEvent:]");
8939   if (!emacs_event)
8940     return;
8942   emacs_event->part = last_hit_part;
8943   emacs_event->code = 0;
8944   emacs_event->modifiers = EV_MODIFIERS (e) | down_modifier;
8945   XSETWINDOW (win, window);
8946   emacs_event->frame_or_window = win;
8947   emacs_event->timestamp = EV_TIMESTAMP (e);
8948   emacs_event->arg = Qnil;
8950   if (horizontal)
8951     {
8952       emacs_event->kind = HORIZONTAL_SCROLL_BAR_CLICK_EVENT;
8953       XSETINT (emacs_event->x, em_whole * loc / pixel_length);
8954       XSETINT (emacs_event->y, em_whole);
8955     }
8956   else
8957     {
8958       emacs_event->kind = SCROLL_BAR_CLICK_EVENT;
8959       XSETINT (emacs_event->x, loc);
8960       XSETINT (emacs_event->y, pixel_length-20);
8961     }
8963   if (q_event_ptr)
8964     {
8965       n_emacs_events_pending++;
8966       kbd_buffer_store_event_hold (emacs_event, q_event_ptr);
8967     }
8968   else
8969     hold_event (emacs_event);
8970   EVENT_INIT (*emacs_event);
8971   ns_send_appdefined (-1);
8975 /* Called manually through timer to implement repeated button action
8976    with hold-down.  */
8977 - (instancetype)repeatScroll: (NSTimer *)scrollEntry
8979   NSEvent *e = [[self window] currentEvent];
8980   NSPoint p =  [[self window] mouseLocationOutsideOfEventStream];
8981   BOOL inKnob = [self testPart: p] == NSScrollerKnob;
8983   NSTRACE ("[EmacsScroller repeatScroll:]");
8985   /* Clear timer if need be.  */
8986   if (inKnob || [scroll_repeat_entry timeInterval] == SCROLL_BAR_FIRST_DELAY)
8987     {
8988         [scroll_repeat_entry invalidate];
8989         [scroll_repeat_entry release];
8990         scroll_repeat_entry = nil;
8992         if (inKnob)
8993           return self;
8995         scroll_repeat_entry
8996           = [[NSTimer scheduledTimerWithTimeInterval:
8997                         SCROLL_BAR_CONTINUOUS_DELAY
8998                                             target: self
8999                                           selector: @selector (repeatScroll:)
9000                                           userInfo: 0
9001                                            repeats: YES]
9002               retain];
9003     }
9005   [self sendScrollEventAtLoc: 0 fromEvent: e];
9006   return self;
9010 /* Asynchronous mouse tracking for scroller.  This allows us to dispatch
9011    mouseDragged events without going into a modal loop.  */
9012 - (void)mouseDown: (NSEvent *)e
9014   NSRect sr, kr;
9015   /* hitPart is only updated AFTER event is passed on.  */
9016   NSScrollerPart part = [self testPart: [e locationInWindow]];
9017   CGFloat loc, kloc, pos UNINIT;
9018   int edge = 0;
9020   NSTRACE ("[EmacsScroller mouseDown:]");
9022   switch (part)
9023     {
9024     case NSScrollerDecrementPage:
9025       last_hit_part = horizontal ? scroll_bar_before_handle : scroll_bar_above_handle; break;
9026     case NSScrollerIncrementPage:
9027       last_hit_part = horizontal ? scroll_bar_after_handle : scroll_bar_below_handle; break;
9028     case NSScrollerDecrementLine:
9029       last_hit_part = horizontal ? scroll_bar_left_arrow : scroll_bar_up_arrow; break;
9030     case NSScrollerIncrementLine:
9031       last_hit_part = horizontal ? scroll_bar_right_arrow : scroll_bar_down_arrow; break;
9032     case NSScrollerKnob:
9033       last_hit_part = horizontal ? scroll_bar_horizontal_handle : scroll_bar_handle; break;
9034     case NSScrollerKnobSlot:  /* GNUstep-only */
9035       last_hit_part = scroll_bar_move_ratio; break;
9036     default:  /* NSScrollerNoPart? */
9037       fprintf (stderr, "EmacsScroller-mouseDown: unexpected part %ld\n",
9038                (long) part);
9039       return;
9040     }
9042   if (part == NSScrollerKnob || part == NSScrollerKnobSlot)
9043     {
9044       /* handle, or on GNUstep possibly slot */
9045       NSEvent *fake_event;
9046       int length;
9048       /* compute float loc in slot and mouse offset on knob */
9049       sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot]
9050                       toView: nil];
9051       if (horizontal)
9052         {
9053           length = NSWidth (sr);
9054           loc = ([e locationInWindow].x - NSMinX (sr));
9055         }
9056       else
9057         {
9058           length = NSHeight (sr);
9059           loc = length - ([e locationInWindow].y - NSMinY (sr));
9060         }
9062       if (loc <= 0.0)
9063         {
9064           loc = 0.0;
9065           edge = -1;
9066         }
9067       else if (loc >= length)
9068         {
9069           loc = length;
9070           edge = 1;
9071         }
9073       if (edge)
9074         kloc = 0.5 * edge;
9075       else
9076         {
9077           kr = [self convertRect: [self rectForPart: NSScrollerKnob]
9078                           toView: nil];
9079           if (horizontal)
9080             kloc = ([e locationInWindow].x - NSMinX (kr));
9081           else
9082             kloc = NSHeight (kr) - ([e locationInWindow].y - NSMinY (kr));
9083         }
9084       last_mouse_offset = kloc;
9086       /* if knob, tell emacs a location offset by knob pos
9087          (to indicate top of handle) */
9088       if (part == NSScrollerKnob)
9089         pos = (loc - last_mouse_offset);
9090       else
9091         /* else this is a slot click on GNUstep: go straight there */
9092         pos = loc;
9094       /* If there are buttons in the scroller area, we need to
9095          recalculate pos as emacs expects the scroller slot to take up
9096          the entire available length.  */
9097       if (length != pixel_length)
9098         pos = pos * pixel_length / length;
9100       /* send a fake mouse-up to super to preempt modal -trackKnob: mode */
9101       fake_event = [NSEvent mouseEventWithType: NSEventTypeLeftMouseUp
9102                                       location: [e locationInWindow]
9103                                  modifierFlags: [e modifierFlags]
9104                                      timestamp: [e timestamp]
9105                                   windowNumber: [e windowNumber]
9106                                        context: nil
9107                                    eventNumber: [e eventNumber]
9108                                     clickCount: [e clickCount]
9109                                       pressure: [e pressure]];
9110       [super mouseUp: fake_event];
9111     }
9112   else
9113     {
9114       pos = 0; /* ignored */
9116       /* Set a timer to repeat, as we can't let superclass do this modally.  */
9117       scroll_repeat_entry
9118         = [[NSTimer scheduledTimerWithTimeInterval: SCROLL_BAR_FIRST_DELAY
9119                                             target: self
9120                                           selector: @selector (repeatScroll:)
9121                                           userInfo: 0
9122                                            repeats: YES]
9123             retain];
9124     }
9126   if (part != NSScrollerKnob)
9127     [self sendScrollEventAtLoc: pos fromEvent: e];
9131 /* Called as we manually track scroller drags, rather than superclass.  */
9132 - (void)mouseDragged: (NSEvent *)e
9134     NSRect sr;
9135     double loc, pos;
9136     int length;
9138     NSTRACE ("[EmacsScroller mouseDragged:]");
9140       sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot]
9141                       toView: nil];
9143       if (horizontal)
9144         {
9145           length = NSWidth (sr);
9146           loc = ([e locationInWindow].x - NSMinX (sr));
9147         }
9148       else
9149         {
9150           length = NSHeight (sr);
9151           loc = length - ([e locationInWindow].y - NSMinY (sr));
9152         }
9154       if (loc <= 0.0)
9155         {
9156           loc = 0.0;
9157         }
9158       else if (loc >= length + last_mouse_offset)
9159         {
9160           loc = length + last_mouse_offset;
9161         }
9163       pos = (loc - last_mouse_offset);
9165       /* If there are buttons in the scroller area, we need to
9166          recalculate pos as emacs expects the scroller slot to take up
9167          the entire available length.  */
9168       if (length != pixel_length)
9169         pos = pos * pixel_length / length;
9171       [self sendScrollEventAtLoc: pos fromEvent: e];
9175 - (void)mouseUp: (NSEvent *)e
9177   NSTRACE ("[EmacsScroller mouseUp:]");
9179   if (scroll_repeat_entry)
9180     {
9181       [scroll_repeat_entry invalidate];
9182       [scroll_repeat_entry release];
9183       scroll_repeat_entry = nil;
9184     }
9185   last_hit_part = scroll_bar_above_handle;
9189 /* Treat scrollwheel events in the bar as though they were in the main window.  */
9190 - (void) scrollWheel: (NSEvent *)theEvent
9192   NSTRACE ("[EmacsScroller scrollWheel:]");
9194   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (frame);
9195   [view mouseDown: theEvent];
9198 @end  /* EmacsScroller */
9201 #ifdef NS_IMPL_GNUSTEP
9202 /* Dummy class to get rid of startup warnings.  */
9203 @implementation EmacsDocument
9205 @end
9206 #endif
9209 /* ==========================================================================
9211    Font-related functions; these used to be in nsfaces.m
9213    ========================================================================== */
9216 Lisp_Object
9217 x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
9219   struct font *font = XFONT_OBJECT (font_object);
9220   EmacsView *view = FRAME_NS_VIEW (f);
9221   int font_ascent, font_descent;
9223   if (fontset < 0)
9224     fontset = fontset_from_font (font_object);
9225   FRAME_FONTSET (f) = fontset;
9227   if (FRAME_FONT (f) == font)
9228     /* This font is already set in frame F.  There's nothing more to
9229        do.  */
9230     return font_object;
9232   FRAME_FONT (f) = font;
9234   FRAME_BASELINE_OFFSET (f) = font->baseline_offset;
9235   FRAME_COLUMN_WIDTH (f) = font->average_width;
9236   get_font_ascent_descent (font, &font_ascent, &font_descent);
9237   FRAME_LINE_HEIGHT (f) = font_ascent + font_descent;
9239   /* Compute the scroll bar width in character columns.  */
9240   if (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) > 0)
9241     {
9242       int wid = FRAME_COLUMN_WIDTH (f);
9243       FRAME_CONFIG_SCROLL_BAR_COLS (f)
9244         = (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) + wid - 1) / wid;
9245     }
9246   else
9247     {
9248       int wid = FRAME_COLUMN_WIDTH (f);
9249       FRAME_CONFIG_SCROLL_BAR_COLS (f) = (14 + wid - 1) / wid;
9250     }
9252   /* Compute the scroll bar height in character lines.  */
9253   if (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) > 0)
9254     {
9255       int height = FRAME_LINE_HEIGHT (f);
9256       FRAME_CONFIG_SCROLL_BAR_LINES (f)
9257         = (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) + height - 1) / height;
9258     }
9259   else
9260     {
9261       int height = FRAME_LINE_HEIGHT (f);
9262       FRAME_CONFIG_SCROLL_BAR_LINES (f) = (14 + height - 1) / height;
9263     }
9265   /* Now make the frame display the given font.  */
9266   if (FRAME_NS_WINDOW (f) != 0 && ! [view isFullscreen])
9267     adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
9268                        FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3,
9269                        false, Qfont);
9271   return font_object;
9275 /* XLFD: -foundry-family-weight-slant-swidth-adstyle-pxlsz-ptSz-resx-resy-spc-avgWidth-rgstry-encoding */
9276 /* Note: ns_font_to_xlfd and ns_fontname_to_xlfd no longer needed, removed
9277          in 1.43.  */
9279 const char *
9280 ns_xlfd_to_fontname (const char *xlfd)
9281 /* --------------------------------------------------------------------------
9282     Convert an X font name (XLFD) to an NS font name.
9283     Only family is used.
9284     The string returned is temporarily allocated.
9285    -------------------------------------------------------------------------- */
9287   char *name = xmalloc (180);
9288   int i, len;
9289   const char *ret;
9291   if (!strncmp (xlfd, "--", 2))
9292     sscanf (xlfd, "--%*[^-]-%179[^-]-", name);
9293   else
9294     sscanf (xlfd, "-%*[^-]-%179[^-]-", name);
9296   /* stopgap for malformed XLFD input */
9297   if (strlen (name) == 0)
9298     strcpy (name, "Monaco");
9300   /* undo hack in ns_fontname_to_xlfd, converting '$' to '-', '_' to ' '
9301      also uppercase after '-' or ' ' */
9302   name[0] = c_toupper (name[0]);
9303   for (len =strlen (name), i =0; i<len; i++)
9304     {
9305       if (name[i] == '$')
9306         {
9307           name[i] = '-';
9308           if (i+1<len)
9309             name[i+1] = c_toupper (name[i+1]);
9310         }
9311       else if (name[i] == '_')
9312         {
9313           name[i] = ' ';
9314           if (i+1<len)
9315             name[i+1] = c_toupper (name[i+1]);
9316         }
9317     }
9318   /* fprintf (stderr, "converted '%s' to '%s'\n",xlfd,name); */
9319   ret = [[NSString stringWithUTF8String: name] UTF8String];
9320   xfree (name);
9321   return ret;
9325 void
9326 syms_of_nsterm (void)
9328   NSTRACE ("syms_of_nsterm");
9330   ns_antialias_threshold = 10.0;
9332   /* From 23+ we need to tell emacs what modifiers there are.  */
9333   DEFSYM (Qmodifier_value, "modifier-value");
9334   DEFSYM (Qalt, "alt");
9335   DEFSYM (Qhyper, "hyper");
9336   DEFSYM (Qmeta, "meta");
9337   DEFSYM (Qsuper, "super");
9338   DEFSYM (Qcontrol, "control");
9339   DEFSYM (QUTF8_STRING, "UTF8_STRING");
9341   DEFSYM (Qfile, "file");
9342   DEFSYM (Qurl, "url");
9344   Fput (Qalt, Qmodifier_value, make_number (alt_modifier));
9345   Fput (Qhyper, Qmodifier_value, make_number (hyper_modifier));
9346   Fput (Qmeta, Qmodifier_value, make_number (meta_modifier));
9347   Fput (Qsuper, Qmodifier_value, make_number (super_modifier));
9348   Fput (Qcontrol, Qmodifier_value, make_number (ctrl_modifier));
9350   DEFVAR_LISP ("ns-input-file", ns_input_file,
9351               "The file specified in the last NS event.");
9352   ns_input_file =Qnil;
9354   DEFVAR_LISP ("ns-working-text", ns_working_text,
9355               "String for visualizing working composition sequence.");
9356   ns_working_text =Qnil;
9358   DEFVAR_LISP ("ns-input-font", ns_input_font,
9359               "The font specified in the last NS event.");
9360   ns_input_font =Qnil;
9362   DEFVAR_LISP ("ns-input-fontsize", ns_input_fontsize,
9363               "The fontsize specified in the last NS event.");
9364   ns_input_fontsize =Qnil;
9366   DEFVAR_LISP ("ns-input-line", ns_input_line,
9367                "The line specified in the last NS event.");
9368   ns_input_line =Qnil;
9370   DEFVAR_LISP ("ns-input-spi-name", ns_input_spi_name,
9371                "The service name specified in the last NS event.");
9372   ns_input_spi_name =Qnil;
9374   DEFVAR_LISP ("ns-input-spi-arg", ns_input_spi_arg,
9375                "The service argument specified in the last NS event.");
9376   ns_input_spi_arg =Qnil;
9378   DEFVAR_LISP ("ns-alternate-modifier", ns_alternate_modifier,
9379                "This variable describes the behavior of the alternate or option key.\n\
9380 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9381 that key.\n\
9382 Set to none means that the alternate / option key is not interpreted by Emacs\n\
9383 at all, allowing it to be used at a lower level for accented character entry.");
9384   ns_alternate_modifier = Qmeta;
9386   DEFVAR_LISP ("ns-right-alternate-modifier", ns_right_alternate_modifier,
9387                "This variable describes the behavior of the right alternate or option key.\n\
9388 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9389 that key.\n\
9390 Set to left means be the same key as `ns-alternate-modifier'.\n\
9391 Set to none means that the alternate / option key is not interpreted by Emacs\n\
9392 at all, allowing it to be used at a lower level for accented character entry.");
9393   ns_right_alternate_modifier = Qleft;
9395   DEFVAR_LISP ("ns-command-modifier", ns_command_modifier,
9396                "This variable describes the behavior of the command key.\n\
9397 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9398 that key.");
9399   ns_command_modifier = Qsuper;
9401   DEFVAR_LISP ("ns-right-command-modifier", ns_right_command_modifier,
9402                "This variable describes the behavior of the right command key.\n\
9403 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9404 that key.\n\
9405 Set to left means be the same key as `ns-command-modifier'.\n\
9406 Set to none means that the command / option key is not interpreted by Emacs\n\
9407 at all, allowing it to be used at a lower level for accented character entry.");
9408   ns_right_command_modifier = Qleft;
9410   DEFVAR_LISP ("ns-control-modifier", ns_control_modifier,
9411                "This variable describes the behavior of the control key.\n\
9412 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9413 that key.");
9414   ns_control_modifier = Qcontrol;
9416   DEFVAR_LISP ("ns-right-control-modifier", ns_right_control_modifier,
9417                "This variable describes the behavior of the right control key.\n\
9418 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9419 that key.\n\
9420 Set to left means be the same key as `ns-control-modifier'.\n\
9421 Set to none means that the control / option key is not interpreted by Emacs\n\
9422 at all, allowing it to be used at a lower level for accented character entry.");
9423   ns_right_control_modifier = Qleft;
9425   DEFVAR_LISP ("ns-function-modifier", ns_function_modifier,
9426                "This variable describes the behavior of the function key (on laptops).\n\
9427 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9428 that key.\n\
9429 Set to none means that the function key is not interpreted by Emacs at all,\n\
9430 allowing it to be used at a lower level for accented character entry.");
9431   ns_function_modifier = Qnone;
9433   DEFVAR_LISP ("ns-antialias-text", ns_antialias_text,
9434                "Non-nil (the default) means to render text antialiased.");
9435   ns_antialias_text = Qt;
9437   DEFVAR_LISP ("ns-use-thin-smoothing", ns_use_thin_smoothing,
9438                "Non-nil turns on a font smoothing method that produces thinner strokes.");
9439   ns_use_thin_smoothing = Qnil;
9441   DEFVAR_LISP ("ns-confirm-quit", ns_confirm_quit,
9442                "Whether to confirm application quit using dialog.");
9443   ns_confirm_quit = Qnil;
9445   DEFVAR_LISP ("ns-auto-hide-menu-bar", ns_auto_hide_menu_bar,
9446                doc: /* Non-nil means that the menu bar is hidden, but appears when the mouse is near.
9447 Only works on Mac OS X.  */);
9448   ns_auto_hide_menu_bar = Qnil;
9450   DEFVAR_BOOL ("ns-use-native-fullscreen", ns_use_native_fullscreen,
9451      doc: /* Non-nil means to use native fullscreen on Mac OS X 10.7 and later.
9452 Nil means use fullscreen the old (< 10.7) way.  The old way works better with
9453 multiple monitors, but lacks tool bar.  This variable is ignored on
9454 Mac OS X < 10.7.  Default is t.  */);
9455   ns_use_native_fullscreen = YES;
9456   ns_last_use_native_fullscreen = ns_use_native_fullscreen;
9458   DEFVAR_BOOL ("ns-use-fullscreen-animation", ns_use_fullscreen_animation,
9459      doc: /* Non-nil means use animation on non-native fullscreen.
9460 For native fullscreen, this does nothing.
9461 Default is nil.  */);
9462   ns_use_fullscreen_animation = NO;
9464   DEFVAR_BOOL ("ns-use-srgb-colorspace", ns_use_srgb_colorspace,
9465      doc: /* Non-nil means to use sRGB colorspace on Mac OS X 10.7 and later.
9466 Note that this does not apply to images.
9467 This variable is ignored on Mac OS X < 10.7 and GNUstep.  */);
9468   ns_use_srgb_colorspace = YES;
9470   DEFVAR_BOOL ("ns-use-mwheel-acceleration",
9471                ns_use_mwheel_acceleration,
9472      doc: /* Non-nil means use macOS's standard mouse wheel acceleration.
9473 This variable is ignored on macOS < 10.7 and GNUstep.  Default is t.  */);
9474   ns_use_mwheel_acceleration = YES;
9476   DEFVAR_LISP ("ns-mwheel-line-height", ns_mwheel_line_height,
9477                doc: /* The number of pixels touchpad scrolling considers one line.
9478 Nil or a non-number means use the default frame line height.
9479 This variable is ignored on macOS < 10.7 and GNUstep.  Default is nil.  */);
9480   ns_mwheel_line_height = Qnil;
9482   DEFVAR_BOOL ("ns-use-mwheel-momentum", ns_use_mwheel_momentum,
9483                doc: /* Non-nil means mouse wheel scrolling uses momentum.
9484 This variable is ignored on macOS < 10.7 and GNUstep.  Default is t.  */);
9485   ns_use_mwheel_momentum = YES;
9487   /* TODO: Move to common code.  */
9488   DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars,
9489                doc: /* SKIP: real doc in xterm.c.  */);
9490   Vx_toolkit_scroll_bars = Qt;
9492   DEFVAR_BOOL ("x-use-underline-position-properties",
9493                x_use_underline_position_properties,
9494      doc: /* SKIP: real doc in xterm.c.  */);
9495   x_use_underline_position_properties = 0;
9496   DEFSYM (Qx_use_underline_position_properties,
9497           "x-use-underline-position-properties");
9499   DEFVAR_BOOL ("x-underline-at-descent-line",
9500                x_underline_at_descent_line,
9501      doc: /* SKIP: real doc in xterm.c.  */);
9502   x_underline_at_descent_line = 0;
9503   DEFSYM (Qx_underline_at_descent_line, "x-underline-at-descent-line");
9505   /* Tell Emacs about this window system.  */
9506   Fprovide (Qns, Qnil);
9508   DEFSYM (Qcocoa, "cocoa");
9509   DEFSYM (Qgnustep, "gnustep");
9511 #ifdef NS_IMPL_COCOA
9512   Fprovide (Qcocoa, Qnil);
9513   syms_of_macfont ();
9514 #else
9515   Fprovide (Qgnustep, Qnil);
9516   syms_of_nsfont ();
9517 #endif