; Fix last commit in tramp.el
[emacs.git] / src / nsterm.m
blobd6c1d72a63168f3516b1886e5166b616b8a93134
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 static NSString *represented_filename = nil;
331 static struct frame *represented_frame = 0;
333 #ifdef NS_IMPL_COCOA
335  * State for pending menu activation:
336  * MENU_NONE     Normal state
337  * MENU_PENDING  A menu has been clicked on, but has been canceled so we can
338  *               run lisp to update the menu.
339  * MENU_OPENING  Menu is up to date, and the click event is redone so the menu
340  *               will open.
341  */
342 #define MENU_NONE 0
343 #define MENU_PENDING 1
344 #define MENU_OPENING 2
345 static int menu_will_open_state = MENU_NONE;
347 /* Saved position for menu click.  */
348 static CGPoint menu_mouse_point;
349 #endif
351 /* Convert modifiers in a NeXTstep event to emacs style modifiers.  */
352 #define NS_FUNCTION_KEY_MASK 0x800000
353 #define NSLeftControlKeyMask    (0x000001 | NSEventModifierFlagControl)
354 #define NSRightControlKeyMask   (0x002000 | NSEventModifierFlagControl)
355 #define NSLeftCommandKeyMask    (0x000008 | NSEventModifierFlagCommand)
356 #define NSRightCommandKeyMask   (0x000010 | NSEventModifierFlagCommand)
357 #define NSLeftAlternateKeyMask  (0x000020 | NSEventModifierFlagOption)
358 #define NSRightAlternateKeyMask (0x000040 | NSEventModifierFlagOption)
360 static unsigned int
361 ev_modifiers_helper (unsigned int flags, unsigned int left_mask,
362                      unsigned int right_mask, unsigned int either_mask,
363                      Lisp_Object left_modifier, Lisp_Object right_modifier)
365   unsigned int modifiers = 0;
367   if (flags & either_mask)
368     {
369       BOOL left_key = (flags & left_mask) == left_mask;
370       BOOL right_key = (flags & right_mask) == right_mask
371         && ! EQ (right_modifier, Qleft);
373       if (right_key)
374         modifiers |= parse_solitary_modifier (right_modifier);
376       /* GNUstep (and possibly macOS in certain circumstances) doesn't
377          differentiate between the left and right keys, so if we can't
378          identify which key it is, we use the left key setting.  */
379       if (left_key || ! right_key)
380         modifiers |= parse_solitary_modifier (left_modifier);
381     }
383   return modifiers;
386 #define EV_MODIFIERS2(flags)                                            \
387   (((flags & NSEventModifierFlagHelp) ?                                 \
388     hyper_modifier : 0)                                                 \
389    | ((flags & NSEventModifierFlagShift) ?                              \
390       shift_modifier : 0)                                               \
391    | ((flags & NS_FUNCTION_KEY_MASK) ?                                  \
392       parse_solitary_modifier (ns_function_modifier) : 0)               \
393    | ev_modifiers_helper (flags, NSLeftControlKeyMask,                  \
394                           NSRightControlKeyMask,                        \
395                           NSEventModifierFlagControl,                   \
396                           ns_control_modifier,                          \
397                           ns_right_control_modifier)                    \
398    | ev_modifiers_helper (flags, NSLeftCommandKeyMask,                  \
399                           NSRightCommandKeyMask,                        \
400                           NSEventModifierFlagCommand,                   \
401                           ns_command_modifier,                          \
402                           ns_right_command_modifier)                    \
403    | ev_modifiers_helper (flags, NSLeftAlternateKeyMask,                \
404                           NSRightAlternateKeyMask,                      \
405                           NSEventModifierFlagOption,                    \
406                           ns_alternate_modifier,                        \
407                           ns_right_alternate_modifier))
409 #define EV_MODIFIERS(e) EV_MODIFIERS2 ([e modifierFlags])
411 #define EV_UDMODIFIERS(e)                                      \
412     ((([e type] == NSEventTypeLeftMouseDown) ? down_modifier : 0)       \
413      | (([e type] == NSEventTypeRightMouseDown) ? down_modifier : 0)    \
414      | (([e type] == NSEventTypeOtherMouseDown) ? down_modifier : 0)    \
415      | (([e type] == NSEventTypeLeftMouseDragged) ? down_modifier : 0)  \
416      | (([e type] == NSEventTypeRightMouseDragged) ? down_modifier : 0) \
417      | (([e type] == NSEventTypeOtherMouseDragged) ? down_modifier : 0) \
418      | (([e type] == NSEventTypeLeftMouseUp)   ? up_modifier   : 0)     \
419      | (([e type] == NSEventTypeRightMouseUp)   ? up_modifier   : 0)    \
420      | (([e type] == NSEventTypeOtherMouseUp)   ? up_modifier   : 0))
422 #define EV_BUTTON(e)                                                         \
423     ((([e type] == NSEventTypeLeftMouseDown) || ([e type] == NSEventTypeLeftMouseUp)) ? 0 :    \
424       (([e type] == NSEventTypeRightMouseDown) || ([e type] == NSEventTypeRightMouseUp)) ? 2 : \
425      [e buttonNumber] - 1)
427 /* Convert the time field to a timestamp in milliseconds.  */
428 #define EV_TIMESTAMP(e) ([e timestamp] * 1000)
430 /* This is a piece of code which is common to all the event handling
431    methods.  Maybe it should even be a function.  */
432 #define EV_TRAILER(e)                                                   \
433   {                                                                     \
434     XSETFRAME (emacs_event->frame_or_window, emacsframe);               \
435     EV_TRAILER2 (e);                                                    \
436   }
438 #define EV_TRAILER2(e)                                                  \
439   {                                                                     \
440       if (e) emacs_event->timestamp = EV_TIMESTAMP (e);                 \
441       if (q_event_ptr)                                                  \
442         {                                                               \
443           Lisp_Object tem = Vinhibit_quit;                              \
444           Vinhibit_quit = Qt;                                           \
445           n_emacs_events_pending++;                                     \
446           kbd_buffer_store_event_hold (emacs_event, q_event_ptr);       \
447           Vinhibit_quit = tem;                                          \
448         }                                                               \
449       else                                                              \
450         hold_event (emacs_event);                                       \
451       EVENT_INIT (*emacs_event);                                        \
452       ns_send_appdefined (-1);                                          \
453     }
456 /* These flags will be OR'd or XOR'd with the NSWindow's styleMask
457    property depending on what we're doing.  */
458 #define FRAME_DECORATED_FLAGS (NSWindowStyleMaskTitled              \
459                                | NSWindowStyleMaskResizable         \
460                                | NSWindowStyleMaskMiniaturizable    \
461                                | NSWindowStyleMaskClosable)
462 #define FRAME_UNDECORATED_FLAGS NSWindowStyleMaskBorderless
464 /* TODO: Get rid of need for these forward declarations.  */
465 static void ns_condemn_scroll_bars (struct frame *f);
466 static void ns_judge_scroll_bars (struct frame *f);
469 /* ==========================================================================
471     Utilities
473    ========================================================================== */
475 void
476 ns_set_represented_filename (struct frame *f)
478   Lisp_Object filename, encoded_filename;
479   Lisp_Object buf = XWINDOW (f->selected_window)->contents;
480   NSAutoreleasePool *pool;
481   NSString *fstr;
483   NSTRACE ("ns_set_represented_filename");
485   if (f->explicit_name || ! NILP (f->title))
486     return;
488   block_input ();
489   pool = [[NSAutoreleasePool alloc] init];
490   filename = BVAR (XBUFFER (buf), filename);
492   if (! NILP (filename))
493     {
494       encoded_filename = ENCODE_UTF_8 (filename);
496       fstr = [NSString stringWithUTF8String: SSDATA (encoded_filename)];
497       if (fstr == nil) fstr = @"";
498     }
499   else
500     fstr = @"";
502   represented_filename = [fstr retain];
503   represented_frame = f;
505   [pool release];
506   unblock_input ();
509 void
510 ns_init_events (struct input_event *ev)
512   EVENT_INIT (*ev);
513   emacs_event = ev;
516 void
517 ns_finish_events (void)
519   emacs_event = NULL;
522 static void
523 hold_event (struct input_event *event)
525   if (hold_event_q.nr == hold_event_q.cap)
526     {
527       if (hold_event_q.cap == 0) hold_event_q.cap = 10;
528       else hold_event_q.cap *= 2;
529       hold_event_q.q =
530         xrealloc (hold_event_q.q, hold_event_q.cap * sizeof *hold_event_q.q);
531     }
533   hold_event_q.q[hold_event_q.nr++] = *event;
534   /* Make sure ns_read_socket is called, i.e. we have input.  */
535   raise (SIGIO);
536   send_appdefined = YES;
539 static Lisp_Object
540 append2 (Lisp_Object list, Lisp_Object item)
541 /* --------------------------------------------------------------------------
542    Utility to append to a list
543    -------------------------------------------------------------------------- */
545   return CALLN (Fnconc, list, list1 (item));
549 const char *
550 ns_etc_directory (void)
551 /* If running as a self-contained app bundle, return as a string the
552    filename of the etc directory, if present; else nil.  */
554   NSBundle *bundle = [NSBundle mainBundle];
555   NSString *resourceDir = [bundle resourcePath];
556   NSString *resourcePath;
557   NSFileManager *fileManager = [NSFileManager defaultManager];
558   BOOL isDir;
560   resourcePath = [resourceDir stringByAppendingPathComponent: @"etc"];
561   if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
562     {
563       if (isDir) return [resourcePath UTF8String];
564     }
565   return NULL;
569 const char *
570 ns_exec_path (void)
571 /* If running as a self-contained app bundle, return as a path string
572    the filenames of the libexec and bin directories, ie libexec:bin.
573    Otherwise, return nil.
574    Normally, Emacs does not add its own bin/ directory to the PATH.
575    However, a self-contained NS build has a different layout, with
576    bin/ and libexec/ subdirectories in the directory that contains
577    Emacs.app itself.
578    We put libexec first, because init_callproc_1 uses the first
579    element to initialize exec-directory.  An alternative would be
580    for init_callproc to check for invocation-directory/libexec.
583   NSBundle *bundle = [NSBundle mainBundle];
584   NSString *resourceDir = [bundle resourcePath];
585   NSString *binDir = [bundle bundlePath];
586   NSString *resourcePath, *resourcePaths;
587   NSRange range;
588   NSString *pathSeparator = [NSString stringWithFormat: @"%c", SEPCHAR];
589   NSFileManager *fileManager = [NSFileManager defaultManager];
590   NSArray *paths;
591   NSEnumerator *pathEnum;
592   BOOL isDir;
594   range = [resourceDir rangeOfString: @"Contents"];
595   if (range.location != NSNotFound)
596     {
597       binDir = [binDir stringByAppendingPathComponent: @"Contents"];
598 #ifdef NS_IMPL_COCOA
599       binDir = [binDir stringByAppendingPathComponent: @"MacOS"];
600 #endif
601     }
603   paths = [binDir stringsByAppendingPaths:
604                 [NSArray arrayWithObjects: @"libexec", @"bin", nil]];
605   pathEnum = [paths objectEnumerator];
606   resourcePaths = @"";
608   while ((resourcePath = [pathEnum nextObject]))
609     {
610       if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
611         if (isDir)
612           {
613             if ([resourcePaths length] > 0)
614               resourcePaths
615                 = [resourcePaths stringByAppendingString: pathSeparator];
616             resourcePaths
617               = [resourcePaths stringByAppendingString: resourcePath];
618           }
619     }
620   if ([resourcePaths length] > 0) return [resourcePaths UTF8String];
622   return NULL;
626 const char *
627 ns_load_path (void)
628 /* If running as a self-contained app bundle, return as a path string
629    the filenames of the site-lisp and lisp directories.
630    Ie, site-lisp:lisp.  Otherwise, return nil.  */
632   NSBundle *bundle = [NSBundle mainBundle];
633   NSString *resourceDir = [bundle resourcePath];
634   NSString *resourcePath, *resourcePaths;
635   NSString *pathSeparator = [NSString stringWithFormat: @"%c", SEPCHAR];
636   NSFileManager *fileManager = [NSFileManager defaultManager];
637   BOOL isDir;
638   NSArray *paths = [resourceDir stringsByAppendingPaths:
639                               [NSArray arrayWithObjects:
640                                          @"site-lisp", @"lisp", nil]];
641   NSEnumerator *pathEnum = [paths objectEnumerator];
642   resourcePaths = @"";
644   /* Hack to skip site-lisp.  */
645   if (no_site_lisp) resourcePath = [pathEnum nextObject];
647   while ((resourcePath = [pathEnum nextObject]))
648     {
649       if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
650         if (isDir)
651           {
652             if ([resourcePaths length] > 0)
653               resourcePaths
654                 = [resourcePaths stringByAppendingString: pathSeparator];
655             resourcePaths
656               = [resourcePaths stringByAppendingString: resourcePath];
657           }
658     }
659   if ([resourcePaths length] > 0) return [resourcePaths UTF8String];
661   return NULL;
665 void
666 ns_init_locale (void)
667 /* macOS doesn't set any environment variables for the locale when run
668    from the GUI. Get the locale from the OS and set LANG.  */
670   NSLocale *locale = [NSLocale currentLocale];
672   NSTRACE ("ns_init_locale");
674   @try
675     {
676       /* It seems macOS should probably use UTF-8 everywhere.
677          'localeIdentifier' does not specify the encoding, and I can't
678          find any way to get the OS to tell us which encoding to use,
679          so hard-code '.UTF-8'.  */
680       NSString *localeID = [NSString stringWithFormat:@"%@.UTF-8",
681                                      [locale localeIdentifier]];
683       /* Set LANG to locale, but not if LANG is already set.  */
684       setenv("LANG", [localeID UTF8String], 0);
685     }
686   @catch (NSException *e)
687     {
688       NSLog (@"Locale detection failed: %@: %@", [e name], [e reason]);
689     }
693 void
694 ns_release_object (void *obj)
695 /* --------------------------------------------------------------------------
696     Release an object (callable from C)
697    -------------------------------------------------------------------------- */
699     [(id)obj release];
703 void
704 ns_retain_object (void *obj)
705 /* --------------------------------------------------------------------------
706      Retain an object (callable from C)
707    -------------------------------------------------------------------------- */
709     [(id)obj retain];
713 void *
714 ns_alloc_autorelease_pool (void)
715 /* --------------------------------------------------------------------------
716      Allocate a pool for temporary objects (callable from C)
717    -------------------------------------------------------------------------- */
719   return [[NSAutoreleasePool alloc] init];
723 void
724 ns_release_autorelease_pool (void *pool)
725 /* --------------------------------------------------------------------------
726      Free a pool and temporary objects it refers to (callable from C)
727    -------------------------------------------------------------------------- */
729   ns_release_object (pool);
733 #ifdef NS_IMPL_COCOA
734 /* Disabling screen updates can be used to make several actions appear
735    "atomic" to the end user.  It seems some actions can still update
736    the display, though.
738    When we re-enable screen updates the number of calls to
739    NSEnableScreenUpdates should match the number to
740    NSDisableScreenUpdates.
742    We use these functions to prevent the user seeing a blank frame
743    after it has been resized.  x_set_window_size disables updates and
744    when redisplay completes unwind_redisplay enables them again
745    (bug#30699).  */
747 static void
748 ns_disable_screen_updates (void)
750   NSDisableScreenUpdates ();
751   disable_screen_updates_count++;
754 void
755 ns_enable_screen_updates (void)
756 /* Re-enable screen updates.  Called from unwind_redisplay.  */
758   while (disable_screen_updates_count > 0)
759     {
760       NSEnableScreenUpdates ();
761       disable_screen_updates_count--;
762     }
764 #endif
767 static BOOL
768 ns_menu_bar_should_be_hidden (void)
769 /* True, if the menu bar should be hidden.  */
771   return !NILP (ns_auto_hide_menu_bar)
772     && [NSApp respondsToSelector:@selector(setPresentationOptions:)];
776 struct EmacsMargins
778   CGFloat top;
779   CGFloat bottom;
780   CGFloat left;
781   CGFloat right;
785 static struct EmacsMargins
786 ns_screen_margins (NSScreen *screen)
787 /* The parts of SCREEN used by the operating system.  */
789   NSTRACE ("ns_screen_margins");
791   struct EmacsMargins margins;
793   NSRect screenFrame = [screen frame];
794   NSRect screenVisibleFrame = [screen visibleFrame];
796   /* Sometimes, visibleFrame isn't up-to-date with respect to a hidden
797      menu bar, check this explicitly.  */
798   if (ns_menu_bar_should_be_hidden())
799     {
800       margins.top = 0;
801     }
802   else
803     {
804       CGFloat frameTop = screenFrame.origin.y + screenFrame.size.height;
805       CGFloat visibleFrameTop = (screenVisibleFrame.origin.y
806                                  + screenVisibleFrame.size.height);
808       margins.top = frameTop - visibleFrameTop;
809     }
811   {
812     CGFloat frameRight = screenFrame.origin.x + screenFrame.size.width;
813     CGFloat visibleFrameRight = (screenVisibleFrame.origin.x
814                                  + screenVisibleFrame.size.width);
815     margins.right = frameRight - visibleFrameRight;
816   }
818   margins.bottom = screenVisibleFrame.origin.y - screenFrame.origin.y;
819   margins.left   = screenVisibleFrame.origin.x - screenFrame.origin.x;
821   NSTRACE_MSG ("left:%g right:%g top:%g bottom:%g",
822                margins.left,
823                margins.right,
824                margins.top,
825                margins.bottom);
827   return margins;
831 /* A screen margin between 1 and DOCK_IGNORE_LIMIT (inclusive) is
832    assumed to contain a hidden dock.  macOS currently use 4 pixels for
833    this, however, to be future compatible, a larger value is used.  */
834 #define DOCK_IGNORE_LIMIT 6
836 static struct EmacsMargins
837 ns_screen_margins_ignoring_hidden_dock (NSScreen *screen)
838 /* The parts of SCREEN used by the operating system, excluding the parts
839    reserved for a hidden dock.  */
841   NSTRACE ("ns_screen_margins_ignoring_hidden_dock");
843   struct EmacsMargins margins = ns_screen_margins(screen);
845   /* macOS (currently) reserved 4 pixels along the edge where a hidden
846      dock is located.  Unfortunately, it's not possible to find the
847      location and information about if the dock is hidden.  Instead,
848      it is assumed that if the margin of an edge is less than
849      DOCK_IGNORE_LIMIT, it contains a hidden dock.  */
850   if (margins.left <= DOCK_IGNORE_LIMIT)
851     {
852       margins.left = 0;
853     }
854   if (margins.right <= DOCK_IGNORE_LIMIT)
855     {
856       margins.right = 0;
857     }
858   if (margins.top <= DOCK_IGNORE_LIMIT)
859     {
860       margins.top = 0;
861     }
862   /* Note: This doesn't occur in current versions of macOS, but
863      included for completeness and future compatibility.  */
864   if (margins.bottom <= DOCK_IGNORE_LIMIT)
865     {
866       margins.bottom = 0;
867     }
869   NSTRACE_MSG ("left:%g right:%g top:%g bottom:%g",
870                margins.left,
871                margins.right,
872                margins.top,
873                margins.bottom);
875   return margins;
879 static CGFloat
880 ns_menu_bar_height (NSScreen *screen)
881 /* The height of the menu bar, if visible.
883    Note: Don't use this when fullscreen is enabled -- the screen
884    sometimes includes, sometimes excludes the menu bar area.  */
886   struct EmacsMargins margins = ns_screen_margins(screen);
888   CGFloat res = margins.top;
890   NSTRACE ("ns_menu_bar_height " NSTRACE_FMT_RETURN " %.0f", res);
892   return res;
896 /* ==========================================================================
898     Focus (clipping) and screen update
900    ========================================================================== */
903 // Window constraining
904 // -------------------
906 // To ensure that the windows are not placed under the menu bar, they
907 // are typically moved by the call-back constrainFrameRect. However,
908 // by overriding it, it's possible to inhibit this, leaving the window
909 // in it's original position.
911 // It's possible to hide the menu bar. However, technically, it's only
912 // possible to hide it when the application is active. To ensure that
913 // this work properly, the menu bar and window constraining are
914 // deferred until the application becomes active.
916 // Even though it's not possible to manually move a window above the
917 // top of the screen, it is allowed if it's done programmatically,
918 // when the menu is hidden. This allows the editable area to cover the
919 // full screen height.
921 // Test cases
922 // ----------
924 // Use the following extra files:
926 //    init.el:
927 //       ;; Hide menu and place frame slightly above the top of the screen.
928 //       (setq ns-auto-hide-menu-bar t)
929 //       (set-frame-position (selected-frame) 0 -20)
931 // Test 1:
933 //    emacs -Q -l init.el
935 //    Result: No menu bar, and the title bar should be above the screen.
937 // Test 2:
939 //    emacs -Q
941 //    Result: Menu bar visible, frame placed immediately below the menu.
944 static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
946   NSTRACE ("constrain_frame_rect(" NSTRACE_FMT_RECT ")",
947              NSTRACE_ARG_RECT (frameRect));
949   // --------------------
950   // Collect information about the screen the frame is covering.
951   //
953   NSArray *screens = [NSScreen screens];
954   NSUInteger nr_screens = [screens count];
956   int i;
958   // The height of the menu bar, if present in any screen the frame is
959   // displayed in.
960   int menu_bar_height = 0;
962   // A rectangle covering all the screen the frame is displayed in.
963   NSRect multiscreenRect = NSMakeRect(0, 0, 0, 0);
964   for (i = 0; i < nr_screens; ++i )
965     {
966       NSScreen *s = [screens objectAtIndex: i];
967       NSRect scrRect = [s frame];
969       NSTRACE_MSG ("Screen %d: " NSTRACE_FMT_RECT,
970                    i, NSTRACE_ARG_RECT (scrRect));
972       if (NSIntersectionRect (frameRect, scrRect).size.height != 0)
973         {
974           multiscreenRect = NSUnionRect (multiscreenRect, scrRect);
976           if (!isFullscreen)
977             {
978               CGFloat screen_menu_bar_height = ns_menu_bar_height (s);
979               menu_bar_height = max(menu_bar_height, screen_menu_bar_height);
980             }
981         }
982     }
984   NSTRACE_RECT ("multiscreenRect", multiscreenRect);
986   NSTRACE_MSG ("menu_bar_height: %d", menu_bar_height);
988   if (multiscreenRect.size.width == 0
989       || multiscreenRect.size.height == 0)
990     {
991       // Failed to find any monitor, give up.
992       NSTRACE_MSG ("multiscreenRect empty");
993       NSTRACE_RETURN_RECT (frameRect);
994       return frameRect;
995     }
998   // --------------------
999   // Find a suitable placement.
1000   //
1002   if (ns_menu_bar_should_be_hidden())
1003     {
1004       // When the menu bar is hidden, the user may place part of the
1005       // frame above the top of the screen, for example to hide the
1006       // title bar.
1007       //
1008       // Hence, keep the original position.
1009     }
1010   else
1011     {
1012       // Ensure that the frame is below the menu bar, or below the top
1013       // of the screen.
1014       //
1015       // This assume that the menu bar is placed at the top in the
1016       // rectangle that covers the monitors.  (It doesn't have to be,
1017       // but if it's not it's hard to do anything useful.)
1018       CGFloat topOfWorkArea = (multiscreenRect.origin.y
1019                                + multiscreenRect.size.height
1020                                - menu_bar_height);
1022       CGFloat topOfFrame = frameRect.origin.y + frameRect.size.height;
1023       if (topOfFrame > topOfWorkArea)
1024         {
1025           frameRect.origin.y -= topOfFrame - topOfWorkArea;
1026           NSTRACE_RECT ("After placement adjust", frameRect);
1027         }
1028     }
1030   // Include the following section to restrict frame to the screens.
1031   // (If so, update it to allow the frame to stretch down below the
1032   // screen.)
1033 #if 0
1034   // --------------------
1035   // Ensure frame doesn't stretch below the screens.
1036   //
1038   CGFloat diff = multiscreenRect.origin.y - frameRect.origin.y;
1040   if (diff > 0)
1041     {
1042       frameRect.origin.y = multiscreenRect.origin.y;
1043       frameRect.size.height -= diff;
1044     }
1045 #endif
1047   NSTRACE_RETURN_RECT (frameRect);
1048   return frameRect;
1052 static void
1053 ns_constrain_all_frames (void)
1054 /* --------------------------------------------------------------------------
1055      Ensure that the menu bar doesn't cover any frames.
1056    -------------------------------------------------------------------------- */
1058   Lisp_Object tail, frame;
1060   NSTRACE ("ns_constrain_all_frames");
1062   block_input ();
1064   FOR_EACH_FRAME (tail, frame)
1065     {
1066       struct frame *f = XFRAME (frame);
1067       if (FRAME_NS_P (f))
1068         {
1069           EmacsView *view = FRAME_NS_VIEW (f);
1071           if (![view isFullscreen])
1072             {
1073               [[view window]
1074                 setFrame:constrain_frame_rect([[view window] frame], false)
1075                  display:NO];
1076             }
1077         }
1078     }
1080   unblock_input ();
1084 static void
1085 ns_update_auto_hide_menu_bar (void)
1086 /* --------------------------------------------------------------------------
1087      Show or hide the menu bar, based on user setting.
1088    -------------------------------------------------------------------------- */
1090 #ifdef NS_IMPL_COCOA
1091   NSTRACE ("ns_update_auto_hide_menu_bar");
1093   block_input ();
1095   if (NSApp != nil && [NSApp isActive])
1096     {
1097       // Note, "setPresentationOptions" triggers an error unless the
1098       // application is active.
1099       BOOL menu_bar_should_be_hidden = ns_menu_bar_should_be_hidden ();
1101       if (menu_bar_should_be_hidden != ns_menu_bar_is_hidden)
1102         {
1103           NSApplicationPresentationOptions options
1104             = NSApplicationPresentationDefault;
1106           if (menu_bar_should_be_hidden)
1107             options |= NSApplicationPresentationAutoHideMenuBar
1108               | NSApplicationPresentationAutoHideDock;
1110           [NSApp setPresentationOptions: options];
1112           ns_menu_bar_is_hidden = menu_bar_should_be_hidden;
1114           if (!ns_menu_bar_is_hidden)
1115             {
1116               ns_constrain_all_frames ();
1117             }
1118         }
1119     }
1121   unblock_input ();
1122 #endif
1126 static void
1127 ns_update_begin (struct frame *f)
1128 /* --------------------------------------------------------------------------
1129    Prepare for a grouped sequence of drawing calls
1130    external (RIF) call; whole frame, called before update_window_begin
1131    -------------------------------------------------------------------------- */
1133   EmacsView *view = FRAME_NS_VIEW (f);
1134   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_begin");
1136   ns_update_auto_hide_menu_bar ();
1138 #ifdef NS_IMPL_COCOA
1139   if ([view isFullscreen] && [view fsIsNative])
1140   {
1141     // Fix reappearing tool bar in fullscreen for Mac OS X 10.7
1142     BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (f) ? YES : NO;
1143     NSToolbar *toolbar = [FRAME_NS_VIEW (f) toolbar];
1144     if (! tbar_visible != ! [toolbar isVisible])
1145       [toolbar setVisible: tbar_visible];
1146   }
1147 #endif
1149   ns_updating_frame = f;
1150   [view lockFocus];
1152   /* drawRect may have been called for say the minibuffer, and then clip path
1153      is for the minibuffer.  But the display engine may draw more because
1154      we have set the frame as garbaged.  So reset clip path to the whole
1155      view.  */
1156 #ifdef NS_IMPL_COCOA
1157   {
1158     NSBezierPath *bp;
1159     NSRect r = [view frame];
1160     NSRect cr = [[view window] frame];
1161     /* If a large frame size is set, r may be larger than the window frame
1162        before constrained.  In that case don't change the clip path, as we
1163        will clear in to the tool bar and title bar.  */
1164     if (r.size.height
1165         + FRAME_NS_TITLEBAR_HEIGHT (f)
1166         + FRAME_TOOLBAR_HEIGHT (f) <= cr.size.height)
1167       {
1168         bp = [[NSBezierPath bezierPathWithRect: r] retain];
1169         [bp setClip];
1170         [bp release];
1171       }
1172   }
1173 #endif
1175 #ifdef NS_IMPL_GNUSTEP
1176   uRect = NSMakeRect (0, 0, 0, 0);
1177 #endif
1181 static void
1182 ns_update_window_begin (struct window *w)
1183 /* --------------------------------------------------------------------------
1184    Prepare for a grouped sequence of drawing calls
1185    external (RIF) call; for one window, called after update_begin
1186    -------------------------------------------------------------------------- */
1188   struct frame *f = XFRAME (WINDOW_FRAME (w));
1189   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
1191   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_window_begin");
1192   w->output_cursor = w->cursor;
1194   block_input ();
1196   if (f == hlinfo->mouse_face_mouse_frame)
1197     {
1198       /* Don't do highlighting for mouse motion during the update.  */
1199       hlinfo->mouse_face_defer = 1;
1201         /* If the frame needs to be redrawn,
1202            simply forget about any prior mouse highlighting.  */
1203       if (FRAME_GARBAGED_P (f))
1204         hlinfo->mouse_face_window = Qnil;
1206       /* (further code for mouse faces ifdef'd out in other terms elided) */
1207     }
1209   unblock_input ();
1213 static void
1214 ns_update_window_end (struct window *w, bool cursor_on_p,
1215                       bool mouse_face_overwritten_p)
1216 /* --------------------------------------------------------------------------
1217    Finished a grouped sequence of drawing calls
1218    external (RIF) call; for one window called before update_end
1219    -------------------------------------------------------------------------- */
1221   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_window_end");
1223   /* note: this fn is nearly identical in all terms */
1224   if (!w->pseudo_window_p)
1225     {
1226       block_input ();
1228       if (cursor_on_p)
1229         display_and_set_cursor (w, 1,
1230                                 w->output_cursor.hpos, w->output_cursor.vpos,
1231                                 w->output_cursor.x, w->output_cursor.y);
1233       if (draw_window_fringes (w, 1))
1234         {
1235           if (WINDOW_RIGHT_DIVIDER_WIDTH (w))
1236             x_draw_right_divider (w);
1237           else
1238             x_draw_vertical_border (w);
1239         }
1241       unblock_input ();
1242     }
1244   /* If a row with mouse-face was overwritten, arrange for
1245      frame_up_to_date to redisplay the mouse highlight.  */
1246   if (mouse_face_overwritten_p)
1247     reset_mouse_highlight (MOUSE_HL_INFO (XFRAME (w->frame)));
1251 static void
1252 ns_update_end (struct frame *f)
1253 /* --------------------------------------------------------------------------
1254    Finished a grouped sequence of drawing calls
1255    external (RIF) call; for whole frame, called after update_window_end
1256    -------------------------------------------------------------------------- */
1258   EmacsView *view = FRAME_NS_VIEW (f);
1260   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_end");
1262 /*   if (f == MOUSE_HL_INFO (f)->mouse_face_mouse_frame) */
1263   MOUSE_HL_INFO (f)->mouse_face_defer = 0;
1265   block_input ();
1267   [view unlockFocus];
1268   [[view window] flushWindow];
1270   unblock_input ();
1271   ns_updating_frame = NULL;
1274 static void
1275 ns_focus (struct frame *f, NSRect *r, int n)
1276 /* --------------------------------------------------------------------------
1277    Internal: Focus on given frame.  During small local updates this is used to
1278      draw, however during large updates, ns_update_begin and ns_update_end are
1279      called to wrap the whole thing, in which case these calls are stubbed out.
1280      Except, on GNUstep, we accumulate the rectangle being drawn into, because
1281      the back end won't do this automatically, and will just end up flushing
1282      the entire window.
1283    -------------------------------------------------------------------------- */
1285   NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_focus");
1286   if (r != NULL)
1287     {
1288       NSTRACE_RECT ("r", *r);
1289     }
1291   if (f != ns_updating_frame)
1292     {
1293       NSView *view = FRAME_NS_VIEW (f);
1294       if (view != focus_view)
1295         {
1296           if (focus_view != NULL)
1297             {
1298               [focus_view unlockFocus];
1299               [[focus_view window] flushWindow];
1300 /* debug_lock--; */
1301             }
1303           if (view)
1304             [view lockFocus];
1305           focus_view = view;
1306 /* if (view) debug_lock++; */
1307         }
1308     }
1310   /* clipping */
1311   if (r)
1312     {
1313       [[NSGraphicsContext currentContext] saveGraphicsState];
1314       if (n == 2)
1315         NSRectClipList (r, 2);
1316       else
1317         NSRectClip (*r);
1318       gsaved = YES;
1319     }
1323 static void
1324 ns_unfocus (struct frame *f)
1325 /* --------------------------------------------------------------------------
1326      Internal: Remove focus on given frame
1327    -------------------------------------------------------------------------- */
1329   NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_unfocus");
1331   if (gsaved)
1332     {
1333       [[NSGraphicsContext currentContext] restoreGraphicsState];
1334       gsaved = NO;
1335     }
1337   if (f != ns_updating_frame)
1338     {
1339       if (focus_view != NULL)
1340         {
1341           [focus_view unlockFocus];
1342           [[focus_view window] flushWindow];
1343           focus_view = NULL;
1344 /* debug_lock--; */
1345         }
1346     }
1350 static void
1351 ns_clip_to_row (struct window *w, struct glyph_row *row,
1352                 enum glyph_row_area area, BOOL gc)
1353 /* --------------------------------------------------------------------------
1354      Internal (but parallels other terms): Focus drawing on given row
1355    -------------------------------------------------------------------------- */
1357   struct frame *f = XFRAME (WINDOW_FRAME (w));
1358   NSRect clip_rect;
1359   int window_x, window_y, window_width;
1361   window_box (w, area, &window_x, &window_y, &window_width, 0);
1363   clip_rect.origin.x = window_x;
1364   clip_rect.origin.y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, row->y));
1365   clip_rect.origin.y = max (clip_rect.origin.y, window_y);
1366   clip_rect.size.width = window_width;
1367   clip_rect.size.height = row->visible_height;
1369   ns_focus (f, &clip_rect, 1);
1373 /* ==========================================================================
1375     Visible bell and beep.
1377    ========================================================================== */
1380 // This bell implementation shows the visual bell image asynchronously
1381 // from the rest of Emacs. This is done by adding a NSView to the
1382 // superview of the Emacs window and removing it using a timer.
1384 // Unfortunately, some Emacs operations, like scrolling, is done using
1385 // low-level primitives that copy the content of the window, including
1386 // the bell image. To some extent, this is handled by removing the
1387 // image prior to scrolling and marking that the window is in need for
1388 // redisplay.
1390 // To test this code, make sure that there is no artifacts of the bell
1391 // image in the following situations. Use a non-empty buffer (like the
1392 // tutorial) to ensure that a scroll is performed:
1394 // * Single-window: C-g C-v
1396 // * Side-by-windows: C-x 3 C-g C-v
1398 // * Windows above each other: C-x 2 C-g C-v
1400 @interface EmacsBell : NSImageView
1402   // Number of currently active bells.
1403   unsigned int nestCount;
1404   NSView * mView;
1405   bool isAttached;
1407 - (void)show:(NSView *)view;
1408 - (void)hide;
1409 - (void)remove;
1410 @end
1412 @implementation EmacsBell
1414 - (id)init
1416   NSTRACE ("[EmacsBell init]");
1417   if ((self = [super init]))
1418     {
1419       nestCount = 0;
1420       isAttached = false;
1421 #ifdef NS_IMPL_GNUSTEP
1422       // GNUstep doesn't provide named images.  This was reported in
1423       // 2011, see https://savannah.gnu.org/bugs/?33396
1424       //
1425       // As a drop in replacement, a semitransparent gray square is used.
1426       self.image = [[NSImage alloc] initWithSize:NSMakeSize(32 * 5, 32 * 5)];
1427       [self.image lockFocus];
1428       [[NSColor colorForEmacsRed:0.5 green:0.5 blue:0.5 alpha:0.5] set];
1429       NSRectFill(NSMakeRect(0, 0, 32, 32));
1430       [self.image unlockFocus];
1431 #else
1432       self.image = [NSImage imageNamed:NSImageNameCaution];
1433       [self.image setSize:NSMakeSize(self.image.size.width * 5,
1434                                      self.image.size.height * 5)];
1435 #endif
1436     }
1437   return self;
1440 - (void)show:(NSView *)view
1442   NSTRACE ("[EmacsBell show:]");
1443   NSTRACE_MSG ("nestCount: %u", nestCount);
1445   // Show the image, unless it's already shown.
1446   if (nestCount == 0)
1447     {
1448       NSRect rect = [view bounds];
1449       NSPoint pos;
1450       pos.x = rect.origin.x + (rect.size.width  - self.image.size.width )/2;
1451       pos.y = rect.origin.y + (rect.size.height - self.image.size.height)/2;
1453       [self setFrameOrigin:pos];
1454       [self setFrameSize:self.image.size];
1456       isAttached = true;
1457       mView = view;
1458       [[[view window] contentView] addSubview:self
1459                                    positioned:NSWindowAbove
1460                                    relativeTo:nil];
1461     }
1463   ++nestCount;
1465   [self performSelector:@selector(hide) withObject:self afterDelay:0.5];
1469 - (void)hide
1471   // Note: Trace output from this method isn't shown, reason unknown.
1472   // NSTRACE ("[EmacsBell hide]");
1474   if (nestCount > 0)
1475     --nestCount;
1477   // Remove the image once the last bell became inactive.
1478   if (nestCount == 0)
1479     {
1480       [self remove];
1481     }
1485 -(void)remove
1487   NSTRACE ("[EmacsBell remove]");
1488   if (isAttached)
1489     {
1490       NSTRACE_MSG ("removeFromSuperview");
1491       [self removeFromSuperview];
1492       mView.needsDisplay = YES;
1493       isAttached = false;
1494     }
1497 @end
1500 static EmacsBell * bell_view = nil;
1502 static void
1503 ns_ring_bell (struct frame *f)
1504 /* --------------------------------------------------------------------------
1505      "Beep" routine
1506    -------------------------------------------------------------------------- */
1508   NSTRACE ("ns_ring_bell");
1509   if (visible_bell)
1510     {
1511       struct frame *frame = SELECTED_FRAME ();
1512       NSView *view;
1514       if (bell_view == nil)
1515         {
1516           bell_view = [[EmacsBell alloc] init];
1517           [bell_view retain];
1518         }
1520       block_input ();
1522       view = FRAME_NS_VIEW (frame);
1523       if (view != nil)
1524         {
1525           [bell_view show:view];
1526         }
1528       unblock_input ();
1529     }
1530   else
1531     {
1532       NSBeep ();
1533     }
1537 static void
1538 hide_bell (void)
1539 /* --------------------------------------------------------------------------
1540      Ensure the bell is hidden.
1541    -------------------------------------------------------------------------- */
1543   NSTRACE ("hide_bell");
1545   if (bell_view != nil)
1546     {
1547       [bell_view remove];
1548     }
1552 /* ==========================================================================
1554     Frame / window manager related functions
1556    ========================================================================== */
1559 static void
1560 ns_raise_frame (struct frame *f, BOOL make_key)
1561 /* --------------------------------------------------------------------------
1562      Bring window to foreground and if make_key is YES, give it focus.
1563    -------------------------------------------------------------------------- */
1565   NSView *view;
1567   check_window_system (f);
1568   view = FRAME_NS_VIEW (f);
1569   block_input ();
1570   if (FRAME_VISIBLE_P (f))
1571     {
1572       if (make_key)
1573         [[view window] makeKeyAndOrderFront: NSApp];
1574       else
1575         [[view window] orderFront: NSApp];
1576     }
1577   unblock_input ();
1581 static void
1582 ns_lower_frame (struct frame *f)
1583 /* --------------------------------------------------------------------------
1584      Send window to back
1585    -------------------------------------------------------------------------- */
1587   NSView *view;
1589   check_window_system (f);
1590   view = FRAME_NS_VIEW (f);
1591   block_input ();
1592   [[view window] orderBack: NSApp];
1593   unblock_input ();
1597 static void
1598 ns_frame_raise_lower (struct frame *f, bool raise)
1599 /* --------------------------------------------------------------------------
1600      External (hook)
1601    -------------------------------------------------------------------------- */
1603   NSTRACE ("ns_frame_raise_lower");
1605   if (raise)
1606     ns_raise_frame (f, YES);
1607   else
1608     ns_lower_frame (f);
1612 static void
1613 ns_frame_rehighlight (struct frame *frame)
1614 /* --------------------------------------------------------------------------
1615      External (hook): called on things like window switching within frame
1616    -------------------------------------------------------------------------- */
1618   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
1619   struct frame *old_highlight = dpyinfo->x_highlight_frame;
1621   NSTRACE ("ns_frame_rehighlight");
1622   if (dpyinfo->x_focus_frame)
1623     {
1624       dpyinfo->x_highlight_frame
1625         = (FRAMEP (FRAME_FOCUS_FRAME (dpyinfo->x_focus_frame))
1626            ? XFRAME (FRAME_FOCUS_FRAME (dpyinfo->x_focus_frame))
1627            : dpyinfo->x_focus_frame);
1628       if (!FRAME_LIVE_P (dpyinfo->x_highlight_frame))
1629         {
1630           fset_focus_frame (dpyinfo->x_focus_frame, Qnil);
1631           dpyinfo->x_highlight_frame = dpyinfo->x_focus_frame;
1632         }
1633     }
1634   else
1635       dpyinfo->x_highlight_frame = 0;
1637   if (dpyinfo->x_highlight_frame &&
1638          dpyinfo->x_highlight_frame != old_highlight)
1639     {
1640       if (old_highlight)
1641         {
1642           x_update_cursor (old_highlight, 1);
1643           x_set_frame_alpha (old_highlight);
1644         }
1645       if (dpyinfo->x_highlight_frame)
1646         {
1647           x_update_cursor (dpyinfo->x_highlight_frame, 1);
1648           x_set_frame_alpha (dpyinfo->x_highlight_frame);
1649         }
1650     }
1654 void
1655 x_make_frame_visible (struct frame *f)
1656 /* --------------------------------------------------------------------------
1657      External: Show the window (X11 semantics)
1658    -------------------------------------------------------------------------- */
1660   NSTRACE ("x_make_frame_visible");
1661   /* XXX: at some points in past this was not needed, as the only place that
1662      called this (frame.c:Fraise_frame ()) also called raise_lower;
1663      if this ends up the case again, comment this out again.  */
1664   if (!FRAME_VISIBLE_P (f))
1665     {
1666       EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
1667       NSWindow *window = [view window];
1669       SET_FRAME_VISIBLE (f, 1);
1670       ns_raise_frame (f, ! FRAME_NO_FOCUS_ON_MAP (f));
1672       /* Making a new frame from a fullscreen frame will make the new frame
1673          fullscreen also.  So skip handleFS as this will print an error.  */
1674       if ([view fsIsNative] && f->want_fullscreen == FULLSCREEN_BOTH
1675           && [view isFullscreen])
1676         return;
1678       if (f->want_fullscreen != FULLSCREEN_NONE)
1679         {
1680           block_input ();
1681           [view handleFS];
1682           unblock_input ();
1683         }
1685       /* Making a frame invisible seems to break the parent->child
1686          relationship, so reinstate it.  */
1687       if ([window parentWindow] == nil && FRAME_PARENT_FRAME (f) != NULL)
1688         {
1689           NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
1691           block_input ();
1692           [parent addChildWindow: window
1693                          ordered: NSWindowAbove];
1694           unblock_input ();
1696           /* If the parent frame moved while the child frame was
1697              invisible, the child frame's position won't have been
1698              updated.  Make sure it's in the right place now.  */
1699           x_set_offset(f, f->left_pos, f->top_pos, 0);
1700         }
1701     }
1705 void
1706 x_make_frame_invisible (struct frame *f)
1707 /* --------------------------------------------------------------------------
1708      External: Hide the window (X11 semantics)
1709    -------------------------------------------------------------------------- */
1711   NSView *view;
1712   NSTRACE ("x_make_frame_invisible");
1713   check_window_system (f);
1714   view = FRAME_NS_VIEW (f);
1715   [[view window] orderOut: NSApp];
1716   SET_FRAME_VISIBLE (f, 0);
1717   SET_FRAME_ICONIFIED (f, 0);
1721 void
1722 x_iconify_frame (struct frame *f)
1723 /* --------------------------------------------------------------------------
1724      External: Iconify window
1725    -------------------------------------------------------------------------- */
1727   NSView *view;
1728   struct ns_display_info *dpyinfo;
1730   NSTRACE ("x_iconify_frame");
1731   check_window_system (f);
1732   view = FRAME_NS_VIEW (f);
1733   dpyinfo = FRAME_DISPLAY_INFO (f);
1735   if (dpyinfo->x_highlight_frame == f)
1736     dpyinfo->x_highlight_frame = 0;
1738   if ([[view window] windowNumber] <= 0)
1739     {
1740       /* The window is still deferred.  Make it very small, bring it
1741          on screen and order it out.  */
1742       NSRect s = { { 100, 100}, {0, 0} };
1743       NSRect t;
1744       t = [[view window] frame];
1745       [[view window] setFrame: s display: NO];
1746       [[view window] orderBack: NSApp];
1747       [[view window] orderOut: NSApp];
1748       [[view window] setFrame: t display: NO];
1749     }
1751   /* Processing input while Emacs is being minimized can cause a
1752      crash, so block it for the duration.  */
1753   block_input();
1754   [[view window] miniaturize: NSApp];
1755   unblock_input();
1758 /* Free X resources of frame F.  */
1760 void
1761 x_free_frame_resources (struct frame *f)
1763   NSView *view;
1764   struct ns_display_info *dpyinfo;
1765   Mouse_HLInfo *hlinfo;
1767   NSTRACE ("x_free_frame_resources");
1768   check_window_system (f);
1769   view = FRAME_NS_VIEW (f);
1770   dpyinfo = FRAME_DISPLAY_INFO (f);
1771   hlinfo = MOUSE_HL_INFO (f);
1773   [(EmacsView *)view setWindowClosing: YES]; /* may not have been informed */
1775   block_input ();
1777   free_frame_menubar (f);
1778   free_frame_faces (f);
1780   if (f == dpyinfo->x_focus_frame)
1781     dpyinfo->x_focus_frame = 0;
1782   if (f == dpyinfo->x_highlight_frame)
1783     dpyinfo->x_highlight_frame = 0;
1784   if (f == hlinfo->mouse_face_mouse_frame)
1785     reset_mouse_highlight (hlinfo);
1787   if (f->output_data.ns->miniimage != nil)
1788     [f->output_data.ns->miniimage release];
1790   [[view window] close];
1791   [view release];
1793   xfree (f->output_data.ns);
1795   unblock_input ();
1798 void
1799 x_destroy_window (struct frame *f)
1800 /* --------------------------------------------------------------------------
1801      External: Delete the window
1802    -------------------------------------------------------------------------- */
1804   NSTRACE ("x_destroy_window");
1806   /* If this frame has a parent window, detach it as not doing so can
1807      cause a crash in GNUStep.  */
1808   if (FRAME_PARENT_FRAME (f) != NULL)
1809     {
1810       NSWindow *child = [FRAME_NS_VIEW (f) window];
1811       NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
1813       [parent removeChildWindow: child];
1814     }
1816   check_window_system (f);
1817   x_free_frame_resources (f);
1818   ns_window_num--;
1822 void
1823 x_set_offset (struct frame *f, int xoff, int yoff, int change_grav)
1824 /* --------------------------------------------------------------------------
1825      External: Position the window
1826    -------------------------------------------------------------------------- */
1828   NSView *view = FRAME_NS_VIEW (f);
1829   NSScreen *screen = [[view window] screen];
1831   NSTRACE ("x_set_offset");
1833   block_input ();
1835   f->left_pos = xoff;
1836   f->top_pos = yoff;
1838   if (view != nil)
1839     {
1840       if (FRAME_PARENT_FRAME (f) == NULL && screen)
1841         {
1842           f->left_pos = f->size_hint_flags & XNegative
1843             ? [screen visibleFrame].size.width + f->left_pos - FRAME_PIXEL_WIDTH (f)
1844             : f->left_pos;
1845           /* We use visibleFrame here to take menu bar into account.
1846              Ideally we should also adjust left/top with visibleFrame.origin.  */
1848           f->top_pos = f->size_hint_flags & YNegative
1849             ? ([screen visibleFrame].size.height + f->top_pos
1850                - FRAME_PIXEL_HEIGHT (f) - FRAME_NS_TITLEBAR_HEIGHT (f)
1851                - FRAME_TOOLBAR_HEIGHT (f))
1852             : f->top_pos;
1853 #ifdef NS_IMPL_GNUSTEP
1854           if (f->left_pos < 100)
1855             f->left_pos = 100;  /* don't overlap menu */
1856 #endif
1857         }
1858       else if (FRAME_PARENT_FRAME (f) != NULL)
1859         {
1860           struct frame *parent = FRAME_PARENT_FRAME (f);
1862           /* On X negative values for child frames always result in
1863              positioning relative to the bottom right corner of the
1864              parent frame.  */
1865           if (f->left_pos < 0)
1866             f->left_pos = FRAME_PIXEL_WIDTH (parent) - FRAME_PIXEL_WIDTH (f) + f->left_pos;
1868           if (f->top_pos < 0)
1869             f->top_pos = FRAME_PIXEL_HEIGHT (parent) + FRAME_TOOLBAR_HEIGHT (parent)
1870               - FRAME_PIXEL_HEIGHT (f) + f->top_pos;
1871         }
1873       /* Constrain the setFrameTopLeftPoint so we don't move behind the
1874          menu bar.  */
1875       NSPoint pt = NSMakePoint (SCREENMAXBOUND (f->left_pos
1876                                                 + NS_PARENT_WINDOW_LEFT_POS (f)),
1877                                 SCREENMAXBOUND (NS_PARENT_WINDOW_TOP_POS (f)
1878                                                 - f->top_pos));
1879       NSTRACE_POINT ("setFrameTopLeftPoint", pt);
1880       [[view window] setFrameTopLeftPoint: pt];
1881       f->size_hint_flags &= ~(XNegative|YNegative);
1882     }
1884   unblock_input ();
1888 void
1889 x_set_window_size (struct frame *f,
1890                    bool change_gravity,
1891                    int width,
1892                    int height,
1893                    bool pixelwise)
1894 /* --------------------------------------------------------------------------
1895      Adjust window pixel size based on given character grid size
1896      Impl is a bit more complex than other terms, need to do some
1897      internal clipping.
1898    -------------------------------------------------------------------------- */
1900   EmacsView *view = FRAME_NS_VIEW (f);
1901   NSWindow *window = [view window];
1902   NSRect wr = [window frame];
1903   int pixelwidth, pixelheight;
1904   int orig_height = wr.size.height;
1906   NSTRACE ("x_set_window_size");
1908   if (view == nil)
1909     return;
1911   NSTRACE_RECT ("current", wr);
1912   NSTRACE_MSG ("Width:%d Height:%d Pixelwise:%d", width, height, pixelwise);
1913   NSTRACE_MSG ("Font %d x %d", FRAME_COLUMN_WIDTH (f), FRAME_LINE_HEIGHT (f));
1915   block_input ();
1917 #ifdef NS_IMPL_COCOA
1918   /* To prevent showing the user a blank frame, stop updates being
1919      flushed to the screen until after redisplay has completed.  This
1920      breaks live resize (resizing with a mouse), so don't do it if
1921      we're in a live resize loop.  */
1922   if (![view inLiveResize])
1923     ns_disable_screen_updates ();
1924 #endif
1926   if (pixelwise)
1927     {
1928       pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
1929       pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
1930     }
1931   else
1932     {
1933       pixelwidth =  FRAME_TEXT_COLS_TO_PIXEL_WIDTH   (f, width);
1934       pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, height);
1935     }
1937   wr.size.width = pixelwidth + f->border_width;
1938   wr.size.height = pixelheight;
1939   if (! [view isFullscreen])
1940     wr.size.height += FRAME_NS_TITLEBAR_HEIGHT (f)
1941       + FRAME_TOOLBAR_HEIGHT (f);
1943   /* Do not try to constrain to this screen.  We may have multiple
1944      screens, and want Emacs to span those.  Constraining to screen
1945      prevents that, and that is not nice to the user.  */
1946  if (f->output_data.ns->zooming)
1947    f->output_data.ns->zooming = 0;
1948  else
1949    wr.origin.y += orig_height - wr.size.height;
1951  frame_size_history_add
1952    (f, Qx_set_window_size_1, width, height,
1953     list5 (Fcons (make_number (pixelwidth), make_number (pixelheight)),
1954            Fcons (make_number (wr.size.width), make_number (wr.size.height)),
1955            make_number (f->border_width),
1956            make_number (FRAME_NS_TITLEBAR_HEIGHT (f)),
1957            make_number (FRAME_TOOLBAR_HEIGHT (f))));
1959   [window setFrame: wr display: YES];
1961   [view updateFrameSize: NO];
1962   unblock_input ();
1965 #ifdef NS_IMPL_COCOA
1966 void
1967 x_set_undecorated (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1968 /* --------------------------------------------------------------------------
1969      Set frame F's `undecorated' parameter.  If non-nil, F's window-system
1970      window is drawn without decorations, title, minimize/maximize boxes
1971      and external borders.  This usually means that the window cannot be
1972      dragged, resized, iconified, maximized or deleted with the mouse.  If
1973      nil, draw the frame with all the elements listed above unless these
1974      have been suspended via window manager settings.
1976      GNUStep cannot change an existing window's style.
1977    -------------------------------------------------------------------------- */
1979   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
1980   NSWindow *window = [view window];
1982   NSTRACE ("x_set_undecorated");
1984   if (!EQ (new_value, old_value))
1985     {
1986       block_input ();
1988       if (NILP (new_value))
1989         {
1990           FRAME_UNDECORATED (f) = false;
1991           [window setStyleMask: ((window.styleMask | FRAME_DECORATED_FLAGS)
1992                                   ^ FRAME_UNDECORATED_FLAGS)];
1994           [view createToolbar: f];
1995         }
1996       else
1997         {
1998           [window setToolbar: nil];
1999           /* Do I need to release the toolbar here?  */
2001           FRAME_UNDECORATED (f) = true;
2002           [window setStyleMask: ((window.styleMask | FRAME_UNDECORATED_FLAGS)
2003                                  ^ FRAME_DECORATED_FLAGS)];
2004         }
2006       /* At this point it seems we don't have an active NSResponder,
2007          so some key presses (TAB) are swallowed by the system.  */
2008       [window makeFirstResponder: view];
2010       [view updateFrameSize: NO];
2011       unblock_input ();
2012     }
2014 #endif /* NS_IMPL_COCOA */
2016 void
2017 x_set_parent_frame (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
2018 /* --------------------------------------------------------------------------
2019      Set frame F's `parent-frame' parameter.  If non-nil, make F a child
2020      frame of the frame specified by that parameter.  Technically, this
2021      makes F's window-system window a child window of the parent frame's
2022      window-system window.  If nil, make F's window-system window a
2023      top-level window--a child of its display's root window.
2025      A child frame's `left' and `top' parameters specify positions
2026      relative to the top-left corner of its parent frame's native
2027      rectangle.  On macOS moving a parent frame moves all its child
2028      frames too, keeping their position relative to the parent
2029      unaltered.  When a parent frame is iconified or made invisible, its
2030      child frames are made invisible.  When a parent frame is deleted,
2031      its child frames are deleted too.
2033      Whether a child frame has a tool bar may be window-system or window
2034      manager dependent.  It's advisable to disable it via the frame
2035      parameter settings.
2037      Some window managers may not honor this parameter.
2038    -------------------------------------------------------------------------- */
2040   struct frame *p = NULL;
2041   NSWindow *parent, *child;
2043   NSTRACE ("x_set_parent_frame");
2045   if (!NILP (new_value)
2046       && (!FRAMEP (new_value)
2047           || !FRAME_LIVE_P (p = XFRAME (new_value))
2048           || !FRAME_NS_P (p)))
2049     {
2050       store_frame_param (f, Qparent_frame, old_value);
2051       error ("Invalid specification of `parent-frame'");
2052     }
2054   if (p != FRAME_PARENT_FRAME (f))
2055     {
2056       parent = [FRAME_NS_VIEW (p) window];
2057       child = [FRAME_NS_VIEW (f) window];
2059       block_input ();
2060       [parent addChildWindow: child
2061                      ordered: NSWindowAbove];
2062       unblock_input ();
2064       fset_parent_frame (f, new_value);
2065     }
2068 void
2069 x_set_no_focus_on_map (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
2070 /* Set frame F's `no-focus-on-map' parameter which, if non-nil, means
2071  * that F's window-system window does not want to receive input focus
2072  * when it is mapped.  (A frame's window is mapped when the frame is
2073  * displayed for the first time and when the frame changes its state
2074  * from `iconified' or `invisible' to `visible'.)
2076  * Some window managers may not honor this parameter.  */
2078   NSTRACE ("x_set_no_focus_on_map");
2080   if (!EQ (new_value, old_value))
2081     {
2082       FRAME_NO_FOCUS_ON_MAP (f) = !NILP (new_value);
2083     }
2086 void
2087 x_set_no_accept_focus (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
2088 /*  Set frame F's `no-accept-focus' parameter which, if non-nil, hints
2089  * that F's window-system window does not want to receive input focus
2090  * via mouse clicks or by moving the mouse into it.
2092  * If non-nil, this may have the unwanted side-effect that a user cannot
2093  * scroll a non-selected frame with the mouse.
2095  * Some window managers may not honor this parameter.  */
2097   NSTRACE ("x_set_no_accept_focus");
2099   if (!EQ (new_value, old_value))
2100     FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value);
2103 void
2104 x_set_z_group (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
2105 /* Set frame F's `z-group' parameter.  If `above', F's window-system
2106    window is displayed above all windows that do not have the `above'
2107    property set.  If nil, F's window is shown below all windows that
2108    have the `above' property set and above all windows that have the
2109    `below' property set.  If `below', F's window is displayed below
2110    all windows that do.
2112    Some window managers may not honor this parameter.  */
2114   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2115   NSWindow *window = [view window];
2117   NSTRACE ("x_set_z_group");
2119   if (NILP (new_value))
2120     {
2121       window.level = NSNormalWindowLevel;
2122       FRAME_Z_GROUP (f) = z_group_none;
2123     }
2124   else if (EQ (new_value, Qabove))
2125     {
2126       window.level = NSNormalWindowLevel + 1;
2127       FRAME_Z_GROUP (f) = z_group_above;
2128     }
2129   else if (EQ (new_value, Qabove_suspended))
2130     {
2131       /* Not sure what level this should be.  */
2132       window.level = NSNormalWindowLevel + 1;
2133       FRAME_Z_GROUP (f) = z_group_above_suspended;
2134     }
2135   else if (EQ (new_value, Qbelow))
2136     {
2137       window.level = NSNormalWindowLevel - 1;
2138       FRAME_Z_GROUP (f) = z_group_below;
2139     }
2140   else
2141     error ("Invalid z-group specification");
2144 #ifdef NS_IMPL_COCOA
2145 void
2146 ns_set_appearance (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
2148 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
2149   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2150   NSWindow *window = [view window];
2152   NSTRACE ("ns_set_appearance");
2154 #ifndef NSAppKitVersionNumber10_10
2155 #define NSAppKitVersionNumber10_10 1343
2156 #endif
2158   if (NSAppKitVersionNumber < NSAppKitVersionNumber10_10)
2159     return;
2161   if (EQ (new_value, Qdark))
2162     {
2163       window.appearance = [NSAppearance
2164                             appearanceNamed: NSAppearanceNameVibrantDark];
2165       FRAME_NS_APPEARANCE (f) = ns_appearance_vibrant_dark;
2166     }
2167   else
2168     {
2169       window.appearance = [NSAppearance
2170                             appearanceNamed: NSAppearanceNameAqua];
2171       FRAME_NS_APPEARANCE (f) = ns_appearance_aqua;
2172     }
2173 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 */
2176 void
2177 ns_set_transparent_titlebar (struct frame *f, Lisp_Object new_value,
2178                              Lisp_Object old_value)
2180 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
2181   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2182   NSWindow *window = [view window];
2184   NSTRACE ("ns_set_transparent_titlebar");
2186   if ([window respondsToSelector: @selector(titlebarAppearsTransparent)]
2187       && !EQ (new_value, old_value))
2188     {
2189       window.titlebarAppearsTransparent = !NILP (new_value);
2190       FRAME_NS_TRANSPARENT_TITLEBAR (f) = !NILP (new_value);
2191     }
2192 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 */
2194 #endif /* NS_IMPL_COCOA */
2196 static void
2197 ns_fullscreen_hook (struct frame *f)
2199   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2201   NSTRACE ("ns_fullscreen_hook");
2203   if (!FRAME_VISIBLE_P (f))
2204     return;
2206    if (! [view fsIsNative] && f->want_fullscreen == FULLSCREEN_BOTH)
2207     {
2208       /* Old style fs don't initiate correctly if created from
2209          init/default-frame alist, so use a timer (not nice...).  */
2210       [NSTimer scheduledTimerWithTimeInterval: 0.5 target: view
2211                                      selector: @selector (handleFS)
2212                                      userInfo: nil repeats: NO];
2213       return;
2214     }
2216   block_input ();
2217   [view handleFS];
2218   unblock_input ();
2221 /* ==========================================================================
2223     Color management
2225    ========================================================================== */
2228 NSColor *
2229 ns_lookup_indexed_color (unsigned long idx, struct frame *f)
2231   struct ns_color_table *color_table = FRAME_DISPLAY_INFO (f)->color_table;
2232   if (idx < 1 || idx >= color_table->avail)
2233     return nil;
2234   return color_table->colors[idx];
2238 unsigned long
2239 ns_index_color (NSColor *color, struct frame *f)
2241   struct ns_color_table *color_table = FRAME_DISPLAY_INFO (f)->color_table;
2242   ptrdiff_t idx;
2243   ptrdiff_t i;
2245   if (!color_table->colors)
2246     {
2247       color_table->size = NS_COLOR_CAPACITY;
2248       color_table->avail = 1; /* skip idx=0 as marker */
2249       color_table->colors = xmalloc (color_table->size * sizeof (NSColor *));
2250       color_table->colors[0] = nil;
2251       color_table->empty_indices = [[NSMutableSet alloc] init];
2252     }
2254   /* Do we already have this color?  */
2255   for (i = 1; i < color_table->avail; i++)
2256     if (color_table->colors[i] && [color_table->colors[i] isEqual: color])
2257       return i;
2259   if ([color_table->empty_indices count] > 0)
2260     {
2261       NSNumber *index = [color_table->empty_indices anyObject];
2262       [color_table->empty_indices removeObject: index];
2263       idx = [index unsignedLongValue];
2264     }
2265   else
2266     {
2267       if (color_table->avail == color_table->size)
2268         color_table->colors =
2269           xpalloc (color_table->colors, &color_table->size, 1,
2270                    min (ULONG_MAX, PTRDIFF_MAX), sizeof *color_table->colors);
2271       idx = color_table->avail++;
2272     }
2274   color_table->colors[idx] = color;
2275   [color retain];
2276   /* fprintf(stderr, "color_table: allocated %d\n",idx); */
2277   return idx;
2281 static int
2282 ns_get_color (const char *name, NSColor **col)
2283 /* --------------------------------------------------------------------------
2284      Parse a color name
2285    -------------------------------------------------------------------------- */
2286 /* On *Step, we attempt to mimic the X11 platform here, down to installing an
2287    X11 rgb.txt-compatible color list in Emacs.clr (see ns_term_init()).
2288    See https://lists.gnu.org/r/emacs-devel/2009-07/msg01203.html.  */
2290   NSColor *new = nil;
2291   static char hex[20];
2292   int scaling = 0;
2293   float r = -1.0, g, b;
2294   NSString *nsname = [NSString stringWithUTF8String: name];
2296   NSTRACE ("ns_get_color(%s, **)", name);
2298   block_input ();
2300   if ([nsname isEqualToString: @"ns_selection_bg_color"])
2301     {
2302 #ifdef NS_IMPL_COCOA
2303       NSString *defname = [[NSUserDefaults standardUserDefaults]
2304                             stringForKey: @"AppleHighlightColor"];
2305       if (defname != nil)
2306         nsname = defname;
2307       else
2308 #endif
2309       if ((new = [NSColor selectedTextBackgroundColor]) != nil)
2310         {
2311           *col = [new colorUsingDefaultColorSpace];
2312           unblock_input ();
2313           return 0;
2314         }
2315       else
2316         nsname = NS_SELECTION_BG_COLOR_DEFAULT;
2318       name = [nsname UTF8String];
2319     }
2320   else if ([nsname isEqualToString: @"ns_selection_fg_color"])
2321     {
2322       /* NOTE: macOS applications normally don't set foreground
2323          selection, but text may be unreadable if we don't.  */
2324       if ((new = [NSColor selectedTextColor]) != nil)
2325         {
2326           *col = [new colorUsingDefaultColorSpace];
2327           unblock_input ();
2328           return 0;
2329         }
2331       nsname = NS_SELECTION_FG_COLOR_DEFAULT;
2332       name = [nsname UTF8String];
2333     }
2335   /* First, check for some sort of numeric specification.  */
2336   hex[0] = '\0';
2338   if (name[0] == '0' || name[0] == '1' || name[0] == '.')  /* RGB decimal */
2339     {
2340       NSScanner *scanner = [NSScanner scannerWithString: nsname];
2341       [scanner scanFloat: &r];
2342       [scanner scanFloat: &g];
2343       [scanner scanFloat: &b];
2344     }
2345   else if (!strncmp(name, "rgb:", 4))  /* A newer X11 format -- rgb:r/g/b */
2346     scaling = (snprintf (hex, sizeof hex, "%s", name + 4) - 2) / 3;
2347   else if (name[0] == '#')        /* An old X11 format; convert to newer */
2348     {
2349       int len = (strlen(name) - 1);
2350       int start = (len % 3 == 0) ? 1 : len / 4 + 1;
2351       int i;
2352       scaling = strlen(name+start) / 3;
2353       for (i = 0; i < 3; i++)
2354         sprintf (hex + i * (scaling + 1), "%.*s/", scaling,
2355                  name + start + i * scaling);
2356       hex[3 * (scaling + 1) - 1] = '\0';
2357     }
2359   if (hex[0])
2360     {
2361       unsigned int rr, gg, bb;
2362       float fscale = scaling == 4 ? 65535.0 : (scaling == 2 ? 255.0 : 15.0);
2363       if (sscanf (hex, "%x/%x/%x", &rr, &gg, &bb))
2364         {
2365           r = rr / fscale;
2366           g = gg / fscale;
2367           b = bb / fscale;
2368         }
2369     }
2371   if (r >= 0.0F)
2372     {
2373       *col = [NSColor colorForEmacsRed: r green: g blue: b alpha: 1.0];
2374       unblock_input ();
2375       return 0;
2376     }
2378   /* Otherwise, color is expected to be from a list */
2379   {
2380     NSEnumerator *lenum, *cenum;
2381     NSString *name;
2382     NSColorList *clist;
2384 #ifdef NS_IMPL_GNUSTEP
2385     /* XXX: who is wrong, the requestor or the implementation?  */
2386     if ([nsname compare: @"Highlight" options: NSCaseInsensitiveSearch]
2387         == NSOrderedSame)
2388       nsname = @"highlightColor";
2389 #endif
2391     lenum = [[NSColorList availableColorLists] objectEnumerator];
2392     while ( (clist = [lenum nextObject]) && new == nil)
2393       {
2394         cenum = [[clist allKeys] objectEnumerator];
2395         while ( (name = [cenum nextObject]) && new == nil )
2396           {
2397             if ([name compare: nsname
2398                       options: NSCaseInsensitiveSearch] == NSOrderedSame )
2399               new = [clist colorWithKey: name];
2400           }
2401       }
2402   }
2404   if (new)
2405     *col = [new colorUsingDefaultColorSpace];
2406   unblock_input ();
2407   return new ? 0 : 1;
2412 ns_lisp_to_color (Lisp_Object color, NSColor **col)
2413 /* --------------------------------------------------------------------------
2414      Convert a Lisp string object to a NS color.
2415    -------------------------------------------------------------------------- */
2417   NSTRACE ("ns_lisp_to_color");
2418   if (STRINGP (color))
2419     return ns_get_color (SSDATA (color), col);
2420   else if (SYMBOLP (color))
2421     return ns_get_color (SSDATA (SYMBOL_NAME (color)), col);
2422   return 1;
2426 void
2427 ns_query_color(void *col, XColor *color_def, int setPixel)
2428 /* --------------------------------------------------------------------------
2429          Get ARGB values out of NSColor col and put them into color_def.
2430          If setPixel, set the pixel to a concatenated version.
2431          and set color_def pixel to the resulting index.
2432    -------------------------------------------------------------------------- */
2434   EmacsCGFloat r, g, b, a;
2436   [((NSColor *)col) getRed: &r green: &g blue: &b alpha: &a];
2437   color_def->red   = r * 65535;
2438   color_def->green = g * 65535;
2439   color_def->blue  = b * 65535;
2441   if (setPixel == YES)
2442     color_def->pixel
2443       = ARGB_TO_ULONG((int)(a*255),
2444                       (int)(r*255), (int)(g*255), (int)(b*255));
2448 bool
2449 ns_defined_color (struct frame *f,
2450                   const char *name,
2451                   XColor *color_def,
2452                   bool alloc,
2453                   bool makeIndex)
2454 /* --------------------------------------------------------------------------
2455          Return true if named color found, and set color_def rgb accordingly.
2456          If makeIndex and alloc are nonzero put the color in the color_table,
2457          and set color_def pixel to the resulting index.
2458          If makeIndex is zero, set color_def pixel to ARGB.
2459          Return false if not found.
2460    -------------------------------------------------------------------------- */
2462   NSColor *col;
2463   NSTRACE_WHEN (NSTRACE_GROUP_COLOR, "ns_defined_color");
2465   block_input ();
2466   if (ns_get_color (name, &col) != 0) /* Color not found  */
2467     {
2468       unblock_input ();
2469       return 0;
2470     }
2471   if (makeIndex && alloc)
2472     color_def->pixel = ns_index_color (col, f);
2473   ns_query_color (col, color_def, !makeIndex);
2474   unblock_input ();
2475   return 1;
2479 void
2480 x_set_frame_alpha (struct frame *f)
2481 /* --------------------------------------------------------------------------
2482      change the entire-frame transparency
2483    -------------------------------------------------------------------------- */
2485   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
2486   double alpha = 1.0;
2487   double alpha_min = 1.0;
2489   NSTRACE ("x_set_frame_alpha");
2491   if (dpyinfo->x_highlight_frame == f)
2492     alpha = f->alpha[0];
2493   else
2494     alpha = f->alpha[1];
2496   if (FLOATP (Vframe_alpha_lower_limit))
2497     alpha_min = XFLOAT_DATA (Vframe_alpha_lower_limit);
2498   else if (INTEGERP (Vframe_alpha_lower_limit))
2499     alpha_min = (XINT (Vframe_alpha_lower_limit)) / 100.0;
2501   if (alpha < 0.0)
2502     return;
2503   else if (1.0 < alpha)
2504     alpha = 1.0;
2505   else if (0.0 <= alpha && alpha < alpha_min && alpha_min <= 1.0)
2506     alpha = alpha_min;
2508 #ifdef NS_IMPL_COCOA
2509   {
2510     EmacsView *view = FRAME_NS_VIEW (f);
2511   [[view window] setAlphaValue: alpha];
2512   }
2513 #endif
2517 /* ==========================================================================
2519     Mouse handling
2521    ========================================================================== */
2524 void
2525 frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y)
2526 /* --------------------------------------------------------------------------
2527      Programmatically reposition mouse pointer in pixel coordinates
2528    -------------------------------------------------------------------------- */
2530   NSTRACE ("frame_set_mouse_pixel_position");
2532   /* FIXME: what about GNUstep?  */
2533 #ifdef NS_IMPL_COCOA
2534   CGPoint mouse_pos =
2535     CGPointMake(f->left_pos + pix_x,
2536                 f->top_pos + pix_y +
2537                 FRAME_NS_TITLEBAR_HEIGHT(f) + FRAME_TOOLBAR_HEIGHT(f));
2538   CGWarpMouseCursorPosition (mouse_pos);
2539 #endif
2542 static int
2543 note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y)
2544 /*   ------------------------------------------------------------------------
2545      Called by EmacsView on mouseMovement events.  Passes on
2546      to emacs mainstream code if we moved off of a rect of interest
2547      known as last_mouse_glyph.
2548      ------------------------------------------------------------------------ */
2550   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
2551   NSRect *r;
2553   // NSTRACE ("note_mouse_movement");
2555   dpyinfo->last_mouse_motion_frame = frame;
2556   r = &dpyinfo->last_mouse_glyph;
2558   /* Note, this doesn't get called for enter/leave, since we don't have a
2559      position.  Those are taken care of in the corresponding NSView methods.  */
2561   /* Has movement gone beyond last rect we were tracking?  */
2562   if (x < r->origin.x || x >= r->origin.x + r->size.width
2563       || y < r->origin.y || y >= r->origin.y + r->size.height)
2564     {
2565       ns_update_begin (frame);
2566       frame->mouse_moved = 1;
2567       note_mouse_highlight (frame, x, y);
2568       remember_mouse_glyph (frame, x, y, r);
2569       ns_update_end (frame);
2570       return 1;
2571     }
2573   return 0;
2577 static void
2578 ns_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window,
2579                    enum scroll_bar_part *part, Lisp_Object *x, Lisp_Object *y,
2580                    Time *time)
2581 /* --------------------------------------------------------------------------
2582     External (hook): inform emacs about mouse position and hit parts.
2583     If a scrollbar is being dragged, set bar_window, part, x, y, time.
2584     x & y should be position in the scrollbar (the whole bar, not the handle)
2585     and length of scrollbar respectively.
2586    -------------------------------------------------------------------------- */
2588   id view;
2589   NSPoint position;
2590   Lisp_Object frame, tail;
2591   struct frame *f;
2592   struct ns_display_info *dpyinfo;
2594   NSTRACE ("ns_mouse_position");
2596   if (*fp == NULL)
2597     {
2598       fprintf (stderr, "Warning: ns_mouse_position () called with null *fp.\n");
2599       return;
2600     }
2602   dpyinfo = FRAME_DISPLAY_INFO (*fp);
2604   block_input ();
2606   /* Clear the mouse-moved flag for every frame on this display.  */
2607   FOR_EACH_FRAME (tail, frame)
2608     if (FRAME_NS_P (XFRAME (frame))
2609         && FRAME_NS_DISPLAY (XFRAME (frame)) == FRAME_NS_DISPLAY (*fp))
2610       XFRAME (frame)->mouse_moved = 0;
2612   dpyinfo->last_mouse_scroll_bar = nil;
2613   if (dpyinfo->last_mouse_frame
2614       && FRAME_LIVE_P (dpyinfo->last_mouse_frame))
2615     f = dpyinfo->last_mouse_frame;
2616   else
2617     f = dpyinfo->x_focus_frame ? dpyinfo->x_focus_frame : SELECTED_FRAME ();
2619   if (f && FRAME_NS_P (f))
2620     {
2621       view = FRAME_NS_VIEW (*fp);
2623       position = [[view window] mouseLocationOutsideOfEventStream];
2624       position = [view convertPoint: position fromView: nil];
2625       remember_mouse_glyph (f, position.x, position.y,
2626                             &dpyinfo->last_mouse_glyph);
2627       NSTRACE_POINT ("position", position);
2629       if (bar_window) *bar_window = Qnil;
2630       if (part) *part = scroll_bar_above_handle;
2632       if (x) XSETINT (*x, lrint (position.x));
2633       if (y) XSETINT (*y, lrint (position.y));
2634       if (time)
2635         *time = dpyinfo->last_mouse_movement_time;
2636       *fp = f;
2637     }
2639   unblock_input ();
2643 static void
2644 ns_frame_up_to_date (struct frame *f)
2645 /* --------------------------------------------------------------------------
2646     External (hook): Fix up mouse highlighting right after a full update.
2647     Can't use FRAME_MOUSE_UPDATE due to ns_frame_begin and ns_frame_end calls.
2648    -------------------------------------------------------------------------- */
2650   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_frame_up_to_date");
2652   if (FRAME_NS_P (f))
2653     {
2654       Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
2655       if (f == hlinfo->mouse_face_mouse_frame)
2656         {
2657           block_input ();
2658           ns_update_begin(f);
2659           note_mouse_highlight (hlinfo->mouse_face_mouse_frame,
2660                                 hlinfo->mouse_face_mouse_x,
2661                                 hlinfo->mouse_face_mouse_y);
2662           ns_update_end(f);
2663           unblock_input ();
2664         }
2665     }
2669 static void
2670 ns_define_frame_cursor (struct frame *f, Cursor cursor)
2671 /* --------------------------------------------------------------------------
2672     External (RIF): set frame mouse pointer type.
2673    -------------------------------------------------------------------------- */
2675   NSTRACE ("ns_define_frame_cursor");
2676   if (FRAME_POINTER_TYPE (f) != cursor)
2677     {
2678       EmacsView *view = FRAME_NS_VIEW (f);
2679       FRAME_POINTER_TYPE (f) = cursor;
2680       [[view window] invalidateCursorRectsForView: view];
2681       /* Redisplay assumes this function also draws the changed frame
2682          cursor, but this function doesn't, so do it explicitly.  */
2683       x_update_cursor (f, 1);
2684     }
2689 /* ==========================================================================
2691     Keyboard handling
2693    ========================================================================== */
2696 static unsigned
2697 ns_convert_key (unsigned code)
2698 /* --------------------------------------------------------------------------
2699     Internal call used by NSView-keyDown.
2700    -------------------------------------------------------------------------- */
2702   const unsigned last_keysym = ARRAYELTS (convert_ns_to_X_keysym);
2703   unsigned keysym;
2704   /* An array would be faster, but less easy to read.  */
2705   for (keysym = 0; keysym < last_keysym; keysym += 2)
2706     if (code == convert_ns_to_X_keysym[keysym])
2707       return 0xFF00 | convert_ns_to_X_keysym[keysym+1];
2708   return 0;
2709 /* if decide to use keyCode and Carbon table, use this line:
2710      return code > 0xff ? 0 : 0xFF00 | ns_keycode_to_xkeysym_table[code]; */
2714 char *
2715 x_get_keysym_name (int keysym)
2716 /* --------------------------------------------------------------------------
2717     Called by keyboard.c.  Not sure if the return val is important, except
2718     that it be unique.
2719    -------------------------------------------------------------------------- */
2721   static char value[16];
2722   NSTRACE ("x_get_keysym_name");
2723   sprintf (value, "%d", keysym);
2724   return value;
2727 #ifdef NS_IMPL_COCOA
2728 static UniChar
2729 ns_get_shifted_character (NSEvent *event)
2730 /* Look up the character corresponding to the key pressed on the
2731    current keyboard layout and the currently configured shift-like
2732    modifiers.  This ignores the control-like modifiers that cause
2733    [event characters] to give us the wrong result.
2735    Although UCKeyTranslate doesn't require the Carbon framework, some
2736    of the surrounding paraphernalia does, so this function makes
2737    Carbon a requirement.  */
2739   static UInt32 dead_key_state;
2741   /* UCKeyTranslate may return up to 255 characters.  If the buffer
2742      isn't large enough then it produces an error.  What kind of
2743      keyboard inputs 255 characters in a single keypress?  */
2744   UniChar buf[255];
2745   UniCharCount max_string_length = 255;
2746   UniCharCount actual_string_length = 0;
2747   OSStatus result;
2749   CFDataRef layout_ref = (CFDataRef) TISGetInputSourceProperty
2750     (TISCopyCurrentKeyboardLayoutInputSource (), kTISPropertyUnicodeKeyLayoutData);
2751   UCKeyboardLayout* layout = (UCKeyboardLayout*) CFDataGetBytePtr (layout_ref);
2753   UInt32 flags = [event modifierFlags];
2754   UInt32 modifiers = (flags & NSEventModifierFlagShift) ? shiftKey : 0;
2756   NSTRACE ("ns_get_shifted_character");
2758   if ((flags & NSRightAlternateKeyMask) == NSRightAlternateKeyMask
2759       && (EQ (ns_right_alternate_modifier, Qnone)
2760           || (EQ (ns_right_alternate_modifier, Qleft)
2761               && EQ (ns_alternate_modifier, Qnone))))
2762     modifiers |= rightOptionKey;
2764   if ((flags & NSLeftAlternateKeyMask) == NSLeftAlternateKeyMask
2765       && EQ (ns_alternate_modifier, Qnone))
2766     modifiers |= optionKey;
2768   if ((flags & NSRightCommandKeyMask) == NSRightCommandKeyMask
2769       && (EQ (ns_right_command_modifier, Qnone)
2770           || (EQ (ns_right_command_modifier, Qleft)
2771               && EQ (ns_command_modifier, Qnone))))
2772     /* Carbon doesn't differentiate between left and right command
2773        keys.  */
2774     modifiers |= cmdKey;
2776   if ((flags & NSLeftCommandKeyMask) == NSLeftCommandKeyMask
2777       && EQ (ns_command_modifier, Qnone))
2778     modifiers |= cmdKey;
2780   result = UCKeyTranslate (layout, [event keyCode], kUCKeyActionDown,
2781                            (modifiers >> 8) & 0xFF, LMGetKbdType (),
2782                            kUCKeyTranslateNoDeadKeysBit, &dead_key_state,
2783                            max_string_length, &actual_string_length, buf);
2785   if (result != 0)
2786     {
2787       NSLog(@"Failed to translate character '%@' with modifiers %x",
2788             [event characters], modifiers);
2789       return 0;
2790     }
2792   /* FIXME: What do we do if more than one code unit is returned?  */
2793   if (actual_string_length > 0)
2794     return buf[0];
2796   return 0;
2798 #endif /* NS_IMPL_COCOA */
2800 /* ==========================================================================
2802     Block drawing operations
2804    ========================================================================== */
2807 static void
2808 ns_redraw_scroll_bars (struct frame *f)
2810   int i;
2811   id view;
2812   NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews];
2813   NSTRACE ("ns_redraw_scroll_bars");
2814   for (i =[subviews count]-1; i >= 0; i--)
2815     {
2816       view = [subviews objectAtIndex: i];
2817       if (![view isKindOfClass: [EmacsScroller class]]) continue;
2818       [view display];
2819     }
2823 void
2824 ns_clear_frame (struct frame *f)
2825 /* --------------------------------------------------------------------------
2826       External (hook): Erase the entire frame
2827    -------------------------------------------------------------------------- */
2829   NSView *view = FRAME_NS_VIEW (f);
2830   NSRect r;
2832   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_clear_frame");
2834  /* comes on initial frame because we have
2835     after-make-frame-functions = select-frame */
2836  if (!FRAME_DEFAULT_FACE (f))
2837    return;
2839   mark_window_cursors_off (XWINDOW (FRAME_ROOT_WINDOW (f)));
2841   r = [view bounds];
2843   block_input ();
2844   ns_focus (f, &r, 1);
2845   [ns_lookup_indexed_color (NS_FACE_BACKGROUND
2846                             (FACE_FROM_ID (f, DEFAULT_FACE_ID)), f) set];
2847   NSRectFill (r);
2848   ns_unfocus (f);
2850   /* as of 2006/11 or so this is now needed */
2851   ns_redraw_scroll_bars (f);
2852   unblock_input ();
2856 static void
2857 ns_clear_frame_area (struct frame *f, int x, int y, int width, int height)
2858 /* --------------------------------------------------------------------------
2859     External (RIF):  Clear section of frame
2860    -------------------------------------------------------------------------- */
2862   NSRect r = NSMakeRect (x, y, width, height);
2863   NSView *view = FRAME_NS_VIEW (f);
2864   struct face *face = FRAME_DEFAULT_FACE (f);
2866   if (!view || !face)
2867     return;
2869   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_clear_frame_area");
2871   r = NSIntersectionRect (r, [view frame]);
2872   ns_focus (f, &r, 1);
2873   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), f) set];
2875   NSRectFill (r);
2877   ns_unfocus (f);
2878   return;
2881 static void
2882 ns_copy_bits (struct frame *f, NSRect src, NSRect dest)
2884   NSTRACE ("ns_copy_bits");
2886   if (FRAME_NS_VIEW (f))
2887     {
2888       hide_bell();              // Ensure the bell image isn't scrolled.
2890       ns_focus (f, &dest, 1);
2891       [FRAME_NS_VIEW (f) scrollRect: src
2892                                  by: NSMakeSize (dest.origin.x - src.origin.x,
2893                                                  dest.origin.y - src.origin.y)];
2894       ns_unfocus (f);
2895     }
2898 static void
2899 ns_scroll_run (struct window *w, struct run *run)
2900 /* --------------------------------------------------------------------------
2901     External (RIF):  Insert or delete n lines at line vpos.
2902    -------------------------------------------------------------------------- */
2904   struct frame *f = XFRAME (w->frame);
2905   int x, y, width, height, from_y, to_y, bottom_y;
2907   NSTRACE ("ns_scroll_run");
2909   /* begin copy from other terms */
2910   /* Get frame-relative bounding box of the text display area of W,
2911      without mode lines.  Include in this box the left and right
2912      fringe of W.  */
2913   window_box (w, ANY_AREA, &x, &y, &width, &height);
2915   from_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->current_y);
2916   to_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->desired_y);
2917   bottom_y = y + height;
2919   if (to_y < from_y)
2920     {
2921       /* Scrolling up.  Make sure we don't copy part of the mode
2922          line at the bottom.  */
2923       if (from_y + run->height > bottom_y)
2924         height = bottom_y - from_y;
2925       else
2926         height = run->height;
2927     }
2928   else
2929     {
2930       /* Scrolling down.  Make sure we don't copy over the mode line.
2931          at the bottom.  */
2932       if (to_y + run->height > bottom_y)
2933         height = bottom_y - to_y;
2934       else
2935         height = run->height;
2936     }
2937   /* end copy from other terms */
2939   if (height == 0)
2940       return;
2942   block_input ();
2944   x_clear_cursor (w);
2946   {
2947     NSRect srcRect = NSMakeRect (x, from_y, width, height);
2948     NSRect dstRect = NSMakeRect (x, to_y, width, height);
2950     ns_copy_bits (f, srcRect , dstRect);
2951   }
2953   unblock_input ();
2957 static void
2958 ns_after_update_window_line (struct window *w, struct glyph_row *desired_row)
2959 /* --------------------------------------------------------------------------
2960     External (RIF): preparatory to fringe update after text was updated
2961    -------------------------------------------------------------------------- */
2963   struct frame *f;
2964   int width, height;
2966   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_after_update_window_line");
2968   /* begin copy from other terms */
2969   eassert (w);
2971   if (!desired_row->mode_line_p && !w->pseudo_window_p)
2972     desired_row->redraw_fringe_bitmaps_p = 1;
2974   /* When a window has disappeared, make sure that no rest of
2975      full-width rows stays visible in the internal border.  */
2976   if (windows_or_buffers_changed
2977       && desired_row->full_width_p
2978       && (f = XFRAME (w->frame),
2979           width = FRAME_INTERNAL_BORDER_WIDTH (f),
2980           width != 0)
2981       && (height = desired_row->visible_height,
2982           height > 0))
2983     {
2984       int y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, desired_row->y));
2986       block_input ();
2987       ns_clear_frame_area (f, 0, y, width, height);
2988       ns_clear_frame_area (f,
2989                            FRAME_PIXEL_WIDTH (f) - width,
2990                            y, width, height);
2991       unblock_input ();
2992     }
2996 static void
2997 ns_shift_glyphs_for_insert (struct frame *f,
2998                            int x, int y, int width, int height,
2999                            int shift_by)
3000 /* --------------------------------------------------------------------------
3001     External (RIF): copy an area horizontally, don't worry about clearing src
3002    -------------------------------------------------------------------------- */
3004   NSRect srcRect = NSMakeRect (x, y, width, height);
3005   NSRect dstRect = NSMakeRect (x+shift_by, y, width, height);
3007   NSTRACE ("ns_shift_glyphs_for_insert");
3009   ns_copy_bits (f, srcRect, dstRect);
3014 /* ==========================================================================
3016     Character encoding and metrics
3018    ========================================================================== */
3021 static void
3022 ns_compute_glyph_string_overhangs (struct glyph_string *s)
3023 /* --------------------------------------------------------------------------
3024      External (RIF); compute left/right overhang of whole string and set in s
3025    -------------------------------------------------------------------------- */
3027   struct font *font = s->font;
3029   if (s->char2b)
3030     {
3031       struct font_metrics metrics;
3032       unsigned int codes[2];
3033       codes[0] = *(s->char2b);
3034       codes[1] = *(s->char2b + s->nchars - 1);
3036       font->driver->text_extents (font, codes, 2, &metrics);
3037       s->left_overhang = -metrics.lbearing;
3038       s->right_overhang
3039         = metrics.rbearing > metrics.width
3040         ? metrics.rbearing - metrics.width : 0;
3041     }
3042   else
3043     {
3044       s->left_overhang = 0;
3045       if (EQ (font->driver->type, Qns))
3046         s->right_overhang = ((struct nsfont_info *)font)->ital ?
3047           FONT_HEIGHT (font) * 0.2 : 0;
3048       else
3049         s->right_overhang = 0;
3050     }
3055 /* ==========================================================================
3057     Fringe and cursor drawing
3059    ========================================================================== */
3062 extern int max_used_fringe_bitmap;
3063 static void
3064 ns_draw_fringe_bitmap (struct window *w, struct glyph_row *row,
3065                       struct draw_fringe_bitmap_params *p)
3066 /* --------------------------------------------------------------------------
3067     External (RIF); fringe-related
3068    -------------------------------------------------------------------------- */
3070   /* Fringe bitmaps comes in two variants, normal and periodic.  A
3071      periodic bitmap is used to create a continuous pattern.  Since a
3072      bitmap is rendered one text line at a time, the start offset (dh)
3073      of the bitmap varies.  Concretely, this is used for the empty
3074      line indicator.
3076      For a bitmap, "h + dh" is the full height and is always
3077      invariant.  For a normal bitmap "dh" is zero.
3079      For example, when the period is three and the full height is 72
3080      the following combinations exists:
3082        h=72 dh=0
3083        h=71 dh=1
3084        h=70 dh=2 */
3086   struct frame *f = XFRAME (WINDOW_FRAME (w));
3087   struct face *face = p->face;
3088   static EmacsImage **bimgs = NULL;
3089   static int nBimgs = 0;
3091   NSTRACE_WHEN (NSTRACE_GROUP_FRINGE, "ns_draw_fringe_bitmap");
3092   NSTRACE_MSG ("which:%d cursor:%d overlay:%d width:%d height:%d period:%d",
3093                p->which, p->cursor_p, p->overlay_p, p->wd, p->h, p->dh);
3095   /* grow bimgs if needed */
3096   if (nBimgs < max_used_fringe_bitmap)
3097     {
3098       bimgs = xrealloc (bimgs, max_used_fringe_bitmap * sizeof *bimgs);
3099       memset (bimgs + nBimgs, 0,
3100               (max_used_fringe_bitmap - nBimgs) * sizeof *bimgs);
3101       nBimgs = max_used_fringe_bitmap;
3102     }
3104   /* Must clip because of partially visible lines.  */
3105   ns_clip_to_row (w, row, ANY_AREA, YES);
3107   if (!p->overlay_p)
3108     {
3109       int bx = p->bx, by = p->by, nx = p->nx, ny = p->ny;
3111       if (bx >= 0 && nx > 0)
3112         {
3113           NSRect r = NSMakeRect (bx, by, nx, ny);
3114           NSRectClip (r);
3115           [ns_lookup_indexed_color (face->background, f) set];
3116           NSRectFill (r);
3117         }
3118     }
3120   if (p->which)
3121     {
3122       NSRect r = NSMakeRect (p->x, p->y, p->wd, p->h);
3123       EmacsImage *img = bimgs[p->which - 1];
3125       if (!img)
3126         {
3127           // Note: For "periodic" images, allocate one EmacsImage for
3128           // the base image, and use it for all dh:s.
3129           unsigned short *bits = p->bits;
3130           int full_height = p->h + p->dh;
3131           int i;
3132           unsigned char *cbits = xmalloc (full_height);
3134           for (i = 0; i < full_height; i++)
3135             cbits[i] = bits[i];
3136           img = [[EmacsImage alloc] initFromXBM: cbits width: 8
3137                                          height: full_height
3138                                              fg: 0 bg: 0];
3139           bimgs[p->which - 1] = img;
3140           xfree (cbits);
3141         }
3143       NSTRACE_RECT ("r", r);
3145       NSRectClip (r);
3146       /* Since we composite the bitmap instead of just blitting it, we need
3147          to erase the whole background.  */
3148       [ns_lookup_indexed_color(face->background, f) set];
3149       NSRectFill (r);
3151       {
3152         NSColor *bm_color;
3153         if (!p->cursor_p)
3154           bm_color = ns_lookup_indexed_color(face->foreground, f);
3155         else if (p->overlay_p)
3156           bm_color = ns_lookup_indexed_color(face->background, f);
3157         else
3158           bm_color = f->output_data.ns->cursor_color;
3159         [img setXBMColor: bm_color];
3160       }
3162 #ifdef NS_IMPL_COCOA
3163       // Note: For periodic images, the full image height is "h + hd".
3164       // By using the height h, a suitable part of the image is used.
3165       NSRect fromRect = NSMakeRect(0, 0, p->wd, p->h);
3167       NSTRACE_RECT ("fromRect", fromRect);
3169       [img drawInRect: r
3170               fromRect: fromRect
3171              operation: NSCompositingOperationSourceOver
3172               fraction: 1.0
3173            respectFlipped: YES
3174                 hints: nil];
3175 #else
3176       {
3177         NSPoint pt = r.origin;
3178         pt.y += p->h;
3179         [img compositeToPoint: pt operation: NSCompositingOperationSourceOver];
3180       }
3181 #endif
3182     }
3183   ns_unfocus (f);
3187 static void
3188 ns_draw_window_cursor (struct window *w, struct glyph_row *glyph_row,
3189                        int x, int y, enum text_cursor_kinds cursor_type,
3190                        int cursor_width, bool on_p, bool active_p)
3191 /* --------------------------------------------------------------------------
3192      External call (RIF): draw cursor.
3193      Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
3194    -------------------------------------------------------------------------- */
3196   NSRect r, s;
3197   int fx, fy, h, cursor_height;
3198   struct frame *f = WINDOW_XFRAME (w);
3199   struct glyph *phys_cursor_glyph;
3200   struct glyph *cursor_glyph;
3201   struct face *face;
3202   NSColor *hollow_color = FRAME_BACKGROUND_COLOR (f);
3204   /* If cursor is out of bounds, don't draw garbage.  This can happen
3205      in mini-buffer windows when switching between echo area glyphs
3206      and mini-buffer.  */
3208   NSTRACE ("ns_draw_window_cursor");
3210   if (!on_p)
3211     return;
3213   w->phys_cursor_type = cursor_type;
3214   w->phys_cursor_on_p = on_p;
3216   if (cursor_type == NO_CURSOR)
3217     {
3218       w->phys_cursor_width = 0;
3219       return;
3220     }
3222   if ((phys_cursor_glyph = get_phys_cursor_glyph (w)) == NULL)
3223     {
3224       if (glyph_row->exact_window_width_line_p
3225           && w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])
3226         {
3227           glyph_row->cursor_in_fringe_p = 1;
3228           draw_fringe_bitmap (w, glyph_row, 0);
3229         }
3230       return;
3231     }
3233   /* We draw the cursor (with NSRectFill), then draw the glyph on top
3234      (other terminals do it the other way round).  We must set
3235      w->phys_cursor_width to the cursor width.  For bar cursors, that
3236      is CURSOR_WIDTH; for box cursors, it is the glyph width.  */
3237   get_phys_cursor_geometry (w, glyph_row, phys_cursor_glyph, &fx, &fy, &h);
3239   /* The above get_phys_cursor_geometry call set w->phys_cursor_width
3240      to the glyph width; replace with CURSOR_WIDTH for (V)BAR cursors.  */
3241   if (cursor_type == BAR_CURSOR)
3242     {
3243       if (cursor_width < 1)
3244         cursor_width = max (FRAME_CURSOR_WIDTH (f), 1);
3246       /* The bar cursor should never be wider than the glyph.  */
3247       if (cursor_width < w->phys_cursor_width)
3248         w->phys_cursor_width = cursor_width;
3249     }
3250   /* If we have an HBAR, "cursor_width" MAY specify height.  */
3251   else if (cursor_type == HBAR_CURSOR)
3252     {
3253       cursor_height = (cursor_width < 1) ? lrint (0.25 * h) : cursor_width;
3254       if (cursor_height > glyph_row->height)
3255         cursor_height = glyph_row->height;
3256       if (h > cursor_height) // Cursor smaller than line height, move down
3257         fy += h - cursor_height;
3258       h = cursor_height;
3259     }
3261   r.origin.x = fx, r.origin.y = fy;
3262   r.size.height = h;
3263   r.size.width = w->phys_cursor_width;
3265   /* Prevent the cursor from being drawn outside the text area.  */
3266   ns_clip_to_row (w, glyph_row, TEXT_AREA, NO); /* do ns_focus(f, &r, 1); if remove */
3269   face = FACE_FROM_ID_OR_NULL (f, phys_cursor_glyph->face_id);
3270   if (face && NS_FACE_BACKGROUND (face)
3271       == ns_index_color (FRAME_CURSOR_COLOR (f), f))
3272     {
3273       [ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), f) set];
3274       hollow_color = FRAME_CURSOR_COLOR (f);
3275     }
3276   else
3277     [FRAME_CURSOR_COLOR (f) set];
3279 #ifdef NS_IMPL_COCOA
3280   /* TODO: This makes drawing of cursor plus that of phys_cursor_glyph
3281            atomic.  Cleaner ways of doing this should be investigated.
3282            One way would be to set a global variable DRAWING_CURSOR
3283            when making the call to draw_phys..(), don't focus in that
3284            case, then move the ns_unfocus() here after that call.  */
3285   NSDisableScreenUpdates ();
3286 #endif
3288   switch (cursor_type)
3289     {
3290     case DEFAULT_CURSOR:
3291     case NO_CURSOR:
3292       break;
3293     case FILLED_BOX_CURSOR:
3294       NSRectFill (r);
3295       break;
3296     case HOLLOW_BOX_CURSOR:
3297       NSRectFill (r);
3298       [hollow_color set];
3299       NSRectFill (NSInsetRect (r, 1, 1));
3300       [FRAME_CURSOR_COLOR (f) set];
3301       break;
3302     case HBAR_CURSOR:
3303       NSRectFill (r);
3304       break;
3305     case BAR_CURSOR:
3306       s = r;
3307       /* If the character under cursor is R2L, draw the bar cursor
3308          on the right of its glyph, rather than on the left.  */
3309       cursor_glyph = get_phys_cursor_glyph (w);
3310       if ((cursor_glyph->resolved_level & 1) != 0)
3311         s.origin.x += cursor_glyph->pixel_width - s.size.width;
3313       NSRectFill (s);
3314       break;
3315     }
3316   ns_unfocus (f);
3318   /* draw the character under the cursor */
3319   if (cursor_type != NO_CURSOR)
3320     draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
3322 #ifdef NS_IMPL_COCOA
3323   NSEnableScreenUpdates ();
3324 #endif
3329 static void
3330 ns_draw_vertical_window_border (struct window *w, int x, int y0, int y1)
3331 /* --------------------------------------------------------------------------
3332      External (RIF): Draw a vertical line.
3333    -------------------------------------------------------------------------- */
3335   struct frame *f = XFRAME (WINDOW_FRAME (w));
3336   struct face *face;
3337   NSRect r = NSMakeRect (x, y0, 1, y1-y0);
3339   NSTRACE ("ns_draw_vertical_window_border");
3341   face = FACE_FROM_ID_OR_NULL (f, VERTICAL_BORDER_FACE_ID);
3343   ns_focus (f, &r, 1);
3344   if (face)
3345     [ns_lookup_indexed_color(face->foreground, f) set];
3347   NSRectFill(r);
3348   ns_unfocus (f);
3352 static void
3353 ns_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1)
3354 /* --------------------------------------------------------------------------
3355      External (RIF): Draw a window divider.
3356    -------------------------------------------------------------------------- */
3358   struct frame *f = XFRAME (WINDOW_FRAME (w));
3359   struct face *face = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FACE_ID);
3360   struct face *face_first
3361     = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FIRST_PIXEL_FACE_ID);
3362   struct face *face_last
3363     = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_LAST_PIXEL_FACE_ID);
3364   unsigned long color = face ? face->foreground : FRAME_FOREGROUND_PIXEL (f);
3365   unsigned long color_first = (face_first
3366                                ? face_first->foreground
3367                                : FRAME_FOREGROUND_PIXEL (f));
3368   unsigned long color_last = (face_last
3369                               ? face_last->foreground
3370                               : FRAME_FOREGROUND_PIXEL (f));
3371   NSRect divider = NSMakeRect (x0, y0, x1-x0, y1-y0);
3373   NSTRACE ("ns_draw_window_divider");
3375   ns_focus (f, &divider, 1);
3377   if ((y1 - y0 > x1 - x0) && (x1 - x0 >= 3))
3378     /* A vertical divider, at least three pixels wide: Draw first and
3379        last pixels differently.  */
3380     {
3381       [ns_lookup_indexed_color(color_first, f) set];
3382       NSRectFill(NSMakeRect (x0, y0, 1, y1 - y0));
3383       [ns_lookup_indexed_color(color, f) set];
3384       NSRectFill(NSMakeRect (x0 + 1, y0, x1 - x0 - 2, y1 - y0));
3385       [ns_lookup_indexed_color(color_last, f) set];
3386       NSRectFill(NSMakeRect (x1 - 1, y0, 1, y1 - y0));
3387     }
3388   else if ((x1 - x0 > y1 - y0) && (y1 - y0 >= 3))
3389     /* A horizontal divider, at least three pixels high: Draw first and
3390        last pixels differently.  */
3391     {
3392       [ns_lookup_indexed_color(color_first, f) set];
3393       NSRectFill(NSMakeRect (x0, y0, x1 - x0, 1));
3394       [ns_lookup_indexed_color(color, f) set];
3395       NSRectFill(NSMakeRect (x0, y0 + 1, x1 - x0, y1 - y0 - 2));
3396       [ns_lookup_indexed_color(color_last, f) set];
3397       NSRectFill(NSMakeRect (x0, y1 - 1, x1 - x0, 1));
3398     }
3399   else
3400     {
3401       /* In any other case do not draw the first and last pixels
3402          differently.  */
3403       [ns_lookup_indexed_color(color, f) set];
3404       NSRectFill(divider);
3405     }
3407   ns_unfocus (f);
3410 static void
3411 ns_show_hourglass (struct frame *f)
3413   /* TODO: add NSProgressIndicator to all frames.  */
3416 static void
3417 ns_hide_hourglass (struct frame *f)
3419   /* TODO: remove NSProgressIndicator from all frames.  */
3422 /* ==========================================================================
3424     Glyph drawing operations
3426    ========================================================================== */
3428 static int
3429 ns_get_glyph_string_clip_rect (struct glyph_string *s, NativeRectangle *nr)
3430 /* --------------------------------------------------------------------------
3431     Wrapper utility to account for internal border width on full-width lines,
3432     and allow top full-width rows to hit the frame top.  nr should be pointer
3433     to two successive NSRects.  Number of rects actually used is returned.
3434    -------------------------------------------------------------------------- */
3436   int n = get_glyph_string_clip_rects (s, nr, 2);
3437   return n;
3440 /* --------------------------------------------------------------------
3441    Draw a wavy line under glyph string s. The wave fills wave_height
3442    pixels from y.
3444                     x          wave_length = 2
3445                                  --
3446                 y    *   *   *   *   *
3447                      |* * * * * * * * *
3448     wave_height = 3  | *   *   *   *
3449   --------------------------------------------------------------------- */
3451 static void
3452 ns_draw_underwave (struct glyph_string *s, EmacsCGFloat width, EmacsCGFloat x)
3454   int wave_height = 3, wave_length = 2;
3455   int y, dx, dy, odd, xmax;
3456   NSPoint a, b;
3457   NSRect waveClip;
3459   dx = wave_length;
3460   dy = wave_height - 1;
3461   y =  s->ybase - wave_height + 3;
3462   xmax = x + width;
3464   /* Find and set clipping rectangle */
3465   waveClip = NSMakeRect (x, y, width, wave_height);
3466   [[NSGraphicsContext currentContext] saveGraphicsState];
3467   NSRectClip (waveClip);
3469   /* Draw the waves */
3470   a.x = x - ((int)(x) % dx) + (EmacsCGFloat) 0.5;
3471   b.x = a.x + dx;
3472   odd = (int)(a.x/dx) % 2;
3473   a.y = b.y = y + 0.5;
3475   if (odd)
3476     a.y += dy;
3477   else
3478     b.y += dy;
3480   while (a.x <= xmax)
3481     {
3482       [NSBezierPath strokeLineFromPoint:a toPoint:b];
3483       a.x = b.x, a.y = b.y;
3484       b.x += dx, b.y = y + 0.5 + odd*dy;
3485       odd = !odd;
3486     }
3488   /* Restore previous clipping rectangle(s) */
3489   [[NSGraphicsContext currentContext] restoreGraphicsState];
3494 static void
3495 ns_draw_text_decoration (struct glyph_string *s, struct face *face,
3496                          NSColor *defaultCol, CGFloat width, CGFloat x)
3497 /* --------------------------------------------------------------------------
3498    Draw underline, overline, and strike-through on glyph string s.
3499    -------------------------------------------------------------------------- */
3501   if (s->for_overlaps)
3502     return;
3504   /* Do underline.  */
3505   if (face->underline_p)
3506     {
3507       if (s->face->underline_type == FACE_UNDER_WAVE)
3508         {
3509           if (face->underline_defaulted_p)
3510             [defaultCol set];
3511           else
3512             [ns_lookup_indexed_color (face->underline_color, s->f) set];
3514           ns_draw_underwave (s, width, x);
3515         }
3516       else if (s->face->underline_type == FACE_UNDER_LINE)
3517         {
3519           NSRect r;
3520           unsigned long thickness, position;
3522           /* If the prev was underlined, match its appearance.  */
3523           if (s->prev && s->prev->face->underline_p
3524               && s->prev->face->underline_type == FACE_UNDER_LINE
3525               && s->prev->underline_thickness > 0)
3526             {
3527               thickness = s->prev->underline_thickness;
3528               position = s->prev->underline_position;
3529             }
3530           else
3531             {
3532               struct font *font = font_for_underline_metrics (s);
3533               unsigned long descent = s->y + s->height - s->ybase;
3534               unsigned long minimum_offset;
3535               BOOL underline_at_descent_line, use_underline_position_properties;
3536               Lisp_Object val = buffer_local_value (Qunderline_minimum_offset,
3537                                                     s->w->contents);
3538               if (INTEGERP (val))
3539                 minimum_offset = XFASTINT (val);
3540               else
3541                 minimum_offset = 1;
3542               val = buffer_local_value (Qx_underline_at_descent_line,
3543                                         s->w->contents);
3544               underline_at_descent_line = !(NILP (val) || EQ (val, Qunbound));
3545               val = buffer_local_value (Qx_use_underline_position_properties,
3546                                         s->w->contents);
3547               use_underline_position_properties =
3548                 !(NILP (val) || EQ (val, Qunbound));
3550               /* Use underline thickness of font, defaulting to 1.  */
3551               thickness = (font && font->underline_thickness > 0)
3552                 ? font->underline_thickness : 1;
3554               /* Determine the offset of underlining from the baseline.  */
3555               if (underline_at_descent_line)
3556                 position = descent - thickness;
3557               else if (use_underline_position_properties
3558                        && font && font->underline_position >= 0)
3559                 position = font->underline_position;
3560               else if (font)
3561                 position = lround (font->descent / 2);
3562               else
3563                 position = minimum_offset;
3565               position = max (position, minimum_offset);
3567               /* Ensure underlining is not cropped.  */
3568               if (descent <= position)
3569                 {
3570                   position = descent - 1;
3571                   thickness = 1;
3572                 }
3573               else if (descent < position + thickness)
3574                 thickness = 1;
3575             }
3577           s->underline_thickness = thickness;
3578           s->underline_position = position;
3580           r = NSMakeRect (x, s->ybase + position, width, thickness);
3582           if (face->underline_defaulted_p)
3583             [defaultCol set];
3584           else
3585             [ns_lookup_indexed_color (face->underline_color, s->f) set];
3586           NSRectFill (r);
3587         }
3588     }
3589   /* Do overline. We follow other terms in using a thickness of 1
3590      and ignoring overline_margin.  */
3591   if (face->overline_p)
3592     {
3593       NSRect r;
3594       r = NSMakeRect (x, s->y, width, 1);
3596       if (face->overline_color_defaulted_p)
3597         [defaultCol set];
3598       else
3599         [ns_lookup_indexed_color (face->overline_color, s->f) set];
3600       NSRectFill (r);
3601     }
3603   /* Do strike-through.  We follow other terms for thickness and
3604      vertical position.  */
3605   if (face->strike_through_p)
3606     {
3607       NSRect r;
3608       /* Y-coordinate and height of the glyph string's first glyph.
3609          We cannot use s->y and s->height because those could be
3610          larger if there are taller display elements (e.g., characters
3611          displayed with a larger font) in the same glyph row.  */
3612       int glyph_y = s->ybase - s->first_glyph->ascent;
3613       int glyph_height = s->first_glyph->ascent + s->first_glyph->descent;
3614       /* Strike-through width and offset from the glyph string's
3615          top edge.  */
3616       unsigned long h = 1;
3617       unsigned long dy;
3619       dy = lrint ((glyph_height - h) / 2);
3620       r = NSMakeRect (x, glyph_y + dy, width, 1);
3622       if (face->strike_through_color_defaulted_p)
3623         [defaultCol set];
3624       else
3625         [ns_lookup_indexed_color (face->strike_through_color, s->f) set];
3626       NSRectFill (r);
3627     }
3630 static void
3631 ns_draw_box (NSRect r, CGFloat thickness, NSColor *col,
3632              char left_p, char right_p)
3633 /* --------------------------------------------------------------------------
3634     Draw an unfilled rect inside r, optionally leaving left and/or right open.
3635     Note we can't just use an NSDrawRect command, because of the possibility
3636     of some sides not being drawn, and because the rect will be filled.
3637    -------------------------------------------------------------------------- */
3639   NSRect s = r;
3640   [col set];
3642   /* top, bottom */
3643   s.size.height = thickness;
3644   NSRectFill (s);
3645   s.origin.y += r.size.height - thickness;
3646   NSRectFill (s);
3648   s.size.height = r.size.height;
3649   s.origin.y = r.origin.y;
3651   /* left, right (optional) */
3652   s.size.width = thickness;
3653   if (left_p)
3654     NSRectFill (s);
3655   if (right_p)
3656     {
3657       s.origin.x += r.size.width - thickness;
3658       NSRectFill (s);
3659     }
3663 static void
3664 ns_draw_relief (NSRect r, int thickness, char raised_p,
3665                char top_p, char bottom_p, char left_p, char right_p,
3666                struct glyph_string *s)
3667 /* --------------------------------------------------------------------------
3668     Draw a relief rect inside r, optionally leaving some sides open.
3669     Note we can't just use an NSDrawBezel command, because of the possibility
3670     of some sides not being drawn, and because the rect will be filled.
3671    -------------------------------------------------------------------------- */
3673   static NSColor *baseCol = nil, *lightCol = nil, *darkCol = nil;
3674   NSColor *newBaseCol = nil;
3675   NSRect sr = r;
3677   NSTRACE ("ns_draw_relief");
3679   /* set up colors */
3681   if (s->face->use_box_color_for_shadows_p)
3682     {
3683       newBaseCol = ns_lookup_indexed_color (s->face->box_color, s->f);
3684     }
3685 /*     else if (s->first_glyph->type == IMAGE_GLYPH
3686            && s->img->pixmap
3687            && !IMAGE_BACKGROUND_TRANSPARENT (s->img, s->f, 0))
3688        {
3689          newBaseCol = IMAGE_BACKGROUND  (s->img, s->f, 0);
3690        } */
3691   else
3692     {
3693       newBaseCol = ns_lookup_indexed_color (s->face->background, s->f);
3694     }
3696   if (newBaseCol == nil)
3697     newBaseCol = [NSColor grayColor];
3699   if (newBaseCol != baseCol)  /* TODO: better check */
3700     {
3701       [baseCol release];
3702       baseCol = [newBaseCol retain];
3703       [lightCol release];
3704       lightCol = [[baseCol highlightWithLevel: 0.2] retain];
3705       [darkCol release];
3706       darkCol = [[baseCol shadowWithLevel: 0.3] retain];
3707     }
3709   [(raised_p ? lightCol : darkCol) set];
3711   /* TODO: mitering. Using NSBezierPath doesn't work because of color switch.  */
3713   /* top */
3714   sr.size.height = thickness;
3715   if (top_p) NSRectFill (sr);
3717   /* left */
3718   sr.size.height = r.size.height;
3719   sr.size.width = thickness;
3720   if (left_p) NSRectFill (sr);
3722   [(raised_p ? darkCol : lightCol) set];
3724   /* bottom */
3725   sr.size.width = r.size.width;
3726   sr.size.height = thickness;
3727   sr.origin.y += r.size.height - thickness;
3728   if (bottom_p) NSRectFill (sr);
3730   /* right */
3731   sr.size.height = r.size.height;
3732   sr.origin.y = r.origin.y;
3733   sr.size.width = thickness;
3734   sr.origin.x += r.size.width - thickness;
3735   if (right_p) NSRectFill (sr);
3739 static void
3740 ns_dumpglyphs_box_or_relief (struct glyph_string *s)
3741 /* --------------------------------------------------------------------------
3742       Function modeled after x_draw_glyph_string_box ().
3743       Sets up parameters for drawing.
3744    -------------------------------------------------------------------------- */
3746   int right_x, last_x;
3747   char left_p, right_p;
3748   struct glyph *last_glyph;
3749   NSRect r;
3750   int thickness;
3751   struct face *face;
3753   if (s->hl == DRAW_MOUSE_FACE)
3754     {
3755       face = FACE_FROM_ID_OR_NULL (s->f,
3756                                    MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3757       if (!face)
3758         face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3759     }
3760   else
3761     face = s->face;
3763   thickness = face->box_line_width;
3765   NSTRACE ("ns_dumpglyphs_box_or_relief");
3767   last_x = ((s->row->full_width_p && !s->w->pseudo_window_p)
3768             ? WINDOW_RIGHT_EDGE_X (s->w)
3769             : window_box_right (s->w, s->area));
3770   last_glyph = (s->cmp || s->img
3771                 ? s->first_glyph : s->first_glyph + s->nchars-1);
3773   right_x = ((s->row->full_width_p && s->extends_to_end_of_line_p
3774               ? last_x - 1 : min (last_x, s->x + s->background_width) - 1));
3776   left_p = (s->first_glyph->left_box_line_p
3777             || (s->hl == DRAW_MOUSE_FACE
3778                 && (s->prev == NULL || s->prev->hl != s->hl)));
3779   right_p = (last_glyph->right_box_line_p
3780              || (s->hl == DRAW_MOUSE_FACE
3781                  && (s->next == NULL || s->next->hl != s->hl)));
3783   r = NSMakeRect (s->x, s->y, right_x - s->x + 1, s->height);
3785   /* TODO: Sometimes box_color is 0 and this seems wrong; should investigate.  */
3786   if (s->face->box == FACE_SIMPLE_BOX && s->face->box_color)
3787     {
3788       ns_draw_box (r, abs (thickness),
3789                    ns_lookup_indexed_color (face->box_color, s->f),
3790                   left_p, right_p);
3791     }
3792   else
3793     {
3794       ns_draw_relief (r, abs (thickness), s->face->box == FACE_RAISED_BOX,
3795                      1, 1, left_p, right_p, s);
3796     }
3800 static void
3801 ns_maybe_dumpglyphs_background (struct glyph_string *s, char force_p)
3802 /* --------------------------------------------------------------------------
3803       Modeled after x_draw_glyph_string_background, which draws BG in
3804       certain cases.  Others are left to the text rendering routine.
3805    -------------------------------------------------------------------------- */
3807   NSTRACE ("ns_maybe_dumpglyphs_background");
3809   if (!s->background_filled_p/* || s->hl == DRAW_MOUSE_FACE*/)
3810     {
3811       int box_line_width = max (s->face->box_line_width, 0);
3812       if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width
3813           /* When xdisp.c ignores FONT_HEIGHT, we cannot trust font
3814              dimensions, since the actual glyphs might be much
3815              smaller.  So in that case we always clear the rectangle
3816              with background color.  */
3817           || FONT_TOO_HIGH (s->font)
3818           || s->font_not_found_p || s->extends_to_end_of_line_p || force_p)
3819         {
3820           struct face *face;
3821           if (s->hl == DRAW_MOUSE_FACE)
3822             {
3823               face
3824                 = FACE_FROM_ID_OR_NULL (s->f,
3825                                         MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3826               if (!face)
3827                 face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3828             }
3829           else
3830             face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3831           if (!face->stipple)
3832             [(NS_FACE_BACKGROUND (face) != 0
3833               ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
3834               : FRAME_BACKGROUND_COLOR (s->f)) set];
3835           else
3836             {
3837               struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
3838               [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set];
3839             }
3841           if (s->hl != DRAW_CURSOR)
3842             {
3843               NSRect r = NSMakeRect (s->x, s->y + box_line_width,
3844                                     s->background_width,
3845                                     s->height-2*box_line_width);
3846               NSRectFill (r);
3847             }
3849           s->background_filled_p = 1;
3850         }
3851     }
3855 static void
3856 ns_dumpglyphs_image (struct glyph_string *s, NSRect r)
3857 /* --------------------------------------------------------------------------
3858       Renders an image and associated borders.
3859    -------------------------------------------------------------------------- */
3861   EmacsImage *img = s->img->pixmap;
3862   int box_line_vwidth = max (s->face->box_line_width, 0);
3863   int x = s->x, y = s->ybase - image_ascent (s->img, s->face, &s->slice);
3864   int bg_x, bg_y, bg_height;
3865   int th;
3866   char raised_p;
3867   NSRect br;
3868   struct face *face;
3869   NSColor *tdCol;
3871   NSTRACE ("ns_dumpglyphs_image");
3873   if (s->face->box != FACE_NO_BOX
3874       && s->first_glyph->left_box_line_p && s->slice.x == 0)
3875     x += abs (s->face->box_line_width);
3877   bg_x = x;
3878   bg_y =  s->slice.y == 0 ? s->y : s->y + box_line_vwidth;
3879   bg_height = s->height;
3880   /* other terms have this, but was causing problems w/tabbar mode */
3881   /* - 2 * box_line_vwidth; */
3883   if (s->slice.x == 0) x += s->img->hmargin;
3884   if (s->slice.y == 0) y += s->img->vmargin;
3886   /* Draw BG: if we need larger area than image itself cleared, do that,
3887      otherwise, since we composite the image under NS (instead of mucking
3888      with its background color), we must clear just the image area.  */
3889   if (s->hl == DRAW_MOUSE_FACE)
3890     {
3891       face = FACE_FROM_ID_OR_NULL (s->f,
3892                                    MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3893       if (!face)
3894        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3895     }
3896   else
3897     face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3899   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f) set];
3901   if (bg_height > s->slice.height || s->img->hmargin || s->img->vmargin
3902       || s->img->mask || s->img->pixmap == 0 || s->width != s->background_width)
3903     {
3904       br = NSMakeRect (bg_x, bg_y, s->background_width, bg_height);
3905       s->background_filled_p = 1;
3906     }
3907   else
3908     {
3909       br = NSMakeRect (x, y, s->slice.width, s->slice.height);
3910     }
3912   NSRectFill (br);
3914   /* Draw the image... do we need to draw placeholder if img == nil?  */
3915   if (img != nil)
3916     {
3917 #ifdef NS_IMPL_COCOA
3918       NSRect dr = NSMakeRect (x, y, s->slice.width, s->slice.height);
3919       NSRect ir = NSMakeRect (s->slice.x,
3920                               s->img->height - s->slice.y - s->slice.height,
3921                               s->slice.width, s->slice.height);
3922       [img drawInRect: dr
3923              fromRect: ir
3924              operation: NSCompositingOperationSourceOver
3925               fraction: 1.0
3926            respectFlipped: YES
3927                 hints: nil];
3928 #else
3929       [img compositeToPoint: NSMakePoint (x, y + s->slice.height)
3930                   operation: NSCompositingOperationSourceOver];
3931 #endif
3932     }
3934   if (s->hl == DRAW_CURSOR)
3935     {
3936     [FRAME_CURSOR_COLOR (s->f) set];
3937     if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
3938       tdCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
3939     else
3940       /* Currently on NS img->mask is always 0.  Since
3941          get_window_cursor_type specifies a hollow box cursor when on
3942          a non-masked image we never reach this clause.  But we put it
3943          in, in anticipation of better support for image masks on
3944          NS.  */
3945       tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3946     }
3947   else
3948     {
3949       tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3950     }
3952   /* Draw underline, overline, strike-through.  */
3953   ns_draw_text_decoration (s, face, tdCol, br.size.width, br.origin.x);
3955   /* Draw relief, if requested */
3956   if (s->img->relief || s->hl ==DRAW_IMAGE_RAISED || s->hl ==DRAW_IMAGE_SUNKEN)
3957     {
3958       if (s->hl == DRAW_IMAGE_SUNKEN || s->hl == DRAW_IMAGE_RAISED)
3959         {
3960           th = tool_bar_button_relief >= 0 ?
3961             tool_bar_button_relief : DEFAULT_TOOL_BAR_BUTTON_RELIEF;
3962           raised_p = (s->hl == DRAW_IMAGE_RAISED);
3963         }
3964       else
3965         {
3966           th = abs (s->img->relief);
3967           raised_p = (s->img->relief > 0);
3968         }
3970       r.origin.x = x - th;
3971       r.origin.y = y - th;
3972       r.size.width = s->slice.width + 2*th-1;
3973       r.size.height = s->slice.height + 2*th-1;
3974       ns_draw_relief (r, th, raised_p,
3975                       s->slice.y == 0,
3976                       s->slice.y + s->slice.height == s->img->height,
3977                       s->slice.x == 0,
3978                       s->slice.x + s->slice.width == s->img->width, s);
3979     }
3981   /* If there is no mask, the background won't be seen,
3982      so draw a rectangle on the image for the cursor.
3983      Do this for all images, getting transparency right is not reliable.  */
3984   if (s->hl == DRAW_CURSOR)
3985     {
3986       int thickness = abs (s->img->relief);
3987       if (thickness == 0) thickness = 1;
3988       ns_draw_box (br, thickness, FRAME_CURSOR_COLOR (s->f), 1, 1);
3989     }
3993 static void
3994 ns_dumpglyphs_stretch (struct glyph_string *s)
3996   NSRect r[2];
3997   int n, i;
3998   struct face *face;
3999   NSColor *fgCol, *bgCol;
4001   if (!s->background_filled_p)
4002     {
4003       n = ns_get_glyph_string_clip_rect (s, r);
4004       *r = NSMakeRect (s->x, s->y, s->background_width, s->height);
4006       ns_focus (s->f, r, n);
4008       if (s->hl == DRAW_MOUSE_FACE)
4009        {
4010          face = FACE_FROM_ID_OR_NULL (s->f,
4011                                       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
4012          if (!face)
4013            face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
4014        }
4015       else
4016        face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
4018       bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
4019       fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
4021       for (i = 0; i < n; ++i)
4022         {
4023           if (!s->row->full_width_p)
4024             {
4025               int overrun, leftoverrun;
4027               /* truncate to avoid overwriting fringe and/or scrollbar */
4028               overrun = max (0, (s->x + s->background_width)
4029                              - (WINDOW_BOX_RIGHT_EDGE_X (s->w)
4030                                 - WINDOW_RIGHT_FRINGE_WIDTH (s->w)));
4031               r[i].size.width -= overrun;
4033               /* truncate to avoid overwriting to left of the window box */
4034               leftoverrun = (WINDOW_BOX_LEFT_EDGE_X (s->w)
4035                              + WINDOW_LEFT_FRINGE_WIDTH (s->w)) - s->x;
4037               if (leftoverrun > 0)
4038                 {
4039                   r[i].origin.x += leftoverrun;
4040                   r[i].size.width -= leftoverrun;
4041                 }
4043               /* XXX: Try to work between problem where a stretch glyph on
4044                  a partially-visible bottom row will clear part of the
4045                  modeline, and another where list-buffers headers and similar
4046                  rows erroneously have visible_height set to 0.  Not sure
4047                  where this is coming from as other terms seem not to show.  */
4048               r[i].size.height = min (s->height, s->row->visible_height);
4049             }
4051           [bgCol set];
4053           /* NOTE: under NS this is NOT used to draw cursors, but we must avoid
4054              overwriting cursor (usually when cursor on a tab).  */
4055           if (s->hl == DRAW_CURSOR)
4056             {
4057               CGFloat x, width;
4059               x = r[i].origin.x;
4060               width = s->w->phys_cursor_width;
4061               r[i].size.width -= width;
4062               r[i].origin.x += width;
4064               NSRectFill (r[i]);
4066               /* Draw overlining, etc. on the cursor.  */
4067               if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
4068                 ns_draw_text_decoration (s, face, bgCol, width, x);
4069               else
4070                 ns_draw_text_decoration (s, face, fgCol, width, x);
4071             }
4072           else
4073             {
4074               NSRectFill (r[i]);
4075             }
4077           /* Draw overlining, etc. on the stretch glyph (or the part
4078              of the stretch glyph after the cursor).  */
4079           ns_draw_text_decoration (s, face, fgCol, r[i].size.width,
4080                                    r[i].origin.x);
4081         }
4082       ns_unfocus (s->f);
4083       s->background_filled_p = 1;
4084     }
4088 static void
4089 ns_draw_glyph_string_foreground (struct glyph_string *s)
4091   int x, flags;
4092   struct font *font = s->font;
4094   /* If first glyph of S has a left box line, start drawing the text
4095      of S to the right of that box line.  */
4096   if (s->face && s->face->box != FACE_NO_BOX
4097       && s->first_glyph->left_box_line_p)
4098     x = s->x + eabs (s->face->box_line_width);
4099   else
4100     x = s->x;
4102   flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
4103     (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
4104      (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
4105       NS_DUMPGLYPH_NORMAL));
4107   font->driver->draw
4108     (s, s->cmp_from, s->nchars, x, s->ybase,
4109      (flags == NS_DUMPGLYPH_NORMAL && !s->background_filled_p)
4110      || flags == NS_DUMPGLYPH_MOUSEFACE);
4114 static void
4115 ns_draw_composite_glyph_string_foreground (struct glyph_string *s)
4117   int i, j, x;
4118   struct font *font = s->font;
4120   /* If first glyph of S has a left box line, start drawing the text
4121      of S to the right of that box line.  */
4122   if (s->face && s->face->box != FACE_NO_BOX
4123       && s->first_glyph->left_box_line_p)
4124     x = s->x + eabs (s->face->box_line_width);
4125   else
4126     x = s->x;
4128   /* S is a glyph string for a composition.  S->cmp_from is the index
4129      of the first character drawn for glyphs of this composition.
4130      S->cmp_from == 0 means we are drawing the very first character of
4131      this composition.  */
4133   /* Draw a rectangle for the composition if the font for the very
4134      first character of the composition could not be loaded.  */
4135   if (s->font_not_found_p)
4136     {
4137       if (s->cmp_from == 0)
4138         {
4139           NSRect r = NSMakeRect (s->x, s->y, s->width-1, s->height -1);
4140           ns_draw_box (r, 1, FRAME_CURSOR_COLOR (s->f), 1, 1);
4141         }
4142     }
4143   else if (! s->first_glyph->u.cmp.automatic)
4144     {
4145       int y = s->ybase;
4147       for (i = 0, j = s->cmp_from; i < s->nchars; i++, j++)
4148         /* TAB in a composition means display glyphs with padding
4149            space on the left or right.  */
4150         if (COMPOSITION_GLYPH (s->cmp, j) != '\t')
4151           {
4152             int xx = x + s->cmp->offsets[j * 2];
4153             int yy = y - s->cmp->offsets[j * 2 + 1];
4155             font->driver->draw (s, j, j + 1, xx, yy, false);
4156             if (s->face->overstrike)
4157               font->driver->draw (s, j, j + 1, xx + 1, yy, false);
4158           }
4159     }
4160   else
4161     {
4162       Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
4163       Lisp_Object glyph;
4164       int y = s->ybase;
4165       int width = 0;
4167       for (i = j = s->cmp_from; i < s->cmp_to; i++)
4168         {
4169           glyph = LGSTRING_GLYPH (gstring, i);
4170           if (NILP (LGLYPH_ADJUSTMENT (glyph)))
4171             width += LGLYPH_WIDTH (glyph);
4172           else
4173             {
4174               int xoff, yoff, wadjust;
4176               if (j < i)
4177                 {
4178                   font->driver->draw (s, j, i, x, y, false);
4179                   if (s->face->overstrike)
4180                     font->driver->draw (s, j, i, x + 1, y, false);
4181                   x += width;
4182                 }
4183               xoff = LGLYPH_XOFF (glyph);
4184               yoff = LGLYPH_YOFF (glyph);
4185               wadjust = LGLYPH_WADJUST (glyph);
4186               font->driver->draw (s, i, i + 1, x + xoff, y + yoff, false);
4187               if (s->face->overstrike)
4188                 font->driver->draw (s, i, i + 1, x + xoff + 1, y + yoff,
4189                                     false);
4190               x += wadjust;
4191               j = i + 1;
4192               width = 0;
4193             }
4194         }
4195       if (j < i)
4196         {
4197           font->driver->draw (s, j, i, x, y, false);
4198           if (s->face->overstrike)
4199             font->driver->draw (s, j, i, x + 1, y, false);
4200         }
4201     }
4204 static void
4205 ns_draw_glyph_string (struct glyph_string *s)
4206 /* --------------------------------------------------------------------------
4207       External (RIF): Main draw-text call.
4208    -------------------------------------------------------------------------- */
4210   /* TODO (optimize): focus for box and contents draw */
4211   NSRect r[2];
4212   int n;
4213   char box_drawn_p = 0;
4214   struct font *font = s->face->font;
4215   if (! font) font = FRAME_FONT (s->f);
4217   NSTRACE_WHEN (NSTRACE_GROUP_GLYPHS, "ns_draw_glyph_string");
4219   if (s->next && s->right_overhang && !s->for_overlaps/*&&s->hl!=DRAW_CURSOR*/)
4220     {
4221       int width;
4222       struct glyph_string *next;
4224       for (width = 0, next = s->next;
4225            next && width < s->right_overhang;
4226            width += next->width, next = next->next)
4227         if (next->first_glyph->type != IMAGE_GLYPH)
4228           {
4229             if (next->first_glyph->type != STRETCH_GLYPH)
4230               {
4231                 n = ns_get_glyph_string_clip_rect (s->next, r);
4232                 ns_focus (s->f, r, n);
4233                 ns_maybe_dumpglyphs_background (s->next, 1);
4234                 ns_unfocus (s->f);
4235               }
4236             else
4237               {
4238                 ns_dumpglyphs_stretch (s->next);
4239               }
4240             next->num_clips = 0;
4241           }
4242     }
4244   if (!s->for_overlaps && s->face->box != FACE_NO_BOX
4245         && (s->first_glyph->type == CHAR_GLYPH
4246             || s->first_glyph->type == COMPOSITE_GLYPH))
4247     {
4248       n = ns_get_glyph_string_clip_rect (s, r);
4249       ns_focus (s->f, r, n);
4250       ns_maybe_dumpglyphs_background (s, 1);
4251       ns_dumpglyphs_box_or_relief (s);
4252       ns_unfocus (s->f);
4253       box_drawn_p = 1;
4254     }
4256   switch (s->first_glyph->type)
4257     {
4259     case IMAGE_GLYPH:
4260       n = ns_get_glyph_string_clip_rect (s, r);
4261       ns_focus (s->f, r, n);
4262       ns_dumpglyphs_image (s, r[0]);
4263       ns_unfocus (s->f);
4264       break;
4266     case STRETCH_GLYPH:
4267       ns_dumpglyphs_stretch (s);
4268       break;
4270     case CHAR_GLYPH:
4271     case COMPOSITE_GLYPH:
4272       n = ns_get_glyph_string_clip_rect (s, r);
4273       ns_focus (s->f, r, n);
4275       if (s->for_overlaps || (s->cmp_from > 0
4276                               && ! s->first_glyph->u.cmp.automatic))
4277         s->background_filled_p = 1;
4278       else
4279         ns_maybe_dumpglyphs_background
4280           (s, s->first_glyph->type == COMPOSITE_GLYPH);
4282       if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
4283         {
4284           unsigned long tmp = NS_FACE_BACKGROUND (s->face);
4285           NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
4286           NS_FACE_FOREGROUND (s->face) = tmp;
4287         }
4289       {
4290         BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
4292         if (isComposite)
4293           ns_draw_composite_glyph_string_foreground (s);
4294         else
4295           ns_draw_glyph_string_foreground (s);
4296       }
4298       {
4299         NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
4300                         ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face),
4301                                                    s->f)
4302                         : FRAME_FOREGROUND_COLOR (s->f));
4303         [col set];
4305         /* Draw underline, overline, strike-through.  */
4306         ns_draw_text_decoration (s, s->face, col, s->width, s->x);
4307       }
4309       if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
4310         {
4311           unsigned long tmp = NS_FACE_BACKGROUND (s->face);
4312           NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
4313           NS_FACE_FOREGROUND (s->face) = tmp;
4314         }
4316       ns_unfocus (s->f);
4317       break;
4319     case GLYPHLESS_GLYPH:
4320       n = ns_get_glyph_string_clip_rect (s, r);
4321       ns_focus (s->f, r, n);
4323       if (s->for_overlaps || (s->cmp_from > 0
4324                               && ! s->first_glyph->u.cmp.automatic))
4325         s->background_filled_p = 1;
4326       else
4327         ns_maybe_dumpglyphs_background
4328           (s, s->first_glyph->type == COMPOSITE_GLYPH);
4329       /* ... */
4330       /* Not yet implemented.  */
4331       /* ... */
4332       ns_unfocus (s->f);
4333       break;
4335     default:
4336       emacs_abort ();
4337     }
4339   /* Draw box if not done already.  */
4340   if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX)
4341     {
4342       n = ns_get_glyph_string_clip_rect (s, r);
4343       ns_focus (s->f, r, n);
4344       ns_dumpglyphs_box_or_relief (s);
4345       ns_unfocus (s->f);
4346     }
4348   s->num_clips = 0;
4353 /* ==========================================================================
4355     Event loop
4357    ========================================================================== */
4360 static void
4361 ns_send_appdefined (int value)
4362 /* --------------------------------------------------------------------------
4363     Internal: post an appdefined event which EmacsApp-sendEvent will
4364               recognize and take as a command to halt the event loop.
4365    -------------------------------------------------------------------------- */
4367   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_send_appdefined(%d)", value);
4369   // GNUstep needs postEvent to happen on the main thread.
4370   // Cocoa needs nextEventMatchingMask to happen on the main thread too.
4371   if (! [[NSThread currentThread] isMainThread])
4372     {
4373       EmacsApp *app = (EmacsApp *)NSApp;
4374       app->nextappdefined = value;
4375       [app performSelectorOnMainThread:@selector (sendFromMainThread:)
4376                             withObject:nil
4377                          waitUntilDone:NO];
4378       return;
4379     }
4381   /* Only post this event if we haven't already posted one.  This will end
4382      the [NXApp run] main loop after having processed all events queued at
4383      this moment.  */
4385 #ifdef NS_IMPL_COCOA
4386   if (! send_appdefined)
4387     {
4388       /* OS X 10.10.1 swallows the AppDefined event we are sending ourselves
4389          in certain situations (rapid incoming events).
4390          So check if we have one, if not add one.  */
4391       NSEvent *appev = [NSApp nextEventMatchingMask:NSEventMaskApplicationDefined
4392                                           untilDate:[NSDate distantPast]
4393                                              inMode:NSDefaultRunLoopMode
4394                                             dequeue:NO];
4395       if (! appev) send_appdefined = YES;
4396     }
4397 #endif
4399   if (send_appdefined)
4400     {
4401       NSEvent *nxev;
4403       /* We only need one NX_APPDEFINED event to stop NXApp from running.  */
4404       send_appdefined = NO;
4406       /* Don't need wakeup timer any more.  */
4407       if (timed_entry)
4408         {
4409           [timed_entry invalidate];
4410           [timed_entry release];
4411           timed_entry = nil;
4412         }
4414       nxev = [NSEvent otherEventWithType: NSEventTypeApplicationDefined
4415                                 location: NSMakePoint (0, 0)
4416                            modifierFlags: 0
4417                                timestamp: 0
4418                             windowNumber: [[NSApp mainWindow] windowNumber]
4419                                  context: [NSApp context]
4420                                  subtype: 0
4421                                    data1: value
4422                                    data2: 0];
4424       /* Post an application defined event on the event queue.  When this is
4425          received the [NXApp run] will return, thus having processed all
4426          events which are currently queued.  */
4427       [NSApp postEvent: nxev atStart: NO];
4428     }
4431 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4432 static void
4433 check_native_fs ()
4435   Lisp_Object frame, tail;
4437   if (ns_last_use_native_fullscreen == ns_use_native_fullscreen)
4438     return;
4440   ns_last_use_native_fullscreen = ns_use_native_fullscreen;
4442   FOR_EACH_FRAME (tail, frame)
4443     {
4444       struct frame *f = XFRAME (frame);
4445       if (FRAME_NS_P (f))
4446         {
4447           EmacsView *view = FRAME_NS_VIEW (f);
4448           [view updateCollectionBehavior];
4449         }
4450     }
4452 #endif
4454 /* GNUstep does not have cancelTracking.  */
4455 #ifdef NS_IMPL_COCOA
4456 /* Check if menu open should be canceled or continued as normal.  */
4457 void
4458 ns_check_menu_open (NSMenu *menu)
4460   /* Click in menu bar?  */
4461   NSArray *a = [[NSApp mainMenu] itemArray];
4462   int i;
4463   BOOL found = NO;
4465   if (menu == nil) // Menu tracking ended.
4466     {
4467       if (menu_will_open_state == MENU_OPENING)
4468         menu_will_open_state = MENU_NONE;
4469       return;
4470     }
4472   for (i = 0; ! found && i < [a count]; i++)
4473     found = menu == [[a objectAtIndex:i] submenu];
4474   if (found)
4475     {
4476       if (menu_will_open_state == MENU_NONE && emacs_event)
4477         {
4478           NSEvent *theEvent = [NSApp currentEvent];
4479           struct frame *emacsframe = SELECTED_FRAME ();
4481           [menu cancelTracking];
4482           menu_will_open_state = MENU_PENDING;
4483           emacs_event->kind = MENU_BAR_ACTIVATE_EVENT;
4484           EV_TRAILER (theEvent);
4486           CGEventRef ourEvent = CGEventCreate (NULL);
4487           menu_mouse_point = CGEventGetLocation (ourEvent);
4488           CFRelease (ourEvent);
4489         }
4490       else if (menu_will_open_state == MENU_OPENING)
4491         {
4492           menu_will_open_state = MENU_NONE;
4493         }
4494     }
4497 /* Redo saved menu click if state is MENU_PENDING.  */
4498 void
4499 ns_check_pending_open_menu ()
4501   if (menu_will_open_state == MENU_PENDING)
4502     {
4503       CGEventSourceRef source
4504         = CGEventSourceCreate (kCGEventSourceStateHIDSystemState);
4506       CGEventRef event = CGEventCreateMouseEvent (source,
4507                                                   kCGEventLeftMouseDown,
4508                                                   menu_mouse_point,
4509                                                   kCGMouseButtonLeft);
4510       CGEventSetType (event, kCGEventLeftMouseDown);
4511       CGEventPost (kCGHIDEventTap, event);
4512       CFRelease (event);
4513       CFRelease (source);
4515       menu_will_open_state = MENU_OPENING;
4516     }
4518 #endif /* NS_IMPL_COCOA */
4520 static int
4521 ns_read_socket (struct terminal *terminal, struct input_event *hold_quit)
4522 /* --------------------------------------------------------------------------
4523      External (hook): Post an event to ourself and keep reading events until
4524      we read it back again.  In effect process all events which were waiting.
4525      From 21+ we have to manage the event buffer ourselves.
4526    -------------------------------------------------------------------------- */
4528   struct input_event ev;
4529   int nevents;
4531   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_read_socket");
4533 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4534   check_native_fs ();
4535 #endif
4537   if ([NSApp modalWindow] != nil)
4538     return -1;
4540   if (hold_event_q.nr > 0)
4541     {
4542       int i;
4543       for (i = 0; i < hold_event_q.nr; ++i)
4544         kbd_buffer_store_event_hold (&hold_event_q.q[i], hold_quit);
4545       hold_event_q.nr = 0;
4546       return i;
4547     }
4549   if ([NSThread isMainThread])
4550     {
4551       block_input ();
4552       n_emacs_events_pending = 0;
4553       ns_init_events (&ev);
4554       q_event_ptr = hold_quit;
4556       /* We manage autorelease pools by allocate/reallocate each time around
4557          the loop; strict nesting is occasionally violated but seems not to
4558          matter... earlier methods using full nesting caused major memory leaks.  */
4559       [outerpool release];
4560       outerpool = [[NSAutoreleasePool alloc] init];
4562       /* If have pending open-file requests, attend to the next one of those.  */
4563       if (ns_pending_files && [ns_pending_files count] != 0
4564           && [(EmacsApp *)NSApp openFile: [ns_pending_files objectAtIndex: 0]])
4565         {
4566           [ns_pending_files removeObjectAtIndex: 0];
4567         }
4568       /* Deal with pending service requests.  */
4569       else if (ns_pending_service_names && [ns_pending_service_names count] != 0
4570                && [(EmacsApp *)
4571                     NSApp fulfillService: [ns_pending_service_names objectAtIndex: 0]
4572                                  withArg: [ns_pending_service_args objectAtIndex: 0]])
4573         {
4574           [ns_pending_service_names removeObjectAtIndex: 0];
4575           [ns_pending_service_args removeObjectAtIndex: 0];
4576         }
4577       else
4578         {
4579           /* Run and wait for events.  We must always send one NX_APPDEFINED event
4580              to ourself, otherwise [NXApp run] will never exit.  */
4581           send_appdefined = YES;
4582           ns_send_appdefined (-1);
4584           [NSApp run];
4585         }
4587       nevents = n_emacs_events_pending;
4588       n_emacs_events_pending = 0;
4589       ns_finish_events ();
4590       q_event_ptr = NULL;
4591       unblock_input ();
4592     }
4593   else
4594     return -1;
4596   return nevents;
4601 ns_select (int nfds, fd_set *readfds, fd_set *writefds,
4602            fd_set *exceptfds, struct timespec *timeout,
4603            sigset_t *sigmask)
4604 /* --------------------------------------------------------------------------
4605      Replacement for select, checking for events
4606    -------------------------------------------------------------------------- */
4608   int result;
4609   int t, k, nr = 0;
4610   struct input_event event;
4611   char c;
4613   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_select");
4615 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4616   check_native_fs ();
4617 #endif
4619   if (hold_event_q.nr > 0)
4620     {
4621       /* We already have events pending.  */
4622       raise (SIGIO);
4623       errno = EINTR;
4624       return -1;
4625     }
4627   for (k = 0; k < nfds+1; k++)
4628     {
4629       if (readfds && FD_ISSET(k, readfds)) ++nr;
4630       if (writefds && FD_ISSET(k, writefds)) ++nr;
4631     }
4633   if (NSApp == nil
4634       || ![NSThread isMainThread]
4635       || (timeout && timeout->tv_sec == 0 && timeout->tv_nsec == 0))
4636     return thread_select(pselect, nfds, readfds, writefds,
4637                          exceptfds, timeout, sigmask);
4638   else
4639     {
4640       struct timespec t = {0, 0};
4641       thread_select(pselect, 0, NULL, NULL, NULL, &t, sigmask);
4642     }
4644   [outerpool release];
4645   outerpool = [[NSAutoreleasePool alloc] init];
4648   send_appdefined = YES;
4649   if (nr > 0)
4650     {
4651       pthread_mutex_lock (&select_mutex);
4652       select_nfds = nfds;
4653       select_valid = 0;
4654       if (readfds)
4655         {
4656           select_readfds = *readfds;
4657           select_valid += SELECT_HAVE_READ;
4658         }
4659       if (writefds)
4660         {
4661           select_writefds = *writefds;
4662           select_valid += SELECT_HAVE_WRITE;
4663         }
4665       if (timeout)
4666         {
4667           select_timeout = *timeout;
4668           select_valid += SELECT_HAVE_TMO;
4669         }
4671       pthread_mutex_unlock (&select_mutex);
4673       /* Inform fd_handler that select should be called.  */
4674       c = 'g';
4675       emacs_write_sig (selfds[1], &c, 1);
4676     }
4677   else if (nr == 0 && timeout)
4678     {
4679       /* No file descriptor, just a timeout, no need to wake fd_handler.  */
4680       double time = timespectod (*timeout);
4681       timed_entry = [[NSTimer scheduledTimerWithTimeInterval: time
4682                                                       target: NSApp
4683                                                     selector:
4684                                   @selector (timeout_handler:)
4685                                                     userInfo: 0
4686                                                      repeats: NO]
4687                       retain];
4688     }
4689   else /* No timeout and no file descriptors, can this happen?  */
4690     {
4691       /* Send appdefined so we exit from the loop.  */
4692       ns_send_appdefined (-1);
4693     }
4695   block_input ();
4696   ns_init_events (&event);
4698   [NSApp run];
4700   ns_finish_events ();
4701   if (nr > 0 && readfds)
4702     {
4703       c = 's';
4704       emacs_write_sig (selfds[1], &c, 1);
4705     }
4706   unblock_input ();
4708   t = last_appdefined_event_data;
4710   if (t != NO_APPDEFINED_DATA)
4711     {
4712       last_appdefined_event_data = NO_APPDEFINED_DATA;
4714       if (t == -2)
4715         {
4716           /* The NX_APPDEFINED event we received was a timeout.  */
4717           result = 0;
4718         }
4719       else if (t == -1)
4720         {
4721           /* The NX_APPDEFINED event we received was the result of
4722              at least one real input event arriving.  */
4723           errno = EINTR;
4724           result = -1;
4725         }
4726       else
4727         {
4728           /* Received back from select () in fd_handler; copy the results.  */
4729           pthread_mutex_lock (&select_mutex);
4730           if (readfds) *readfds = select_readfds;
4731           if (writefds) *writefds = select_writefds;
4732           pthread_mutex_unlock (&select_mutex);
4733           result = t;
4734         }
4735     }
4736   else
4737     {
4738       errno = EINTR;
4739       result = -1;
4740     }
4742   return result;
4745 #ifdef HAVE_PTHREAD
4746 void
4747 ns_run_loop_break ()
4748 /* Break out of the NS run loop in ns_select or ns_read_socket.  */
4750   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_run_loop_break");
4752   /* If we don't have a GUI, don't send the event.  */
4753   if (NSApp != NULL)
4754     ns_send_appdefined(-1);
4756 #endif
4759 /* ==========================================================================
4761     Scrollbar handling
4763    ========================================================================== */
4766 static void
4767 ns_set_vertical_scroll_bar (struct window *window,
4768                            int portion, int whole, int position)
4769 /* --------------------------------------------------------------------------
4770       External (hook): Update or add scrollbar
4771    -------------------------------------------------------------------------- */
4773   Lisp_Object win;
4774   NSRect r, v;
4775   struct frame *f = XFRAME (WINDOW_FRAME (window));
4776   EmacsView *view = FRAME_NS_VIEW (f);
4777   EmacsScroller *bar;
4778   int window_y, window_height;
4779   int top, left, height, width;
4780   BOOL update_p = YES;
4782   /* Optimization; display engine sends WAY too many of these.  */
4783   if (!NILP (window->vertical_scroll_bar))
4784     {
4785       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4786       if ([bar checkSamePosition: position portion: portion whole: whole])
4787         {
4788           if (view->scrollbarsNeedingUpdate == 0)
4789             {
4790               if (!windows_or_buffers_changed)
4791                   return;
4792             }
4793           else
4794             view->scrollbarsNeedingUpdate--;
4795           update_p = NO;
4796         }
4797     }
4799   NSTRACE ("ns_set_vertical_scroll_bar");
4801   /* Get dimensions.  */
4802   window_box (window, ANY_AREA, 0, &window_y, 0, &window_height);
4803   top = window_y;
4804   height = window_height;
4805   width = NS_SCROLL_BAR_WIDTH (f);
4806   left = WINDOW_SCROLL_BAR_AREA_X (window);
4808   r = NSMakeRect (left, top, width, height);
4809   /* The parent view is flipped, so we need to flip y value.  */
4810   v = [view frame];
4811   r.origin.y = (v.size.height - r.size.height - r.origin.y);
4813   XSETWINDOW (win, window);
4814   block_input ();
4816   /* We want at least 5 lines to display a scrollbar.  */
4817   if (WINDOW_TOTAL_LINES (window) < 5)
4818     {
4819       if (!NILP (window->vertical_scroll_bar))
4820         {
4821           bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4822           [bar removeFromSuperview];
4823           wset_vertical_scroll_bar (window, Qnil);
4824           [bar release];
4825         }
4826       ns_clear_frame_area (f, left, top, width, height);
4827       unblock_input ();
4828       return;
4829     }
4831   if (NILP (window->vertical_scroll_bar))
4832     {
4833       if (width > 0 && height > 0)
4834         ns_clear_frame_area (f, left, top, width, height);
4836       bar = [[EmacsScroller alloc] initFrame: r window: win];
4837       wset_vertical_scroll_bar (window, make_save_ptr (bar));
4838       update_p = YES;
4839     }
4840   else
4841     {
4842       NSRect oldRect;
4843       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4844       oldRect = [bar frame];
4845       r.size.width = oldRect.size.width;
4846       if (FRAME_LIVE_P (f) && !NSEqualRects (oldRect, r))
4847         {
4848           if (oldRect.origin.x != r.origin.x)
4849               ns_clear_frame_area (f, left, top, width, height);
4850           [bar setFrame: r];
4851         }
4852     }
4854   if (update_p)
4855     [bar setPosition: position portion: portion whole: whole];
4856   unblock_input ();
4860 static void
4861 ns_set_horizontal_scroll_bar (struct window *window,
4862                               int portion, int whole, int position)
4863 /* --------------------------------------------------------------------------
4864       External (hook): Update or add scrollbar.
4865    -------------------------------------------------------------------------- */
4867   Lisp_Object win;
4868   NSRect r, v;
4869   struct frame *f = XFRAME (WINDOW_FRAME (window));
4870   EmacsView *view = FRAME_NS_VIEW (f);
4871   EmacsScroller *bar;
4872   int top, height, left, width;
4873   int window_x, window_width;
4874   BOOL update_p = YES;
4876   /* Optimization; display engine sends WAY too many of these.  */
4877   if (!NILP (window->horizontal_scroll_bar))
4878     {
4879       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4880       if ([bar checkSamePosition: position portion: portion whole: whole])
4881         {
4882           if (view->scrollbarsNeedingUpdate == 0)
4883             {
4884               if (!windows_or_buffers_changed)
4885                   return;
4886             }
4887           else
4888             view->scrollbarsNeedingUpdate--;
4889           update_p = NO;
4890         }
4891     }
4893   NSTRACE ("ns_set_horizontal_scroll_bar");
4895   /* Get dimensions.  */
4896   window_box (window, ANY_AREA, &window_x, 0, &window_width, 0);
4897   left = window_x;
4898   width = window_width;
4899   height = NS_SCROLL_BAR_HEIGHT (f);
4900   top = WINDOW_SCROLL_BAR_AREA_Y (window);
4902   r = NSMakeRect (left, top, width, height);
4903   /* The parent view is flipped, so we need to flip y value.  */
4904   v = [view frame];
4905   r.origin.y = (v.size.height - r.size.height - r.origin.y);
4907   XSETWINDOW (win, window);
4908   block_input ();
4910   if (NILP (window->horizontal_scroll_bar))
4911     {
4912       if (width > 0 && height > 0)
4913         ns_clear_frame_area (f, left, top, width, height);
4915       bar = [[EmacsScroller alloc] initFrame: r window: win];
4916       wset_horizontal_scroll_bar (window, make_save_ptr (bar));
4917       update_p = YES;
4918     }
4919   else
4920     {
4921       NSRect oldRect;
4922       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4923       oldRect = [bar frame];
4924       if (FRAME_LIVE_P (f) && !NSEqualRects (oldRect, r))
4925         {
4926           if (oldRect.origin.y != r.origin.y)
4927             ns_clear_frame_area (f, left, top, width, height);
4928           [bar setFrame: r];
4929           update_p = YES;
4930         }
4931     }
4933   /* If there are both horizontal and vertical scroll-bars they leave
4934      a square that belongs to neither. We need to clear it otherwise
4935      it fills with junk.  */
4936   if (!NILP (window->vertical_scroll_bar))
4937     ns_clear_frame_area (f, WINDOW_SCROLL_BAR_AREA_X (window), top,
4938                          NS_SCROLL_BAR_HEIGHT (f), height);
4940   if (update_p)
4941     [bar setPosition: position portion: portion whole: whole];
4942   unblock_input ();
4946 static void
4947 ns_condemn_scroll_bars (struct frame *f)
4948 /* --------------------------------------------------------------------------
4949      External (hook): arrange for all frame's scrollbars to be removed
4950      at next call to judge_scroll_bars, except for those redeemed.
4951    -------------------------------------------------------------------------- */
4953   int i;
4954   id view;
4955   NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews];
4957   NSTRACE ("ns_condemn_scroll_bars");
4959   for (i =[subviews count]-1; i >= 0; i--)
4960     {
4961       view = [subviews objectAtIndex: i];
4962       if ([view isKindOfClass: [EmacsScroller class]])
4963         [view condemn];
4964     }
4968 static void
4969 ns_redeem_scroll_bar (struct window *window)
4970 /* --------------------------------------------------------------------------
4971      External (hook): arrange to spare this window's scrollbar
4972      at next call to judge_scroll_bars.
4973    -------------------------------------------------------------------------- */
4975   id bar;
4976   NSTRACE ("ns_redeem_scroll_bar");
4977   if (!NILP (window->vertical_scroll_bar)
4978       && WINDOW_HAS_VERTICAL_SCROLL_BAR (window))
4979     {
4980       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4981       [bar reprieve];
4982     }
4984   if (!NILP (window->horizontal_scroll_bar)
4985       && WINDOW_HAS_HORIZONTAL_SCROLL_BAR (window))
4986     {
4987       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4988       [bar reprieve];
4989     }
4993 static void
4994 ns_judge_scroll_bars (struct frame *f)
4995 /* --------------------------------------------------------------------------
4996      External (hook): destroy all scrollbars on frame that weren't
4997      redeemed after call to condemn_scroll_bars.
4998    -------------------------------------------------------------------------- */
5000   int i;
5001   id view;
5002   EmacsView *eview = FRAME_NS_VIEW (f);
5003   NSArray *subviews = [[eview superview] subviews];
5004   BOOL removed = NO;
5006   NSTRACE ("ns_judge_scroll_bars");
5007   for (i = [subviews count]-1; i >= 0; --i)
5008     {
5009       view = [subviews objectAtIndex: i];
5010       if (![view isKindOfClass: [EmacsScroller class]]) continue;
5011       if ([view judge])
5012         removed = YES;
5013     }
5015   if (removed)
5016     [eview updateFrameSize: NO];
5019 /* ==========================================================================
5021     Initialization
5023    ========================================================================== */
5026 x_display_pixel_height (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 NSHeight (frame);
5041 x_display_pixel_width (struct ns_display_info *dpyinfo)
5043   NSArray *screens = [NSScreen screens];
5044   NSEnumerator *enumerator = [screens objectEnumerator];
5045   NSScreen *screen;
5046   NSRect frame;
5048   frame = NSZeroRect;
5049   while ((screen = [enumerator nextObject]) != nil)
5050     frame = NSUnionRect (frame, [screen frame]);
5052   return NSWidth (frame);
5056 static Lisp_Object ns_string_to_lispmod (const char *s)
5057 /* --------------------------------------------------------------------------
5058      Convert modifier name to lisp symbol.
5059    -------------------------------------------------------------------------- */
5061   if (!strncmp (SSDATA (SYMBOL_NAME (Qmeta)), s, 10))
5062     return Qmeta;
5063   else if (!strncmp (SSDATA (SYMBOL_NAME (Qsuper)), s, 10))
5064     return Qsuper;
5065   else if (!strncmp (SSDATA (SYMBOL_NAME (Qcontrol)), s, 10))
5066     return Qcontrol;
5067   else if (!strncmp (SSDATA (SYMBOL_NAME (Qalt)), s, 10))
5068     return Qalt;
5069   else if (!strncmp (SSDATA (SYMBOL_NAME (Qhyper)), s, 10))
5070     return Qhyper;
5071   else if (!strncmp (SSDATA (SYMBOL_NAME (Qnone)), s, 10))
5072     return Qnone;
5073   else
5074     return Qnil;
5078 static void
5079 ns_default (const char *parameter, Lisp_Object *result,
5080            Lisp_Object yesval, Lisp_Object noval,
5081            BOOL is_float, BOOL is_modstring)
5082 /* --------------------------------------------------------------------------
5083       Check a parameter value in user's preferences.
5084    -------------------------------------------------------------------------- */
5086   const char *value = ns_get_defaults_value (parameter);
5088   if (value)
5089     {
5090       double f;
5091       char *pos;
5092       if (c_strcasecmp (value, "YES") == 0)
5093         *result = yesval;
5094       else if (c_strcasecmp (value, "NO") == 0)
5095         *result = noval;
5096       else if (is_float && (f = strtod (value, &pos), pos != value))
5097         *result = make_float (f);
5098       else if (is_modstring && value)
5099         *result = ns_string_to_lispmod (value);
5100       else fprintf (stderr,
5101                    "Bad value for default \"%s\": \"%s\"\n", parameter, value);
5102     }
5106 static void
5107 ns_initialize_display_info (struct ns_display_info *dpyinfo)
5108 /* --------------------------------------------------------------------------
5109       Initialize global info and storage for display.
5110    -------------------------------------------------------------------------- */
5112     NSScreen *screen = [NSScreen mainScreen];
5113     NSWindowDepth depth = [screen depth];
5115     dpyinfo->resx = 72.27; /* used 75.0, but this makes pt == pixel, expected */
5116     dpyinfo->resy = 72.27;
5117     dpyinfo->color_p = ![NSDeviceWhiteColorSpace isEqualToString:
5118                                                   NSColorSpaceFromDepth (depth)]
5119                 && ![NSCalibratedWhiteColorSpace isEqualToString:
5120                                                  NSColorSpaceFromDepth (depth)];
5121     dpyinfo->n_planes = NSBitsPerPixelFromDepth (depth);
5122     dpyinfo->color_table = xmalloc (sizeof *dpyinfo->color_table);
5123     dpyinfo->color_table->colors = NULL;
5124     dpyinfo->root_window = 42; /* A placeholder.  */
5125     dpyinfo->x_highlight_frame = dpyinfo->x_focus_frame = NULL;
5126     dpyinfo->n_fonts = 0;
5127     dpyinfo->smallest_font_height = 1;
5128     dpyinfo->smallest_char_width = 1;
5130     reset_mouse_highlight (&dpyinfo->mouse_highlight);
5134 /* This and next define (many of the) public functions in this file.  */
5135 /* x_... are generic versions in xdisp.c that we, and other terms, get away
5136          with using despite presence in the "system dependent" redisplay
5137          interface.  In addition, many of the ns_ methods have code that is
5138          shared with all terms, indicating need for further refactoring.  */
5139 extern frame_parm_handler ns_frame_parm_handlers[];
5140 static struct redisplay_interface ns_redisplay_interface =
5142   ns_frame_parm_handlers,
5143   x_produce_glyphs,
5144   x_write_glyphs,
5145   x_insert_glyphs,
5146   x_clear_end_of_line,
5147   ns_scroll_run,
5148   ns_after_update_window_line,
5149   ns_update_window_begin,
5150   ns_update_window_end,
5151   0, /* flush_display */
5152   x_clear_window_mouse_face,
5153   x_get_glyph_overhangs,
5154   x_fix_overlapping_area,
5155   ns_draw_fringe_bitmap,
5156   0, /* define_fringe_bitmap */ /* FIXME: simplify ns_draw_fringe_bitmap */
5157   0, /* destroy_fringe_bitmap */
5158   ns_compute_glyph_string_overhangs,
5159   ns_draw_glyph_string,
5160   ns_define_frame_cursor,
5161   ns_clear_frame_area,
5162   ns_draw_window_cursor,
5163   ns_draw_vertical_window_border,
5164   ns_draw_window_divider,
5165   ns_shift_glyphs_for_insert,
5166   ns_show_hourglass,
5167   ns_hide_hourglass
5171 static void
5172 ns_delete_display (struct ns_display_info *dpyinfo)
5174   /* TODO...  */
5178 /* This function is called when the last frame on a display is deleted.  */
5179 static void
5180 ns_delete_terminal (struct terminal *terminal)
5182   struct ns_display_info *dpyinfo = terminal->display_info.ns;
5184   NSTRACE ("ns_delete_terminal");
5186   /* Protect against recursive calls.  delete_frame in
5187      delete_terminal calls us back when it deletes our last frame.  */
5188   if (!terminal->name)
5189     return;
5191   block_input ();
5193   x_destroy_all_bitmaps (dpyinfo);
5194   ns_delete_display (dpyinfo);
5195   unblock_input ();
5199 static struct terminal *
5200 ns_create_terminal (struct ns_display_info *dpyinfo)
5201 /* --------------------------------------------------------------------------
5202       Set up use of NS before we make the first connection.
5203    -------------------------------------------------------------------------- */
5205   struct terminal *terminal;
5207   NSTRACE ("ns_create_terminal");
5209   terminal = create_terminal (output_ns, &ns_redisplay_interface);
5211   terminal->display_info.ns = dpyinfo;
5212   dpyinfo->terminal = terminal;
5214   terminal->clear_frame_hook = ns_clear_frame;
5215   terminal->ring_bell_hook = ns_ring_bell;
5216   terminal->update_begin_hook = ns_update_begin;
5217   terminal->update_end_hook = ns_update_end;
5218   terminal->read_socket_hook = ns_read_socket;
5219   terminal->frame_up_to_date_hook = ns_frame_up_to_date;
5220   terminal->mouse_position_hook = ns_mouse_position;
5221   terminal->frame_rehighlight_hook = ns_frame_rehighlight;
5222   terminal->frame_raise_lower_hook = ns_frame_raise_lower;
5223   terminal->fullscreen_hook = ns_fullscreen_hook;
5224   terminal->menu_show_hook = ns_menu_show;
5225   terminal->popup_dialog_hook = ns_popup_dialog;
5226   terminal->set_vertical_scroll_bar_hook = ns_set_vertical_scroll_bar;
5227   terminal->set_horizontal_scroll_bar_hook = ns_set_horizontal_scroll_bar;
5228   terminal->condemn_scroll_bars_hook = ns_condemn_scroll_bars;
5229   terminal->redeem_scroll_bar_hook = ns_redeem_scroll_bar;
5230   terminal->judge_scroll_bars_hook = ns_judge_scroll_bars;
5231   terminal->delete_frame_hook = x_destroy_window;
5232   terminal->delete_terminal_hook = ns_delete_terminal;
5233   /* Other hooks are NULL by default.  */
5235   return terminal;
5239 struct ns_display_info *
5240 ns_term_init (Lisp_Object display_name)
5241 /* --------------------------------------------------------------------------
5242      Start the Application and get things rolling.
5243    -------------------------------------------------------------------------- */
5245   struct terminal *terminal;
5246   struct ns_display_info *dpyinfo;
5247   static int ns_initialized = 0;
5248   Lisp_Object tmp;
5250   if (ns_initialized) return x_display_list;
5251   ns_initialized = 1;
5253   block_input ();
5255   NSTRACE ("ns_term_init");
5257   [outerpool release];
5258   outerpool = [[NSAutoreleasePool alloc] init];
5260   /* count object allocs (About, click icon); on macOS use ObjectAlloc tool */
5261   /*GSDebugAllocationActive (YES); */
5262   block_input ();
5264   baud_rate = 38400;
5265   Fset_input_interrupt_mode (Qnil);
5267   if (selfds[0] == -1)
5268     {
5269       if (emacs_pipe (selfds) != 0)
5270         {
5271           fprintf (stderr, "Failed to create pipe: %s\n",
5272                    emacs_strerror (errno));
5273           emacs_abort ();
5274         }
5276       fcntl (selfds[0], F_SETFL, O_NONBLOCK|fcntl (selfds[0], F_GETFL));
5277       FD_ZERO (&select_readfds);
5278       FD_ZERO (&select_writefds);
5279       pthread_mutex_init (&select_mutex, NULL);
5280     }
5282   ns_pending_files = [[NSMutableArray alloc] init];
5283   ns_pending_service_names = [[NSMutableArray alloc] init];
5284   ns_pending_service_args = [[NSMutableArray alloc] init];
5286   /* Start app and create the main menu, window, view.
5287      Needs to be here because ns_initialize_display_info () uses AppKit classes.
5288      The view will then ask the NSApp to stop and return to Emacs.  */
5289   [EmacsApp sharedApplication];
5290   if (NSApp == nil)
5291     return NULL;
5292   [NSApp setDelegate: NSApp];
5294   /* Start the select thread.  */
5295   [NSThread detachNewThreadSelector:@selector (fd_handler:)
5296                            toTarget:NSApp
5297                          withObject:nil];
5299   /* debugging: log all notifications */
5300   /*   [[NSNotificationCenter defaultCenter] addObserver: NSApp
5301                                          selector: @selector (logNotification:)
5302                                              name: nil object: nil]; */
5304   dpyinfo = xzalloc (sizeof *dpyinfo);
5306   ns_initialize_display_info (dpyinfo);
5307   terminal = ns_create_terminal (dpyinfo);
5309   terminal->kboard = allocate_kboard (Qns);
5310   /* Don't let the initial kboard remain current longer than necessary.
5311      That would cause problems if a file loaded on startup tries to
5312      prompt in the mini-buffer.  */
5313   if (current_kboard == initial_kboard)
5314     current_kboard = terminal->kboard;
5315   terminal->kboard->reference_count++;
5317   dpyinfo->next = x_display_list;
5318   x_display_list = dpyinfo;
5320   dpyinfo->name_list_element = Fcons (display_name, Qnil);
5322   terminal->name = xlispstrdup (display_name);
5324   unblock_input ();
5326   if (!inhibit_x_resources)
5327     {
5328       ns_default ("GSFontAntiAlias", &ns_antialias_text,
5329                  Qt, Qnil, NO, NO);
5330       tmp = Qnil;
5331       /* this is a standard variable */
5332       ns_default ("AppleAntiAliasingThreshold", &tmp,
5333                  make_float (10.0), make_float (6.0), YES, NO);
5334       ns_antialias_threshold = NILP (tmp) ? 10.0 : extract_float (tmp);
5335     }
5337   NSTRACE_MSG ("Colors");
5339   {
5340     NSColorList *cl = [NSColorList colorListNamed: @"Emacs"];
5342     if ( cl == nil )
5343       {
5344         Lisp_Object color_file, color_map, color;
5345         unsigned long c;
5346         char *name;
5348         color_file = Fexpand_file_name (build_string ("rgb.txt"),
5349                          Fsymbol_value (intern ("data-directory")));
5351         color_map = Fx_load_color_file (color_file);
5352         if (NILP (color_map))
5353           fatal ("Could not read %s.\n", SDATA (color_file));
5355         cl = [[NSColorList alloc] initWithName: @"Emacs"];
5356         for ( ; CONSP (color_map); color_map = XCDR (color_map))
5357           {
5358             color = XCAR (color_map);
5359             name = SSDATA (XCAR (color));
5360             c = XINT (XCDR (color));
5361             [cl setColor:
5362                   [NSColor colorForEmacsRed: RED_FROM_ULONG (c) / 255.0
5363                                       green: GREEN_FROM_ULONG (c) / 255.0
5364                                        blue: BLUE_FROM_ULONG (c) / 255.0
5365                                       alpha: 1.0]
5366                   forKey: [NSString stringWithUTF8String: name]];
5367           }
5368         [cl writeToFile: nil];
5369       }
5370   }
5372   NSTRACE_MSG ("Versions");
5374   {
5375 #ifdef NS_IMPL_GNUSTEP
5376     Vwindow_system_version = build_string (gnustep_base_version);
5377 #else
5378     /* PSnextrelease (128, c); */
5379     char c[DBL_BUFSIZE_BOUND];
5380     int len = dtoastr (c, sizeof c, 0, 0, NSAppKitVersionNumber);
5381     Vwindow_system_version = make_unibyte_string (c, len);
5382 #endif
5383   }
5385   delete_keyboard_wait_descriptor (0);
5387   ns_app_name = [[NSProcessInfo processInfo] processName];
5389   /* Set up macOS app menu */
5391   NSTRACE_MSG ("Menu init");
5393 #ifdef NS_IMPL_COCOA
5394   {
5395     NSMenu *appMenu;
5396     NSMenuItem *item;
5397     /* set up the application menu */
5398     svcsMenu = [[EmacsMenu alloc] initWithTitle: @"Services"];
5399     [svcsMenu setAutoenablesItems: NO];
5400     appMenu = [[EmacsMenu alloc] initWithTitle: @"Emacs"];
5401     [appMenu setAutoenablesItems: NO];
5402     mainMenu = [[EmacsMenu alloc] initWithTitle: @""];
5403     dockMenu = [[EmacsMenu alloc] initWithTitle: @""];
5405     [appMenu insertItemWithTitle: @"About Emacs"
5406                           action: @selector (orderFrontStandardAboutPanel:)
5407                    keyEquivalent: @""
5408                          atIndex: 0];
5409     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 1];
5410     [appMenu insertItemWithTitle: @"Preferences..."
5411                           action: @selector (showPreferencesWindow:)
5412                    keyEquivalent: @","
5413                          atIndex: 2];
5414     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 3];
5415     item = [appMenu insertItemWithTitle: @"Services"
5416                                  action: @selector (menuDown:)
5417                           keyEquivalent: @""
5418                                 atIndex: 4];
5419     [appMenu setSubmenu: svcsMenu forItem: item];
5420     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 5];
5421     [appMenu insertItemWithTitle: @"Hide Emacs"
5422                           action: @selector (hide:)
5423                    keyEquivalent: @"h"
5424                          atIndex: 6];
5425     item =  [appMenu insertItemWithTitle: @"Hide Others"
5426                           action: @selector (hideOtherApplications:)
5427                    keyEquivalent: @"h"
5428                          atIndex: 7];
5429     [item setKeyEquivalentModifierMask: NSEventModifierFlagCommand | NSEventModifierFlagOption];
5430     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 8];
5431     [appMenu insertItemWithTitle: @"Quit Emacs"
5432                           action: @selector (terminate:)
5433                    keyEquivalent: @"q"
5434                          atIndex: 9];
5436     item = [mainMenu insertItemWithTitle: ns_app_name
5437                                   action: @selector (menuDown:)
5438                            keyEquivalent: @""
5439                                  atIndex: 0];
5440     [mainMenu setSubmenu: appMenu forItem: item];
5441     [dockMenu insertItemWithTitle: @"New Frame"
5442                            action: @selector (newFrame:)
5443                     keyEquivalent: @""
5444                           atIndex: 0];
5446     [NSApp setMainMenu: mainMenu];
5447     [NSApp setAppleMenu: appMenu];
5448     [NSApp setServicesMenu: svcsMenu];
5449     /* Needed at least on Cocoa, to get dock menu to show windows */
5450     [NSApp setWindowsMenu: [[NSMenu alloc] init]];
5452     [[NSNotificationCenter defaultCenter]
5453       addObserver: mainMenu
5454          selector: @selector (trackingNotification:)
5455              name: NSMenuDidBeginTrackingNotification object: mainMenu];
5456     [[NSNotificationCenter defaultCenter]
5457       addObserver: mainMenu
5458          selector: @selector (trackingNotification:)
5459              name: NSMenuDidEndTrackingNotification object: mainMenu];
5460   }
5461 #endif /* macOS menu setup */
5463   /* Register our external input/output types, used for determining
5464      applicable services and also drag/drop eligibility.  */
5466   NSTRACE_MSG ("Input/output types");
5468   ns_send_types = [[NSArray arrayWithObjects: NSStringPboardType, nil] retain];
5469   ns_return_types = [[NSArray arrayWithObjects: NSStringPboardType, nil]
5470                       retain];
5471   ns_drag_types = [[NSArray arrayWithObjects:
5472                             NSStringPboardType,
5473                             NSTabularTextPboardType,
5474                             NSFilenamesPboardType,
5475                             NSURLPboardType, nil] retain];
5477   /* If fullscreen is in init/default-frame-alist, focus isn't set
5478      right for fullscreen windows, so set this.  */
5479   [NSApp activateIgnoringOtherApps:YES];
5481   NSTRACE_MSG ("Call NSApp run");
5483   [NSApp run];
5484   ns_do_open_file = YES;
5486 #ifdef NS_IMPL_GNUSTEP
5487   /* GNUstep steals SIGCHLD for use in NSTask, but we don't use NSTask.
5488      We must re-catch it so subprocess works.  */
5489   catch_child_signal ();
5490 #endif
5492   NSTRACE_MSG ("ns_term_init done");
5494   unblock_input ();
5496   return dpyinfo;
5500 void
5501 ns_term_shutdown (int sig)
5503   [[NSUserDefaults standardUserDefaults] synchronize];
5505   /* code not reached in emacs.c after this is called by shut_down_emacs: */
5506   if (STRINGP (Vauto_save_list_file_name))
5507     unlink (SSDATA (Vauto_save_list_file_name));
5509   if (sig == 0 || sig == SIGTERM)
5510     {
5511       [NSApp terminate: NSApp];
5512     }
5513   else // force a stack trace to happen
5514     {
5515       emacs_abort ();
5516     }
5520 /* ==========================================================================
5522     EmacsApp implementation
5524    ========================================================================== */
5527 @implementation EmacsApp
5529 - (id)init
5531   NSTRACE ("[EmacsApp init]");
5533   if ((self = [super init]))
5534     {
5535 #ifdef NS_IMPL_COCOA
5536       self->isFirst = YES;
5537 #endif
5538 #ifdef NS_IMPL_GNUSTEP
5539       self->applicationDidFinishLaunchingCalled = NO;
5540 #endif
5541     }
5543   return self;
5546 #ifdef NS_IMPL_COCOA
5547 - (void)run
5549   NSTRACE ("[EmacsApp run]");
5551 #ifndef NSAppKitVersionNumber10_9
5552 #define NSAppKitVersionNumber10_9 1265
5553 #endif
5555     if ((int)NSAppKitVersionNumber != NSAppKitVersionNumber10_9)
5556       {
5557         [super run];
5558         return;
5559       }
5561   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5563   if (isFirst) [self finishLaunching];
5564   isFirst = NO;
5566   shouldKeepRunning = YES;
5567   do
5568     {
5569       [pool release];
5570       pool = [[NSAutoreleasePool alloc] init];
5572       NSEvent *event =
5573         [self nextEventMatchingMask:NSEventMaskAny
5574                           untilDate:[NSDate distantFuture]
5575                              inMode:NSDefaultRunLoopMode
5576                             dequeue:YES];
5578       [self sendEvent:event];
5579       [self updateWindows];
5580     } while (shouldKeepRunning);
5582   [pool release];
5585 - (void)stop: (id)sender
5587   NSTRACE ("[EmacsApp stop:]");
5589     shouldKeepRunning = NO;
5590     // Stop possible dialog also.  Noop if no dialog present.
5591     // The file dialog still leaks 7k - 10k on 10.9 though.
5592     [super stop:sender];
5594 #endif /* NS_IMPL_COCOA */
5596 - (void)logNotification: (NSNotification *)notification
5598   NSTRACE ("[EmacsApp logNotification:]");
5600   const char *name = [[notification name] UTF8String];
5601   if (!strstr (name, "Update") && !strstr (name, "NSMenu")
5602       && !strstr (name, "WindowNumber"))
5603     NSLog (@"notification: '%@'", [notification name]);
5607 - (void)sendEvent: (NSEvent *)theEvent
5608 /* --------------------------------------------------------------------------
5609      Called when NSApp is running for each event received.  Used to stop
5610      the loop when we choose, since there's no way to just run one iteration.
5611    -------------------------------------------------------------------------- */
5613   int type = [theEvent type];
5614   NSWindow *window = [theEvent window];
5616   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsApp sendEvent:]");
5617   NSTRACE_MSG ("Type: %d", type);
5619 #ifdef NS_IMPL_GNUSTEP
5620   // Keyboard events aren't propagated to file dialogs for some reason.
5621   if ([NSApp modalWindow] != nil &&
5622       (type == NSEventTypeKeyDown || type == NSEventTypeKeyUp || type == NSEventTypeFlagsChanged))
5623     {
5624       [[NSApp modalWindow] sendEvent: theEvent];
5625       return;
5626     }
5627 #endif
5629   if (represented_filename != nil && represented_frame)
5630     {
5631       NSString *fstr = represented_filename;
5632       NSView *view = FRAME_NS_VIEW (represented_frame);
5633 #ifdef NS_IMPL_COCOA
5634       /* Work around a bug observed on 10.3 and later where
5635          setTitleWithRepresentedFilename does not clear out previous state
5636          if given filename does not exist.  */
5637       if (! [[NSFileManager defaultManager] fileExistsAtPath: fstr])
5638         [[view window] setRepresentedFilename: @""];
5639 #endif
5640       [[view window] setRepresentedFilename: fstr];
5641       [represented_filename release];
5642       represented_filename = nil;
5643       represented_frame = NULL;
5644     }
5646   if (type == NSEventTypeApplicationDefined)
5647     {
5648       switch ([theEvent data2])
5649         {
5650 #ifdef NS_IMPL_COCOA
5651         case NSAPP_DATA2_RUNASSCRIPT:
5652           ns_run_ascript ();
5653           [self stop: self];
5654           return;
5655 #endif
5656         case NSAPP_DATA2_RUNFILEDIALOG:
5657           ns_run_file_dialog ();
5658           [self stop: self];
5659           return;
5660         }
5661     }
5663   if (type == NSEventTypeCursorUpdate && window == nil)
5664     {
5665       fprintf (stderr, "Dropping external cursor update event.\n");
5666       return;
5667     }
5669   if (type == NSEventTypeApplicationDefined)
5670     {
5671       /* Events posted by ns_send_appdefined interrupt the run loop here.
5672          But, if a modal window is up, an appdefined can still come through,
5673          (e.g., from a makeKeyWindow event) but stopping self also stops the
5674          modal loop. Just defer it until later.  */
5675       if ([NSApp modalWindow] == nil)
5676         {
5677           last_appdefined_event_data = [theEvent data1];
5678           [self stop: self];
5679         }
5680       else
5681         {
5682           send_appdefined = YES;
5683         }
5684     }
5687 #ifdef NS_IMPL_COCOA
5688   /* If no dialog and none of our frames have focus and it is a move, skip it.
5689      It is a mouse move in an auxiliary menu, i.e. on the top right on macOS,
5690      such as Wifi, sound, date or similar.
5691      This prevents "spooky" highlighting in the frame under the menu.  */
5692   if (type == NSEventTypeMouseMoved && [NSApp modalWindow] == nil)
5693     {
5694       struct ns_display_info *di;
5695       BOOL has_focus = NO;
5696       for (di = x_display_list; ! has_focus && di; di = di->next)
5697         has_focus = di->x_focus_frame != 0;
5698       if (! has_focus)
5699         return;
5700     }
5701 #endif
5703   NSTRACE_UNSILENCE();
5705   [super sendEvent: theEvent];
5709 - (void)showPreferencesWindow: (id)sender
5711   struct frame *emacsframe = SELECTED_FRAME ();
5712   NSEvent *theEvent = [NSApp currentEvent];
5714   if (!emacs_event)
5715     return;
5716   emacs_event->kind = NS_NONKEY_EVENT;
5717   emacs_event->code = KEY_NS_SHOW_PREFS;
5718   emacs_event->modifiers = 0;
5719   EV_TRAILER (theEvent);
5723 - (void)newFrame: (id)sender
5725   NSTRACE ("[EmacsApp newFrame:]");
5727   struct frame *emacsframe = SELECTED_FRAME ();
5728   NSEvent *theEvent = [NSApp currentEvent];
5730   if (!emacs_event)
5731     return;
5732   emacs_event->kind = NS_NONKEY_EVENT;
5733   emacs_event->code = KEY_NS_NEW_FRAME;
5734   emacs_event->modifiers = 0;
5735   EV_TRAILER (theEvent);
5739 /* Open a file (used by below, after going into queue read by ns_read_socket).  */
5740 - (BOOL) openFile: (NSString *)fileName
5742   NSTRACE ("[EmacsApp openFile:]");
5744   struct frame *emacsframe = SELECTED_FRAME ();
5745   NSEvent *theEvent = [NSApp currentEvent];
5747   if (!emacs_event)
5748     return NO;
5750   emacs_event->kind = NS_NONKEY_EVENT;
5751   emacs_event->code = KEY_NS_OPEN_FILE_LINE;
5752   ns_input_file = append2 (ns_input_file, build_string ([fileName UTF8String]));
5753   ns_input_line = Qnil; /* can be start or cons start,end */
5754   emacs_event->modifiers =0;
5755   EV_TRAILER (theEvent);
5757   return YES;
5761 /* **************************************************************************
5763       EmacsApp delegate implementation
5765    ************************************************************************** */
5767 - (void)applicationDidFinishLaunching: (NSNotification *)notification
5768 /* --------------------------------------------------------------------------
5769      When application is loaded, terminate event loop in ns_term_init.
5770    -------------------------------------------------------------------------- */
5772   NSTRACE ("[EmacsApp applicationDidFinishLaunching:]");
5774 #ifdef NS_IMPL_GNUSTEP
5775   ((EmacsApp *)self)->applicationDidFinishLaunchingCalled = YES;
5776 #endif
5777   [NSApp setServicesProvider: NSApp];
5779   [self antialiasThresholdDidChange:nil];
5780 #ifdef NS_IMPL_COCOA
5781   [[NSNotificationCenter defaultCenter]
5782     addObserver:self
5783        selector:@selector(antialiasThresholdDidChange:)
5784            name:NSAntialiasThresholdChangedNotification
5785          object:nil];
5786 #endif
5788 #ifdef NS_IMPL_COCOA
5789   if ([NSApp activationPolicy] == NSApplicationActivationPolicyProhibited) {
5790     /* Set the app's activation policy to regular when we run outside
5791        of a bundle.  This is already done for us by Info.plist when we
5792        run inside a bundle.  */
5793     [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
5794     [NSApp setApplicationIconImage:
5795              [EmacsImage
5796                allocInitFromFile:
5797                  build_string("icons/hicolor/128x128/apps/emacs.png")]];
5798   }
5799 #endif
5801   ns_send_appdefined (-2);
5804 - (void)antialiasThresholdDidChange:(NSNotification *)notification
5806 #ifdef NS_IMPL_COCOA
5807   macfont_update_antialias_threshold ();
5808 #endif
5812 /* Termination sequences:
5813     C-x C-c:
5814     Cmd-Q:
5815     MenuBar | File | Exit:
5816     Select Quit from App menubar:
5817         -terminate
5818         KEY_NS_POWER_OFF, (save-buffers-kill-emacs)
5819         ns_term_shutdown()
5821     Select Quit from Dock menu:
5822     Logout attempt:
5823         -appShouldTerminate
5824           Cancel -> Nothing else
5825           Accept ->
5827           -terminate
5828           KEY_NS_POWER_OFF, (save-buffers-kill-emacs)
5829           ns_term_shutdown()
5833 - (void) terminate: (id)sender
5835   NSTRACE ("[EmacsApp terminate:]");
5837   struct frame *emacsframe = SELECTED_FRAME ();
5839   if (!emacs_event)
5840     return;
5842   emacs_event->kind = NS_NONKEY_EVENT;
5843   emacs_event->code = KEY_NS_POWER_OFF;
5844   emacs_event->arg = Qt; /* mark as non-key event */
5845   EV_TRAILER ((id)nil);
5848 static bool
5849 runAlertPanel(NSString *title,
5850               NSString *msgFormat,
5851               NSString *defaultButton,
5852               NSString *alternateButton)
5854 #ifdef NS_IMPL_GNUSTEP
5855   return NSRunAlertPanel(title, msgFormat, defaultButton, alternateButton, nil)
5856     == NSAlertDefaultReturn;
5857 #else
5858   NSAlert *alert = [[NSAlert alloc] init];
5859   [alert setAlertStyle: NSAlertStyleCritical];
5860   [alert setMessageText: msgFormat];
5861   [alert addButtonWithTitle: defaultButton];
5862   [alert addButtonWithTitle: alternateButton];
5863   NSInteger ret = [alert runModal];
5864   [alert release];
5865   return ret == NSAlertFirstButtonReturn;
5866 #endif
5870 - (NSApplicationTerminateReply)applicationShouldTerminate: (id)sender
5872   NSTRACE ("[EmacsApp applicationShouldTerminate:]");
5874   bool ret;
5876   if (NILP (ns_confirm_quit)) //   || ns_shutdown_properly  --> TO DO
5877     return NSTerminateNow;
5879   ret = runAlertPanel(ns_app_name,
5880                       @"Exit requested.  Would you like to Save Buffers and Exit, or Cancel the request?",
5881                       @"Save Buffers and Exit", @"Cancel");
5883   return ret ? NSTerminateNow : NSTerminateCancel;
5886 static int
5887 not_in_argv (NSString *arg)
5889   int k;
5890   const char *a = [arg UTF8String];
5891   for (k = 1; k < initial_argc; ++k)
5892     if (strcmp (a, initial_argv[k]) == 0) return 0;
5893   return 1;
5896 /* Notification from the Workspace to open a file.  */
5897 - (BOOL)application: sender openFile: (NSString *)file
5899   if (ns_do_open_file || not_in_argv (file))
5900     [ns_pending_files addObject: file];
5901   return YES;
5905 /* Open a file as a temporary file.  */
5906 - (BOOL)application: sender openTempFile: (NSString *)file
5908   if (ns_do_open_file || not_in_argv (file))
5909     [ns_pending_files addObject: file];
5910   return YES;
5914 /* Notification from the Workspace to open a file noninteractively (?).  */
5915 - (BOOL)application: sender openFileWithoutUI: (NSString *)file
5917   if (ns_do_open_file || not_in_argv (file))
5918     [ns_pending_files addObject: file];
5919   return YES;
5922 /* Notification from the Workspace to open multiple files.  */
5923 - (void)application: sender openFiles: (NSArray *)fileList
5925   NSEnumerator *files = [fileList objectEnumerator];
5926   NSString *file;
5927   /* Don't open files from the command line unconditionally,
5928      Cocoa parses the command line wrong, --option value tries to open value
5929      if --option is the last option.  */
5930   while ((file = [files nextObject]) != nil)
5931     if (ns_do_open_file || not_in_argv (file))
5932       [ns_pending_files addObject: file];
5934   [self replyToOpenOrPrint: NSApplicationDelegateReplySuccess];
5939 /* Handle dock menu requests.  */
5940 - (NSMenu *)applicationDockMenu: (NSApplication *) sender
5942   return dockMenu;
5946 /* TODO: these may help w/IO switching between terminal and NSApp.  */
5947 - (void)applicationWillBecomeActive: (NSNotification *)notification
5949   NSTRACE ("[EmacsApp applicationWillBecomeActive:]");
5950   // ns_app_active=YES;
5953 - (void)applicationDidBecomeActive: (NSNotification *)notification
5955   NSTRACE ("[EmacsApp applicationDidBecomeActive:]");
5957 #ifdef NS_IMPL_GNUSTEP
5958   if (! applicationDidFinishLaunchingCalled)
5959     [self applicationDidFinishLaunching:notification];
5960 #endif
5961   // ns_app_active=YES;
5963   ns_update_auto_hide_menu_bar ();
5964   // No constraining takes place when the application is not active.
5965   ns_constrain_all_frames ();
5967 - (void)applicationDidResignActive: (NSNotification *)notification
5969   NSTRACE ("[EmacsApp applicationDidResignActive:]");
5971   // ns_app_active=NO;
5972   ns_send_appdefined (-1);
5977 /* ==========================================================================
5979     EmacsApp aux handlers for managing event loop
5981    ========================================================================== */
5984 - (void)timeout_handler: (NSTimer *)timedEntry
5985 /* --------------------------------------------------------------------------
5986      The timeout specified to ns_select has passed.
5987    -------------------------------------------------------------------------- */
5989   /* NSTRACE ("timeout_handler"); */
5990   ns_send_appdefined (-2);
5993 - (void)sendFromMainThread:(id)unused
5995   ns_send_appdefined (nextappdefined);
5998 - (void)fd_handler:(id)unused
5999 /* --------------------------------------------------------------------------
6000      Check data waiting on file descriptors and terminate if so.
6001    -------------------------------------------------------------------------- */
6003   int result;
6004   int waiting = 1, nfds;
6005   char c;
6007   fd_set readfds, writefds, *wfds;
6008   struct timespec timeout, *tmo;
6009   NSAutoreleasePool *pool = nil;
6011   /* NSTRACE ("fd_handler"); */
6013   for (;;)
6014     {
6015       [pool release];
6016       pool = [[NSAutoreleasePool alloc] init];
6018       if (waiting)
6019         {
6020           fd_set fds;
6021           FD_ZERO (&fds);
6022           FD_SET (selfds[0], &fds);
6023           result = select (selfds[0]+1, &fds, NULL, NULL, NULL);
6024           if (result > 0 && read (selfds[0], &c, 1) == 1 && c == 'g')
6025             waiting = 0;
6026         }
6027       else
6028         {
6029           pthread_mutex_lock (&select_mutex);
6030           nfds = select_nfds;
6032           if (select_valid & SELECT_HAVE_READ)
6033             readfds = select_readfds;
6034           else
6035             FD_ZERO (&readfds);
6037           if (select_valid & SELECT_HAVE_WRITE)
6038             {
6039               writefds = select_writefds;
6040               wfds = &writefds;
6041             }
6042           else
6043             wfds = NULL;
6044           if (select_valid & SELECT_HAVE_TMO)
6045             {
6046               timeout = select_timeout;
6047               tmo = &timeout;
6048             }
6049           else
6050             tmo = NULL;
6052           pthread_mutex_unlock (&select_mutex);
6054           FD_SET (selfds[0], &readfds);
6055           if (selfds[0] >= nfds) nfds = selfds[0]+1;
6057           result = pselect (nfds, &readfds, wfds, NULL, tmo, NULL);
6059           if (result == 0)
6060             ns_send_appdefined (-2);
6061           else if (result > 0)
6062             {
6063               if (FD_ISSET (selfds[0], &readfds))
6064                 {
6065                   if (read (selfds[0], &c, 1) == 1 && c == 's')
6066                     waiting = 1;
6067                 }
6068               else
6069                 {
6070                   pthread_mutex_lock (&select_mutex);
6071                   if (select_valid & SELECT_HAVE_READ)
6072                     select_readfds = readfds;
6073                   if (select_valid & SELECT_HAVE_WRITE)
6074                     select_writefds = writefds;
6075                   if (select_valid & SELECT_HAVE_TMO)
6076                     select_timeout = timeout;
6077                   pthread_mutex_unlock (&select_mutex);
6079                   ns_send_appdefined (result);
6080                 }
6081             }
6082           waiting = 1;
6083         }
6084     }
6089 /* ==========================================================================
6091     Service provision
6093    ========================================================================== */
6095 /* Called from system: queue for next pass through event loop.  */
6096 - (void)requestService: (NSPasteboard *)pboard
6097               userData: (NSString *)userData
6098                  error: (NSString **)error
6100   [ns_pending_service_names addObject: userData];
6101   [ns_pending_service_args addObject: [NSString stringWithUTF8String:
6102       SSDATA (ns_string_from_pasteboard (pboard))]];
6106 /* Called from ns_read_socket to clear queue.  */
6107 - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
6109   struct frame *emacsframe = SELECTED_FRAME ();
6110   NSEvent *theEvent = [NSApp currentEvent];
6112   NSTRACE ("[EmacsApp fulfillService:withArg:]");
6114   if (!emacs_event)
6115     return NO;
6117   emacs_event->kind = NS_NONKEY_EVENT;
6118   emacs_event->code = KEY_NS_SPI_SERVICE_CALL;
6119   ns_input_spi_name = build_string ([name UTF8String]);
6120   ns_input_spi_arg = build_string ([arg UTF8String]);
6121   emacs_event->modifiers = EV_MODIFIERS (theEvent);
6122   EV_TRAILER (theEvent);
6124   return YES;
6128 @end  /* EmacsApp */
6131 /* ==========================================================================
6133     EmacsView implementation
6135    ========================================================================== */
6138 @implementation EmacsView
6140 /* Needed to inform when window closed from lisp.  */
6141 - (void) setWindowClosing: (BOOL)closing
6143   NSTRACE ("[EmacsView setWindowClosing:%d]", closing);
6145   windowClosing = closing;
6149 - (void)dealloc
6151   NSTRACE ("[EmacsView dealloc]");
6152   [toolbar release];
6153   if (fs_state == FULLSCREEN_BOTH)
6154     [nonfs_window release];
6155   [super dealloc];
6159 /* Called on font panel selection.  */
6160 - (void)changeFont: (id)sender
6162   NSEvent *e = [[self window] currentEvent];
6163   struct face *face = FACE_FROM_ID (emacsframe, DEFAULT_FACE_ID);
6164   struct font *font = face->font;
6165   id newFont;
6166   CGFloat size;
6167   NSFont *nsfont;
6169   NSTRACE ("[EmacsView changeFont:]");
6171   if (!emacs_event)
6172     return;
6174 #ifdef NS_IMPL_GNUSTEP
6175   nsfont = ((struct nsfont_info *)font)->nsfont;
6176 #endif
6177 #ifdef NS_IMPL_COCOA
6178   nsfont = (NSFont *) macfont_get_nsctfont (font);
6179 #endif
6181   if ((newFont = [sender convertFont: nsfont]))
6182     {
6183       SET_FRAME_GARBAGED (emacsframe); /* now needed as of 2008/10 */
6185       emacs_event->kind = NS_NONKEY_EVENT;
6186       emacs_event->modifiers = 0;
6187       emacs_event->code = KEY_NS_CHANGE_FONT;
6189       size = [newFont pointSize];
6190       ns_input_fontsize = make_number (lrint (size));
6191       ns_input_font = build_string ([[newFont familyName] UTF8String]);
6192       EV_TRAILER (e);
6193     }
6197 - (BOOL)acceptsFirstResponder
6199   NSTRACE ("[EmacsView acceptsFirstResponder]");
6200   return YES;
6204 - (void)resetCursorRects
6206   NSRect visible = [self visibleRect];
6207   NSCursor *currentCursor = FRAME_POINTER_TYPE (emacsframe);
6208   NSTRACE ("[EmacsView resetCursorRects]");
6210   if (currentCursor == nil)
6211     currentCursor = [NSCursor arrowCursor];
6213   if (!NSIsEmptyRect (visible))
6214     [self addCursorRect: visible cursor: currentCursor];
6216 #if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 101300
6217 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
6218   if ([currentCursor respondsToSelector: @selector(setOnMouseEntered)])
6219 #endif
6220     [currentCursor setOnMouseEntered: YES];
6221 #endif
6226 /*****************************************************************************/
6227 /* Keyboard handling.  */
6228 #define NS_KEYLOG 0
6230 - (void)keyDown: (NSEvent *)theEvent
6232   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
6233   int code;
6234   unsigned fnKeysym = 0;
6235   static NSMutableArray *nsEvArray;
6236   unsigned int flags = [theEvent modifierFlags];
6238   NSTRACE ("[EmacsView keyDown:]");
6240   /* Rhapsody and macOS give up and down events for the arrow keys.  */
6241   if (ns_fake_keydown == YES)
6242     ns_fake_keydown = NO;
6243   else if ([theEvent type] != NSEventTypeKeyDown)
6244     return;
6246   if (!emacs_event)
6247     return;
6249  if (![[self window] isKeyWindow]
6250      && [[theEvent window] isKindOfClass: [EmacsWindow class]]
6251      /* We must avoid an infinite loop here.  */
6252      && (EmacsView *)[[theEvent window] delegate] != self)
6253    {
6254      /* XXX: There is an occasional condition in which, when Emacs display
6255          updates a different frame from the current one, and temporarily
6256          selects it, then processes some interrupt-driven input
6257          (dispnew.c:3878), OS will send the event to the correct NSWindow, but
6258          for some reason that window has its first responder set to the NSView
6259          most recently updated (I guess), which is not the correct one.  */
6260      [(EmacsView *)[[theEvent window] delegate] keyDown: theEvent];
6261      return;
6262    }
6264   if (nsEvArray == nil)
6265     nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1];
6267   [NSCursor setHiddenUntilMouseMoves: YES];
6269   if (hlinfo->mouse_face_hidden && INTEGERP (Vmouse_highlight))
6270     {
6271       clear_mouse_face (hlinfo);
6272       hlinfo->mouse_face_hidden = 1;
6273     }
6275   if (!processingCompose)
6276     {
6277       /* FIXME: What should happen for key sequences with more than
6278          one character?  */
6279       code = ([[theEvent charactersIgnoringModifiers] length] == 0) ?
6280         0 : [[theEvent charactersIgnoringModifiers] characterAtIndex: 0];
6282       /* Is it a "function key"?  */
6283       /* Note: Sometimes a plain key will have the NSEventModifierFlagNumericPad
6284          flag set (this is probably a bug in the OS).  */
6285       if (code < 0x00ff && (flags&NSEventModifierFlagNumericPad))
6286         {
6287           fnKeysym = ns_convert_key ([theEvent keyCode] | NSEventModifierFlagNumericPad);
6288         }
6289       if (fnKeysym == 0)
6290         {
6291           fnKeysym = ns_convert_key (code);
6292         }
6294       if (fnKeysym)
6295         {
6296           /* COUNTERHACK: map 'Delete' on upper-right main KB to 'Backspace',
6297              because Emacs treats Delete and KP-Delete same (in simple.el).  */
6298           if ((fnKeysym == 0xFFFF && [theEvent keyCode] == 0x33)
6299 #ifdef NS_IMPL_GNUSTEP
6300               /*  GNUstep uses incompatible keycodes, even for those that are
6301                   supposed to be hardware independent.  Just check for delete.
6302                   Keypad delete does not have keysym 0xFFFF.
6303                   See https://savannah.gnu.org/bugs/?25395  */
6304               || (fnKeysym == 0xFFFF && code == 127)
6305 #endif
6306             )
6307             code = 0xFF08; /* backspace */
6308           else
6309             code = fnKeysym;
6310         }
6312       /* The âŒ˜ and âŒ¥ modifiers can be either shift-like (for alternate
6313          character input) or control-like (as command prefix).  If we
6314          have only shift-like modifiers, then we should use the
6315          translated characters (returned by the characters method); if
6316          we have only control-like modifiers, then we should use the
6317          untranslated characters (returned by the
6318          charactersIgnoringModifiers method).  An annoyance happens if
6319          we have both shift-like and control-like modifiers because
6320          the NSEvent API doesn’t let us ignore only some modifiers.
6321          In that case we use UCKeyTranslate (ns_get_shifted_character)
6322          to look up the correct character.  */
6324       /* EV_MODIFIERS2 uses parse_solitary_modifier on all known
6325          modifier keys, which returns 0 for shift-like modifiers.
6326          Therefore its return value is the set of control-like
6327          modifiers.  */
6328       emacs_event->modifiers = EV_MODIFIERS2 (flags);
6330       /* Function keys (such as the F-keys, arrow keys, etc.) set
6331          modifiers as though the fn key has been pressed when it
6332          hasn't.  Also some combinations of fn and a function key
6333          return a different key than was pressed (e.g. fn-<left> gives
6334          <home>).  We need to unset the fn modifier in these cases.
6335          FIXME: Can we avoid setting it in the first place?  */
6336       if (fnKeysym && (flags & NS_FUNCTION_KEY_MASK))
6337         emacs_event->modifiers ^= parse_solitary_modifier (ns_function_modifier);
6339       if (NS_KEYLOG)
6340         fprintf (stderr, "keyDown: code =%x\tfnKey =%x\tflags = %x\tmods = %x\n",
6341                  code, fnKeysym, flags, emacs_event->modifiers);
6343       /* If it was a function key or had control-like modifiers, pass
6344          it directly to Emacs.  */
6345       if (fnKeysym || (emacs_event->modifiers
6346                        && (emacs_event->modifiers != shift_modifier)
6347                        && [[theEvent charactersIgnoringModifiers] length] > 0))
6348         {
6349           emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
6350           /* FIXME: What are the next four lines supposed to do?  */
6351           if (code < 0x20)
6352             code |= (1<<28)|(3<<16);
6353           else if (code == 0x7f)
6354             code |= (1<<28)|(3<<16);
6355           else if (!fnKeysym)
6356             {
6357 #ifdef NS_IMPL_COCOA
6358               /* We potentially have both shift- and control-like
6359                  modifiers in use, so find the correct character
6360                  ignoring any control-like ones.  */
6361               code = ns_get_shifted_character (theEvent);
6362 #endif
6364               /* FIXME: This seems wrong, characters in the range
6365                  [0x80, 0xFF] are not ASCII characters.  Can’t we just
6366                  use MULTIBYTE_CHAR_KEYSTROKE_EVENT here for all kinds
6367                  of characters?  */
6368               emacs_event->kind = code > 0xFF
6369                 ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT;
6370             }
6372           emacs_event->code = code;
6373           EV_TRAILER (theEvent);
6374           processingCompose = NO;
6375           return;
6376         }
6377     }
6379   /* If we get here, a non-function key without control-like modifiers
6380      was hit.  Use interpretKeyEvents, which in turn will call
6381      insertText; see
6382      https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/EventOverview/HandlingKeyEvents/HandlingKeyEvents.html.  */
6384   if (NS_KEYLOG && !processingCompose)
6385     fprintf (stderr, "keyDown: Begin compose sequence.\n");
6387   /* FIXME: interpretKeyEvents doesn’t seem to send insertText if âŒ˜ is
6388      used as shift-like modifier, at least on El Capitan.  Mask it
6389      out.  This shouldn’t be needed though; we should figure out what
6390      the correct way of handling âŒ˜ is.  */
6391   if ([theEvent modifierFlags] & NSEventModifierFlagCommand)
6392     theEvent = [NSEvent keyEventWithType:[theEvent type]
6393                                 location:[theEvent locationInWindow]
6394                            modifierFlags:[theEvent modifierFlags] & ~NSEventModifierFlagCommand
6395                                timestamp:[theEvent timestamp]
6396                             windowNumber:[theEvent windowNumber]
6397                                  context:nil
6398                               characters:[theEvent characters]
6399                         charactersIgnoringModifiers:[theEvent charactersIgnoringModifiers]
6400                                isARepeat:[theEvent isARepeat]
6401                                  keyCode:[theEvent keyCode]];
6403   processingCompose = YES;
6404   /* FIXME: Use [NSArray arrayWithObject:theEvent]?  */
6405   [nsEvArray addObject: theEvent];
6406   [self interpretKeyEvents: nsEvArray];
6407   [nsEvArray removeObject: theEvent];
6411 /* <NSTextInput> implementation (called through [super interpretKeyEvents:]).  */
6414 /* <NSTextInput>: called when done composing;
6415    NOTE: also called when we delete over working text, followed
6416    immediately by doCommandBySelector: deleteBackward:  */
6417 - (void)insertText: (id)aString
6419   NSString *s;
6420   NSUInteger len;
6422   NSTRACE ("[EmacsView insertText:]");
6424   if ([aString isKindOfClass:[NSAttributedString class]])
6425     s = [aString string];
6426   else
6427     s = aString;
6429   len = [s length];
6431   if (NS_KEYLOG)
6432     NSLog (@"insertText '%@'\tlen = %lu", aString, (unsigned long) len);
6433   processingCompose = NO;
6435   if (!emacs_event)
6436     return;
6438   /* First, clear any working text.  */
6439   if (workingText != nil)
6440     [self deleteWorkingText];
6442   /* It might be preferable to use getCharacters:range: below,
6443      cf. https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CocoaPerformance/Articles/StringDrawing.html#//apple_ref/doc/uid/TP40001445-112378.
6444      However, we probably can't use SAFE_NALLOCA here because it might
6445      exit nonlocally.  */
6447   /* Now insert the string as keystrokes.  */
6448   for (NSUInteger i = 0; i < len; i++)
6449     {
6450       NSUInteger code = [s characterAtIndex:i];
6451       if (UTF_16_HIGH_SURROGATE_P (code) && i < len - 1)
6452         {
6453           unichar low = [s characterAtIndex:i + 1];
6454           if (UTF_16_LOW_SURROGATE_P (low))
6455             {
6456               code = surrogates_to_codepoint (low, code);
6457               ++i;
6458             }
6459         }
6460       /* TODO: still need this?  */
6461       if (code == 0x2DC)
6462         code = '~'; /* 0x7E */
6463       if (code != 32) /* Space */
6464         emacs_event->modifiers = 0;
6465       emacs_event->kind
6466         = code > 0xFF ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT;
6467       emacs_event->code = code;
6468       EV_TRAILER ((id)nil);
6469     }
6473 /* <NSTextInput>: inserts display of composing characters.  */
6474 - (void)setMarkedText: (id)aString selectedRange: (NSRange)selRange
6476   NSString *str = [aString respondsToSelector: @selector (string)] ?
6477     [aString string] : aString;
6479   NSTRACE ("[EmacsView setMarkedText:selectedRange:]");
6481   if (NS_KEYLOG)
6482     NSLog (@"setMarkedText '%@' len =%lu range %lu from %lu",
6483            str, (unsigned long)[str length],
6484            (unsigned long)selRange.length,
6485            (unsigned long)selRange.location);
6487   if (workingText != nil)
6488     [self deleteWorkingText];
6489   if ([str length] == 0)
6490     return;
6492   if (!emacs_event)
6493     return;
6495   processingCompose = YES;
6496   workingText = [str copy];
6497   ns_working_text = build_string ([workingText UTF8String]);
6499   emacs_event->kind = NS_TEXT_EVENT;
6500   emacs_event->code = KEY_NS_PUT_WORKING_TEXT;
6501   EV_TRAILER ((id)nil);
6505 /* Delete display of composing characters [not in <NSTextInput>].  */
6506 - (void)deleteWorkingText
6508   NSTRACE ("[EmacsView deleteWorkingText]");
6510   if (workingText == nil)
6511     return;
6512   if (NS_KEYLOG)
6513     NSLog(@"deleteWorkingText len =%lu\n", (unsigned long)[workingText length]);
6514   [workingText release];
6515   workingText = nil;
6516   processingCompose = NO;
6518   if (!emacs_event)
6519     return;
6521   emacs_event->kind = NS_TEXT_EVENT;
6522   emacs_event->code = KEY_NS_UNPUT_WORKING_TEXT;
6523   EV_TRAILER ((id)nil);
6527 - (BOOL)hasMarkedText
6529   NSTRACE ("[EmacsView hasMarkedText]");
6531   return workingText != nil;
6535 - (NSRange)markedRange
6537   NSTRACE ("[EmacsView markedRange]");
6539   NSRange rng = workingText != nil
6540     ? NSMakeRange (0, [workingText length]) : NSMakeRange (NSNotFound, 0);
6541   if (NS_KEYLOG)
6542     NSLog (@"markedRange request");
6543   return rng;
6547 - (void)unmarkText
6549   NSTRACE ("[EmacsView unmarkText]");
6551   if (NS_KEYLOG)
6552     NSLog (@"unmark (accept) text");
6553   [self deleteWorkingText];
6554   processingCompose = NO;
6558 /* Used to position char selection windows, etc.  */
6559 - (NSRect)firstRectForCharacterRange: (NSRange)theRange
6561   NSRect rect;
6562   NSPoint pt;
6563   struct window *win = XWINDOW (FRAME_SELECTED_WINDOW (emacsframe));
6565   NSTRACE ("[EmacsView firstRectForCharacterRange:]");
6567   if (NS_KEYLOG)
6568     NSLog (@"firstRectForCharRange request");
6570   rect.size.width = theRange.length * FRAME_COLUMN_WIDTH (emacsframe);
6571   rect.size.height = FRAME_LINE_HEIGHT (emacsframe);
6572   pt.x = WINDOW_TEXT_TO_FRAME_PIXEL_X (win, win->phys_cursor.x);
6573   pt.y = WINDOW_TO_FRAME_PIXEL_Y (win, win->phys_cursor.y
6574                                        +FRAME_LINE_HEIGHT (emacsframe));
6576   pt = [self convertPoint: pt toView: nil];
6578 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
6579 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6580   if ([[self window] respondsToSelector: @selector(convertRectToScreen:)])
6581     {
6582 #endif
6583       rect.origin = pt;
6584       rect = [(EmacsWindow *) [self window] convertRectToScreen: rect];
6585 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6586     }
6587   else
6588 #endif
6589 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
6590 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 \
6591   || defined (NS_IMPL_GNUSTEP)
6592     {
6593       pt = [[self window] convertBaseToScreen: pt];
6594       rect.origin = pt;
6595     }
6596 #endif
6598   return rect;
6602 - (NSInteger)conversationIdentifier
6604   return (NSInteger)self;
6608 - (void)doCommandBySelector: (SEL)aSelector
6610   NSTRACE ("[EmacsView doCommandBySelector:]");
6612   if (NS_KEYLOG)
6613     NSLog (@"doCommandBySelector: %@", NSStringFromSelector (aSelector));
6615   processingCompose = NO;
6616   if (aSelector == @selector (deleteBackward:))
6617     {
6618       /* Happens when user backspaces over an ongoing composition:
6619          throw a 'delete' into the event queue.  */
6620       if (!emacs_event)
6621         return;
6622       emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
6623       emacs_event->code = 0xFF08;
6624       EV_TRAILER ((id)nil);
6625     }
6628 - (NSArray *)validAttributesForMarkedText
6630   static NSArray *arr = nil;
6631   if (arr == nil) arr = [NSArray new];
6632  /* [[NSArray arrayWithObject: NSUnderlineStyleAttributeName] retain]; */
6633   return arr;
6636 - (NSRange)selectedRange
6638   if (NS_KEYLOG)
6639     NSLog (@"selectedRange request");
6640   return NSMakeRange (NSNotFound, 0);
6643 #if defined (NS_IMPL_COCOA) || GNUSTEP_GUI_MAJOR_VERSION > 0 || \
6644     GNUSTEP_GUI_MINOR_VERSION > 22
6645 - (NSUInteger)characterIndexForPoint: (NSPoint)thePoint
6646 #else
6647 - (unsigned int)characterIndexForPoint: (NSPoint)thePoint
6648 #endif
6650   if (NS_KEYLOG)
6651     NSLog (@"characterIndexForPoint request");
6652   return 0;
6655 - (NSAttributedString *)attributedSubstringFromRange: (NSRange)theRange
6657   static NSAttributedString *str = nil;
6658   if (str == nil) str = [NSAttributedString new];
6659   if (NS_KEYLOG)
6660     NSLog (@"attributedSubstringFromRange request");
6661   return str;
6664 /* End <NSTextInput> implementation.  */
6665 /*****************************************************************************/
6668 /* This is what happens when the user presses a mouse button.  */
6669 - (void)mouseDown: (NSEvent *)theEvent
6671   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6672   NSPoint p = [self convertPoint: [theEvent locationInWindow] fromView: nil];
6674   NSTRACE ("[EmacsView mouseDown:]");
6676   [self deleteWorkingText];
6678   if (!emacs_event)
6679     return;
6681   dpyinfo->last_mouse_frame = emacsframe;
6682   /* Appears to be needed to prevent spurious movement events generated on
6683      button clicks.  */
6684   emacsframe->mouse_moved = 0;
6686   if ([theEvent type] == NSEventTypeScrollWheel)
6687     {
6688 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
6689 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6690       if ([theEvent respondsToSelector:@selector(hasPreciseScrollingDeltas)])
6691         {
6692 #endif
6693           /* If the input device is a touchpad or similar, use precise
6694            * scrolling deltas.  These are measured in pixels, so we
6695            * have to add them up until they exceed one line height,
6696            * then we can send a scroll wheel event.
6697            *
6698            * If the device only has coarse scrolling deltas, like a
6699            * real mousewheel, the deltas represent a ratio of whole
6700            * lines, so round up the number of lines.  This means we
6701            * always send one scroll event per click, but can still
6702            * scroll more than one line if the OS tells us to.
6703            */
6704           bool horizontal;
6705           int lines = 0;
6706           int scrollUp = NO;
6708           /* FIXME: At the top or bottom of the buffer we should
6709            * ignore momentum-phase events.  */
6710           if (! ns_use_mwheel_momentum
6711               && [theEvent momentumPhase] != NSEventPhaseNone)
6712             return;
6714           if ([theEvent hasPreciseScrollingDeltas])
6715             {
6716               static int totalDeltaX, totalDeltaY;
6717               int lineHeight;
6719               if (NUMBERP (ns_mwheel_line_height))
6720                 lineHeight = XINT (ns_mwheel_line_height);
6721               else
6722                 {
6723                   /* FIXME: Use actual line height instead of the default.  */
6724                   lineHeight = default_line_pixel_height
6725                     (XWINDOW (FRAME_SELECTED_WINDOW (emacsframe)));
6726                 }
6728               if ([theEvent phase] == NSEventPhaseBegan)
6729                 {
6730                   totalDeltaX = 0;
6731                   totalDeltaY = 0;
6732                 }
6734               totalDeltaX += [theEvent scrollingDeltaX];
6735               totalDeltaY += [theEvent scrollingDeltaY];
6737               /* Calculate the number of lines, if any, to scroll, and
6738                * reset the total delta for the direction we're NOT
6739                * scrolling so that small movements don't add up.  */
6740               if (abs (totalDeltaX) > abs (totalDeltaY)
6741                   && abs (totalDeltaX) > lineHeight)
6742                 {
6743                   horizontal = YES;
6744                   scrollUp = totalDeltaX > 0;
6746                   lines = abs (totalDeltaX / lineHeight);
6747                   totalDeltaX = totalDeltaX % lineHeight;
6748                   totalDeltaY = 0;
6749                 }
6750               else if (abs (totalDeltaY) >= abs (totalDeltaX)
6751                        && abs (totalDeltaY) > lineHeight)
6752                 {
6753                   horizontal = NO;
6754                   scrollUp = totalDeltaY > 0;
6756                   lines = abs (totalDeltaY / lineHeight);
6757                   totalDeltaY = totalDeltaY % lineHeight;
6758                   totalDeltaX = 0;
6759                 }
6761               if (lines > 1 && ! ns_use_mwheel_acceleration)
6762                 lines = 1;
6763             }
6764           else
6765             {
6766               CGFloat delta;
6768               if ([theEvent scrollingDeltaY] == 0)
6769                 {
6770                   horizontal = YES;
6771                   delta = [theEvent scrollingDeltaX];
6772                 }
6773               else
6774                 {
6775                   horizontal = NO;
6776                   delta = [theEvent scrollingDeltaY];
6777                 }
6779               lines = (ns_use_mwheel_acceleration)
6780                 ? ceil (fabs (delta)) : 1;
6782               scrollUp = delta > 0;
6783             }
6785           if (lines == 0)
6786             return;
6788           emacs_event->kind = horizontal ? HORIZ_WHEEL_EVENT : WHEEL_EVENT;
6789           emacs_event->arg = (make_number (lines));
6791           emacs_event->code = 0;
6792           emacs_event->modifiers = EV_MODIFIERS (theEvent) |
6793             (scrollUp ? up_modifier : down_modifier);
6794 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6795         }
6796       else
6797 #endif
6798 #endif /* defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
6799 #if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6800         {
6801           CGFloat delta = [theEvent deltaY];
6802           /* Mac notebooks send wheel events with delta equal to 0
6803              when trackpad scrolling.  */
6804           if (delta == 0)
6805             {
6806               delta = [theEvent deltaX];
6807               if (delta == 0)
6808                 {
6809                   NSTRACE_MSG ("deltaIsZero");
6810                   return;
6811                 }
6812               emacs_event->kind = HORIZ_WHEEL_EVENT;
6813             }
6814           else
6815             emacs_event->kind = WHEEL_EVENT;
6817           emacs_event->code = 0;
6818           emacs_event->modifiers = EV_MODIFIERS (theEvent) |
6819             ((delta > 0) ? up_modifier : down_modifier);
6820         }
6821 #endif
6822     }
6823   else
6824     {
6825       emacs_event->kind = MOUSE_CLICK_EVENT;
6826       emacs_event->code = EV_BUTTON (theEvent);
6827       emacs_event->modifiers = EV_MODIFIERS (theEvent)
6828                              | EV_UDMODIFIERS (theEvent);
6829     }
6831   XSETINT (emacs_event->x, lrint (p.x));
6832   XSETINT (emacs_event->y, lrint (p.y));
6833   EV_TRAILER (theEvent);
6834   return;
6838 - (void)rightMouseDown: (NSEvent *)theEvent
6840   NSTRACE ("[EmacsView rightMouseDown:]");
6841   [self mouseDown: theEvent];
6845 - (void)otherMouseDown: (NSEvent *)theEvent
6847   NSTRACE ("[EmacsView otherMouseDown:]");
6848   [self mouseDown: theEvent];
6852 - (void)mouseUp: (NSEvent *)theEvent
6854   NSTRACE ("[EmacsView mouseUp:]");
6855   [self mouseDown: theEvent];
6859 - (void)rightMouseUp: (NSEvent *)theEvent
6861   NSTRACE ("[EmacsView rightMouseUp:]");
6862   [self mouseDown: theEvent];
6866 - (void)otherMouseUp: (NSEvent *)theEvent
6868   NSTRACE ("[EmacsView otherMouseUp:]");
6869   [self mouseDown: theEvent];
6873 - (void) scrollWheel: (NSEvent *)theEvent
6875   NSTRACE ("[EmacsView scrollWheel:]");
6876   [self mouseDown: theEvent];
6880 /* Tell emacs the mouse has moved.  */
6881 - (void)mouseMoved: (NSEvent *)e
6883   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
6884   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6885   Lisp_Object frame;
6886   NSPoint pt;
6888   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsView mouseMoved:]");
6890   dpyinfo->last_mouse_movement_time = EV_TIMESTAMP (e);
6891   pt = [self convertPoint: [e locationInWindow] fromView: nil];
6892   dpyinfo->last_mouse_motion_x = pt.x;
6893   dpyinfo->last_mouse_motion_y = pt.y;
6895   /* Update any mouse face.  */
6896   if (hlinfo->mouse_face_hidden)
6897     {
6898       hlinfo->mouse_face_hidden = 0;
6899       clear_mouse_face (hlinfo);
6900     }
6902   /* Tooltip handling.  */
6903   previous_help_echo_string = help_echo_string;
6904   help_echo_string = Qnil;
6906   if (!NILP (Vmouse_autoselect_window))
6907     {
6908       NSTRACE_MSG ("mouse_autoselect_window");
6909       static Lisp_Object last_mouse_window;
6910       Lisp_Object window
6911         = window_from_coordinates (emacsframe, pt.x, pt.y, 0, 0);
6913       if (WINDOWP (window)
6914           && !EQ (window, last_mouse_window)
6915           && !EQ (window, selected_window)
6916           && (!NILP (focus_follows_mouse)
6917               || (EQ (XWINDOW (window)->frame,
6918                       XWINDOW (selected_window)->frame))))
6919         {
6920           NSTRACE_MSG ("in_window");
6921           emacs_event->kind = SELECT_WINDOW_EVENT;
6922           emacs_event->frame_or_window = window;
6923           EV_TRAILER2 (e);
6924         }
6925       /* Remember the last window where we saw the mouse.  */
6926       last_mouse_window = window;
6927     }
6929   if (!note_mouse_movement (emacsframe, pt.x, pt.y))
6930     help_echo_string = previous_help_echo_string;
6932   XSETFRAME (frame, emacsframe);
6933   if (!NILP (help_echo_string) || !NILP (previous_help_echo_string))
6934     {
6935       /* NOTE: help_echo_{window,pos,object} are set in xdisp.c
6936          (note_mouse_highlight), which is called through the
6937          note_mouse_movement () call above.  */
6938       any_help_event_p = YES;
6939       gen_help_event (help_echo_string, frame, help_echo_window,
6940                       help_echo_object, help_echo_pos);
6941     }
6943   if (emacsframe->mouse_moved && send_appdefined)
6944     ns_send_appdefined (-1);
6948 - (void)mouseDragged: (NSEvent *)e
6950   NSTRACE ("[EmacsView mouseDragged:]");
6951   [self mouseMoved: e];
6955 - (void)rightMouseDragged: (NSEvent *)e
6957   NSTRACE ("[EmacsView rightMouseDragged:]");
6958   [self mouseMoved: e];
6962 - (void)otherMouseDragged: (NSEvent *)e
6964   NSTRACE ("[EmacsView otherMouseDragged:]");
6965   [self mouseMoved: e];
6969 - (BOOL)windowShouldClose: (id)sender
6971   NSEvent *e =[[self window] currentEvent];
6973   NSTRACE ("[EmacsView windowShouldClose:]");
6974   windowClosing = YES;
6975   if (!emacs_event)
6976     return NO;
6977   emacs_event->kind = DELETE_WINDOW_EVENT;
6978   emacs_event->modifiers = 0;
6979   emacs_event->code = 0;
6980   EV_TRAILER (e);
6981   /* Don't close this window, let this be done from lisp code.  */
6982   return NO;
6985 - (void) updateFrameSize: (BOOL) delay
6987   NSWindow *window = [self window];
6988   NSRect wr = [window frame];
6989   int extra = 0;
6990   int oldc = cols, oldr = rows;
6991   int oldw = FRAME_PIXEL_WIDTH (emacsframe);
6992   int oldh = FRAME_PIXEL_HEIGHT (emacsframe);
6993   int neww, newh;
6995   NSTRACE ("[EmacsView updateFrameSize:]");
6996   NSTRACE_SIZE ("Original size", NSMakeSize (oldw, oldh));
6997   NSTRACE_RECT ("Original frame", wr);
6998   NSTRACE_MSG  ("Original columns: %d", cols);
6999   NSTRACE_MSG  ("Original rows: %d", rows);
7001   if (! [self isFullscreen])
7002     {
7003       int toolbar_height;
7004 #ifdef NS_IMPL_GNUSTEP
7005       // GNUstep does not always update the tool bar height.  Force it.
7006       if (toolbar && [toolbar isVisible])
7007           update_frame_tool_bar (emacsframe);
7008 #endif
7010       toolbar_height = FRAME_TOOLBAR_HEIGHT (emacsframe);
7011       if (toolbar_height < 0)
7012         toolbar_height = 35;
7014       extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
7015         + toolbar_height;
7016     }
7018   if (wait_for_tool_bar)
7019     {
7020       /* The toolbar height is always 0 in fullscreen and undecorated
7021          frames, so don't wait for it to become available.  */
7022       if (FRAME_TOOLBAR_HEIGHT (emacsframe) == 0
7023           && FRAME_UNDECORATED (emacsframe) == false
7024           && ! [self isFullscreen])
7025         {
7026           NSTRACE_MSG ("Waiting for toolbar");
7027           return;
7028         }
7029       wait_for_tool_bar = NO;
7030     }
7032   neww = (int)wr.size.width - emacsframe->border_width;
7033   newh = (int)wr.size.height - extra;
7035   NSTRACE_SIZE ("New size", NSMakeSize (neww, newh));
7036   NSTRACE_MSG ("FRAME_TOOLBAR_HEIGHT: %d", FRAME_TOOLBAR_HEIGHT (emacsframe));
7037   NSTRACE_MSG ("FRAME_NS_TITLEBAR_HEIGHT: %d", FRAME_NS_TITLEBAR_HEIGHT (emacsframe));
7039   cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, neww);
7040   rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe, newh);
7042   if (cols < MINWIDTH)
7043     cols = MINWIDTH;
7045   if (rows < MINHEIGHT)
7046     rows = MINHEIGHT;
7048   NSTRACE_MSG ("New columns: %d", cols);
7049   NSTRACE_MSG ("New rows: %d", rows);
7051   if (oldr != rows || oldc != cols || neww != oldw || newh != oldh)
7052     {
7053       NSView *view = FRAME_NS_VIEW (emacsframe);
7055       change_frame_size (emacsframe,
7056                          FRAME_PIXEL_TO_TEXT_WIDTH (emacsframe, neww),
7057                          FRAME_PIXEL_TO_TEXT_HEIGHT (emacsframe, newh),
7058                          0, delay, 0, 1);
7059       SET_FRAME_GARBAGED (emacsframe);
7060       cancel_mouse_face (emacsframe);
7062       /* The next two lines set the frame to the same size as we've
7063          already set above.  We need to do this when we switch back
7064          from non-native fullscreen, in other circumstances it appears
7065          to be a noop.  (bug#28872) */
7066       wr = NSMakeRect (0, 0, neww, newh);
7067       [view setFrame: wr];
7069       // To do: consider using [NSNotificationCenter postNotificationName:].
7070       [self windowDidMove: // Update top/left.
7071               [NSNotification notificationWithName:NSWindowDidMoveNotification
7072                                             object:[view window]]];
7073     }
7074   else
7075     {
7076       NSTRACE_MSG ("No change");
7077     }
7080 - (NSSize)windowWillResize: (NSWindow *)sender toSize: (NSSize)frameSize
7081 /* Normalize frame to gridded text size.  */
7083   int extra = 0;
7085   NSTRACE ("[EmacsView windowWillResize:toSize: " NSTRACE_FMT_SIZE "]",
7086            NSTRACE_ARG_SIZE (frameSize));
7087   NSTRACE_RECT   ("[sender frame]", [sender frame]);
7088   NSTRACE_FSTYPE ("fs_state", fs_state);
7090   if (!FRAME_LIVE_P (emacsframe))
7091     return frameSize;
7093   if (fs_state == FULLSCREEN_MAXIMIZED
7094       && (maximized_width != (int)frameSize.width
7095           || maximized_height != (int)frameSize.height))
7096     [self setFSValue: FULLSCREEN_NONE];
7097   else if (fs_state == FULLSCREEN_WIDTH
7098            && maximized_width != (int)frameSize.width)
7099     [self setFSValue: FULLSCREEN_NONE];
7100   else if (fs_state == FULLSCREEN_HEIGHT
7101            && maximized_height != (int)frameSize.height)
7102     [self setFSValue: FULLSCREEN_NONE];
7104   if (fs_state == FULLSCREEN_NONE)
7105     maximized_width = maximized_height = -1;
7107   if (! [self isFullscreen])
7108     {
7109       extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
7110         + FRAME_TOOLBAR_HEIGHT (emacsframe);
7111     }
7113   cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, frameSize.width);
7114   if (cols < MINWIDTH)
7115     cols = MINWIDTH;
7117   rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe,
7118                                            frameSize.height - extra);
7119   if (rows < MINHEIGHT)
7120     rows = MINHEIGHT;
7121 #ifdef NS_IMPL_COCOA
7122   {
7123     /* This sets window title to have size in it; the wm does this under GS.  */
7124     NSRect r = [[self window] frame];
7125     if (r.size.height == frameSize.height && r.size.width == frameSize.width)
7126       {
7127         if (old_title != 0)
7128           {
7129             xfree (old_title);
7130             old_title = 0;
7131           }
7132       }
7133     else if (fs_state == FULLSCREEN_NONE && ! maximizing_resize
7134              && [[self window] title] != NULL)
7135       {
7136         char *size_title;
7137         NSWindow *window = [self window];
7138         if (old_title == 0)
7139           {
7140             char *t = strdup ([[[self window] title] UTF8String]);
7141             char *pos = strstr (t, "  â€”  ");
7142             if (pos)
7143               *pos = '\0';
7144             old_title = t;
7145           }
7146         size_title = xmalloc (strlen (old_title) + 40);
7147         esprintf (size_title, "%s  â€”  (%d x %d)", old_title, cols, rows);
7148         [window setTitle: [NSString stringWithUTF8String: size_title]];
7149         [window display];
7150         xfree (size_title);
7151       }
7152   }
7153 #endif /* NS_IMPL_COCOA */
7155   NSTRACE_MSG ("cols: %d  rows: %d", cols, rows);
7157   /* Restrict the new size to the text grid.
7159      Don't restrict the width if the user only adjusted the height, and
7160      vice versa.  (Without this, the frame would shrink, and move
7161      slightly, if the window was resized by dragging one of its
7162      borders.)  */
7163   if (!frame_resize_pixelwise)
7164     {
7165       NSRect r = [[self window] frame];
7167       if (r.size.width != frameSize.width)
7168         {
7169           frameSize.width =
7170             FRAME_TEXT_COLS_TO_PIXEL_WIDTH  (emacsframe, cols);
7171         }
7173       if (r.size.height != frameSize.height)
7174         {
7175           frameSize.height =
7176             FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (emacsframe, rows) + extra;
7177         }
7178     }
7180   NSTRACE_RETURN_SIZE (frameSize);
7182   return frameSize;
7186 - (void)windowDidResize: (NSNotification *)notification
7188   NSTRACE ("[EmacsView windowDidResize:]");
7189   if (!FRAME_LIVE_P (emacsframe))
7190     {
7191       NSTRACE_MSG ("Ignored (frame dead)");
7192       return;
7193     }
7194   if (emacsframe->output_data.ns->in_animation)
7195     {
7196       NSTRACE_MSG ("Ignored (in animation)");
7197       return;
7198     }
7200   if (! [self fsIsNative])
7201     {
7202       NSWindow *theWindow = [notification object];
7203       /* We can get notification on the non-FS window when in
7204          fullscreen mode.  */
7205       if ([self window] != theWindow) return;
7206     }
7208   NSTRACE_RECT ("frame", [[notification object] frame]);
7210 #ifdef NS_IMPL_GNUSTEP
7211   NSWindow *theWindow = [notification object];
7213    /* In GNUstep, at least currently, it's possible to get a didResize
7214       without getting a willResize, therefore we need to act as if we got
7215       the willResize now.  */
7216   NSSize sz = [theWindow frame].size;
7217   sz = [self windowWillResize: theWindow toSize: sz];
7218 #endif /* NS_IMPL_GNUSTEP */
7220   if (cols > 0 && rows > 0)
7221     {
7222       [self updateFrameSize: YES];
7223     }
7225   ns_send_appdefined (-1);
7228 #ifdef NS_IMPL_COCOA
7229 - (void)viewDidEndLiveResize
7231   NSTRACE ("[EmacsView viewDidEndLiveResize]");
7233   [super viewDidEndLiveResize];
7234   if (old_title != 0)
7235     {
7236       [[self window] setTitle: [NSString stringWithUTF8String: old_title]];
7237       xfree (old_title);
7238       old_title = 0;
7239     }
7240   maximizing_resize = NO;
7242 #endif /* NS_IMPL_COCOA */
7245 - (void)windowDidBecomeKey: (NSNotification *)notification
7246 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
7248   [self windowDidBecomeKey];
7252 - (void)windowDidBecomeKey      /* for direct calls */
7254   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
7255   struct frame *old_focus = dpyinfo->x_focus_frame;
7257   NSTRACE ("[EmacsView windowDidBecomeKey]");
7259   if (emacsframe != old_focus)
7260     dpyinfo->x_focus_frame = emacsframe;
7262   ns_frame_rehighlight (emacsframe);
7264   if (emacs_event)
7265     {
7266       emacs_event->kind = FOCUS_IN_EVENT;
7267       EV_TRAILER ((id)nil);
7268     }
7272 - (void)windowDidResignKey: (NSNotification *)notification
7273 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
7275   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
7276   BOOL is_focus_frame = dpyinfo->x_focus_frame == emacsframe;
7277   NSTRACE ("[EmacsView windowDidResignKey:]");
7279   if (is_focus_frame)
7280     dpyinfo->x_focus_frame = 0;
7282   emacsframe->mouse_moved = 0;
7283   ns_frame_rehighlight (emacsframe);
7285   /* FIXME: for some reason needed on second and subsequent clicks away
7286             from sole-frame Emacs to get hollow box to show.  */
7287   if (!windowClosing && [[self window] isVisible] == YES)
7288     {
7289       x_update_cursor (emacsframe, 1);
7290       x_set_frame_alpha (emacsframe);
7291     }
7293   if (any_help_event_p)
7294     {
7295       Lisp_Object frame;
7296       XSETFRAME (frame, emacsframe);
7297       help_echo_string = Qnil;
7298       gen_help_event (Qnil, frame, Qnil, Qnil, 0);
7299     }
7301   if (emacs_event && is_focus_frame)
7302     {
7303       [self deleteWorkingText];
7304       emacs_event->kind = FOCUS_OUT_EVENT;
7305       EV_TRAILER ((id)nil);
7306     }
7310 - (void)windowWillMiniaturize: sender
7312   NSTRACE ("[EmacsView windowWillMiniaturize:]");
7316 - (void)setFrame:(NSRect)frameRect
7318   NSTRACE ("[EmacsView setFrame:" NSTRACE_FMT_RECT "]",
7319            NSTRACE_ARG_RECT (frameRect));
7321   [super setFrame:(NSRect)frameRect];
7325 - (BOOL)isFlipped
7327   return YES;
7331 - (BOOL)isOpaque
7333   return NO;
7337 - (void)createToolbar: (struct frame *)f
7339   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
7340   NSWindow *window = [view window];
7342   toolbar = [[EmacsToolbar alloc] initForView: self withIdentifier:
7343                    [NSString stringWithFormat: @"Emacs Frame %d",
7344                              ns_window_num]];
7345   [toolbar setVisible: NO];
7346   [window setToolbar: toolbar];
7348   /* Don't set frame garbaged until tool bar is up to date?
7349      This avoids an extra clear and redraw (flicker) at frame creation.  */
7350   if (FRAME_EXTERNAL_TOOL_BAR (f)) wait_for_tool_bar = YES;
7351   else wait_for_tool_bar = NO;
7354 #ifdef NS_IMPL_COCOA
7355   {
7356     NSButton *toggleButton;
7357     toggleButton = [window standardWindowButton: NSWindowToolbarButton];
7358     [toggleButton setTarget: self];
7359     [toggleButton setAction: @selector (toggleToolbar: )];
7360   }
7361 #endif
7365 - (instancetype) initFrameFromEmacs: (struct frame *)f
7367   NSRect r, wr;
7368   Lisp_Object tem;
7369   NSWindow *win;
7370   NSColor *col;
7371   NSString *name;
7373   NSTRACE ("[EmacsView initFrameFromEmacs:]");
7374   NSTRACE_MSG ("cols:%d lines:%d", f->text_cols, f->text_lines);
7376   windowClosing = NO;
7377   processingCompose = NO;
7378   scrollbarsNeedingUpdate = 0;
7379   fs_state = FULLSCREEN_NONE;
7380   fs_before_fs = next_maximized = -1;
7382   fs_is_native = NO;
7383 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7384 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7385   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7386 #endif
7387     fs_is_native = ns_use_native_fullscreen;
7388 #endif
7390   maximized_width = maximized_height = -1;
7391   nonfs_window = nil;
7393   ns_userRect = NSMakeRect (0, 0, 0, 0);
7394   r = NSMakeRect (0, 0, FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, f->text_cols),
7395                  FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, f->text_lines));
7396   [self initWithFrame: r];
7397   [self setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
7399   FRAME_NS_VIEW (f) = self;
7400   emacsframe = f;
7401 #ifdef NS_IMPL_COCOA
7402   old_title = 0;
7403   maximizing_resize = NO;
7404 #endif
7406   win = [[EmacsWindow alloc]
7407             initWithContentRect: r
7408                       styleMask: (FRAME_UNDECORATED (f)
7409                                   ? FRAME_UNDECORATED_FLAGS
7410                                   : FRAME_DECORATED_FLAGS)
7411                         backing: NSBackingStoreBuffered
7412                           defer: YES];
7414 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7415 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7416   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7417 #endif
7418     [win setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
7419 #endif
7421   wr = [win frame];
7422   bwidth = f->border_width = wr.size.width - r.size.width;
7424   [win setAcceptsMouseMovedEvents: YES];
7425   [win setDelegate: self];
7426 #if !defined (NS_IMPL_COCOA) || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7427 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7428   if ([win respondsToSelector: @selector(useOptimizedDrawing:)])
7429 #endif
7430     [win useOptimizedDrawing: YES];
7431 #endif
7433   [[win contentView] addSubview: self];
7435   if (ns_drag_types)
7436     [self registerForDraggedTypes: ns_drag_types];
7438   tem = f->name;
7439   name = [NSString stringWithUTF8String:
7440                    NILP (tem) ? "Emacs" : SSDATA (tem)];
7441   [win setTitle: name];
7443   /* toolbar support */
7444   if (! FRAME_UNDECORATED (f))
7445     [self createToolbar: f];
7447 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
7448 #ifndef NSAppKitVersionNumber10_10
7449 #define NSAppKitVersionNumber10_10 1343
7450 #endif
7452   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_10
7453       && FRAME_NS_APPEARANCE (f) != ns_appearance_aqua)
7454     win.appearance = [NSAppearance
7455                           appearanceNamed: NSAppearanceNameVibrantDark];
7456 #endif
7458 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
7459   if ([win respondsToSelector: @selector(titlebarAppearsTransparent)])
7460     win.titlebarAppearsTransparent = FRAME_NS_TRANSPARENT_TITLEBAR (f);
7461 #endif
7463   tem = f->icon_name;
7464   if (!NILP (tem))
7465     [win setMiniwindowTitle:
7466            [NSString stringWithUTF8String: SSDATA (tem)]];
7468   if (FRAME_PARENT_FRAME (f) != NULL)
7469     {
7470       NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
7471       [parent addChildWindow: win
7472                      ordered: NSWindowAbove];
7473     }
7475   if (FRAME_Z_GROUP (f) != z_group_none)
7476       win.level = NSNormalWindowLevel
7477         + (FRAME_Z_GROUP_BELOW (f) ? -1 : 1);
7479   {
7480     NSScreen *screen = [win screen];
7482     if (screen != 0)
7483       {
7484         NSPoint pt = NSMakePoint
7485           (IN_BOUND (-SCREENMAX, f->left_pos
7486                      + NS_PARENT_WINDOW_LEFT_POS (f), SCREENMAX),
7487            IN_BOUND (-SCREENMAX,
7488                      NS_PARENT_WINDOW_TOP_POS (f) - f->top_pos,
7489                      SCREENMAX));
7491         [win setFrameTopLeftPoint: pt];
7493         NSTRACE_RECT ("new frame", [win frame]);
7494       }
7495   }
7497   [win makeFirstResponder: self];
7499   col = ns_lookup_indexed_color (NS_FACE_BACKGROUND
7500                                  (FACE_FROM_ID (emacsframe, DEFAULT_FACE_ID)),
7501                                  emacsframe);
7502   [win setBackgroundColor: col];
7503   if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7504     [win setOpaque: NO];
7506 #if !defined (NS_IMPL_COCOA) \
7507   || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7508 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7509   if ([self respondsToSelector: @selector(allocateGState)])
7510 #endif
7511     [self allocateGState];
7512 #endif
7513   [NSApp registerServicesMenuSendTypes: ns_send_types
7514                            returnTypes: [NSArray array]];
7516   /* macOS Sierra automatically enables tabbed windows.  We can't
7517      allow this to be enabled until it's available on a Free system.
7518      Currently it only happens by accident and is buggy anyway.  */
7519 #if defined (NS_IMPL_COCOA) \
7520   && MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
7521 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101200
7522   if ([win respondsToSelector: @selector(setTabbingMode:)])
7523 #endif
7524     [win setTabbingMode: NSWindowTabbingModeDisallowed];
7525 #endif
7527   ns_window_num++;
7528   return self;
7532 - (void)windowDidMove: sender
7534   NSWindow *win = [self window];
7535   NSRect r = [win frame];
7536   NSArray *screens = [NSScreen screens];
7537   NSScreen *screen = [screens objectAtIndex: 0];
7539   NSTRACE ("[EmacsView windowDidMove:]");
7541   if (!emacsframe->output_data.ns)
7542     return;
7543   if (screen != nil)
7544     {
7545       emacsframe->left_pos = r.origin.x - NS_PARENT_WINDOW_LEFT_POS (emacsframe);
7546       emacsframe->top_pos =
7547         NS_PARENT_WINDOW_TOP_POS (emacsframe) - (r.origin.y + r.size.height);
7549       if (emacs_event)
7550         {
7551           emacs_event->kind = MOVE_FRAME_EVENT;
7552           EV_TRAILER ((id)nil);
7553         }
7554     }
7558 /* Called AFTER method below, but before our windowWillResize call there leads
7559    to windowDidResize -> x_set_window_size.  Update emacs' notion of frame
7560    location so set_window_size moves the frame.  */
7561 - (BOOL)windowShouldZoom: (NSWindow *)sender toFrame: (NSRect)newFrame
7563   NSTRACE (("[EmacsView windowShouldZoom:toFrame:" NSTRACE_FMT_RECT "]"
7564             NSTRACE_FMT_RETURN "YES"),
7565            NSTRACE_ARG_RECT (newFrame));
7567   emacsframe->output_data.ns->zooming = 1;
7568   return YES;
7572 /* Override to do something slightly nonstandard, but nice.  First click on
7573    zoom button will zoom vertically.  Second will zoom completely.  Third
7574    returns to original.  */
7575 - (NSRect)windowWillUseStandardFrame:(NSWindow *)sender
7576                         defaultFrame:(NSRect)defaultFrame
7578   // TODO: Rename to "currentFrame" and assign "result" properly in
7579   // all paths.
7580   NSRect result = [sender frame];
7582   NSTRACE (("[EmacsView windowWillUseStandardFrame:defaultFrame:"
7583             NSTRACE_FMT_RECT "]"),
7584            NSTRACE_ARG_RECT (defaultFrame));
7585   NSTRACE_FSTYPE ("fs_state", fs_state);
7586   NSTRACE_FSTYPE ("fs_before_fs", fs_before_fs);
7587   NSTRACE_FSTYPE ("next_maximized", next_maximized);
7588   NSTRACE_RECT   ("ns_userRect", ns_userRect);
7589   NSTRACE_RECT   ("[sender frame]", [sender frame]);
7591   if (fs_before_fs != -1) /* Entering fullscreen */
7592     {
7593       NSTRACE_MSG ("Entering fullscreen");
7594       result = defaultFrame;
7595     }
7596   else
7597     {
7598       // Save the window size and position (frame) before the resize.
7599       if (fs_state != FULLSCREEN_MAXIMIZED
7600           && fs_state != FULLSCREEN_WIDTH)
7601         {
7602           ns_userRect.size.width = result.size.width;
7603           ns_userRect.origin.x   = result.origin.x;
7604         }
7606       if (fs_state != FULLSCREEN_MAXIMIZED
7607           && fs_state != FULLSCREEN_HEIGHT)
7608         {
7609           ns_userRect.size.height = result.size.height;
7610           ns_userRect.origin.y    = result.origin.y;
7611         }
7613       NSTRACE_RECT ("ns_userRect (2)", ns_userRect);
7615       if (next_maximized == FULLSCREEN_HEIGHT
7616           || (next_maximized == -1
7617               && abs ((int)(defaultFrame.size.height - result.size.height))
7618               > FRAME_LINE_HEIGHT (emacsframe)))
7619         {
7620           /* first click */
7621           NSTRACE_MSG ("FULLSCREEN_HEIGHT");
7622           maximized_height = result.size.height = defaultFrame.size.height;
7623           maximized_width = -1;
7624           result.origin.y = defaultFrame.origin.y;
7625           if (ns_userRect.size.height != 0)
7626             {
7627               result.origin.x = ns_userRect.origin.x;
7628               result.size.width = ns_userRect.size.width;
7629             }
7630           [self setFSValue: FULLSCREEN_HEIGHT];
7631 #ifdef NS_IMPL_COCOA
7632           maximizing_resize = YES;
7633 #endif
7634         }
7635       else if (next_maximized == FULLSCREEN_WIDTH)
7636         {
7637           NSTRACE_MSG ("FULLSCREEN_WIDTH");
7638           maximized_width = result.size.width = defaultFrame.size.width;
7639           maximized_height = -1;
7640           result.origin.x = defaultFrame.origin.x;
7641           if (ns_userRect.size.width != 0)
7642             {
7643               result.origin.y = ns_userRect.origin.y;
7644               result.size.height = ns_userRect.size.height;
7645             }
7646           [self setFSValue: FULLSCREEN_WIDTH];
7647         }
7648       else if (next_maximized == FULLSCREEN_MAXIMIZED
7649                || (next_maximized == -1
7650                    && abs ((int)(defaultFrame.size.width - result.size.width))
7651                    > FRAME_COLUMN_WIDTH (emacsframe)))
7652         {
7653           NSTRACE_MSG ("FULLSCREEN_MAXIMIZED");
7655           result = defaultFrame; /* second click */
7656           maximized_width = result.size.width;
7657           maximized_height = result.size.height;
7658           [self setFSValue: FULLSCREEN_MAXIMIZED];
7659 #ifdef NS_IMPL_COCOA
7660           maximizing_resize = YES;
7661 #endif
7662         }
7663       else
7664         {
7665           /* restore */
7666           NSTRACE_MSG ("Restore");
7667           result = ns_userRect.size.height ? ns_userRect : result;
7668           NSTRACE_RECT ("restore (2)", result);
7669           ns_userRect = NSMakeRect (0, 0, 0, 0);
7670 #ifdef NS_IMPL_COCOA
7671           maximizing_resize = fs_state != FULLSCREEN_NONE;
7672 #endif
7673           [self setFSValue: FULLSCREEN_NONE];
7674           maximized_width = maximized_height = -1;
7675         }
7676     }
7678   if (fs_before_fs == -1) next_maximized = -1;
7680   NSTRACE_RECT   ("Final ns_userRect", ns_userRect);
7681   NSTRACE_MSG    ("Final maximized_width: %d", maximized_width);
7682   NSTRACE_MSG    ("Final maximized_height: %d", maximized_height);
7683   NSTRACE_FSTYPE ("Final next_maximized", next_maximized);
7685   [self windowWillResize: sender toSize: result.size];
7687   NSTRACE_RETURN_RECT (result);
7689   return result;
7693 - (void)windowDidDeminiaturize: sender
7695   NSTRACE ("[EmacsView windowDidDeminiaturize:]");
7696   if (!emacsframe->output_data.ns)
7697     return;
7699   SET_FRAME_ICONIFIED (emacsframe, 0);
7700   SET_FRAME_VISIBLE (emacsframe, 1);
7701   windows_or_buffers_changed = 63;
7703   if (emacs_event)
7704     {
7705       emacs_event->kind = DEICONIFY_EVENT;
7706       EV_TRAILER ((id)nil);
7707     }
7711 - (void)windowDidExpose: sender
7713   NSTRACE ("[EmacsView windowDidExpose:]");
7714   if (!emacsframe->output_data.ns)
7715     return;
7717   SET_FRAME_VISIBLE (emacsframe, 1);
7718   SET_FRAME_GARBAGED (emacsframe);
7720   if (send_appdefined)
7721     ns_send_appdefined (-1);
7725 - (void)windowDidMiniaturize: sender
7727   NSTRACE ("[EmacsView windowDidMiniaturize:]");
7728   if (!emacsframe->output_data.ns)
7729     return;
7731   SET_FRAME_ICONIFIED (emacsframe, 1);
7732   SET_FRAME_VISIBLE (emacsframe, 0);
7734   if (emacs_event)
7735     {
7736       emacs_event->kind = ICONIFY_EVENT;
7737       EV_TRAILER ((id)nil);
7738     }
7741 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7742 - (NSApplicationPresentationOptions)window:(NSWindow *)window
7743       willUseFullScreenPresentationOptions:
7744   (NSApplicationPresentationOptions)proposedOptions
7746   return proposedOptions|NSApplicationPresentationAutoHideToolbar;
7748 #endif
7750 - (void)windowWillEnterFullScreen:(NSNotification *)notification
7752   NSTRACE ("[EmacsView windowWillEnterFullScreen:]");
7753   [self windowWillEnterFullScreen];
7755 - (void)windowWillEnterFullScreen /* provided for direct calls */
7757   NSTRACE ("[EmacsView windowWillEnterFullScreen]");
7758   fs_before_fs = fs_state;
7761 - (void)windowDidEnterFullScreen:(NSNotification *)notification
7763   NSTRACE ("[EmacsView windowDidEnterFullScreen:]");
7764   [self windowDidEnterFullScreen];
7767 - (void)windowDidEnterFullScreen /* provided for direct calls */
7769   NSTRACE ("[EmacsView windowDidEnterFullScreen]");
7770   [self setFSValue: FULLSCREEN_BOTH];
7771   if (! [self fsIsNative])
7772     {
7773       [self windowDidBecomeKey];
7774       [nonfs_window orderOut:self];
7775     }
7776   else
7777     {
7778       BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (emacsframe) ? YES : NO;
7779 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 \
7780   && MAC_OS_X_VERSION_MIN_REQUIRED <= 1070
7781       unsigned val = (unsigned)[NSApp presentationOptions];
7783       // Mac OS X 10.7 bug fix, the menu won't appear without this.
7784       // val is non-zero on other macOS versions.
7785       if (val == 0)
7786         {
7787           NSApplicationPresentationOptions options
7788             = NSApplicationPresentationAutoHideDock
7789             | NSApplicationPresentationAutoHideMenuBar
7790             | NSApplicationPresentationFullScreen
7791             | NSApplicationPresentationAutoHideToolbar;
7793           [NSApp setPresentationOptions: options];
7794         }
7795 #endif
7796       [toolbar setVisible:tbar_visible];
7797     }
7800 - (void)windowWillExitFullScreen:(NSNotification *)notification
7802   NSTRACE ("[EmacsView windowWillExitFullScreen:]");
7803   [self windowWillExitFullScreen];
7806 - (void)windowWillExitFullScreen /* provided for direct calls */
7808   NSTRACE ("[EmacsView windowWillExitFullScreen]");
7809   if (!FRAME_LIVE_P (emacsframe))
7810     {
7811       NSTRACE_MSG ("Ignored (frame dead)");
7812       return;
7813     }
7814   if (next_maximized != -1)
7815     fs_before_fs = next_maximized;
7818 - (void)windowDidExitFullScreen:(NSNotification *)notification
7820   NSTRACE ("[EmacsView windowDidExitFullScreen:]");
7821   [self windowDidExitFullScreen];
7824 - (void)windowDidExitFullScreen /* provided for direct calls */
7826   NSTRACE ("[EmacsView windowDidExitFullScreen]");
7827   if (!FRAME_LIVE_P (emacsframe))
7828     {
7829       NSTRACE_MSG ("Ignored (frame dead)");
7830       return;
7831     }
7832   [self setFSValue: fs_before_fs];
7833   fs_before_fs = -1;
7834 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7835   [self updateCollectionBehavior];
7836 #endif
7837   if (FRAME_EXTERNAL_TOOL_BAR (emacsframe))
7838     {
7839       [toolbar setVisible:YES];
7840       update_frame_tool_bar (emacsframe);
7841       [self updateFrameSize:YES];
7842       [[self window] display];
7843     }
7844   else
7845     [toolbar setVisible:NO];
7847   if (next_maximized != -1)
7848     [[self window] performZoom:self];
7851 - (BOOL)fsIsNative
7853   return fs_is_native;
7856 - (BOOL)isFullscreen
7858   BOOL res;
7860   if (! fs_is_native)
7861     {
7862       res = (nonfs_window != nil);
7863     }
7864   else
7865     {
7866 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7867       res = (([[self window] styleMask] & NSWindowStyleMaskFullScreen) != 0);
7868 #else
7869       res = NO;
7870 #endif
7871     }
7873   NSTRACE ("[EmacsView isFullscreen] " NSTRACE_FMT_RETURN " %d",
7874            (int) res);
7876   return res;
7879 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7880 - (void)updateCollectionBehavior
7882   NSTRACE ("[EmacsView updateCollectionBehavior]");
7884   if (! [self isFullscreen])
7885     {
7886       NSWindow *win = [self window];
7887       NSWindowCollectionBehavior b = [win collectionBehavior];
7888       if (ns_use_native_fullscreen)
7889         b |= NSWindowCollectionBehaviorFullScreenPrimary;
7890       else
7891         b &= ~NSWindowCollectionBehaviorFullScreenPrimary;
7893       [win setCollectionBehavior: b];
7894 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7895       if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7896 #endif
7897         fs_is_native = ns_use_native_fullscreen;
7898     }
7900 #endif
7902 - (void)toggleFullScreen: (id)sender
7904   NSWindow *w, *fw;
7905   BOOL onFirstScreen;
7906   struct frame *f;
7907   NSRect r, wr;
7908   NSColor *col;
7910   NSTRACE ("[EmacsView toggleFullScreen:]");
7912   if (fs_is_native)
7913     {
7914 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7915 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7916       if ([[self window] respondsToSelector: @selector(toggleFullScreen:)])
7917 #endif
7918         [[self window] toggleFullScreen:sender];
7919 #endif
7920       return;
7921     }
7923   w = [self window];
7924   onFirstScreen = [[w screen] isEqual:[[NSScreen screens] objectAtIndex:0]];
7925   f = emacsframe;
7926   wr = [w frame];
7927   col = ns_lookup_indexed_color (NS_FACE_BACKGROUND
7928                                  (FACE_FROM_ID (f, DEFAULT_FACE_ID)),
7929                                  f);
7931   if (fs_state != FULLSCREEN_BOTH)
7932     {
7933       NSScreen *screen = [w screen];
7935 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
7936       /* Hide ghost menu bar on secondary monitor?  */
7937       if (! onFirstScreen
7938 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
7939           && [NSScreen respondsToSelector: @selector(screensHaveSeparateSpaces)]
7940 #endif
7941           )
7942         onFirstScreen = [NSScreen screensHaveSeparateSpaces];
7943 #endif
7944       /* Hide dock and menubar if we are on the primary screen.  */
7945       if (onFirstScreen)
7946         {
7947 #ifdef NS_IMPL_COCOA
7948           NSApplicationPresentationOptions options
7949             = NSApplicationPresentationAutoHideDock
7950             | NSApplicationPresentationAutoHideMenuBar;
7952           [NSApp setPresentationOptions: options];
7953 #else
7954           [NSMenu setMenuBarVisible:NO];
7955 #endif
7956         }
7958       fw = [[EmacsFSWindow alloc]
7959                        initWithContentRect:[w contentRectForFrameRect:wr]
7960                                  styleMask:NSWindowStyleMaskBorderless
7961                                    backing:NSBackingStoreBuffered
7962                                      defer:YES
7963                                     screen:screen];
7965       [fw setContentView:[w contentView]];
7966       [fw setTitle:[w title]];
7967       [fw setDelegate:self];
7968       [fw setAcceptsMouseMovedEvents: YES];
7969 #if !defined (NS_IMPL_COCOA) \
7970   || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7971 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7972       if ([fw respondsToSelector: @selector(useOptimizedDrawing:)])
7973 #endif
7974         [fw useOptimizedDrawing: YES];
7975 #endif
7976       [fw setBackgroundColor: col];
7977       if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7978         [fw setOpaque: NO];
7980       f->border_width = 0;
7982       nonfs_window = w;
7984       [self windowWillEnterFullScreen];
7985       [fw makeKeyAndOrderFront:NSApp];
7986       [fw makeFirstResponder:self];
7987       [w orderOut:self];
7988       r = [fw frameRectForContentRect:[screen frame]];
7989       [fw setFrame: r display:YES animate:ns_use_fullscreen_animation];
7990       [self windowDidEnterFullScreen];
7991       [fw display];
7992     }
7993   else
7994     {
7995       fw = w;
7996       w = nonfs_window;
7997       nonfs_window = nil;
7999       if (onFirstScreen)
8000         {
8001 #ifdef NS_IMPL_COCOA
8002           [NSApp setPresentationOptions: NSApplicationPresentationDefault];
8003 #else
8004           [NSMenu setMenuBarVisible:YES];
8005 #endif
8006         }
8008       [w setContentView:[fw contentView]];
8009       [w setBackgroundColor: col];
8010       if ([col alphaComponent] != (EmacsCGFloat) 1.0)
8011         [w setOpaque: NO];
8013       f->border_width = bwidth;
8015       // To do: consider using [NSNotificationCenter postNotificationName:] to
8016       // send notifications.
8018       [self windowWillExitFullScreen];
8019       [fw setFrame: [w frame] display:YES animate:ns_use_fullscreen_animation];
8020       [fw close];
8021       [w makeKeyAndOrderFront:NSApp];
8022       [self windowDidExitFullScreen];
8023       [self updateFrameSize:YES];
8024     }
8027 - (void)handleFS
8029   NSTRACE ("[EmacsView handleFS]");
8031   if (fs_state != emacsframe->want_fullscreen)
8032     {
8033       if (fs_state == FULLSCREEN_BOTH)
8034         {
8035           NSTRACE_MSG ("fs_state == FULLSCREEN_BOTH");
8036           [self toggleFullScreen:self];
8037         }
8039       switch (emacsframe->want_fullscreen)
8040         {
8041         case FULLSCREEN_BOTH:
8042           NSTRACE_MSG ("FULLSCREEN_BOTH");
8043           [self toggleFullScreen:self];
8044           break;
8045         case FULLSCREEN_WIDTH:
8046           NSTRACE_MSG ("FULLSCREEN_WIDTH");
8047           next_maximized = FULLSCREEN_WIDTH;
8048           if (fs_state != FULLSCREEN_BOTH)
8049             [[self window] performZoom:self];
8050           break;
8051         case FULLSCREEN_HEIGHT:
8052           NSTRACE_MSG ("FULLSCREEN_HEIGHT");
8053           next_maximized = FULLSCREEN_HEIGHT;
8054           if (fs_state != FULLSCREEN_BOTH)
8055             [[self window] performZoom:self];
8056           break;
8057         case FULLSCREEN_MAXIMIZED:
8058           NSTRACE_MSG ("FULLSCREEN_MAXIMIZED");
8059           next_maximized = FULLSCREEN_MAXIMIZED;
8060           if (fs_state != FULLSCREEN_BOTH)
8061             [[self window] performZoom:self];
8062           break;
8063         case FULLSCREEN_NONE:
8064           NSTRACE_MSG ("FULLSCREEN_NONE");
8065           if (fs_state != FULLSCREEN_BOTH)
8066             {
8067               next_maximized = FULLSCREEN_NONE;
8068               [[self window] performZoom:self];
8069             }
8070           break;
8071         }
8073       emacsframe->want_fullscreen = FULLSCREEN_NONE;
8074     }
8078 - (void) setFSValue: (int)value
8080   NSTRACE ("[EmacsView setFSValue:" NSTRACE_FMT_FSTYPE "]",
8081            NSTRACE_ARG_FSTYPE(value));
8083   Lisp_Object lval = Qnil;
8084   switch (value)
8085     {
8086     case FULLSCREEN_BOTH:
8087       lval = Qfullboth;
8088       break;
8089     case FULLSCREEN_WIDTH:
8090       lval = Qfullwidth;
8091       break;
8092     case FULLSCREEN_HEIGHT:
8093       lval = Qfullheight;
8094       break;
8095     case FULLSCREEN_MAXIMIZED:
8096       lval = Qmaximized;
8097       break;
8098     }
8099   store_frame_param (emacsframe, Qfullscreen, lval);
8100   fs_state = value;
8103 - (void)mouseEntered: (NSEvent *)theEvent
8105   NSTRACE ("[EmacsView mouseEntered:]");
8106   if (emacsframe)
8107     FRAME_DISPLAY_INFO (emacsframe)->last_mouse_movement_time
8108       = EV_TIMESTAMP (theEvent);
8112 - (void)mouseExited: (NSEvent *)theEvent
8114   Mouse_HLInfo *hlinfo = emacsframe ? MOUSE_HL_INFO (emacsframe) : NULL;
8116   NSTRACE ("[EmacsView mouseExited:]");
8118   if (!hlinfo)
8119     return;
8121   FRAME_DISPLAY_INFO (emacsframe)->last_mouse_movement_time
8122     = EV_TIMESTAMP (theEvent);
8124   if (emacsframe == hlinfo->mouse_face_mouse_frame)
8125     {
8126       clear_mouse_face (hlinfo);
8127       hlinfo->mouse_face_mouse_frame = 0;
8128     }
8132 - (instancetype)menuDown: sender
8134   NSTRACE ("[EmacsView menuDown:]");
8135   if (context_menu_value == -1)
8136     context_menu_value = [sender tag];
8137   else
8138     {
8139       NSInteger tag = [sender tag];
8140       find_and_call_menu_selection (emacsframe, emacsframe->menu_bar_items_used,
8141                                     emacsframe->menu_bar_vector,
8142                                     (void *)tag);
8143     }
8145   ns_send_appdefined (-1);
8146   return self;
8150 - (EmacsToolbar *)toolbar
8152   return toolbar;
8156 /* This gets called on toolbar button click.  */
8157 - (instancetype)toolbarClicked: (id)item
8159   NSEvent *theEvent;
8160   int idx = [item tag] * TOOL_BAR_ITEM_NSLOTS;
8162   NSTRACE ("[EmacsView toolbarClicked:]");
8164   if (!emacs_event)
8165     return self;
8167   /* Send first event (for some reason two needed).  */
8168   theEvent = [[self window] currentEvent];
8169   emacs_event->kind = TOOL_BAR_EVENT;
8170   XSETFRAME (emacs_event->arg, emacsframe);
8171   EV_TRAILER (theEvent);
8173   emacs_event->kind = TOOL_BAR_EVENT;
8174   /* XSETINT (emacs_event->code, 0); */
8175   emacs_event->arg = AREF (emacsframe->tool_bar_items,
8176                            idx + TOOL_BAR_ITEM_KEY);
8177   emacs_event->modifiers = EV_MODIFIERS (theEvent);
8178   EV_TRAILER (theEvent);
8179   return self;
8183 - (instancetype)toggleToolbar: (id)sender
8185   NSTRACE ("[EmacsView toggleToolbar:]");
8187   if (!emacs_event)
8188     return self;
8190   emacs_event->kind = NS_NONKEY_EVENT;
8191   emacs_event->code = KEY_NS_TOGGLE_TOOLBAR;
8192   EV_TRAILER ((id)nil);
8193   return self;
8197 - (void)drawRect: (NSRect)rect
8199   int x = NSMinX (rect), y = NSMinY (rect);
8200   int width = NSWidth (rect), height = NSHeight (rect);
8202   NSTRACE ("[EmacsView drawRect:" NSTRACE_FMT_RECT "]",
8203            NSTRACE_ARG_RECT(rect));
8205   if (!emacsframe || !emacsframe->output_data.ns)
8206     return;
8208   ns_clear_frame_area (emacsframe, x, y, width, height);
8209   block_input ();
8210   expose_frame (emacsframe, x, y, width, height);
8211   unblock_input ();
8213   /*
8214     drawRect: may be called (at least in Mac OS X 10.5) for invisible
8215     views as well for some reason.  Thus, do not infer visibility
8216     here.
8218     emacsframe->async_visible = 1;
8219     emacsframe->async_iconified = 0;
8220   */
8224 /* NSDraggingDestination protocol methods.  Actually this is not really a
8225    protocol, but a category of Object.  O well...  */
8227 -(NSDragOperation) draggingEntered: (id <NSDraggingInfo>) sender
8229   NSTRACE ("[EmacsView draggingEntered:]");
8230   return NSDragOperationGeneric;
8234 -(BOOL)prepareForDragOperation: (id <NSDraggingInfo>) sender
8236   return YES;
8240 -(BOOL)performDragOperation: (id <NSDraggingInfo>) sender
8242   id pb;
8243   int x, y;
8244   NSString *type;
8245   NSEvent *theEvent = [[self window] currentEvent];
8246   NSPoint position;
8247   NSDragOperation op = [sender draggingSourceOperationMask];
8248   int modifiers = 0;
8250   NSTRACE ("[EmacsView performDragOperation:]");
8252   if (!emacs_event)
8253     return NO;
8255   position = [self convertPoint: [sender draggingLocation] fromView: nil];
8256   x = lrint (position.x);  y = lrint (position.y);
8258   pb = [sender draggingPasteboard];
8259   type = [pb availableTypeFromArray: ns_drag_types];
8261   if (! (op & (NSDragOperationMove|NSDragOperationDelete)) &&
8262       // URL drags contain all operations (0xf), don't allow all to be set.
8263       (op & 0xf) != 0xf)
8264     {
8265       if (op & NSDragOperationLink)
8266         modifiers |= NSEventModifierFlagControl;
8267       if (op & NSDragOperationCopy)
8268         modifiers |= NSEventModifierFlagOption;
8269       if (op & NSDragOperationGeneric)
8270         modifiers |= NSEventModifierFlagCommand;
8271     }
8273   modifiers = EV_MODIFIERS2 (modifiers);
8274   if (type == 0)
8275     {
8276       return NO;
8277     }
8278   else if ([type isEqualToString: NSFilenamesPboardType])
8279     {
8280       NSArray *files;
8281       NSEnumerator *fenum;
8282       NSString *file;
8284       if (!(files = [pb propertyListForType: type]))
8285         return NO;
8287       fenum = [files objectEnumerator];
8288       while ( (file = [fenum nextObject]) )
8289         {
8290           emacs_event->kind = DRAG_N_DROP_EVENT;
8291           XSETINT (emacs_event->x, x);
8292           XSETINT (emacs_event->y, y);
8293           emacs_event->modifiers = modifiers;
8294           emacs_event->arg =  list2 (Qfile, build_string ([file UTF8String]));
8295           EV_TRAILER (theEvent);
8296         }
8297       return YES;
8298     }
8299   else if ([type isEqualToString: NSURLPboardType])
8300     {
8301       NSURL *url = [NSURL URLFromPasteboard: pb];
8302       if (url == nil) return NO;
8304       emacs_event->kind = DRAG_N_DROP_EVENT;
8305       XSETINT (emacs_event->x, x);
8306       XSETINT (emacs_event->y, y);
8307       emacs_event->modifiers = modifiers;
8308       emacs_event->arg =  list2 (Qurl,
8309                                  build_string ([[url absoluteString]
8310                                                  UTF8String]));
8311       EV_TRAILER (theEvent);
8313       if ([url isFileURL] != NO)
8314         {
8315           NSString *file = [url path];
8316           ns_input_file = append2 (ns_input_file,
8317                                    build_string ([file UTF8String]));
8318         }
8319       return YES;
8320     }
8321   else if ([type isEqualToString: NSStringPboardType]
8322            || [type isEqualToString: NSTabularTextPboardType])
8323     {
8324       NSString *data;
8326       if (! (data = [pb stringForType: type]))
8327         return NO;
8329       emacs_event->kind = DRAG_N_DROP_EVENT;
8330       XSETINT (emacs_event->x, x);
8331       XSETINT (emacs_event->y, y);
8332       emacs_event->modifiers = modifiers;
8333       emacs_event->arg =  list2 (Qnil, build_string ([data UTF8String]));
8334       EV_TRAILER (theEvent);
8335       return YES;
8336     }
8337   else
8338     {
8339       fprintf (stderr, "Invalid data type in dragging pasteboard");
8340       return NO;
8341     }
8345 - (id) validRequestorForSendType: (NSString *)typeSent
8346                       returnType: (NSString *)typeReturned
8348   NSTRACE ("[EmacsView validRequestorForSendType:returnType:]");
8349   if (typeSent != nil && [ns_send_types indexOfObject: typeSent] != NSNotFound
8350       && typeReturned == nil)
8351     {
8352       if (! NILP (ns_get_local_selection (QPRIMARY, QUTF8_STRING)))
8353         return self;
8354     }
8356   return [super validRequestorForSendType: typeSent
8357                                returnType: typeReturned];
8361 /* The next two methods are part of NSServicesRequests informal protocol,
8362    supposedly called when a services menu item is chosen from this app.
8363    But this should not happen because we override the services menu with our
8364    own entries which call ns-perform-service.
8365    Nonetheless, it appeared to happen (under strange circumstances): bug#1435.
8366    So let's at least stub them out until further investigation can be done.  */
8368 - (BOOL) readSelectionFromPasteboard: (NSPasteboard *)pb
8370   /* We could call ns_string_from_pasteboard(pboard) here but then it should
8371      be written into the buffer in place of the existing selection.
8372      Ordinary service calls go through functions defined in ns-win.el.  */
8373   return NO;
8376 - (BOOL) writeSelectionToPasteboard: (NSPasteboard *)pb types: (NSArray *)types
8378   NSArray *typesDeclared;
8379   Lisp_Object val;
8381   NSTRACE ("[EmacsView writeSelectionToPasteboard:types:]");
8383   /* We only support NSStringPboardType.  */
8384   if ([types containsObject:NSStringPboardType] == NO) {
8385     return NO;
8386   }
8388   val = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8389   if (CONSP (val) && SYMBOLP (XCAR (val)))
8390     {
8391       val = XCDR (val);
8392       if (CONSP (val) && NILP (XCDR (val)))
8393         val = XCAR (val);
8394     }
8395   if (! STRINGP (val))
8396     return NO;
8398   typesDeclared = [NSArray arrayWithObject:NSStringPboardType];
8399   [pb declareTypes:typesDeclared owner:nil];
8400   ns_string_to_pasteboard (pb, val);
8401   return YES;
8405 /* setMini = YES means set from internal (gives a finder icon), NO means set nil
8406    (gives a miniaturized version of the window); currently we use the latter for
8407    frames whose active buffer doesn't correspond to any file
8408    (e.g., '*scratch*').  */
8409 - (instancetype)setMiniwindowImage: (BOOL) setMini
8411   id image = [[self window] miniwindowImage];
8412   NSTRACE ("[EmacsView setMiniwindowImage:%d]", setMini);
8414   /* NOTE: under Cocoa miniwindowImage always returns nil, documentation
8415      about "AppleDockIconEnabled" notwithstanding, however the set message
8416      below has its effect nonetheless.  */
8417   if (image != emacsframe->output_data.ns->miniimage)
8418     {
8419       if (image && [image isKindOfClass: [EmacsImage class]])
8420         [image release];
8421       [[self window] setMiniwindowImage:
8422                        setMini ? emacsframe->output_data.ns->miniimage : nil];
8423     }
8425   return self;
8429 - (void) setRows: (int) r andColumns: (int) c
8431   NSTRACE ("[EmacsView setRows:%d andColumns:%d]", r, c);
8432   rows = r;
8433   cols = c;
8436 - (int) fullscreenState
8438   return fs_state;
8441 @end  /* EmacsView */
8445 /* ==========================================================================
8447     EmacsWindow implementation
8449    ========================================================================== */
8451 @implementation EmacsWindow
8453 #ifdef NS_IMPL_COCOA
8454 - (id)accessibilityAttributeValue:(NSString *)attribute
8456   Lisp_Object str = Qnil;
8457   struct frame *f = SELECTED_FRAME ();
8458   struct buffer *curbuf = XBUFFER (XWINDOW (f->selected_window)->contents);
8460   NSTRACE ("[EmacsWindow accessibilityAttributeValue:]");
8462   if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
8463     return NSAccessibilityTextFieldRole;
8465   if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]
8466       && curbuf && ! NILP (BVAR (curbuf, mark_active)))
8467     {
8468       str = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8469     }
8470   else if (curbuf && [attribute isEqualToString:NSAccessibilityValueAttribute])
8471     {
8472       if (! NILP (BVAR (curbuf, mark_active)))
8473           str = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8475       if (NILP (str))
8476         {
8477           ptrdiff_t start_byte = BUF_BEGV_BYTE (curbuf);
8478           ptrdiff_t byte_range = BUF_ZV_BYTE (curbuf) - start_byte;
8479           ptrdiff_t range = BUF_ZV (curbuf) - BUF_BEGV (curbuf);
8481           if (! NILP (BVAR (curbuf, enable_multibyte_characters)))
8482             str = make_uninit_multibyte_string (range, byte_range);
8483           else
8484             str = make_uninit_string (range);
8485           /* To check: This returns emacs-utf-8, which is a superset of utf-8.
8486              Is this a problem?  */
8487           memcpy (SDATA (str), BYTE_POS_ADDR (start_byte), byte_range);
8488         }
8489     }
8492   if (! NILP (str))
8493     {
8494       if (CONSP (str) && SYMBOLP (XCAR (str)))
8495         {
8496           str = XCDR (str);
8497           if (CONSP (str) && NILP (XCDR (str)))
8498             str = XCAR (str);
8499         }
8500       if (STRINGP (str))
8501         {
8502           const char *utfStr = SSDATA (str);
8503           NSString *nsStr = [NSString stringWithUTF8String: utfStr];
8504           return nsStr;
8505         }
8506     }
8508   return [super accessibilityAttributeValue:attribute];
8510 #endif /* NS_IMPL_COCOA */
8512 /* Constrain size and placement of a frame.
8514    By returning the original "frameRect", the frame is not
8515    constrained. This can lead to unwanted situations where, for
8516    example, the menu bar covers the frame.
8518    The default implementation (accessed using "super") constrains the
8519    frame to the visible area of SCREEN, minus the menu bar (if
8520    present) and the Dock.  Note that default implementation also calls
8521    windowWillResize, with the frame it thinks should have.  (This can
8522    make the frame exit maximized mode.)
8524    Note that this should work in situations where multiple monitors
8525    are present.  Common configurations are side-by-side monitors and a
8526    monitor on top of another (e.g. when a laptop is placed under a
8527    large screen).  */
8528 - (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
8530   NSTRACE ("[EmacsWindow constrainFrameRect:" NSTRACE_FMT_RECT " toScreen:]",
8531              NSTRACE_ARG_RECT (frameRect));
8533 #ifdef NS_IMPL_COCOA
8534 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
8535   // If separate spaces is on, it is like each screen is independent.  There is
8536   // no spanning of frames across screens.
8537   if (
8538 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
8539       [NSScreen respondsToSelector: @selector(screensHaveSeparateSpaces)] &&
8540 #endif
8541       [NSScreen screensHaveSeparateSpaces])
8542     {
8543       NSTRACE_MSG ("Screens have separate spaces");
8544       frameRect = [super constrainFrameRect:frameRect toScreen:screen];
8545       NSTRACE_RETURN_RECT (frameRect);
8546       return frameRect;
8547     }
8548   else
8549 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 */
8551     // Check that the proposed frameRect is visible in at least one
8552     // screen.  If it is not, ask the system to reposition it (only
8553     // for non-child windows).
8555     if (!FRAME_PARENT_FRAME (((EmacsView *)[self delegate])->emacsframe))
8556     {
8557       NSArray *screens = [NSScreen screens];
8558       NSUInteger nr_screens = [screens count];
8560       int i;
8561       BOOL frame_on_screen = NO;
8563       for (i = 0; i < nr_screens; ++i)
8564         {
8565           NSScreen *s = [screens objectAtIndex: i];
8566           NSRect scrRect = [s frame];
8568           if (NSIntersectsRect(frameRect, scrRect))
8569             {
8570               frame_on_screen = YES;
8571               break;
8572             }
8573         }
8575       if (!frame_on_screen)
8576         {
8577           NSTRACE_MSG ("Frame outside screens; constraining");
8578           frameRect = [super constrainFrameRect:frameRect toScreen:screen];
8579           NSTRACE_RETURN_RECT (frameRect);
8580           return frameRect;
8581         }
8582     }
8583 #endif
8585   return constrain_frame_rect(frameRect,
8586                               [(EmacsView *)[self delegate] isFullscreen]);
8590 - (void)performZoom:(id)sender
8592   NSTRACE ("[EmacsWindow performZoom:]");
8594   return [super performZoom:sender];
8597 - (void)zoom:(id)sender
8599   NSTRACE ("[EmacsWindow zoom:]");
8601   ns_update_auto_hide_menu_bar();
8603   // Below are three zoom implementations.  In the final commit, the
8604   // idea is that the last should be included.
8606 #if 0
8607   // Native zoom done using the standard zoom animation.  Size of the
8608   // resulting frame reduced to accommodate the Dock and, if present,
8609   // the menu-bar.
8610   [super zoom:sender];
8612 #elif 0
8613   // Native zoom done using the standard zoom animation, plus an
8614   // explicit resize to cover the full screen, except the menu-bar and
8615   // dock, if present.
8616   [super zoom:sender];
8618   // After the native zoom, resize the resulting frame to fill the
8619   // entire screen, except the menu-bar.
8620   //
8621   // This works for all practical purposes.  (The only minor oddity is
8622   // when transiting from full-height frame to a maximized, the
8623   // animation reduces the height of the frame slightly (to the 4
8624   // pixels needed to accommodate the Doc) before it snaps back into
8625   // full height.  The user would need a very trained eye to spot
8626   // this.)
8627   NSScreen * screen = [self screen];
8628   if (screen != nil)
8629     {
8630       int fs_state = [(EmacsView *)[self delegate] fullscreenState];
8632       NSTRACE_FSTYPE ("fullscreenState", fs_state);
8634       NSRect sr = [screen frame];
8635       struct EmacsMargins margins
8636         = ns_screen_margins_ignoring_hidden_dock(screen);
8638       NSRect wr = [self frame];
8639       NSTRACE_RECT ("Rect after zoom", wr);
8641       NSRect newWr = wr;
8643       if (fs_state == FULLSCREEN_MAXIMIZED
8644           || fs_state == FULLSCREEN_HEIGHT)
8645         {
8646           newWr.origin.y = sr.origin.y + margins.bottom;
8647           newWr.size.height = sr.size.height - margins.top - margins.bottom;
8648         }
8650       if (fs_state == FULLSCREEN_MAXIMIZED
8651           || fs_state == FULLSCREEN_WIDTH)
8652         {
8653           newWr.origin.x = sr.origin.x + margins.left;
8654           newWr.size.width = sr.size.width - margins.right - margins.left;
8655         }
8657       if (newWr.size.width     != wr.size.width
8658           || newWr.size.height != wr.size.height
8659           || newWr.origin.x    != wr.origin.x
8660           || newWr.origin.y    != wr.origin.y)
8661         {
8662           NSTRACE_MSG ("New frame different");
8663           [self setFrame: newWr display: NO];
8664         }
8665     }
8666 #else
8667   // Non-native zoom which is done instantaneously.  The resulting
8668   // frame covers the entire screen, except the menu-bar and dock, if
8669   // present.
8670   NSScreen * screen = [self screen];
8671   if (screen != nil)
8672     {
8673       NSRect sr = [screen frame];
8674       struct EmacsMargins margins
8675         = ns_screen_margins_ignoring_hidden_dock(screen);
8677       sr.size.height -= (margins.top + margins.bottom);
8678       sr.size.width  -= (margins.left + margins.right);
8679       sr.origin.x += margins.left;
8680       sr.origin.y += margins.bottom;
8682       sr = [[self delegate] windowWillUseStandardFrame:self
8683                                           defaultFrame:sr];
8684       [self setFrame: sr display: NO];
8685     }
8686 #endif
8689 - (void)setFrame:(NSRect)windowFrame
8690          display:(BOOL)displayViews
8692   NSTRACE ("[EmacsWindow setFrame:" NSTRACE_FMT_RECT " display:%d]",
8693            NSTRACE_ARG_RECT (windowFrame), displayViews);
8695   [super setFrame:windowFrame display:displayViews];
8698 - (void)setFrame:(NSRect)windowFrame
8699          display:(BOOL)displayViews
8700          animate:(BOOL)performAnimation
8702   NSTRACE ("[EmacsWindow setFrame:" NSTRACE_FMT_RECT
8703            " display:%d performAnimation:%d]",
8704            NSTRACE_ARG_RECT (windowFrame), displayViews, performAnimation);
8706   [super setFrame:windowFrame display:displayViews animate:performAnimation];
8709 - (void)setFrameTopLeftPoint:(NSPoint)point
8711   NSTRACE ("[EmacsWindow setFrameTopLeftPoint:" NSTRACE_FMT_POINT "]",
8712            NSTRACE_ARG_POINT (point));
8714   [super setFrameTopLeftPoint:point];
8717 - (BOOL)canBecomeKeyWindow
8719   return !FRAME_NO_ACCEPT_FOCUS (((EmacsView *)[self delegate])->emacsframe);
8721 @end /* EmacsWindow */
8724 @implementation EmacsFSWindow
8726 - (BOOL)canBecomeKeyWindow
8728   return YES;
8731 - (BOOL)canBecomeMainWindow
8733   return YES;
8736 @end
8738 /* ==========================================================================
8740     EmacsScroller implementation
8742    ========================================================================== */
8745 @implementation EmacsScroller
8747 /* for repeat button push */
8748 #define SCROLL_BAR_FIRST_DELAY 0.5
8749 #define SCROLL_BAR_CONTINUOUS_DELAY (1.0 / 15)
8751 + (CGFloat) scrollerWidth
8753   /* TODO: if we want to allow variable widths, this is the place to do it,
8754            however neither GNUstep nor Cocoa support it very well.  */
8755   CGFloat r;
8756 #if defined (NS_IMPL_COCOA) \
8757   && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
8758 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
8759   if ([NSScroller respondsToSelector:
8760                     @selector(scrollerWidthForControlSize:scrollerStyle:)])
8761 #endif
8762     r = [NSScroller scrollerWidthForControlSize: NSControlSizeRegular
8763                                   scrollerStyle: NSScrollerStyleLegacy];
8764 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
8765   else
8766 #endif
8767 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
8768 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 \
8769   || defined (NS_IMPL_GNUSTEP)
8770     r = [NSScroller scrollerWidth];
8771 #endif
8772   return r;
8775 - (instancetype)initFrame: (NSRect )r window: (Lisp_Object)nwin
8777   NSTRACE ("[EmacsScroller initFrame: window:]");
8779   if (r.size.width > r.size.height)
8780       horizontal = YES;
8781   else
8782       horizontal = NO;
8784   [super initWithFrame: r/*NSMakeRect (0, 0, 0, 0)*/];
8785   [self setContinuous: YES];
8786   [self setEnabled: YES];
8788   /* Ensure auto resizing of scrollbars occurs within the emacs frame's view
8789      locked against the top and bottom edges, and right edge on macOS, where
8790      scrollers are on right.  */
8791 #ifdef NS_IMPL_GNUSTEP
8792   [self setAutoresizingMask: NSViewMaxXMargin | NSViewHeightSizable];
8793 #else
8794   [self setAutoresizingMask: NSViewMinXMargin | NSViewHeightSizable];
8795 #endif
8797   window = XWINDOW (nwin);
8798   condemned = NO;
8799   if (horizontal)
8800     pixel_length = NSWidth (r);
8801   else
8802     pixel_length = NSHeight (r);
8803   if (pixel_length == 0) pixel_length = 1;
8804   min_portion = 20 / pixel_length;
8806   frame = XFRAME (window->frame);
8807   if (FRAME_LIVE_P (frame))
8808     {
8809       int i;
8810       EmacsView *view = FRAME_NS_VIEW (frame);
8811       NSView *sview = [[view window] contentView];
8812       NSArray *subs = [sview subviews];
8814       /* Disable optimization stopping redraw of other scrollbars.  */
8815       view->scrollbarsNeedingUpdate = 0;
8816       for (i =[subs count]-1; i >= 0; i--)
8817         if ([[subs objectAtIndex: i] isKindOfClass: [EmacsScroller class]])
8818           view->scrollbarsNeedingUpdate++;
8819       [sview addSubview: self];
8820     }
8822   /* [self setFrame: r]; */
8824   return self;
8828 - (void)setFrame: (NSRect)newRect
8830   NSTRACE ("[EmacsScroller setFrame:]");
8832   /* block_input (); */
8833   if (horizontal)
8834     pixel_length = NSWidth (newRect);
8835   else
8836     pixel_length = NSHeight (newRect);
8837   if (pixel_length == 0) pixel_length = 1;
8838   min_portion = 20 / pixel_length;
8839   [super setFrame: newRect];
8840   /* unblock_input (); */
8844 - (void)dealloc
8846   NSTRACE ("[EmacsScroller dealloc]");
8847   if (window)
8848     {
8849       if (horizontal)
8850         wset_horizontal_scroll_bar (window, Qnil);
8851       else
8852         wset_vertical_scroll_bar (window, Qnil);
8853     }
8854   window = 0;
8855   [super dealloc];
8859 - (instancetype)condemn
8861   NSTRACE ("[EmacsScroller condemn]");
8862   condemned =YES;
8863   return self;
8867 - (instancetype)reprieve
8869   NSTRACE ("[EmacsScroller reprieve]");
8870   condemned =NO;
8871   return self;
8875 -(bool)judge
8877   NSTRACE ("[EmacsScroller judge]");
8878   bool ret = condemned;
8879   if (condemned)
8880     {
8881       EmacsView *view;
8882       block_input ();
8883       /* Ensure other scrollbar updates after deletion.  */
8884       view = (EmacsView *)FRAME_NS_VIEW (frame);
8885       if (view != nil)
8886         view->scrollbarsNeedingUpdate++;
8887       if (window)
8888         {
8889           if (horizontal)
8890             wset_horizontal_scroll_bar (window, Qnil);
8891           else
8892             wset_vertical_scroll_bar (window, Qnil);
8893         }
8894       window = 0;
8895       [self removeFromSuperview];
8896       [self release];
8897       unblock_input ();
8898     }
8899   return ret;
8903 - (void)resetCursorRects
8905   NSRect visible = [self visibleRect];
8906   NSTRACE ("[EmacsScroller resetCursorRects]");
8908   if (!NSIsEmptyRect (visible))
8909     [self addCursorRect: visible cursor: [NSCursor arrowCursor]];
8911 #if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 101300
8912 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
8913   if ([[NSCursor arrowCursor] respondsToSelector:
8914                                 @selector(setOnMouseEntered)])
8915 #endif
8916     [[NSCursor arrowCursor] setOnMouseEntered: YES];
8917 #endif
8921 - (int) checkSamePosition: (int) position portion: (int) portion
8922                     whole: (int) whole
8924   return em_position ==position && em_portion ==portion && em_whole ==whole
8925     && portion != whole; /* Needed for resizing empty buffer.  */
8929 - (instancetype)setPosition: (int)position portion: (int)portion whole: (int)whole
8931   NSTRACE ("[EmacsScroller setPosition:portion:whole:]");
8933   em_position = position;
8934   em_portion = portion;
8935   em_whole = whole;
8937   if (portion >= whole)
8938     {
8939 #ifdef NS_IMPL_COCOA
8940       [self setKnobProportion: 1.0];
8941       [self setDoubleValue: 1.0];
8942 #else
8943       [self setFloatValue: 0.0 knobProportion: 1.0];
8944 #endif
8945     }
8946   else
8947     {
8948       float pos;
8949       CGFloat por;
8950       portion = max ((float)whole*min_portion/pixel_length, portion);
8951       pos = (float)position / (whole - portion);
8952       por = (CGFloat)portion/whole;
8953 #ifdef NS_IMPL_COCOA
8954       [self setKnobProportion: por];
8955       [self setDoubleValue: pos];
8956 #else
8957       [self setFloatValue: pos knobProportion: por];
8958 #endif
8959     }
8961   return self;
8964 /* Set up emacs_event.  */
8965 - (void) sendScrollEventAtLoc: (float)loc fromEvent: (NSEvent *)e
8967   Lisp_Object win;
8969   NSTRACE ("[EmacsScroller sendScrollEventAtLoc:fromEvent:]");
8971   if (!emacs_event)
8972     return;
8974   emacs_event->part = last_hit_part;
8975   emacs_event->code = 0;
8976   emacs_event->modifiers = EV_MODIFIERS (e) | down_modifier;
8977   XSETWINDOW (win, window);
8978   emacs_event->frame_or_window = win;
8979   emacs_event->timestamp = EV_TIMESTAMP (e);
8980   emacs_event->arg = Qnil;
8982   if (horizontal)
8983     {
8984       emacs_event->kind = HORIZONTAL_SCROLL_BAR_CLICK_EVENT;
8985       XSETINT (emacs_event->x, em_whole * loc / pixel_length);
8986       XSETINT (emacs_event->y, em_whole);
8987     }
8988   else
8989     {
8990       emacs_event->kind = SCROLL_BAR_CLICK_EVENT;
8991       XSETINT (emacs_event->x, loc);
8992       XSETINT (emacs_event->y, pixel_length-20);
8993     }
8995   if (q_event_ptr)
8996     {
8997       n_emacs_events_pending++;
8998       kbd_buffer_store_event_hold (emacs_event, q_event_ptr);
8999     }
9000   else
9001     hold_event (emacs_event);
9002   EVENT_INIT (*emacs_event);
9003   ns_send_appdefined (-1);
9007 /* Called manually through timer to implement repeated button action
9008    with hold-down.  */
9009 - (instancetype)repeatScroll: (NSTimer *)scrollEntry
9011   NSEvent *e = [[self window] currentEvent];
9012   NSPoint p =  [[self window] mouseLocationOutsideOfEventStream];
9013   BOOL inKnob = [self testPart: p] == NSScrollerKnob;
9015   NSTRACE ("[EmacsScroller repeatScroll:]");
9017   /* Clear timer if need be.  */
9018   if (inKnob || [scroll_repeat_entry timeInterval] == SCROLL_BAR_FIRST_DELAY)
9019     {
9020         [scroll_repeat_entry invalidate];
9021         [scroll_repeat_entry release];
9022         scroll_repeat_entry = nil;
9024         if (inKnob)
9025           return self;
9027         scroll_repeat_entry
9028           = [[NSTimer scheduledTimerWithTimeInterval:
9029                         SCROLL_BAR_CONTINUOUS_DELAY
9030                                             target: self
9031                                           selector: @selector (repeatScroll:)
9032                                           userInfo: 0
9033                                            repeats: YES]
9034               retain];
9035     }
9037   [self sendScrollEventAtLoc: 0 fromEvent: e];
9038   return self;
9042 /* Asynchronous mouse tracking for scroller.  This allows us to dispatch
9043    mouseDragged events without going into a modal loop.  */
9044 - (void)mouseDown: (NSEvent *)e
9046   NSRect sr, kr;
9047   /* hitPart is only updated AFTER event is passed on.  */
9048   NSScrollerPart part = [self testPart: [e locationInWindow]];
9049   CGFloat loc, kloc, pos UNINIT;
9050   int edge = 0;
9052   NSTRACE ("[EmacsScroller mouseDown:]");
9054   switch (part)
9055     {
9056     case NSScrollerDecrementPage:
9057       last_hit_part = horizontal ? scroll_bar_before_handle : scroll_bar_above_handle; break;
9058     case NSScrollerIncrementPage:
9059       last_hit_part = horizontal ? scroll_bar_after_handle : scroll_bar_below_handle; break;
9060     case NSScrollerDecrementLine:
9061       last_hit_part = horizontal ? scroll_bar_left_arrow : scroll_bar_up_arrow; break;
9062     case NSScrollerIncrementLine:
9063       last_hit_part = horizontal ? scroll_bar_right_arrow : scroll_bar_down_arrow; break;
9064     case NSScrollerKnob:
9065       last_hit_part = horizontal ? scroll_bar_horizontal_handle : scroll_bar_handle; break;
9066     case NSScrollerKnobSlot:  /* GNUstep-only */
9067       last_hit_part = scroll_bar_move_ratio; break;
9068     default:  /* NSScrollerNoPart? */
9069       fprintf (stderr, "EmacsScroller-mouseDown: unexpected part %ld\n",
9070                (long) part);
9071       return;
9072     }
9074   if (part == NSScrollerKnob || part == NSScrollerKnobSlot)
9075     {
9076       /* handle, or on GNUstep possibly slot */
9077       NSEvent *fake_event;
9078       int length;
9080       /* compute float loc in slot and mouse offset on knob */
9081       sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot]
9082                       toView: nil];
9083       if (horizontal)
9084         {
9085           length = NSWidth (sr);
9086           loc = ([e locationInWindow].x - NSMinX (sr));
9087         }
9088       else
9089         {
9090           length = NSHeight (sr);
9091           loc = length - ([e locationInWindow].y - NSMinY (sr));
9092         }
9094       if (loc <= 0.0)
9095         {
9096           loc = 0.0;
9097           edge = -1;
9098         }
9099       else if (loc >= length)
9100         {
9101           loc = length;
9102           edge = 1;
9103         }
9105       if (edge)
9106         kloc = 0.5 * edge;
9107       else
9108         {
9109           kr = [self convertRect: [self rectForPart: NSScrollerKnob]
9110                           toView: nil];
9111           if (horizontal)
9112             kloc = ([e locationInWindow].x - NSMinX (kr));
9113           else
9114             kloc = NSHeight (kr) - ([e locationInWindow].y - NSMinY (kr));
9115         }
9116       last_mouse_offset = kloc;
9118       /* if knob, tell emacs a location offset by knob pos
9119          (to indicate top of handle) */
9120       if (part == NSScrollerKnob)
9121         pos = (loc - last_mouse_offset);
9122       else
9123         /* else this is a slot click on GNUstep: go straight there */
9124         pos = loc;
9126       /* If there are buttons in the scroller area, we need to
9127          recalculate pos as emacs expects the scroller slot to take up
9128          the entire available length.  */
9129       if (length != pixel_length)
9130         pos = pos * pixel_length / length;
9132       /* send a fake mouse-up to super to preempt modal -trackKnob: mode */
9133       fake_event = [NSEvent mouseEventWithType: NSEventTypeLeftMouseUp
9134                                       location: [e locationInWindow]
9135                                  modifierFlags: [e modifierFlags]
9136                                      timestamp: [e timestamp]
9137                                   windowNumber: [e windowNumber]
9138                                        context: nil
9139                                    eventNumber: [e eventNumber]
9140                                     clickCount: [e clickCount]
9141                                       pressure: [e pressure]];
9142       [super mouseUp: fake_event];
9143     }
9144   else
9145     {
9146       pos = 0; /* ignored */
9148       /* Set a timer to repeat, as we can't let superclass do this modally.  */
9149       scroll_repeat_entry
9150         = [[NSTimer scheduledTimerWithTimeInterval: SCROLL_BAR_FIRST_DELAY
9151                                             target: self
9152                                           selector: @selector (repeatScroll:)
9153                                           userInfo: 0
9154                                            repeats: YES]
9155             retain];
9156     }
9158   if (part != NSScrollerKnob)
9159     [self sendScrollEventAtLoc: pos fromEvent: e];
9163 /* Called as we manually track scroller drags, rather than superclass.  */
9164 - (void)mouseDragged: (NSEvent *)e
9166     NSRect sr;
9167     double loc, pos;
9168     int length;
9170     NSTRACE ("[EmacsScroller mouseDragged:]");
9172       sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot]
9173                       toView: nil];
9175       if (horizontal)
9176         {
9177           length = NSWidth (sr);
9178           loc = ([e locationInWindow].x - NSMinX (sr));
9179         }
9180       else
9181         {
9182           length = NSHeight (sr);
9183           loc = length - ([e locationInWindow].y - NSMinY (sr));
9184         }
9186       if (loc <= 0.0)
9187         {
9188           loc = 0.0;
9189         }
9190       else if (loc >= length + last_mouse_offset)
9191         {
9192           loc = length + last_mouse_offset;
9193         }
9195       pos = (loc - last_mouse_offset);
9197       /* If there are buttons in the scroller area, we need to
9198          recalculate pos as emacs expects the scroller slot to take up
9199          the entire available length.  */
9200       if (length != pixel_length)
9201         pos = pos * pixel_length / length;
9203       [self sendScrollEventAtLoc: pos fromEvent: e];
9207 - (void)mouseUp: (NSEvent *)e
9209   NSTRACE ("[EmacsScroller mouseUp:]");
9211   if (scroll_repeat_entry)
9212     {
9213       [scroll_repeat_entry invalidate];
9214       [scroll_repeat_entry release];
9215       scroll_repeat_entry = nil;
9216     }
9217   last_hit_part = scroll_bar_above_handle;
9221 /* Treat scrollwheel events in the bar as though they were in the main window.  */
9222 - (void) scrollWheel: (NSEvent *)theEvent
9224   NSTRACE ("[EmacsScroller scrollWheel:]");
9226   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (frame);
9227   [view mouseDown: theEvent];
9230 @end  /* EmacsScroller */
9233 #ifdef NS_IMPL_GNUSTEP
9234 /* Dummy class to get rid of startup warnings.  */
9235 @implementation EmacsDocument
9237 @end
9238 #endif
9241 /* ==========================================================================
9243    Font-related functions; these used to be in nsfaces.m
9245    ========================================================================== */
9248 Lisp_Object
9249 x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
9251   struct font *font = XFONT_OBJECT (font_object);
9252   EmacsView *view = FRAME_NS_VIEW (f);
9253   int font_ascent, font_descent;
9255   if (fontset < 0)
9256     fontset = fontset_from_font (font_object);
9257   FRAME_FONTSET (f) = fontset;
9259   if (FRAME_FONT (f) == font)
9260     /* This font is already set in frame F.  There's nothing more to
9261        do.  */
9262     return font_object;
9264   FRAME_FONT (f) = font;
9266   FRAME_BASELINE_OFFSET (f) = font->baseline_offset;
9267   FRAME_COLUMN_WIDTH (f) = font->average_width;
9268   get_font_ascent_descent (font, &font_ascent, &font_descent);
9269   FRAME_LINE_HEIGHT (f) = font_ascent + font_descent;
9271   /* Compute the scroll bar width in character columns.  */
9272   if (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) > 0)
9273     {
9274       int wid = FRAME_COLUMN_WIDTH (f);
9275       FRAME_CONFIG_SCROLL_BAR_COLS (f)
9276         = (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) + wid - 1) / wid;
9277     }
9278   else
9279     {
9280       int wid = FRAME_COLUMN_WIDTH (f);
9281       FRAME_CONFIG_SCROLL_BAR_COLS (f) = (14 + wid - 1) / wid;
9282     }
9284   /* Compute the scroll bar height in character lines.  */
9285   if (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) > 0)
9286     {
9287       int height = FRAME_LINE_HEIGHT (f);
9288       FRAME_CONFIG_SCROLL_BAR_LINES (f)
9289         = (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) + height - 1) / height;
9290     }
9291   else
9292     {
9293       int height = FRAME_LINE_HEIGHT (f);
9294       FRAME_CONFIG_SCROLL_BAR_LINES (f) = (14 + height - 1) / height;
9295     }
9297   /* Now make the frame display the given font.  */
9298   if (FRAME_NS_WINDOW (f) != 0 && ! [view isFullscreen])
9299     adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
9300                        FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3,
9301                        false, Qfont);
9303   return font_object;
9307 /* XLFD: -foundry-family-weight-slant-swidth-adstyle-pxlsz-ptSz-resx-resy-spc-avgWidth-rgstry-encoding */
9308 /* Note: ns_font_to_xlfd and ns_fontname_to_xlfd no longer needed, removed
9309          in 1.43.  */
9311 const char *
9312 ns_xlfd_to_fontname (const char *xlfd)
9313 /* --------------------------------------------------------------------------
9314     Convert an X font name (XLFD) to an NS font name.
9315     Only family is used.
9316     The string returned is temporarily allocated.
9317    -------------------------------------------------------------------------- */
9319   char *name = xmalloc (180);
9320   int i, len;
9321   const char *ret;
9323   if (!strncmp (xlfd, "--", 2))
9324     sscanf (xlfd, "--%*[^-]-%179[^-]-", name);
9325   else
9326     sscanf (xlfd, "-%*[^-]-%179[^-]-", name);
9328   /* stopgap for malformed XLFD input */
9329   if (strlen (name) == 0)
9330     strcpy (name, "Monaco");
9332   /* undo hack in ns_fontname_to_xlfd, converting '$' to '-', '_' to ' '
9333      also uppercase after '-' or ' ' */
9334   name[0] = c_toupper (name[0]);
9335   for (len =strlen (name), i =0; i<len; i++)
9336     {
9337       if (name[i] == '$')
9338         {
9339           name[i] = '-';
9340           if (i+1<len)
9341             name[i+1] = c_toupper (name[i+1]);
9342         }
9343       else if (name[i] == '_')
9344         {
9345           name[i] = ' ';
9346           if (i+1<len)
9347             name[i+1] = c_toupper (name[i+1]);
9348         }
9349     }
9350   /* fprintf (stderr, "converted '%s' to '%s'\n",xlfd,name); */
9351   ret = [[NSString stringWithUTF8String: name] UTF8String];
9352   xfree (name);
9353   return ret;
9357 void
9358 syms_of_nsterm (void)
9360   NSTRACE ("syms_of_nsterm");
9362   ns_antialias_threshold = 10.0;
9364   /* From 23+ we need to tell emacs what modifiers there are.  */
9365   DEFSYM (Qmodifier_value, "modifier-value");
9366   DEFSYM (Qalt, "alt");
9367   DEFSYM (Qhyper, "hyper");
9368   DEFSYM (Qmeta, "meta");
9369   DEFSYM (Qsuper, "super");
9370   DEFSYM (Qcontrol, "control");
9371   DEFSYM (QUTF8_STRING, "UTF8_STRING");
9373   DEFSYM (Qfile, "file");
9374   DEFSYM (Qurl, "url");
9376   Fput (Qalt, Qmodifier_value, make_number (alt_modifier));
9377   Fput (Qhyper, Qmodifier_value, make_number (hyper_modifier));
9378   Fput (Qmeta, Qmodifier_value, make_number (meta_modifier));
9379   Fput (Qsuper, Qmodifier_value, make_number (super_modifier));
9380   Fput (Qcontrol, Qmodifier_value, make_number (ctrl_modifier));
9382   DEFVAR_LISP ("ns-input-file", ns_input_file,
9383               "The file specified in the last NS event.");
9384   ns_input_file =Qnil;
9386   DEFVAR_LISP ("ns-working-text", ns_working_text,
9387               "String for visualizing working composition sequence.");
9388   ns_working_text =Qnil;
9390   DEFVAR_LISP ("ns-input-font", ns_input_font,
9391               "The font specified in the last NS event.");
9392   ns_input_font =Qnil;
9394   DEFVAR_LISP ("ns-input-fontsize", ns_input_fontsize,
9395               "The fontsize specified in the last NS event.");
9396   ns_input_fontsize =Qnil;
9398   DEFVAR_LISP ("ns-input-line", ns_input_line,
9399                "The line specified in the last NS event.");
9400   ns_input_line =Qnil;
9402   DEFVAR_LISP ("ns-input-spi-name", ns_input_spi_name,
9403                "The service name specified in the last NS event.");
9404   ns_input_spi_name =Qnil;
9406   DEFVAR_LISP ("ns-input-spi-arg", ns_input_spi_arg,
9407                "The service argument specified in the last NS event.");
9408   ns_input_spi_arg =Qnil;
9410   DEFVAR_LISP ("ns-alternate-modifier", ns_alternate_modifier,
9411                "This variable describes the behavior of the alternate or option key.\n\
9412 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9413 that key.\n\
9414 Set to none means that the alternate / option key is not interpreted by Emacs\n\
9415 at all, allowing it to be used at a lower level for accented character entry.");
9416   ns_alternate_modifier = Qmeta;
9418   DEFVAR_LISP ("ns-right-alternate-modifier", ns_right_alternate_modifier,
9419                "This variable describes the behavior of the right alternate or option key.\n\
9420 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9421 that key.\n\
9422 Set to left means be the same key as `ns-alternate-modifier'.\n\
9423 Set to none means that the alternate / option key is not interpreted by Emacs\n\
9424 at all, allowing it to be used at a lower level for accented character entry.");
9425   ns_right_alternate_modifier = Qleft;
9427   DEFVAR_LISP ("ns-command-modifier", ns_command_modifier,
9428                "This variable describes the behavior of the command key.\n\
9429 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9430 that key.");
9431   ns_command_modifier = Qsuper;
9433   DEFVAR_LISP ("ns-right-command-modifier", ns_right_command_modifier,
9434                "This variable describes the behavior of the right command key.\n\
9435 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9436 that key.\n\
9437 Set to left means be the same key as `ns-command-modifier'.\n\
9438 Set to none means that the command / option key is not interpreted by Emacs\n\
9439 at all, allowing it to be used at a lower level for accented character entry.");
9440   ns_right_command_modifier = Qleft;
9442   DEFVAR_LISP ("ns-control-modifier", ns_control_modifier,
9443                "This variable describes the behavior of the control key.\n\
9444 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9445 that key.");
9446   ns_control_modifier = Qcontrol;
9448   DEFVAR_LISP ("ns-right-control-modifier", ns_right_control_modifier,
9449                "This variable describes the behavior of the right control key.\n\
9450 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9451 that key.\n\
9452 Set to left means be the same key as `ns-control-modifier'.\n\
9453 Set to none means that the control / option key is not interpreted by Emacs\n\
9454 at all, allowing it to be used at a lower level for accented character entry.");
9455   ns_right_control_modifier = Qleft;
9457   DEFVAR_LISP ("ns-function-modifier", ns_function_modifier,
9458                "This variable describes the behavior of the function key (on laptops).\n\
9459 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9460 that key.\n\
9461 Set to none means that the function key is not interpreted by Emacs at all,\n\
9462 allowing it to be used at a lower level for accented character entry.");
9463   ns_function_modifier = Qnone;
9465   DEFVAR_LISP ("ns-antialias-text", ns_antialias_text,
9466                "Non-nil (the default) means to render text antialiased.");
9467   ns_antialias_text = Qt;
9469   DEFVAR_LISP ("ns-use-thin-smoothing", ns_use_thin_smoothing,
9470                "Non-nil turns on a font smoothing method that produces thinner strokes.");
9471   ns_use_thin_smoothing = Qnil;
9473   DEFVAR_LISP ("ns-confirm-quit", ns_confirm_quit,
9474                "Whether to confirm application quit using dialog.");
9475   ns_confirm_quit = Qnil;
9477   DEFVAR_LISP ("ns-auto-hide-menu-bar", ns_auto_hide_menu_bar,
9478                doc: /* Non-nil means that the menu bar is hidden, but appears when the mouse is near.
9479 Only works on Mac OS X.  */);
9480   ns_auto_hide_menu_bar = Qnil;
9482   DEFVAR_BOOL ("ns-use-native-fullscreen", ns_use_native_fullscreen,
9483      doc: /* Non-nil means to use native fullscreen on Mac OS X 10.7 and later.
9484 Nil means use fullscreen the old (< 10.7) way.  The old way works better with
9485 multiple monitors, but lacks tool bar.  This variable is ignored on
9486 Mac OS X < 10.7.  Default is t.  */);
9487   ns_use_native_fullscreen = YES;
9488   ns_last_use_native_fullscreen = ns_use_native_fullscreen;
9490   DEFVAR_BOOL ("ns-use-fullscreen-animation", ns_use_fullscreen_animation,
9491      doc: /* Non-nil means use animation on non-native fullscreen.
9492 For native fullscreen, this does nothing.
9493 Default is nil.  */);
9494   ns_use_fullscreen_animation = NO;
9496   DEFVAR_BOOL ("ns-use-srgb-colorspace", ns_use_srgb_colorspace,
9497      doc: /* Non-nil means to use sRGB colorspace on Mac OS X 10.7 and later.
9498 Note that this does not apply to images.
9499 This variable is ignored on Mac OS X < 10.7 and GNUstep.  */);
9500   ns_use_srgb_colorspace = YES;
9502   DEFVAR_BOOL ("ns-use-mwheel-acceleration",
9503                ns_use_mwheel_acceleration,
9504      doc: /* Non-nil means use macOS's standard mouse wheel acceleration.
9505 This variable is ignored on macOS < 10.7 and GNUstep.  Default is t.  */);
9506   ns_use_mwheel_acceleration = YES;
9508   DEFVAR_LISP ("ns-mwheel-line-height", ns_mwheel_line_height,
9509                doc: /* The number of pixels touchpad scrolling considers one line.
9510 Nil or a non-number means use the default frame line height.
9511 This variable is ignored on macOS < 10.7 and GNUstep.  Default is nil.  */);
9512   ns_mwheel_line_height = Qnil;
9514   DEFVAR_BOOL ("ns-use-mwheel-momentum", ns_use_mwheel_momentum,
9515                doc: /* Non-nil means mouse wheel scrolling uses momentum.
9516 This variable is ignored on macOS < 10.7 and GNUstep.  Default is t.  */);
9517   ns_use_mwheel_momentum = YES;
9519   /* TODO: Move to common code.  */
9520   DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars,
9521                doc: /* SKIP: real doc in xterm.c.  */);
9522   Vx_toolkit_scroll_bars = Qt;
9524   DEFVAR_BOOL ("x-use-underline-position-properties",
9525                x_use_underline_position_properties,
9526      doc: /* SKIP: real doc in xterm.c.  */);
9527   x_use_underline_position_properties = 0;
9528   DEFSYM (Qx_use_underline_position_properties,
9529           "x-use-underline-position-properties");
9531   DEFVAR_BOOL ("x-underline-at-descent-line",
9532                x_underline_at_descent_line,
9533      doc: /* SKIP: real doc in xterm.c.  */);
9534   x_underline_at_descent_line = 0;
9535   DEFSYM (Qx_underline_at_descent_line, "x-underline-at-descent-line");
9537   /* Tell Emacs about this window system.  */
9538   Fprovide (Qns, Qnil);
9540   DEFSYM (Qcocoa, "cocoa");
9541   DEFSYM (Qgnustep, "gnustep");
9543 #ifdef NS_IMPL_COCOA
9544   Fprovide (Qcocoa, Qnil);
9545   syms_of_macfont ();
9546 #else
9547   Fprovide (Qgnustep, Qnil);
9548   syms_of_nsfont ();
9549 #endif