; * lisp/ldefs-boot.el: Update.
[emacs.git] / src / nsterm.m
blobbbd2c84214c0cad10057d11e237f63f3adb05db4
1 /* NeXT/Open/GNUstep / macOS communication module.      -*- coding: utf-8 -*-
3 Copyright (C) 1989, 1993-1994, 2005-2006, 2008-2019 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>
41 #include <c-ctype.h>
42 #include <c-strcase.h>
43 #include <ftoastr.h>
45 #include "lisp.h"
46 #include "blockinput.h"
47 #include "sysselect.h"
48 #include "nsterm.h"
49 #include "systime.h"
50 #include "character.h"
51 #include "fontset.h"
52 #include "composite.h"
53 #include "ccl.h"
55 #include "termhooks.h"
56 #include "termchar.h"
57 #include "menu.h"
58 #include "window.h"
59 #include "keyboard.h"
60 #include "buffer.h"
61 #include "font.h"
63 #ifdef NS_IMPL_GNUSTEP
64 #include "process.h"
65 #endif
67 #ifdef NS_IMPL_COCOA
68 #include "macfont.h"
69 #endif
71 static EmacsMenu *dockMenu;
72 #ifdef NS_IMPL_COCOA
73 static EmacsMenu *mainMenu;
74 #endif
76 /* ==========================================================================
78    NSTRACE, Trace support.
80    ========================================================================== */
82 #if NSTRACE_ENABLED
84 /* The following use "volatile" since they can be accessed from
85    parallel threads. */
86 volatile int nstrace_num = 0;
87 volatile int nstrace_depth = 0;
89 /* When 0, no trace is emitted.  This is used by NSTRACE_WHEN and
90    NSTRACE_UNLESS to silence functions called.
92    TODO: This should really be a thread-local variable, to avoid that
93    a function with disabled trace thread silence trace output in
94    another.  However, in practice this seldom is a problem. */
95 volatile int nstrace_enabled_global = 1;
97 /* Called when nstrace_enabled goes out of scope. */
98 void nstrace_leave(int * pointer_to_nstrace_enabled)
100   if (*pointer_to_nstrace_enabled)
101     {
102       --nstrace_depth;
103     }
107 /* Called when nstrace_saved_enabled_global goes out of scope. */
108 void nstrace_restore_global_trace_state(int * pointer_to_saved_enabled_global)
110   nstrace_enabled_global = *pointer_to_saved_enabled_global;
114 char const * nstrace_fullscreen_type_name (int fs_type)
116   switch (fs_type)
117     {
118     case -1:                   return "-1";
119     case FULLSCREEN_NONE:      return "FULLSCREEN_NONE";
120     case FULLSCREEN_WIDTH:     return "FULLSCREEN_WIDTH";
121     case FULLSCREEN_HEIGHT:    return "FULLSCREEN_HEIGHT";
122     case FULLSCREEN_BOTH:      return "FULLSCREEN_BOTH";
123     case FULLSCREEN_MAXIMIZED: return "FULLSCREEN_MAXIMIZED";
124     default:                   return "FULLSCREEN_?????";
125     }
127 #endif
130 /* ==========================================================================
132    NSColor, EmacsColor category.
134    ========================================================================== */
135 @implementation NSColor (EmacsColor)
136 + (NSColor *)colorForEmacsRed:(CGFloat)red green:(CGFloat)green
137                          blue:(CGFloat)blue alpha:(CGFloat)alpha
139 #if defined (NS_IMPL_COCOA) \
140   && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
141   if (ns_use_srgb_colorspace
142 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
143       && [NSColor respondsToSelector:
144                     @selector(colorWithSRGBRed:green:blue:alpha:)]
145 #endif
146       )
147     return [NSColor colorWithSRGBRed: red
148                                green: green
149                                 blue: blue
150                                alpha: alpha];
151 #endif
152   return [NSColor colorWithCalibratedRed: red
153                                    green: green
154                                     blue: blue
155                                    alpha: alpha];
158 - (NSColor *)colorUsingDefaultColorSpace
160   /* FIXMES: We're checking for colorWithSRGBRed here so this will
161      only work in the same place as in the method above.  It should
162      really be a check whether we're on macOS 10.7 or above. */
163 #if defined (NS_IMPL_COCOA) \
164   && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
165   if (ns_use_srgb_colorspace
166 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
167       && [NSColor respondsToSelector:
168                     @selector(colorWithSRGBRed:green:blue:alpha:)]
169 #endif
170       )
171     return [self colorUsingColorSpace: [NSColorSpace sRGBColorSpace]];
172 #endif
173   return [self colorUsingColorSpaceName: NSCalibratedRGBColorSpace];
176 @end
178 /* ==========================================================================
180     Local declarations
182    ========================================================================== */
184 /* Convert a symbol indexed with an NSxxx value to a value as defined
185    in keyboard.c (lispy_function_key). I hope this is a correct way
186    of doing things... */
187 static unsigned convert_ns_to_X_keysym[] =
189   NSHomeFunctionKey,            0x50,
190   NSLeftArrowFunctionKey,       0x51,
191   NSUpArrowFunctionKey,         0x52,
192   NSRightArrowFunctionKey,      0x53,
193   NSDownArrowFunctionKey,       0x54,
194   NSPageUpFunctionKey,          0x55,
195   NSPageDownFunctionKey,        0x56,
196   NSEndFunctionKey,             0x57,
197   NSBeginFunctionKey,           0x58,
198   NSSelectFunctionKey,          0x60,
199   NSPrintFunctionKey,           0x61,
200   NSClearLineFunctionKey,       0x0B,
201   NSExecuteFunctionKey,         0x62,
202   NSInsertFunctionKey,          0x63,
203   NSUndoFunctionKey,            0x65,
204   NSRedoFunctionKey,            0x66,
205   NSMenuFunctionKey,            0x67,
206   NSFindFunctionKey,            0x68,
207   NSHelpFunctionKey,            0x6A,
208   NSBreakFunctionKey,           0x6B,
210   NSF1FunctionKey,              0xBE,
211   NSF2FunctionKey,              0xBF,
212   NSF3FunctionKey,              0xC0,
213   NSF4FunctionKey,              0xC1,
214   NSF5FunctionKey,              0xC2,
215   NSF6FunctionKey,              0xC3,
216   NSF7FunctionKey,              0xC4,
217   NSF8FunctionKey,              0xC5,
218   NSF9FunctionKey,              0xC6,
219   NSF10FunctionKey,             0xC7,
220   NSF11FunctionKey,             0xC8,
221   NSF12FunctionKey,             0xC9,
222   NSF13FunctionKey,             0xCA,
223   NSF14FunctionKey,             0xCB,
224   NSF15FunctionKey,             0xCC,
225   NSF16FunctionKey,             0xCD,
226   NSF17FunctionKey,             0xCE,
227   NSF18FunctionKey,             0xCF,
228   NSF19FunctionKey,             0xD0,
229   NSF20FunctionKey,             0xD1,
230   NSF21FunctionKey,             0xD2,
231   NSF22FunctionKey,             0xD3,
232   NSF23FunctionKey,             0xD4,
233   NSF24FunctionKey,             0xD5,
235   NSBackspaceCharacter,         0x08,  /* 8: Not on some KBs. */
236   NSDeleteCharacter,            0xFF,  /* 127: Big 'delete' key upper right. */
237   NSDeleteFunctionKey,          0x9F,  /* 63272: Del forw key off main array. */
239   NSTabCharacter,               0x09,
240   0x19,                         0x09,  /* left tab->regular since pass shift */
241   NSCarriageReturnCharacter,    0x0D,
242   NSNewlineCharacter,           0x0D,
243   NSEnterCharacter,             0x8D,
245   0x41|NSEventModifierFlagNumericPad,   0xAE,  /* KP_Decimal */
246   0x43|NSEventModifierFlagNumericPad,   0xAA,  /* KP_Multiply */
247   0x45|NSEventModifierFlagNumericPad,   0xAB,  /* KP_Add */
248   0x4B|NSEventModifierFlagNumericPad,   0xAF,  /* KP_Divide */
249   0x4E|NSEventModifierFlagNumericPad,   0xAD,  /* KP_Subtract */
250   0x51|NSEventModifierFlagNumericPad,   0xBD,  /* KP_Equal */
251   0x52|NSEventModifierFlagNumericPad,   0xB0,  /* KP_0 */
252   0x53|NSEventModifierFlagNumericPad,   0xB1,  /* KP_1 */
253   0x54|NSEventModifierFlagNumericPad,   0xB2,  /* KP_2 */
254   0x55|NSEventModifierFlagNumericPad,   0xB3,  /* KP_3 */
255   0x56|NSEventModifierFlagNumericPad,   0xB4,  /* KP_4 */
256   0x57|NSEventModifierFlagNumericPad,   0xB5,  /* KP_5 */
257   0x58|NSEventModifierFlagNumericPad,   0xB6,  /* KP_6 */
258   0x59|NSEventModifierFlagNumericPad,   0xB7,  /* KP_7 */
259   0x5B|NSEventModifierFlagNumericPad,   0xB8,  /* KP_8 */
260   0x5C|NSEventModifierFlagNumericPad,   0xB9,  /* KP_9 */
262   0x1B,                         0x1B   /* escape */
265 /* On macOS picks up the default NSGlobalDomain AppleAntiAliasingThreshold,
266    the maximum font size to NOT antialias.  On GNUstep there is currently
267    no way to control this behavior. */
268 float ns_antialias_threshold;
270 NSArray *ns_send_types = 0, *ns_return_types = 0;
271 static NSArray *ns_drag_types = 0;
272 NSString *ns_app_name = @"Emacs";  /* default changed later */
274 /* Display variables */
275 struct ns_display_info *x_display_list; /* Chain of existing displays */
276 long context_menu_value = 0;
278 /* display update */
279 static int ns_window_num = 0;
280 static BOOL ns_fake_keydown = NO;
281 #ifdef NS_IMPL_COCOA
282 static BOOL ns_menu_bar_is_hidden = NO;
283 #endif
284 /*static int debug_lock = 0; */
286 /* event loop */
287 static BOOL send_appdefined = YES;
288 #define NO_APPDEFINED_DATA (-8)
289 static int last_appdefined_event_data = NO_APPDEFINED_DATA;
290 static NSTimer *timed_entry = 0;
291 static NSTimer *scroll_repeat_entry = nil;
292 static fd_set select_readfds, select_writefds;
293 enum { SELECT_HAVE_READ = 1, SELECT_HAVE_WRITE = 2, SELECT_HAVE_TMO = 4 };
294 static int select_nfds = 0, select_valid = 0;
295 static struct timespec select_timeout = { 0, 0 };
296 static int selfds[2] = { -1, -1 };
297 static pthread_mutex_t select_mutex;
298 static NSAutoreleasePool *outerpool;
299 static struct input_event *emacs_event = NULL;
300 static struct input_event *q_event_ptr = NULL;
301 static int n_emacs_events_pending = 0;
302 static NSMutableArray *ns_pending_files, *ns_pending_service_names,
303   *ns_pending_service_args;
304 static BOOL ns_do_open_file = NO;
305 static BOOL ns_last_use_native_fullscreen;
307 /* Non-zero means that a HELP_EVENT has been generated since Emacs
308    start.  */
310 static BOOL any_help_event_p = NO;
312 static struct {
313   struct input_event *q;
314   int nr, cap;
315 } hold_event_q = {
316   NULL, 0, 0
319 static NSString *represented_filename = nil;
320 static struct frame *represented_frame = 0;
322 #ifdef NS_IMPL_COCOA
324  * State for pending menu activation:
325  * MENU_NONE     Normal state
326  * MENU_PENDING  A menu has been clicked on, but has been canceled so we can
327  *               run lisp to update the menu.
328  * MENU_OPENING  Menu is up to date, and the click event is redone so the menu
329  *               will open.
330  */
331 #define MENU_NONE 0
332 #define MENU_PENDING 1
333 #define MENU_OPENING 2
334 static int menu_will_open_state = MENU_NONE;
336 /* Saved position for menu click.  */
337 static CGPoint menu_mouse_point;
338 #endif
340 /* Convert modifiers in a NeXTstep event to emacs style modifiers.  */
341 #define NS_FUNCTION_KEY_MASK 0x800000
342 #define NSLeftControlKeyMask    (0x000001 | NSEventModifierFlagControl)
343 #define NSRightControlKeyMask   (0x002000 | NSEventModifierFlagControl)
344 #define NSLeftCommandKeyMask    (0x000008 | NSEventModifierFlagCommand)
345 #define NSRightCommandKeyMask   (0x000010 | NSEventModifierFlagCommand)
346 #define NSLeftAlternateKeyMask  (0x000020 | NSEventModifierFlagOption)
347 #define NSRightAlternateKeyMask (0x000040 | NSEventModifierFlagOption)
348 #define EV_MODIFIERS2(flags)                          \
349     (((flags & NSEventModifierFlagHelp) ?           \
350            hyper_modifier : 0)                        \
351      | (!EQ (ns_right_alternate_modifier, Qleft) && \
352         ((flags & NSRightAlternateKeyMask) \
353          == NSRightAlternateKeyMask) ? \
354            parse_solitary_modifier (ns_right_alternate_modifier) : 0) \
355      | ((flags & NSEventModifierFlagOption) ?                 \
356            parse_solitary_modifier (ns_alternate_modifier) : 0)   \
357      | ((flags & NSEventModifierFlagShift) ?     \
358            shift_modifier : 0)                        \
359      | (!EQ (ns_right_control_modifier, Qleft) && \
360         ((flags & NSRightControlKeyMask) \
361          == NSRightControlKeyMask) ? \
362            parse_solitary_modifier (ns_right_control_modifier) : 0) \
363      | ((flags & NSEventModifierFlagControl) ?      \
364            parse_solitary_modifier (ns_control_modifier) : 0)     \
365      | ((flags & NS_FUNCTION_KEY_MASK) ?  \
366            parse_solitary_modifier (ns_function_modifier) : 0)    \
367      | (!EQ (ns_right_command_modifier, Qleft) && \
368         ((flags & NSRightCommandKeyMask) \
369          == NSRightCommandKeyMask) ? \
370            parse_solitary_modifier (ns_right_command_modifier) : 0) \
371      | ((flags & NSEventModifierFlagCommand) ?      \
372            parse_solitary_modifier (ns_command_modifier):0))
373 #define EV_MODIFIERS(e) EV_MODIFIERS2 ([e modifierFlags])
375 #define EV_UDMODIFIERS(e)                                      \
376     ((([e type] == NSEventTypeLeftMouseDown) ? down_modifier : 0)       \
377      | (([e type] == NSEventTypeRightMouseDown) ? down_modifier : 0)    \
378      | (([e type] == NSEventTypeOtherMouseDown) ? down_modifier : 0)    \
379      | (([e type] == NSEventTypeLeftMouseDragged) ? down_modifier : 0)  \
380      | (([e type] == NSEventTypeRightMouseDragged) ? down_modifier : 0) \
381      | (([e type] == NSEventTypeOtherMouseDragged) ? down_modifier : 0) \
382      | (([e type] == NSEventTypeLeftMouseUp)   ? up_modifier   : 0)     \
383      | (([e type] == NSEventTypeRightMouseUp)   ? up_modifier   : 0)    \
384      | (([e type] == NSEventTypeOtherMouseUp)   ? up_modifier   : 0))
386 #define EV_BUTTON(e)                                                         \
387     ((([e type] == NSEventTypeLeftMouseDown) || ([e type] == NSEventTypeLeftMouseUp)) ? 0 :    \
388       (([e type] == NSEventTypeRightMouseDown) || ([e type] == NSEventTypeRightMouseUp)) ? 2 : \
389      [e buttonNumber] - 1)
391 /* Convert the time field to a timestamp in milliseconds. */
392 #define EV_TIMESTAMP(e) ([e timestamp] * 1000)
394 /* This is a piece of code which is common to all the event handling
395    methods.  Maybe it should even be a function.  */
396 #define EV_TRAILER(e)                                                   \
397   {                                                                     \
398     XSETFRAME (emacs_event->frame_or_window, emacsframe);               \
399     EV_TRAILER2 (e);                                                    \
400   }
402 #define EV_TRAILER2(e)                                                  \
403   {                                                                     \
404       if (e) emacs_event->timestamp = EV_TIMESTAMP (e);                 \
405       if (q_event_ptr)                                                  \
406         {                                                               \
407           Lisp_Object tem = Vinhibit_quit;                              \
408           Vinhibit_quit = Qt;                                           \
409           n_emacs_events_pending++;                                     \
410           kbd_buffer_store_event_hold (emacs_event, q_event_ptr);       \
411           Vinhibit_quit = tem;                                          \
412         }                                                               \
413       else                                                              \
414         hold_event (emacs_event);                                       \
415       EVENT_INIT (*emacs_event);                                        \
416       ns_send_appdefined (-1);                                          \
417     }
420 /* These flags will be OR'd or XOR'd with the NSWindow's styleMask
421    property depending on what we're doing. */
422 #define FRAME_DECORATED_FLAGS (NSWindowStyleMaskTitled              \
423                                | NSWindowStyleMaskResizable         \
424                                | NSWindowStyleMaskMiniaturizable    \
425                                | NSWindowStyleMaskClosable)
426 #define FRAME_UNDECORATED_FLAGS NSWindowStyleMaskBorderless
428 /* TODO: get rid of need for these forward declarations */
429 static void ns_condemn_scroll_bars (struct frame *f);
430 static void ns_judge_scroll_bars (struct frame *f);
433 /* ==========================================================================
435     Utilities
437    ========================================================================== */
439 void
440 ns_set_represented_filename (NSString *fstr, struct frame *f)
442   represented_filename = [fstr retain];
443   represented_frame = f;
446 void
447 ns_init_events (struct input_event *ev)
449   EVENT_INIT (*ev);
450   emacs_event = ev;
453 void
454 ns_finish_events (void)
456   emacs_event = NULL;
459 static void
460 hold_event (struct input_event *event)
462   if (hold_event_q.nr == hold_event_q.cap)
463     {
464       if (hold_event_q.cap == 0) hold_event_q.cap = 10;
465       else hold_event_q.cap *= 2;
466       hold_event_q.q =
467         xrealloc (hold_event_q.q, hold_event_q.cap * sizeof *hold_event_q.q);
468     }
470   hold_event_q.q[hold_event_q.nr++] = *event;
471   /* Make sure ns_read_socket is called, i.e. we have input.  */
472   raise (SIGIO);
473   send_appdefined = YES;
476 static Lisp_Object
477 append2 (Lisp_Object list, Lisp_Object item)
478 /* --------------------------------------------------------------------------
479    Utility to append to a list
480    -------------------------------------------------------------------------- */
482   return CALLN (Fnconc, list, list1 (item));
486 const char *
487 ns_etc_directory (void)
488 /* If running as a self-contained app bundle, return as a string the
489    filename of the etc directory, if present; else nil.  */
491   NSBundle *bundle = [NSBundle mainBundle];
492   NSString *resourceDir = [bundle resourcePath];
493   NSString *resourcePath;
494   NSFileManager *fileManager = [NSFileManager defaultManager];
495   BOOL isDir;
497   resourcePath = [resourceDir stringByAppendingPathComponent: @"etc"];
498   if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
499     {
500       if (isDir) return [resourcePath UTF8String];
501     }
502   return NULL;
506 const char *
507 ns_exec_path (void)
508 /* If running as a self-contained app bundle, return as a path string
509    the filenames of the libexec and bin directories, ie libexec:bin.
510    Otherwise, return nil.
511    Normally, Emacs does not add its own bin/ directory to the PATH.
512    However, a self-contained NS build has a different layout, with
513    bin/ and libexec/ subdirectories in the directory that contains
514    Emacs.app itself.
515    We put libexec first, because init_callproc_1 uses the first
516    element to initialize exec-directory.  An alternative would be
517    for init_callproc to check for invocation-directory/libexec.
520   NSBundle *bundle = [NSBundle mainBundle];
521   NSString *resourceDir = [bundle resourcePath];
522   NSString *binDir = [bundle bundlePath];
523   NSString *resourcePath, *resourcePaths;
524   NSRange range;
525   NSString *pathSeparator = [NSString stringWithFormat: @"%c", SEPCHAR];
526   NSFileManager *fileManager = [NSFileManager defaultManager];
527   NSArray *paths;
528   NSEnumerator *pathEnum;
529   BOOL isDir;
531   range = [resourceDir rangeOfString: @"Contents"];
532   if (range.location != NSNotFound)
533     {
534       binDir = [binDir stringByAppendingPathComponent: @"Contents"];
535 #ifdef NS_IMPL_COCOA
536       binDir = [binDir stringByAppendingPathComponent: @"MacOS"];
537 #endif
538     }
540   paths = [binDir stringsByAppendingPaths:
541                 [NSArray arrayWithObjects: @"libexec", @"bin", nil]];
542   pathEnum = [paths objectEnumerator];
543   resourcePaths = @"";
545   while ((resourcePath = [pathEnum nextObject]))
546     {
547       if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
548         if (isDir)
549           {
550             if ([resourcePaths length] > 0)
551               resourcePaths
552                 = [resourcePaths stringByAppendingString: pathSeparator];
553             resourcePaths
554               = [resourcePaths stringByAppendingString: resourcePath];
555           }
556     }
557   if ([resourcePaths length] > 0) return [resourcePaths UTF8String];
559   return NULL;
563 const char *
564 ns_load_path (void)
565 /* If running as a self-contained app bundle, return as a path string
566    the filenames of the site-lisp and lisp directories.
567    Ie, site-lisp:lisp.  Otherwise, return nil.  */
569   NSBundle *bundle = [NSBundle mainBundle];
570   NSString *resourceDir = [bundle resourcePath];
571   NSString *resourcePath, *resourcePaths;
572   NSString *pathSeparator = [NSString stringWithFormat: @"%c", SEPCHAR];
573   NSFileManager *fileManager = [NSFileManager defaultManager];
574   BOOL isDir;
575   NSArray *paths = [resourceDir stringsByAppendingPaths:
576                               [NSArray arrayWithObjects:
577                                          @"site-lisp", @"lisp", nil]];
578   NSEnumerator *pathEnum = [paths objectEnumerator];
579   resourcePaths = @"";
581   /* Hack to skip site-lisp.  */
582   if (no_site_lisp) resourcePath = [pathEnum nextObject];
584   while ((resourcePath = [pathEnum nextObject]))
585     {
586       if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
587         if (isDir)
588           {
589             if ([resourcePaths length] > 0)
590               resourcePaths
591                 = [resourcePaths stringByAppendingString: pathSeparator];
592             resourcePaths
593               = [resourcePaths stringByAppendingString: resourcePath];
594           }
595     }
596   if ([resourcePaths length] > 0) return [resourcePaths UTF8String];
598   return NULL;
602 void
603 ns_init_locale (void)
604 /* macOS doesn't set any environment variables for the locale when run
605    from the GUI. Get the locale from the OS and set LANG. */
607   NSLocale *locale = [NSLocale currentLocale];
609   NSTRACE ("ns_init_locale");
611   @try
612     {
613       /* It seems macOS should probably use UTF-8 everywhere.
614          'localeIdentifier' does not specify the encoding, and I can't
615          find any way to get the OS to tell us which encoding to use,
616          so hard-code '.UTF-8'. */
617       NSString *localeID = [NSString stringWithFormat:@"%@.UTF-8",
618                                      [locale localeIdentifier]];
620       /* Set LANG to locale, but not if LANG is already set. */
621       setenv("LANG", [localeID UTF8String], 0);
622     }
623   @catch (NSException *e)
624     {
625       NSLog (@"Locale detection failed: %@: %@", [e name], [e reason]);
626     }
630 void
631 ns_release_object (void *obj)
632 /* --------------------------------------------------------------------------
633     Release an object (callable from C)
634    -------------------------------------------------------------------------- */
636     [(id)obj release];
640 void
641 ns_retain_object (void *obj)
642 /* --------------------------------------------------------------------------
643     Retain an object (callable from C)
644    -------------------------------------------------------------------------- */
646     [(id)obj retain];
650 void *
651 ns_alloc_autorelease_pool (void)
652 /* --------------------------------------------------------------------------
653      Allocate a pool for temporary objects (callable from C)
654    -------------------------------------------------------------------------- */
656   return [[NSAutoreleasePool alloc] init];
660 void
661 ns_release_autorelease_pool (void *pool)
662 /* --------------------------------------------------------------------------
663      Free a pool and temporary objects it refers to (callable from C)
664    -------------------------------------------------------------------------- */
666   ns_release_object (pool);
670 static BOOL
671 ns_menu_bar_should_be_hidden (void)
672 /* True, if the menu bar should be hidden.  */
674   return !NILP (ns_auto_hide_menu_bar)
675     && [NSApp respondsToSelector:@selector(setPresentationOptions:)];
679 struct EmacsMargins
681   CGFloat top;
682   CGFloat bottom;
683   CGFloat left;
684   CGFloat right;
688 static struct EmacsMargins
689 ns_screen_margins (NSScreen *screen)
690 /* The parts of SCREEN used by the operating system.  */
692   NSTRACE ("ns_screen_margins");
694   struct EmacsMargins margins;
696   NSRect screenFrame = [screen frame];
697   NSRect screenVisibleFrame = [screen visibleFrame];
699   /* Sometimes, visibleFrame isn't up-to-date with respect to a hidden
700      menu bar, check this explicitly.  */
701   if (ns_menu_bar_should_be_hidden())
702     {
703       margins.top = 0;
704     }
705   else
706     {
707       CGFloat frameTop = screenFrame.origin.y + screenFrame.size.height;
708       CGFloat visibleFrameTop = (screenVisibleFrame.origin.y
709                                  + screenVisibleFrame.size.height);
711       margins.top = frameTop - visibleFrameTop;
712     }
714   {
715     CGFloat frameRight = screenFrame.origin.x + screenFrame.size.width;
716     CGFloat visibleFrameRight = (screenVisibleFrame.origin.x
717                                  + screenVisibleFrame.size.width);
718     margins.right = frameRight - visibleFrameRight;
719   }
721   margins.bottom = screenVisibleFrame.origin.y - screenFrame.origin.y;
722   margins.left   = screenVisibleFrame.origin.x - screenFrame.origin.x;
724   NSTRACE_MSG ("left:%g right:%g top:%g bottom:%g",
725                margins.left,
726                margins.right,
727                margins.top,
728                margins.bottom);
730   return margins;
734 /* A screen margin between 1 and DOCK_IGNORE_LIMIT (inclusive) is
735    assumed to contain a hidden dock.  macOS currently use 4 pixels for
736    this, however, to be future compatible, a larger value is used.  */
737 #define DOCK_IGNORE_LIMIT 6
739 static struct EmacsMargins
740 ns_screen_margins_ignoring_hidden_dock (NSScreen *screen)
741 /* The parts of SCREEN used by the operating system, excluding the parts
742 reserved for an hidden dock.  */
744   NSTRACE ("ns_screen_margins_ignoring_hidden_dock");
746   struct EmacsMargins margins = ns_screen_margins(screen);
748   /* macOS (currently) reserved 4 pixels along the edge where a hidden
749      dock is located.  Unfortunately, it's not possible to find the
750      location and information about if the dock is hidden.  Instead,
751      it is assumed that if the margin of an edge is less than
752      DOCK_IGNORE_LIMIT, it contains a hidden dock.  */
753   if (margins.left <= DOCK_IGNORE_LIMIT)
754     {
755       margins.left = 0;
756     }
757   if (margins.right <= DOCK_IGNORE_LIMIT)
758     {
759       margins.right = 0;
760     }
761   if (margins.top <= DOCK_IGNORE_LIMIT)
762     {
763       margins.top = 0;
764     }
765   /* Note: This doesn't occur in current versions of macOS, but
766      included for completeness and future compatibility.  */
767   if (margins.bottom <= DOCK_IGNORE_LIMIT)
768     {
769       margins.bottom = 0;
770     }
772   NSTRACE_MSG ("left:%g right:%g top:%g bottom:%g",
773                margins.left,
774                margins.right,
775                margins.top,
776                margins.bottom);
778   return margins;
782 static CGFloat
783 ns_menu_bar_height (NSScreen *screen)
784 /* The height of the menu bar, if visible.
786    Note: Don't use this when fullscreen is enabled -- the screen
787    sometimes includes, sometimes excludes the menu bar area.  */
789   struct EmacsMargins margins = ns_screen_margins(screen);
791   CGFloat res = margins.top;
793   NSTRACE ("ns_menu_bar_height " NSTRACE_FMT_RETURN " %.0f", res);
795   return res;
799 static NSRect
800 ns_row_rect (struct window *w, struct glyph_row *row,
801                enum glyph_row_area area)
802 /* Get the row as an NSRect.  */
804   struct frame *f = XFRAME (WINDOW_FRAME (w));
805   NSRect rect;
806   int window_x, window_y, window_width;
808   window_box (w, area, &window_x, &window_y, &window_width, 0);
810   rect.origin.x = window_x;
811   rect.origin.y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, row->y));
812   rect.origin.y = max (rect.origin.y, window_y);
813   rect.size.width = window_width;
814   rect.size.height = row->visible_height;
816   return rect;
820 /* ==========================================================================
822     Focus (clipping) and screen update
824    ========================================================================== */
827 // Window constraining
828 // -------------------
830 // To ensure that the windows are not placed under the menu bar, they
831 // are typically moved by the call-back constrainFrameRect. However,
832 // by overriding it, it's possible to inhibit this, leaving the window
833 // in it's original position.
835 // It's possible to hide the menu bar. However, technically, it's only
836 // possible to hide it when the application is active. To ensure that
837 // this work properly, the menu bar and window constraining are
838 // deferred until the application becomes active.
840 // Even though it's not possible to manually move a window above the
841 // top of the screen, it is allowed if it's done programmatically,
842 // when the menu is hidden. This allows the editable area to cover the
843 // full screen height.
845 // Test cases
846 // ----------
848 // Use the following extra files:
850 //    init.el:
851 //       ;; Hide menu and place frame slightly above the top of the screen.
852 //       (setq ns-auto-hide-menu-bar t)
853 //       (set-frame-position (selected-frame) 0 -20)
855 // Test 1:
857 //    emacs -Q -l init.el
859 //    Result: No menu bar, and the title bar should be above the screen.
861 // Test 2:
863 //    emacs -Q
865 //    Result: Menu bar visible, frame placed immediately below the menu.
868 static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
870   NSTRACE ("constrain_frame_rect(" NSTRACE_FMT_RECT ")",
871              NSTRACE_ARG_RECT (frameRect));
873   // --------------------
874   // Collect information about the screen the frame is covering.
875   //
877   NSArray *screens = [NSScreen screens];
878   NSUInteger nr_screens = [screens count];
880   int i;
882   // The height of the menu bar, if present in any screen the frame is
883   // displayed in.
884   int menu_bar_height = 0;
886   // A rectangle covering all the screen the frame is displayed in.
887   NSRect multiscreenRect = NSMakeRect(0, 0, 0, 0);
888   for (i = 0; i < nr_screens; ++i )
889     {
890       NSScreen *s = [screens objectAtIndex: i];
891       NSRect scrRect = [s frame];
893       NSTRACE_MSG ("Screen %d: " NSTRACE_FMT_RECT,
894                    i, NSTRACE_ARG_RECT (scrRect));
896       if (NSIntersectionRect (frameRect, scrRect).size.height != 0)
897         {
898           multiscreenRect = NSUnionRect (multiscreenRect, scrRect);
900           if (!isFullscreen)
901             {
902               CGFloat screen_menu_bar_height = ns_menu_bar_height (s);
903               menu_bar_height = max(menu_bar_height, screen_menu_bar_height);
904             }
905         }
906     }
908   NSTRACE_RECT ("multiscreenRect", multiscreenRect);
910   NSTRACE_MSG ("menu_bar_height: %d", menu_bar_height);
912   if (multiscreenRect.size.width == 0
913       || multiscreenRect.size.height == 0)
914     {
915       // Failed to find any monitor, give up.
916       NSTRACE_MSG ("multiscreenRect empty");
917       NSTRACE_RETURN_RECT (frameRect);
918       return frameRect;
919     }
922   // --------------------
923   // Find a suitable placement.
924   //
926   if (ns_menu_bar_should_be_hidden())
927     {
928       // When the menu bar is hidden, the user may place part of the
929       // frame above the top of the screen, for example to hide the
930       // title bar.
931       //
932       // Hence, keep the original position.
933     }
934   else
935     {
936       // Ensure that the frame is below the menu bar, or below the top
937       // of the screen.
938       //
939       // This assume that the menu bar is placed at the top in the
940       // rectangle that covers the monitors.  (It doesn't have to be,
941       // but if it's not it's hard to do anything useful.)
942       CGFloat topOfWorkArea = (multiscreenRect.origin.y
943                                + multiscreenRect.size.height
944                                - menu_bar_height);
946       CGFloat topOfFrame = frameRect.origin.y + frameRect.size.height;
947       if (topOfFrame > topOfWorkArea)
948         {
949           frameRect.origin.y -= topOfFrame - topOfWorkArea;
950           NSTRACE_RECT ("After placement adjust", frameRect);
951         }
952     }
954   // Include the following section to restrict frame to the screens.
955   // (If so, update it to allow the frame to stretch down below the
956   // screen.)
957 #if 0
958   // --------------------
959   // Ensure frame doesn't stretch below the screens.
960   //
962   CGFloat diff = multiscreenRect.origin.y - frameRect.origin.y;
964   if (diff > 0)
965     {
966       frameRect.origin.y = multiscreenRect.origin.y;
967       frameRect.size.height -= diff;
968     }
969 #endif
971   NSTRACE_RETURN_RECT (frameRect);
972   return frameRect;
976 static void
977 ns_constrain_all_frames (void)
978 /* --------------------------------------------------------------------------
979      Ensure that the menu bar doesn't cover any frames.
980    -------------------------------------------------------------------------- */
982   Lisp_Object tail, frame;
984   NSTRACE ("ns_constrain_all_frames");
986   block_input ();
988   FOR_EACH_FRAME (tail, frame)
989     {
990       struct frame *f = XFRAME (frame);
991       if (FRAME_NS_P (f))
992         {
993           EmacsView *view = FRAME_NS_VIEW (f);
995           if (![view isFullscreen])
996             {
997               [[view window]
998                 setFrame:constrain_frame_rect([[view window] frame], false)
999                  display:NO];
1000             }
1001         }
1002     }
1004   unblock_input ();
1008 static void
1009 ns_update_auto_hide_menu_bar (void)
1010 /* --------------------------------------------------------------------------
1011      Show or hide the menu bar, based on user setting.
1012    -------------------------------------------------------------------------- */
1014 #ifdef NS_IMPL_COCOA
1015   NSTRACE ("ns_update_auto_hide_menu_bar");
1017   block_input ();
1019   if (NSApp != nil && [NSApp isActive])
1020     {
1021       // Note, "setPresentationOptions" triggers an error unless the
1022       // application is active.
1023       BOOL menu_bar_should_be_hidden = ns_menu_bar_should_be_hidden ();
1025       if (menu_bar_should_be_hidden != ns_menu_bar_is_hidden)
1026         {
1027           NSApplicationPresentationOptions options
1028             = NSApplicationPresentationDefault;
1030           if (menu_bar_should_be_hidden)
1031             options |= NSApplicationPresentationAutoHideMenuBar
1032               | NSApplicationPresentationAutoHideDock;
1034           [NSApp setPresentationOptions: options];
1036           ns_menu_bar_is_hidden = menu_bar_should_be_hidden;
1038           if (!ns_menu_bar_is_hidden)
1039             {
1040               ns_constrain_all_frames ();
1041             }
1042         }
1043     }
1045   unblock_input ();
1046 #endif
1050 static void
1051 ns_update_begin (struct frame *f)
1052 /* --------------------------------------------------------------------------
1053    Prepare for a grouped sequence of drawing calls
1054    external (RIF) call; whole frame, called before update_window_begin
1055    -------------------------------------------------------------------------- */
1057 #ifdef NS_IMPL_COCOA
1058   EmacsView *view = FRAME_NS_VIEW (f);
1060   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_begin");
1062   ns_update_auto_hide_menu_bar ();
1064   if ([view isFullscreen] && [view fsIsNative])
1065   {
1066     // Fix reappearing tool bar in fullscreen for Mac OS X 10.7
1067     BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (f) ? YES : NO;
1068     NSToolbar *toolbar = [FRAME_NS_VIEW (f) toolbar];
1069     if (! tbar_visible != ! [toolbar isVisible])
1070       [toolbar setVisible: tbar_visible];
1071   }
1072 #endif
1076 static void
1077 ns_update_window_begin (struct window *w)
1078 /* --------------------------------------------------------------------------
1079    Prepare for a grouped sequence of drawing calls
1080    external (RIF) call; for one window, called after update_begin
1081    -------------------------------------------------------------------------- */
1083   struct frame *f = XFRAME (WINDOW_FRAME (w));
1084   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
1086   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_window_begin");
1087   w->output_cursor = w->cursor;
1089   block_input ();
1091   if (f == hlinfo->mouse_face_mouse_frame)
1092     {
1093       /* Don't do highlighting for mouse motion during the update.  */
1094       hlinfo->mouse_face_defer = 1;
1096         /* If the frame needs to be redrawn,
1097            simply forget about any prior mouse highlighting.  */
1098       if (FRAME_GARBAGED_P (f))
1099         hlinfo->mouse_face_window = Qnil;
1101       /* (further code for mouse faces ifdef'd out in other terms elided) */
1102     }
1104   unblock_input ();
1108 static void
1109 ns_update_window_end (struct window *w, bool cursor_on_p,
1110                       bool mouse_face_overwritten_p)
1111 /* --------------------------------------------------------------------------
1112    Finished a grouped sequence of drawing calls
1113    external (RIF) call; for one window called before update_end
1114    -------------------------------------------------------------------------- */
1116   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_window_end");
1118   /* note: this fn is nearly identical in all terms */
1119   if (!w->pseudo_window_p)
1120     {
1121       block_input ();
1123       if (cursor_on_p)
1124         display_and_set_cursor (w, 1,
1125                                 w->output_cursor.hpos, w->output_cursor.vpos,
1126                                 w->output_cursor.x, w->output_cursor.y);
1128       if (draw_window_fringes (w, 1))
1129         {
1130           if (WINDOW_RIGHT_DIVIDER_WIDTH (w))
1131             x_draw_right_divider (w);
1132           else
1133             x_draw_vertical_border (w);
1134         }
1136       unblock_input ();
1137     }
1139   /* If a row with mouse-face was overwritten, arrange for
1140      frame_up_to_date to redisplay the mouse highlight.  */
1141   if (mouse_face_overwritten_p)
1142     reset_mouse_highlight (MOUSE_HL_INFO (XFRAME (w->frame)));
1146 static void
1147 ns_update_end (struct frame *f)
1148 /* --------------------------------------------------------------------------
1149    Finished a grouped sequence of drawing calls
1150    external (RIF) call; for whole frame, called after update_window_end
1151    -------------------------------------------------------------------------- */
1153   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_end");
1155 /*   if (f == MOUSE_HL_INFO (f)->mouse_face_mouse_frame) */
1156   MOUSE_HL_INFO (f)->mouse_face_defer = 0;
1160 static BOOL
1161 ns_clip_to_rect (struct frame *f, NSRect *r, int n)
1162 /* --------------------------------------------------------------------------
1163    Clip the drawing area to rectangle r in frame f.  If drawing is not
1164    currently possible mark r as dirty and return NO, otherwise return
1165    YES.
1166    -------------------------------------------------------------------------- */
1168   NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_clip_to_rect");
1169   if (r)
1170     {
1171       NSTRACE_RECT ("r", *r);
1173       if ([NSView focusView] == FRAME_NS_VIEW (f))
1174         {
1175           [[NSGraphicsContext currentContext] saveGraphicsState];
1176           if (n == 2)
1177             NSRectClipList (r, 2);
1178           else
1179             NSRectClip (*r);
1181           return YES;
1182         }
1183       else
1184         {
1185           NSView *view = FRAME_NS_VIEW (f);
1186           int i;
1187           for (i = 0 ; i < n ; i++)
1188             [view setNeedsDisplayInRect:r[i]];
1189         }
1190     }
1192   return NO;
1196 static void
1197 ns_reset_clipping (struct frame *f)
1198 /* Internal: Restore the previous graphics state, unsetting any
1199    clipping areas.  */
1201   NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_reset_clipping");
1203   [[NSGraphicsContext currentContext] restoreGraphicsState];
1207 /* ==========================================================================
1209     Visible bell and beep.
1211    ========================================================================== */
1214 // This bell implementation shows the visual bell image asynchronously
1215 // from the rest of Emacs. This is done by adding a NSView to the
1216 // superview of the Emacs window and removing it using a timer.
1218 // Unfortunately, some Emacs operations, like scrolling, is done using
1219 // low-level primitives that copy the content of the window, including
1220 // the bell image. To some extent, this is handled by removing the
1221 // image prior to scrolling and marking that the window is in need for
1222 // redisplay.
1224 // To test this code, make sure that there is no artifacts of the bell
1225 // image in the following situations. Use a non-empty buffer (like the
1226 // tutorial) to ensure that a scroll is performed:
1228 // * Single-window: C-g C-v
1230 // * Side-by-windows: C-x 3 C-g C-v
1232 // * Windows above each other: C-x 2 C-g C-v
1234 @interface EmacsBell : NSImageView
1236   // Number of currently active bell:s.
1237   unsigned int nestCount;
1238   NSView * mView;
1239   bool isAttached;
1241 - (void)show:(NSView *)view;
1242 - (void)hide;
1243 - (void)remove;
1244 @end
1246 @implementation EmacsBell
1248 - (id)init
1250   NSTRACE ("[EmacsBell init]");
1251   if ((self = [super init]))
1252     {
1253       nestCount = 0;
1254       isAttached = false;
1255 #ifdef NS_IMPL_GNUSTEP
1256       // GNUstep doesn't provide named images.  This was reported in
1257       // 2011, see https://savannah.gnu.org/bugs/?33396
1258       //
1259       // As a drop in replacement, a semitransparent gray square is used.
1260       self.image = [[NSImage alloc] initWithSize:NSMakeSize(32 * 5, 32 * 5)];
1261       [self.image lockFocus];
1262       [[NSColor colorForEmacsRed:0.5 green:0.5 blue:0.5 alpha:0.5] set];
1263       NSRectFill(NSMakeRect(0, 0, 32, 32));
1264       [self.image unlockFocus];
1265 #else
1266       self.image = [NSImage imageNamed:NSImageNameCaution];
1267       [self.image setSize:NSMakeSize(self.image.size.width * 5,
1268                                      self.image.size.height * 5)];
1269 #endif
1270     }
1271   return self;
1274 - (void)show:(NSView *)view
1276   NSTRACE ("[EmacsBell show:]");
1277   NSTRACE_MSG ("nestCount: %u", nestCount);
1279   // Show the image, unless it's already shown.
1280   if (nestCount == 0)
1281     {
1282       NSRect rect = [view bounds];
1283       NSPoint pos;
1284       pos.x = rect.origin.x + (rect.size.width  - self.image.size.width )/2;
1285       pos.y = rect.origin.y + (rect.size.height - self.image.size.height)/2;
1287       [self setFrameOrigin:pos];
1288       [self setFrameSize:self.image.size];
1290       isAttached = true;
1291       mView = view;
1292       [[[view window] contentView] addSubview:self
1293                                    positioned:NSWindowAbove
1294                                    relativeTo:nil];
1295     }
1297   ++nestCount;
1299   [self performSelector:@selector(hide) withObject:self afterDelay:0.5];
1303 - (void)hide
1305   // Note: Trace output from this method isn't shown, reason unknown.
1306   // NSTRACE ("[EmacsBell hide]");
1308   if (nestCount > 0)
1309     --nestCount;
1311   // Remove the image once the last bell became inactive.
1312   if (nestCount == 0)
1313     {
1314       [self remove];
1315     }
1319 -(void)remove
1321   NSTRACE ("[EmacsBell remove]");
1322   if (isAttached)
1323     {
1324       NSTRACE_MSG ("removeFromSuperview");
1325       [self removeFromSuperview];
1326       mView.needsDisplay = YES;
1327       isAttached = false;
1328     }
1331 @end
1334 static EmacsBell * bell_view = nil;
1336 static void
1337 ns_ring_bell (struct frame *f)
1338 /* --------------------------------------------------------------------------
1339      "Beep" routine
1340    -------------------------------------------------------------------------- */
1342   NSTRACE ("ns_ring_bell");
1343   if (visible_bell)
1344     {
1345       struct frame *frame = SELECTED_FRAME ();
1346       NSView *view;
1348       if (bell_view == nil)
1349         {
1350           bell_view = [[EmacsBell alloc] init];
1351           [bell_view retain];
1352         }
1354       block_input ();
1356       view = FRAME_NS_VIEW (frame);
1357       if (view != nil)
1358         {
1359           [bell_view show:view];
1360         }
1362       unblock_input ();
1363     }
1364   else
1365     {
1366       NSBeep ();
1367     }
1371 static void
1372 hide_bell (void)
1373 /* --------------------------------------------------------------------------
1374      Ensure the bell is hidden.
1375    -------------------------------------------------------------------------- */
1377   NSTRACE ("hide_bell");
1379   if (bell_view != nil)
1380     {
1381       [bell_view remove];
1382     }
1386 /* ==========================================================================
1388     Frame / window manager related functions
1390    ========================================================================== */
1393 static void
1394 ns_raise_frame (struct frame *f, BOOL make_key)
1395 /* --------------------------------------------------------------------------
1396      Bring window to foreground and if make_key is YES, give it focus.
1397    -------------------------------------------------------------------------- */
1399   NSView *view;
1401   check_window_system (f);
1402   view = FRAME_NS_VIEW (f);
1403   block_input ();
1404   if (FRAME_VISIBLE_P (f))
1405     {
1406       if (make_key)
1407         [[view window] makeKeyAndOrderFront: NSApp];
1408       else
1409         [[view window] orderFront: NSApp];
1410     }
1411   unblock_input ();
1415 static void
1416 ns_lower_frame (struct frame *f)
1417 /* --------------------------------------------------------------------------
1418      Send window to back
1419    -------------------------------------------------------------------------- */
1421   NSView *view;
1423   check_window_system (f);
1424   view = FRAME_NS_VIEW (f);
1425   block_input ();
1426   [[view window] orderBack: NSApp];
1427   unblock_input ();
1431 static void
1432 ns_frame_raise_lower (struct frame *f, bool raise)
1433 /* --------------------------------------------------------------------------
1434      External (hook)
1435    -------------------------------------------------------------------------- */
1437   NSTRACE ("ns_frame_raise_lower");
1439   if (raise)
1440     ns_raise_frame (f, YES);
1441   else
1442     ns_lower_frame (f);
1446 static void
1447 ns_frame_rehighlight (struct frame *frame)
1448 /* --------------------------------------------------------------------------
1449      External (hook): called on things like window switching within frame
1450    -------------------------------------------------------------------------- */
1452   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
1453   struct frame *old_highlight = dpyinfo->x_highlight_frame;
1455   NSTRACE ("ns_frame_rehighlight");
1456   if (dpyinfo->x_focus_frame)
1457     {
1458       dpyinfo->x_highlight_frame
1459         = (FRAMEP (FRAME_FOCUS_FRAME (dpyinfo->x_focus_frame))
1460            ? XFRAME (FRAME_FOCUS_FRAME (dpyinfo->x_focus_frame))
1461            : dpyinfo->x_focus_frame);
1462       if (!FRAME_LIVE_P (dpyinfo->x_highlight_frame))
1463         {
1464           fset_focus_frame (dpyinfo->x_focus_frame, Qnil);
1465           dpyinfo->x_highlight_frame = dpyinfo->x_focus_frame;
1466         }
1467     }
1468   else
1469       dpyinfo->x_highlight_frame = 0;
1471   if (dpyinfo->x_highlight_frame &&
1472          dpyinfo->x_highlight_frame != old_highlight)
1473     {
1474       if (old_highlight)
1475         {
1476           x_update_cursor (old_highlight, 1);
1477           x_set_frame_alpha (old_highlight);
1478         }
1479       if (dpyinfo->x_highlight_frame)
1480         {
1481           x_update_cursor (dpyinfo->x_highlight_frame, 1);
1482           x_set_frame_alpha (dpyinfo->x_highlight_frame);
1483         }
1484     }
1488 void
1489 x_make_frame_visible (struct frame *f)
1490 /* --------------------------------------------------------------------------
1491      External: Show the window (X11 semantics)
1492    -------------------------------------------------------------------------- */
1494   NSTRACE ("x_make_frame_visible");
1495   /* XXX: at some points in past this was not needed, as the only place that
1496      called this (frame.c:Fraise_frame ()) also called raise_lower;
1497      if this ends up the case again, comment this out again. */
1498   if (!FRAME_VISIBLE_P (f))
1499     {
1500       EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
1501       NSWindow *window = [view window];
1503       SET_FRAME_VISIBLE (f, 1);
1504       ns_raise_frame (f, ! FRAME_NO_FOCUS_ON_MAP (f));
1506       /* Making a new frame from a fullscreen frame will make the new frame
1507          fullscreen also.  So skip handleFS as this will print an error.  */
1508       if ([view fsIsNative] && f->want_fullscreen == FULLSCREEN_BOTH
1509           && [view isFullscreen])
1510         return;
1512       if (f->want_fullscreen != FULLSCREEN_NONE)
1513         {
1514           block_input ();
1515           [view handleFS];
1516           unblock_input ();
1517         }
1519       /* Making a frame invisible seems to break the parent->child
1520          relationship, so reinstate it. */
1521       if ([window parentWindow] == nil && FRAME_PARENT_FRAME (f) != NULL)
1522         {
1523           NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
1525           block_input ();
1526           [parent addChildWindow: window
1527                          ordered: NSWindowAbove];
1528           unblock_input ();
1530           /* If the parent frame moved while the child frame was
1531              invisible, the child frame's position won't have been
1532              updated.  Make sure it's in the right place now. */
1533           x_set_offset(f, f->left_pos, f->top_pos, 0);
1534         }
1535     }
1539 void
1540 x_make_frame_invisible (struct frame *f)
1541 /* --------------------------------------------------------------------------
1542      External: Hide the window (X11 semantics)
1543    -------------------------------------------------------------------------- */
1545   NSView *view;
1546   NSTRACE ("x_make_frame_invisible");
1547   check_window_system (f);
1548   view = FRAME_NS_VIEW (f);
1549   [[view window] orderOut: NSApp];
1550   SET_FRAME_VISIBLE (f, 0);
1551   SET_FRAME_ICONIFIED (f, 0);
1555 void
1556 x_iconify_frame (struct frame *f)
1557 /* --------------------------------------------------------------------------
1558      External: Iconify window
1559    -------------------------------------------------------------------------- */
1561   NSView *view;
1562   struct ns_display_info *dpyinfo;
1564   NSTRACE ("x_iconify_frame");
1565   check_window_system (f);
1566   view = FRAME_NS_VIEW (f);
1567   dpyinfo = FRAME_DISPLAY_INFO (f);
1569   if (dpyinfo->x_highlight_frame == f)
1570     dpyinfo->x_highlight_frame = 0;
1572   if ([[view window] windowNumber] <= 0)
1573     {
1574       /* the window is still deferred.  Make it very small, bring it
1575          on screen and order it out. */
1576       NSRect s = { { 100, 100}, {0, 0} };
1577       NSRect t;
1578       t = [[view window] frame];
1579       [[view window] setFrame: s display: NO];
1580       [[view window] orderBack: NSApp];
1581       [[view window] orderOut: NSApp];
1582       [[view window] setFrame: t display: NO];
1583     }
1585   /* Processing input while Emacs is being minimized can cause a
1586      crash, so block it for the duration. */
1587   block_input();
1588   [[view window] miniaturize: NSApp];
1589   unblock_input();
1592 /* Free X resources of frame F.  */
1594 void
1595 x_free_frame_resources (struct frame *f)
1597   NSView *view;
1598   struct ns_display_info *dpyinfo;
1599   Mouse_HLInfo *hlinfo;
1601   NSTRACE ("x_free_frame_resources");
1602   check_window_system (f);
1603   view = FRAME_NS_VIEW (f);
1604   dpyinfo = FRAME_DISPLAY_INFO (f);
1605   hlinfo = MOUSE_HL_INFO (f);
1607   [(EmacsView *)view setWindowClosing: YES]; /* may not have been informed */
1609   block_input ();
1611   free_frame_menubar (f);
1612   free_frame_faces (f);
1614   if (f == dpyinfo->x_focus_frame)
1615     dpyinfo->x_focus_frame = 0;
1616   if (f == dpyinfo->x_highlight_frame)
1617     dpyinfo->x_highlight_frame = 0;
1618   if (f == hlinfo->mouse_face_mouse_frame)
1619     reset_mouse_highlight (hlinfo);
1620   /* Ensure that sendEvent does not attempt to dereference a freed
1621      frame. (bug#30800) */
1622   if (represented_frame == f)
1623     represented_frame = NULL;
1625   if (f->output_data.ns->miniimage != nil)
1626     [f->output_data.ns->miniimage release];
1628   [[view window] close];
1629   [view release];
1631   xfree (f->output_data.ns);
1633   unblock_input ();
1636 void
1637 x_destroy_window (struct frame *f)
1638 /* --------------------------------------------------------------------------
1639      External: Delete the window
1640    -------------------------------------------------------------------------- */
1642   NSTRACE ("x_destroy_window");
1644   /* If this frame has a parent window, detach it as not doing so can
1645      cause a crash in GNUStep. */
1646   if (FRAME_PARENT_FRAME (f) != NULL)
1647     {
1648       NSWindow *child = [FRAME_NS_VIEW (f) window];
1649       NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
1651       [parent removeChildWindow: child];
1652     }
1654   check_window_system (f);
1655   x_free_frame_resources (f);
1656   ns_window_num--;
1660 void
1661 x_set_offset (struct frame *f, int xoff, int yoff, int change_grav)
1662 /* --------------------------------------------------------------------------
1663      External: Position the window
1664    -------------------------------------------------------------------------- */
1666   NSView *view = FRAME_NS_VIEW (f);
1667   NSArray *screens = [NSScreen screens];
1668   NSScreen *screen = [[view window] screen];
1670   NSTRACE ("x_set_offset");
1672   block_input ();
1674   f->left_pos = xoff;
1675   f->top_pos = yoff;
1677   if (view != nil)
1678     {
1679       if (FRAME_PARENT_FRAME (f) == NULL && screen)
1680         {
1681           f->left_pos = f->size_hint_flags & XNegative
1682             ? [screen visibleFrame].size.width + f->left_pos - FRAME_PIXEL_WIDTH (f)
1683             : f->left_pos;
1684           /* We use visibleFrame here to take menu bar into account.
1685              Ideally we should also adjust left/top with visibleFrame.origin.  */
1687           f->top_pos = f->size_hint_flags & YNegative
1688             ? ([screen visibleFrame].size.height + f->top_pos
1689                - FRAME_PIXEL_HEIGHT (f) - FRAME_NS_TITLEBAR_HEIGHT (f)
1690                - FRAME_TOOLBAR_HEIGHT (f))
1691             : f->top_pos;
1692 #ifdef NS_IMPL_GNUSTEP
1693           if (f->left_pos < 100)
1694             f->left_pos = 100;  /* don't overlap menu */
1695 #endif
1696         }
1697       else if (FRAME_PARENT_FRAME (f) != NULL)
1698         {
1699           struct frame *parent = FRAME_PARENT_FRAME (f);
1701           /* On X negative values for child frames always result in
1702              positioning relative to the bottom right corner of the
1703              parent frame.  */
1704           if (f->left_pos < 0)
1705             f->left_pos = FRAME_PIXEL_WIDTH (parent) - FRAME_PIXEL_WIDTH (f) + f->left_pos;
1707           if (f->top_pos < 0)
1708             f->top_pos = FRAME_PIXEL_HEIGHT (parent) + FRAME_TOOLBAR_HEIGHT (parent)
1709               - FRAME_PIXEL_HEIGHT (f) + f->top_pos;
1710         }
1712       /* Constrain the setFrameTopLeftPoint so we don't move behind the
1713          menu bar.  */
1714       NSPoint pt = NSMakePoint (SCREENMAXBOUND (f->left_pos
1715                                                 + NS_PARENT_WINDOW_LEFT_POS (f)),
1716                                 SCREENMAXBOUND (NS_PARENT_WINDOW_TOP_POS (f)
1717                                                 - f->top_pos));
1718       NSTRACE_POINT ("setFrameTopLeftPoint", pt);
1719       [[view window] setFrameTopLeftPoint: pt];
1720       f->size_hint_flags &= ~(XNegative|YNegative);
1721     }
1723   unblock_input ();
1727 void
1728 x_set_window_size (struct frame *f,
1729                    bool change_gravity,
1730                    int width,
1731                    int height,
1732                    bool pixelwise)
1733 /* --------------------------------------------------------------------------
1734      Adjust window pixel size based on given character grid size
1735      Impl is a bit more complex than other terms, need to do some
1736      internal clipping.
1737    -------------------------------------------------------------------------- */
1739   EmacsView *view = FRAME_NS_VIEW (f);
1740   NSWindow *window = [view window];
1741   NSRect wr = [window frame];
1742   int pixelwidth, pixelheight;
1743   int orig_height = wr.size.height;
1745   NSTRACE ("x_set_window_size");
1747   if (view == nil)
1748     return;
1750   NSTRACE_RECT ("current", wr);
1751   NSTRACE_MSG ("Width:%d Height:%d Pixelwise:%d", width, height, pixelwise);
1752   NSTRACE_MSG ("Font %d x %d", FRAME_COLUMN_WIDTH (f), FRAME_LINE_HEIGHT (f));
1754   block_input ();
1756   if (pixelwise)
1757     {
1758       pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
1759       pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
1760     }
1761   else
1762     {
1763       pixelwidth =  FRAME_TEXT_COLS_TO_PIXEL_WIDTH   (f, width);
1764       pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, height);
1765     }
1767   wr.size.width = pixelwidth + f->border_width;
1768   wr.size.height = pixelheight;
1769   if (! [view isFullscreen])
1770     wr.size.height += FRAME_NS_TITLEBAR_HEIGHT (f)
1771       + FRAME_TOOLBAR_HEIGHT (f);
1773   /* Do not try to constrain to this screen.  We may have multiple
1774      screens, and want Emacs to span those.  Constraining to screen
1775      prevents that, and that is not nice to the user.  */
1776  if (f->output_data.ns->zooming)
1777    f->output_data.ns->zooming = 0;
1778  else
1779    wr.origin.y += orig_height - wr.size.height;
1781  frame_size_history_add
1782    (f, Qx_set_window_size_1, width, height,
1783     list5 (Fcons (make_number (pixelwidth), make_number (pixelheight)),
1784            Fcons (make_number (wr.size.width), make_number (wr.size.height)),
1785            make_number (f->border_width),
1786            make_number (FRAME_NS_TITLEBAR_HEIGHT (f)),
1787            make_number (FRAME_TOOLBAR_HEIGHT (f))));
1789   [window setFrame: wr display: YES];
1791   [view updateFrameSize: NO];
1792   unblock_input ();
1795 #ifdef NS_IMPL_COCOA
1796 void
1797 x_set_undecorated (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1798 /* --------------------------------------------------------------------------
1799      Set frame F's `undecorated' parameter.  If non-nil, F's window-system
1800      window is drawn without decorations, title, minimize/maximize boxes
1801      and external borders.  This usually means that the window cannot be
1802      dragged, resized, iconified, maximized or deleted with the mouse.  If
1803      nil, draw the frame with all the elements listed above unless these
1804      have been suspended via window manager settings.
1806      GNUStep cannot change an existing window's style.
1807    -------------------------------------------------------------------------- */
1809   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
1810   NSWindow *window = [view window];
1812   NSTRACE ("x_set_undecorated");
1814   if (!EQ (new_value, old_value))
1815     {
1816       block_input ();
1818       if (NILP (new_value))
1819         {
1820           FRAME_UNDECORATED (f) = false;
1821           [window setStyleMask: ((window.styleMask | FRAME_DECORATED_FLAGS)
1822                                   ^ FRAME_UNDECORATED_FLAGS)];
1824           [view createToolbar: f];
1825         }
1826       else
1827         {
1828           [window setToolbar: nil];
1829           /* Do I need to release the toolbar here? */
1831           FRAME_UNDECORATED (f) = true;
1832           [window setStyleMask: ((window.styleMask | FRAME_UNDECORATED_FLAGS)
1833                                  ^ FRAME_DECORATED_FLAGS)];
1834         }
1836       /* At this point it seems we don't have an active NSResponder,
1837          so some key presses (TAB) are swallowed by the system. */
1838       [window makeFirstResponder: view];
1840       [view updateFrameSize: NO];
1841       unblock_input ();
1842     }
1844 #endif /* NS_IMPL_COCOA */
1846 void
1847 x_set_parent_frame (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1848 /* --------------------------------------------------------------------------
1849      Set frame F's `parent-frame' parameter.  If non-nil, make F a child
1850      frame of the frame specified by that parameter.  Technically, this
1851      makes F's window-system window a child window of the parent frame's
1852      window-system window.  If nil, make F's window-system window a
1853      top-level window--a child of its display's root window.
1855      A child frame's `left' and `top' parameters specify positions
1856      relative to the top-left corner of its parent frame's native
1857      rectangle.  On macOS moving a parent frame moves all its child
1858      frames too, keeping their position relative to the parent
1859      unaltered.  When a parent frame is iconified or made invisible, its
1860      child frames are made invisible.  When a parent frame is deleted,
1861      its child frames are deleted too.
1863      Whether a child frame has a tool bar may be window-system or window
1864      manager dependent.  It's advisable to disable it via the frame
1865      parameter settings.
1867      Some window managers may not honor this parameter.
1868    -------------------------------------------------------------------------- */
1870   struct frame *p = NULL;
1871   NSWindow *parent, *child;
1873   NSTRACE ("x_set_parent_frame");
1875   if (!NILP (new_value)
1876       && (!FRAMEP (new_value)
1877           || !FRAME_LIVE_P (p = XFRAME (new_value))
1878           || !FRAME_NS_P (p)))
1879     {
1880       store_frame_param (f, Qparent_frame, old_value);
1881       error ("Invalid specification of `parent-frame'");
1882     }
1884   if (p != FRAME_PARENT_FRAME (f))
1885     {
1886       block_input ();
1887       child = [FRAME_NS_VIEW (f) window];
1889       if ([child parentWindow] != nil)
1890         {
1891           [[child parentWindow] removeChildWindow:child];
1892 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
1893 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101000
1894           if ([child respondsToSelector:@selector(setAccessibilitySubrole:)])
1895 #endif
1896               [child setAccessibilitySubrole:NSAccessibilityStandardWindowSubrole];
1897 #endif
1898         }
1900       if (!NILP (new_value))
1901         {
1902           parent = [FRAME_NS_VIEW (p) window];
1904           [parent addChildWindow: child
1905                          ordered: NSWindowAbove];
1906 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
1907 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101000
1908           if ([child respondsToSelector:@selector(setAccessibilitySubrole:)])
1909 #endif
1910               [child setAccessibilitySubrole:NSAccessibilityFloatingWindowSubrole];
1911 #endif
1912         }
1914       unblock_input ();
1916       fset_parent_frame (f, new_value);
1917     }
1920 void
1921 x_set_no_focus_on_map (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1922 /* Set frame F's `no-focus-on-map' parameter which, if non-nil, means
1923  * that F's window-system window does not want to receive input focus
1924  * when it is mapped.  (A frame's window is mapped when the frame is
1925  * displayed for the first time and when the frame changes its state
1926  * from `iconified' or `invisible' to `visible'.)
1928  * Some window managers may not honor this parameter. */
1930   NSTRACE ("x_set_no_focus_on_map");
1932   if (!EQ (new_value, old_value))
1933     {
1934       FRAME_NO_FOCUS_ON_MAP (f) = !NILP (new_value);
1935     }
1938 void
1939 x_set_no_accept_focus (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1940 /*  Set frame F's `no-accept-focus' parameter which, if non-nil, hints
1941  * that F's window-system window does not want to receive input focus
1942  * via mouse clicks or by moving the mouse into it.
1944  * If non-nil, this may have the unwanted side-effect that a user cannot
1945  * scroll a non-selected frame with the mouse.
1947  * Some window managers may not honor this parameter. */
1949   NSTRACE ("x_set_no_accept_focus");
1951   if (!EQ (new_value, old_value))
1952     FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value);
1955 void
1956 x_set_z_group (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1957 /* Set frame F's `z-group' parameter.  If `above', F's window-system
1958    window is displayed above all windows that do not have the `above'
1959    property set.  If nil, F's window is shown below all windows that
1960    have the `above' property set and above all windows that have the
1961    `below' property set.  If `below', F's window is displayed below
1962    all windows that do.
1964    Some window managers may not honor this parameter. */
1966   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
1967   NSWindow *window = [view window];
1969   NSTRACE ("x_set_z_group");
1971   if (NILP (new_value))
1972     {
1973       window.level = NSNormalWindowLevel;
1974       FRAME_Z_GROUP (f) = z_group_none;
1975     }
1976   else if (EQ (new_value, Qabove))
1977     {
1978       window.level = NSNormalWindowLevel + 1;
1979       FRAME_Z_GROUP (f) = z_group_above;
1980     }
1981   else if (EQ (new_value, Qabove_suspended))
1982     {
1983       /* Not sure what level this should be. */
1984       window.level = NSNormalWindowLevel + 1;
1985       FRAME_Z_GROUP (f) = z_group_above_suspended;
1986     }
1987   else if (EQ (new_value, Qbelow))
1988     {
1989       window.level = NSNormalWindowLevel - 1;
1990       FRAME_Z_GROUP (f) = z_group_below;
1991     }
1992   else
1993     error ("Invalid z-group specification");
1996 #ifdef NS_IMPL_COCOA
1997 void
1998 ns_set_appearance (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
2000 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
2001   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2002   NSWindow *window = [view window];
2004   NSTRACE ("ns_set_appearance");
2006 #ifndef NSAppKitVersionNumber10_10
2007 #define NSAppKitVersionNumber10_10 1343
2008 #endif
2010   if (NSAppKitVersionNumber < NSAppKitVersionNumber10_10)
2011     return;
2013   if (EQ (new_value, Qdark))
2014     {
2015       window.appearance = [NSAppearance
2016                             appearanceNamed: NSAppearanceNameVibrantDark];
2017       FRAME_NS_APPEARANCE (f) = ns_appearance_vibrant_dark;
2018     }
2019   else
2020     {
2021       window.appearance = [NSAppearance
2022                             appearanceNamed: NSAppearanceNameAqua];
2023       FRAME_NS_APPEARANCE (f) = ns_appearance_aqua;
2024     }
2025 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 */
2028 void
2029 ns_set_transparent_titlebar (struct frame *f, Lisp_Object new_value,
2030                              Lisp_Object old_value)
2032 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
2033   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2034   NSWindow *window = [view window];
2036   NSTRACE ("ns_set_transparent_titlebar");
2038   if ([window respondsToSelector: @selector(titlebarAppearsTransparent)]
2039       && !EQ (new_value, old_value))
2040     {
2041       window.titlebarAppearsTransparent = !NILP (new_value);
2042       FRAME_NS_TRANSPARENT_TITLEBAR (f) = !NILP (new_value);
2043     }
2044 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 */
2046 #endif /* NS_IMPL_COCOA */
2048 static void
2049 ns_fullscreen_hook (struct frame *f)
2051   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2053   NSTRACE ("ns_fullscreen_hook");
2055   if (!FRAME_VISIBLE_P (f))
2056     return;
2058    if (! [view fsIsNative] && f->want_fullscreen == FULLSCREEN_BOTH)
2059     {
2060       /* Old style fs don't initiate correctly if created from
2061          init/default-frame alist, so use a timer (not nice...).
2062       */
2063       [NSTimer scheduledTimerWithTimeInterval: 0.5 target: view
2064                                      selector: @selector (handleFS)
2065                                      userInfo: nil repeats: NO];
2066       return;
2067     }
2069   block_input ();
2070   [view handleFS];
2071   unblock_input ();
2074 /* ==========================================================================
2076     Color management
2078    ========================================================================== */
2081 NSColor *
2082 ns_lookup_indexed_color (unsigned long idx, struct frame *f)
2084   struct ns_color_table *color_table = FRAME_DISPLAY_INFO (f)->color_table;
2085   if (idx < 1 || idx >= color_table->avail)
2086     return nil;
2087   return color_table->colors[idx];
2091 unsigned long
2092 ns_index_color (NSColor *color, struct frame *f)
2094   struct ns_color_table *color_table = FRAME_DISPLAY_INFO (f)->color_table;
2095   ptrdiff_t idx;
2096   ptrdiff_t i;
2098   if (!color_table->colors)
2099     {
2100       color_table->size = NS_COLOR_CAPACITY;
2101       color_table->avail = 1; /* skip idx=0 as marker */
2102       color_table->colors = xmalloc (color_table->size * sizeof (NSColor *));
2103       color_table->colors[0] = nil;
2104       color_table->empty_indices = [[NSMutableSet alloc] init];
2105     }
2107   /* Do we already have this color?  */
2108   for (i = 1; i < color_table->avail; i++)
2109     if (color_table->colors[i] && [color_table->colors[i] isEqual: color])
2110       return i;
2112   if ([color_table->empty_indices count] > 0)
2113     {
2114       NSNumber *index = [color_table->empty_indices anyObject];
2115       [color_table->empty_indices removeObject: index];
2116       idx = [index unsignedLongValue];
2117     }
2118   else
2119     {
2120       if (color_table->avail == color_table->size)
2121         color_table->colors =
2122           xpalloc (color_table->colors, &color_table->size, 1,
2123                    min (ULONG_MAX, PTRDIFF_MAX), sizeof *color_table->colors);
2124       idx = color_table->avail++;
2125     }
2127   color_table->colors[idx] = color;
2128   [color retain];
2129 /*fprintf(stderr, "color_table: allocated %d\n",idx);*/
2130   return idx;
2134 static int
2135 ns_get_color (const char *name, NSColor **col)
2136 /* --------------------------------------------------------------------------
2137      Parse a color name
2138    -------------------------------------------------------------------------- */
2139 /* On *Step, we attempt to mimic the X11 platform here, down to installing an
2140    X11 rgb.txt-compatible color list in Emacs.clr (see ns_term_init()).
2141    See: http://thread.gmane.org/gmane.emacs.devel/113050/focus=113272). */
2143   NSColor *new = nil;
2144   static char hex[20];
2145   int scaling = 0;
2146   float r = -1.0, g, b;
2147   NSString *nsname = [NSString stringWithUTF8String: name];
2149   NSTRACE ("ns_get_color(%s, **)", name);
2151   block_input ();
2153   if ([nsname isEqualToString: @"ns_selection_bg_color"])
2154     {
2155 #ifdef NS_IMPL_COCOA
2156       NSString *defname = [[NSUserDefaults standardUserDefaults]
2157                             stringForKey: @"AppleHighlightColor"];
2158       if (defname != nil)
2159         nsname = defname;
2160       else
2161 #endif
2162       if ((new = [NSColor selectedTextBackgroundColor]) != nil)
2163         {
2164           *col = [new colorUsingDefaultColorSpace];
2165           unblock_input ();
2166           return 0;
2167         }
2168       else
2169         nsname = NS_SELECTION_BG_COLOR_DEFAULT;
2171       name = [nsname UTF8String];
2172     }
2173   else if ([nsname isEqualToString: @"ns_selection_fg_color"])
2174     {
2175       /* NOTE: macOS applications normally don't set foreground
2176          selection, but text may be unreadable if we don't.
2177       */
2178       if ((new = [NSColor selectedTextColor]) != nil)
2179         {
2180           *col = [new colorUsingDefaultColorSpace];
2181           unblock_input ();
2182           return 0;
2183         }
2185       nsname = NS_SELECTION_FG_COLOR_DEFAULT;
2186       name = [nsname UTF8String];
2187     }
2189   /* First, check for some sort of numeric specification. */
2190   hex[0] = '\0';
2192   if (name[0] == '0' || name[0] == '1' || name[0] == '.')  /* RGB decimal */
2193     {
2194       NSScanner *scanner = [NSScanner scannerWithString: nsname];
2195       [scanner scanFloat: &r];
2196       [scanner scanFloat: &g];
2197       [scanner scanFloat: &b];
2198     }
2199   else if (!strncmp(name, "rgb:", 4))  /* A newer X11 format -- rgb:r/g/b */
2200     scaling = (snprintf (hex, sizeof hex, "%s", name + 4) - 2) / 3;
2201   else if (name[0] == '#')        /* An old X11 format; convert to newer */
2202     {
2203       int len = (strlen(name) - 1);
2204       int start = (len % 3 == 0) ? 1 : len / 4 + 1;
2205       int i;
2206       scaling = strlen(name+start) / 3;
2207       for (i = 0; i < 3; i++)
2208         sprintf (hex + i * (scaling + 1), "%.*s/", scaling,
2209                  name + start + i * scaling);
2210       hex[3 * (scaling + 1) - 1] = '\0';
2211     }
2213   if (hex[0])
2214     {
2215       unsigned int rr, gg, bb;
2216       float fscale = scaling == 4 ? 65535.0 : (scaling == 2 ? 255.0 : 15.0);
2217       if (sscanf (hex, "%x/%x/%x", &rr, &gg, &bb))
2218         {
2219           r = rr / fscale;
2220           g = gg / fscale;
2221           b = bb / fscale;
2222         }
2223     }
2225   if (r >= 0.0F)
2226     {
2227       *col = [NSColor colorForEmacsRed: r green: g blue: b alpha: 1.0];
2228       unblock_input ();
2229       return 0;
2230     }
2232   /* Otherwise, color is expected to be from a list */
2233   {
2234     NSEnumerator *lenum, *cenum;
2235     NSString *name;
2236     NSColorList *clist;
2238 #ifdef NS_IMPL_GNUSTEP
2239     /* XXX: who is wrong, the requestor or the implementation? */
2240     if ([nsname compare: @"Highlight" options: NSCaseInsensitiveSearch]
2241         == NSOrderedSame)
2242       nsname = @"highlightColor";
2243 #endif
2245     lenum = [[NSColorList availableColorLists] objectEnumerator];
2246     while ( (clist = [lenum nextObject]) && new == nil)
2247       {
2248         cenum = [[clist allKeys] objectEnumerator];
2249         while ( (name = [cenum nextObject]) && new == nil )
2250           {
2251             if ([name compare: nsname
2252                       options: NSCaseInsensitiveSearch] == NSOrderedSame )
2253               new = [clist colorWithKey: name];
2254           }
2255       }
2256   }
2258   if (new)
2259     *col = [new colorUsingDefaultColorSpace];
2260   unblock_input ();
2261   return new ? 0 : 1;
2266 ns_lisp_to_color (Lisp_Object color, NSColor **col)
2267 /* --------------------------------------------------------------------------
2268      Convert a Lisp string object to a NS color
2269    -------------------------------------------------------------------------- */
2271   NSTRACE ("ns_lisp_to_color");
2272   if (STRINGP (color))
2273     return ns_get_color (SSDATA (color), col);
2274   else if (SYMBOLP (color))
2275     return ns_get_color (SSDATA (SYMBOL_NAME (color)), col);
2276   return 1;
2280 void
2281 ns_query_color(void *col, XColor *color_def, int setPixel)
2282 /* --------------------------------------------------------------------------
2283          Get ARGB values out of NSColor col and put them into color_def.
2284          If setPixel, set the pixel to a concatenated version.
2285          and set color_def pixel to the resulting index.
2286    -------------------------------------------------------------------------- */
2288   EmacsCGFloat r, g, b, a;
2290   [((NSColor *)col) getRed: &r green: &g blue: &b alpha: &a];
2291   color_def->red   = r * 65535;
2292   color_def->green = g * 65535;
2293   color_def->blue  = b * 65535;
2295   if (setPixel == YES)
2296     color_def->pixel
2297       = ARGB_TO_ULONG((int)(a*255),
2298                       (int)(r*255), (int)(g*255), (int)(b*255));
2302 bool
2303 ns_defined_color (struct frame *f,
2304                   const char *name,
2305                   XColor *color_def,
2306                   bool alloc,
2307                   bool makeIndex)
2308 /* --------------------------------------------------------------------------
2309          Return true if named color found, and set color_def rgb accordingly.
2310          If makeIndex and alloc are nonzero put the color in the color_table,
2311          and set color_def pixel to the resulting index.
2312          If makeIndex is zero, set color_def pixel to ARGB.
2313          Return false if not found
2314    -------------------------------------------------------------------------- */
2316   NSColor *col;
2317   NSTRACE_WHEN (NSTRACE_GROUP_COLOR, "ns_defined_color");
2319   block_input ();
2320   if (ns_get_color (name, &col) != 0) /* Color not found  */
2321     {
2322       unblock_input ();
2323       return 0;
2324     }
2325   if (makeIndex && alloc)
2326     color_def->pixel = ns_index_color (col, f);
2327   ns_query_color (col, color_def, !makeIndex);
2328   unblock_input ();
2329   return 1;
2333 void
2334 x_set_frame_alpha (struct frame *f)
2335 /* --------------------------------------------------------------------------
2336      change the entire-frame transparency
2337    -------------------------------------------------------------------------- */
2339   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
2340   double alpha = 1.0;
2341   double alpha_min = 1.0;
2343   NSTRACE ("x_set_frame_alpha");
2345   if (dpyinfo->x_highlight_frame == f)
2346     alpha = f->alpha[0];
2347   else
2348     alpha = f->alpha[1];
2350   if (FLOATP (Vframe_alpha_lower_limit))
2351     alpha_min = XFLOAT_DATA (Vframe_alpha_lower_limit);
2352   else if (INTEGERP (Vframe_alpha_lower_limit))
2353     alpha_min = (XINT (Vframe_alpha_lower_limit)) / 100.0;
2355   if (alpha < 0.0)
2356     return;
2357   else if (1.0 < alpha)
2358     alpha = 1.0;
2359   else if (0.0 <= alpha && alpha < alpha_min && alpha_min <= 1.0)
2360     alpha = alpha_min;
2362 #ifdef NS_IMPL_COCOA
2363   {
2364     EmacsView *view = FRAME_NS_VIEW (f);
2365   [[view window] setAlphaValue: alpha];
2366   }
2367 #endif
2371 /* ==========================================================================
2373     Mouse handling
2375    ========================================================================== */
2378 void
2379 frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y)
2380 /* --------------------------------------------------------------------------
2381      Programmatically reposition mouse pointer in pixel coordinates
2382    -------------------------------------------------------------------------- */
2384   NSTRACE ("frame_set_mouse_pixel_position");
2386   /* FIXME: what about GNUstep? */
2387 #ifdef NS_IMPL_COCOA
2388   CGPoint mouse_pos =
2389     CGPointMake(f->left_pos + pix_x,
2390                 f->top_pos + pix_y +
2391                 FRAME_NS_TITLEBAR_HEIGHT(f) + FRAME_TOOLBAR_HEIGHT(f));
2392   CGWarpMouseCursorPosition (mouse_pos);
2393 #endif
2396 static int
2397 note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y)
2398 /*   ------------------------------------------------------------------------
2399      Called by EmacsView on mouseMovement events.  Passes on
2400      to emacs mainstream code if we moved off of a rect of interest
2401      known as last_mouse_glyph.
2402      ------------------------------------------------------------------------ */
2404   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
2405   NSRect *r;
2407 //  NSTRACE ("note_mouse_movement");
2409   dpyinfo->last_mouse_motion_frame = frame;
2410   r = &dpyinfo->last_mouse_glyph;
2412   /* Note, this doesn't get called for enter/leave, since we don't have a
2413      position.  Those are taken care of in the corresponding NSView methods. */
2415   /* has movement gone beyond last rect we were tracking? */
2416   if (x < r->origin.x || x >= r->origin.x + r->size.width
2417       || y < r->origin.y || y >= r->origin.y + r->size.height)
2418     {
2419       ns_update_begin (frame);
2420       frame->mouse_moved = 1;
2421       note_mouse_highlight (frame, x, y);
2422       remember_mouse_glyph (frame, x, y, r);
2423       ns_update_end (frame);
2424       return 1;
2425     }
2427   return 0;
2431 static void
2432 ns_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window,
2433                    enum scroll_bar_part *part, Lisp_Object *x, Lisp_Object *y,
2434                    Time *time)
2435 /* --------------------------------------------------------------------------
2436     External (hook): inform emacs about mouse position and hit parts.
2437     If a scrollbar is being dragged, set bar_window, part, x, y, time.
2438     x & y should be position in the scrollbar (the whole bar, not the handle)
2439     and length of scrollbar respectively
2440    -------------------------------------------------------------------------- */
2442   id view;
2443   NSPoint position;
2444   Lisp_Object frame, tail;
2445   struct frame *f;
2446   struct ns_display_info *dpyinfo;
2448   NSTRACE ("ns_mouse_position");
2450   if (*fp == NULL)
2451     {
2452       fprintf (stderr, "Warning: ns_mouse_position () called with null *fp.\n");
2453       return;
2454     }
2456   dpyinfo = FRAME_DISPLAY_INFO (*fp);
2458   block_input ();
2460   /* Clear the mouse-moved flag for every frame on this display.  */
2461   FOR_EACH_FRAME (tail, frame)
2462     if (FRAME_NS_P (XFRAME (frame))
2463         && FRAME_NS_DISPLAY (XFRAME (frame)) == FRAME_NS_DISPLAY (*fp))
2464       XFRAME (frame)->mouse_moved = 0;
2466   dpyinfo->last_mouse_scroll_bar = nil;
2467   if (dpyinfo->last_mouse_frame
2468       && FRAME_LIVE_P (dpyinfo->last_mouse_frame))
2469     f = dpyinfo->last_mouse_frame;
2470   else
2471     f = dpyinfo->x_focus_frame ? dpyinfo->x_focus_frame : SELECTED_FRAME ();
2473   if (f && FRAME_NS_P (f))
2474     {
2475       view = FRAME_NS_VIEW (f);
2477       position = [[view window] mouseLocationOutsideOfEventStream];
2478       position = [view convertPoint: position fromView: nil];
2479       remember_mouse_glyph (f, position.x, position.y,
2480                             &dpyinfo->last_mouse_glyph);
2481       NSTRACE_POINT ("position", position);
2483       if (bar_window) *bar_window = Qnil;
2484       if (part) *part = scroll_bar_above_handle;
2486       if (x) XSETINT (*x, lrint (position.x));
2487       if (y) XSETINT (*y, lrint (position.y));
2488       if (time)
2489         *time = dpyinfo->last_mouse_movement_time;
2490       *fp = f;
2491     }
2493   unblock_input ();
2497 static void
2498 ns_frame_up_to_date (struct frame *f)
2499 /* --------------------------------------------------------------------------
2500     External (hook): Fix up mouse highlighting right after a full update.
2501     Can't use FRAME_MOUSE_UPDATE due to ns_frame_begin and ns_frame_end calls.
2502    -------------------------------------------------------------------------- */
2504   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_frame_up_to_date");
2506   if (FRAME_NS_P (f))
2507     {
2508       Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
2509       if (f == hlinfo->mouse_face_mouse_frame)
2510         {
2511           block_input ();
2512           ns_update_begin(f);
2513           note_mouse_highlight (hlinfo->mouse_face_mouse_frame,
2514                                 hlinfo->mouse_face_mouse_x,
2515                                 hlinfo->mouse_face_mouse_y);
2516           ns_update_end(f);
2517           unblock_input ();
2518         }
2519     }
2523 static void
2524 ns_define_frame_cursor (struct frame *f, Cursor cursor)
2525 /* --------------------------------------------------------------------------
2526     External (RIF): set frame mouse pointer type.
2527    -------------------------------------------------------------------------- */
2529   NSTRACE ("ns_define_frame_cursor");
2530   if (FRAME_POINTER_TYPE (f) != cursor)
2531     {
2532       EmacsView *view = FRAME_NS_VIEW (f);
2533       FRAME_POINTER_TYPE (f) = cursor;
2534       [[view window] invalidateCursorRectsForView: view];
2535       /* Redisplay assumes this function also draws the changed frame
2536          cursor, but this function doesn't, so do it explicitly.  */
2537       x_update_cursor (f, 1);
2538     }
2543 /* ==========================================================================
2545     Keyboard handling
2547    ========================================================================== */
2550 static unsigned
2551 ns_convert_key (unsigned code)
2552 /* --------------------------------------------------------------------------
2553     Internal call used by NSView-keyDown.
2554    -------------------------------------------------------------------------- */
2556   const unsigned last_keysym = ARRAYELTS (convert_ns_to_X_keysym);
2557   unsigned keysym;
2558   /* An array would be faster, but less easy to read. */
2559   for (keysym = 0; keysym < last_keysym; keysym += 2)
2560     if (code == convert_ns_to_X_keysym[keysym])
2561       return 0xFF00 | convert_ns_to_X_keysym[keysym+1];
2562   return 0;
2563 /* if decide to use keyCode and Carbon table, use this line:
2564      return code > 0xff ? 0 : 0xFF00 | ns_keycode_to_xkeysym_table[code]; */
2568 char *
2569 x_get_keysym_name (int keysym)
2570 /* --------------------------------------------------------------------------
2571     Called by keyboard.c.  Not sure if the return val is important, except
2572     that it be unique.
2573    -------------------------------------------------------------------------- */
2575   static char value[16];
2576   NSTRACE ("x_get_keysym_name");
2577   sprintf (value, "%d", keysym);
2578   return value;
2583 /* ==========================================================================
2585     Block drawing operations
2587    ========================================================================== */
2590 static void
2591 ns_redraw_scroll_bars (struct frame *f)
2593   int i;
2594   id view;
2595   NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews];
2596   NSTRACE ("ns_redraw_scroll_bars");
2597   for (i =[subviews count]-1; i >= 0; i--)
2598     {
2599       view = [subviews objectAtIndex: i];
2600       if (![view isKindOfClass: [EmacsScroller class]]) continue;
2601       [view display];
2602     }
2606 void
2607 ns_clear_frame (struct frame *f)
2608 /* --------------------------------------------------------------------------
2609       External (hook): Erase the entire frame
2610    -------------------------------------------------------------------------- */
2612   NSView *view = FRAME_NS_VIEW (f);
2613   NSRect r;
2615   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_clear_frame");
2617  /* comes on initial frame because we have
2618     after-make-frame-functions = select-frame */
2619  if (!FRAME_DEFAULT_FACE (f))
2620    return;
2622   mark_window_cursors_off (XWINDOW (FRAME_ROOT_WINDOW (f)));
2624   r = [view bounds];
2626   block_input ();
2627   if (ns_clip_to_rect (f, &r, 1))
2628     {
2629       [ns_lookup_indexed_color (NS_FACE_BACKGROUND
2630                                 (FACE_FROM_ID (f, DEFAULT_FACE_ID)), f) set];
2631       NSRectFill (r);
2632       ns_reset_clipping (f);
2634       /* as of 2006/11 or so this is now needed */
2635       ns_redraw_scroll_bars (f);
2636     }
2637   unblock_input ();
2641 static void
2642 ns_clear_frame_area (struct frame *f, int x, int y, int width, int height)
2643 /* --------------------------------------------------------------------------
2644     External (RIF):  Clear section of frame
2645    -------------------------------------------------------------------------- */
2647   NSRect r = NSMakeRect (x, y, width, height);
2648   NSView *view = FRAME_NS_VIEW (f);
2649   struct face *face = FRAME_DEFAULT_FACE (f);
2651   if (!view || !face)
2652     return;
2654   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_clear_frame_area");
2656   r = NSIntersectionRect (r, [view frame]);
2657   if (ns_clip_to_rect (f, &r, 1))
2658     {
2659       [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), f) set];
2661       NSRectFill (r);
2663       ns_reset_clipping (f);
2664     }
2667 static void
2668 ns_copy_bits (struct frame *f, NSRect src, NSRect dest)
2670   NSSize delta = NSMakeSize (dest.origin.x - src.origin.x,
2671                              dest.origin.y - src.origin.y);
2672   NSTRACE ("ns_copy_bits");
2674   if (FRAME_NS_VIEW (f))
2675     {
2676       hide_bell();              // Ensure the bell image isn't scrolled.
2678       /* FIXME: scrollRect:by: is deprecated in macOS 10.14.  There is
2679          no obvious replacement so we may have to come up with our own.  */
2680       [FRAME_NS_VIEW (f) scrollRect: src by: delta];
2682 #ifdef NS_IMPL_COCOA
2683       /* As far as I can tell from the documentation, scrollRect:by:,
2684          above, should copy the dirty rectangles from our source
2685          rectangle to our destination, however it appears it clips the
2686          operation to src.  As a result we need to use
2687          translateRectsNeedingDisplayInRect:by: below, and we have to
2688          union src and dest so it can pick up the dirty rectangles,
2689          and place them, as it also clips to the rectangle.
2691          FIXME: We need a GNUstep equivalent.  */
2692       [FRAME_NS_VIEW (f) translateRectsNeedingDisplayInRect:NSUnionRect (src, dest)
2693                                                          by:delta];
2694 #endif
2695     }
2698 static void
2699 ns_scroll_run (struct window *w, struct run *run)
2700 /* --------------------------------------------------------------------------
2701     External (RIF):  Insert or delete n lines at line vpos
2702    -------------------------------------------------------------------------- */
2704   struct frame *f = XFRAME (w->frame);
2705   int x, y, width, height, from_y, to_y, bottom_y;
2707   NSTRACE ("ns_scroll_run");
2709   /* begin copy from other terms */
2710   /* Get frame-relative bounding box of the text display area of W,
2711      without mode lines.  Include in this box the left and right
2712      fringe of W.  */
2713   window_box (w, ANY_AREA, &x, &y, &width, &height);
2715   from_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->current_y);
2716   to_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->desired_y);
2717   bottom_y = y + height;
2719   if (to_y < from_y)
2720     {
2721       /* Scrolling up.  Make sure we don't copy part of the mode
2722          line at the bottom.  */
2723       if (from_y + run->height > bottom_y)
2724         height = bottom_y - from_y;
2725       else
2726         height = run->height;
2727     }
2728   else
2729     {
2730       /* Scrolling down.  Make sure we don't copy over the mode line.
2731          at the bottom.  */
2732       if (to_y + run->height > bottom_y)
2733         height = bottom_y - to_y;
2734       else
2735         height = run->height;
2736     }
2737   /* end copy from other terms */
2739   if (height == 0)
2740       return;
2742   block_input ();
2744   x_clear_cursor (w);
2746   {
2747     NSRect srcRect = NSMakeRect (x, from_y, width, height);
2748     NSRect dstRect = NSMakeRect (x, to_y, width, height);
2750     ns_copy_bits (f, srcRect , dstRect);
2751   }
2753   unblock_input ();
2757 static void
2758 ns_after_update_window_line (struct window *w, struct glyph_row *desired_row)
2759 /* --------------------------------------------------------------------------
2760     External (RIF): preparatory to fringe update after text was updated
2761    -------------------------------------------------------------------------- */
2763   struct frame *f;
2764   int width, height;
2766   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_after_update_window_line");
2768   /* begin copy from other terms */
2769   eassert (w);
2771   if (!desired_row->mode_line_p && !w->pseudo_window_p)
2772     desired_row->redraw_fringe_bitmaps_p = 1;
2774   /* When a window has disappeared, make sure that no rest of
2775      full-width rows stays visible in the internal border.  */
2776   if (windows_or_buffers_changed
2777       && desired_row->full_width_p
2778       && (f = XFRAME (w->frame),
2779           width = FRAME_INTERNAL_BORDER_WIDTH (f),
2780           width != 0)
2781       && (height = desired_row->visible_height,
2782           height > 0))
2783     {
2784       int y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, desired_row->y));
2786       block_input ();
2787       ns_clear_frame_area (f, 0, y, width, height);
2788       ns_clear_frame_area (f,
2789                            FRAME_PIXEL_WIDTH (f) - width,
2790                            y, width, height);
2791       unblock_input ();
2792     }
2796 static void
2797 ns_shift_glyphs_for_insert (struct frame *f,
2798                            int x, int y, int width, int height,
2799                            int shift_by)
2800 /* --------------------------------------------------------------------------
2801     External (RIF): copy an area horizontally, don't worry about clearing src
2802    -------------------------------------------------------------------------- */
2804   //NSRect srcRect = NSMakeRect (x, y, width, height);
2805   NSRect dstRect = NSMakeRect (x+shift_by, y, width, height);
2807   NSTRACE ("ns_shift_glyphs_for_insert");
2809   /* This doesn't work now as we copy the "bits" before we've had a
2810      chance to actually draw any changes to the screen.  This means in
2811      certain circumstances we end up with copies of the cursor all
2812      over the place.  Just mark the area dirty so it is redrawn later.
2814      FIXME: Work out how to do this properly.  */
2815   // ns_copy_bits (f, srcRect, dstRect);
2817   [FRAME_NS_VIEW (f) setNeedsDisplayInRect:dstRect];
2822 /* ==========================================================================
2824     Character encoding and metrics
2826    ========================================================================== */
2829 static void
2830 ns_compute_glyph_string_overhangs (struct glyph_string *s)
2831 /* --------------------------------------------------------------------------
2832      External (RIF); compute left/right overhang of whole string and set in s
2833    -------------------------------------------------------------------------- */
2835   struct font *font = s->font;
2837   if (s->char2b)
2838     {
2839       struct font_metrics metrics;
2840       unsigned int codes[2];
2841       codes[0] = *(s->char2b);
2842       codes[1] = *(s->char2b + s->nchars - 1);
2844       font->driver->text_extents (font, codes, 2, &metrics);
2845       s->left_overhang = -metrics.lbearing;
2846       s->right_overhang
2847         = metrics.rbearing > metrics.width
2848         ? metrics.rbearing - metrics.width : 0;
2849     }
2850   else
2851     {
2852       s->left_overhang = 0;
2853       if (EQ (font->driver->type, Qns))
2854         s->right_overhang = ((struct nsfont_info *)font)->ital ?
2855           FONT_HEIGHT (font) * 0.2 : 0;
2856       else
2857         s->right_overhang = 0;
2858     }
2863 /* ==========================================================================
2865     Fringe and cursor drawing
2867    ========================================================================== */
2870 extern int max_used_fringe_bitmap;
2871 static void
2872 ns_draw_fringe_bitmap (struct window *w, struct glyph_row *row,
2873                       struct draw_fringe_bitmap_params *p)
2874 /* --------------------------------------------------------------------------
2875     External (RIF); fringe-related
2876    -------------------------------------------------------------------------- */
2878   /* Fringe bitmaps comes in two variants, normal and periodic.  A
2879      periodic bitmap is used to create a continuous pattern.  Since a
2880      bitmap is rendered one text line at a time, the start offset (dh)
2881      of the bitmap varies.  Concretely, this is used for the empty
2882      line indicator.
2884      For a bitmap, "h + dh" is the full height and is always
2885      invariant.  For a normal bitmap "dh" is zero.
2887      For example, when the period is three and the full height is 72
2888      the following combinations exists:
2890        h=72 dh=0
2891        h=71 dh=1
2892        h=70 dh=2 */
2894   struct frame *f = XFRAME (WINDOW_FRAME (w));
2895   struct face *face = p->face;
2896   static EmacsImage **bimgs = NULL;
2897   static int nBimgs = 0;
2898   NSRect clearRect = NSZeroRect;
2899   NSRect imageRect = NSZeroRect;
2900   NSRect rowRect = ns_row_rect (w, row, ANY_AREA);
2902   NSTRACE_WHEN (NSTRACE_GROUP_FRINGE, "ns_draw_fringe_bitmap");
2903   NSTRACE_MSG ("which:%d cursor:%d overlay:%d width:%d height:%d period:%d",
2904                p->which, p->cursor_p, p->overlay_p, p->wd, p->h, p->dh);
2906   /* grow bimgs if needed */
2907   if (nBimgs < max_used_fringe_bitmap)
2908     {
2909       bimgs = xrealloc (bimgs, max_used_fringe_bitmap * sizeof *bimgs);
2910       memset (bimgs + nBimgs, 0,
2911               (max_used_fringe_bitmap - nBimgs) * sizeof *bimgs);
2912       nBimgs = max_used_fringe_bitmap;
2913     }
2915   /* Work out the rectangle we will composite into.  */
2916   if (p->which)
2917     imageRect = NSMakeRect (p->x, p->y, p->wd, p->h);
2919   /* Work out the rectangle we will need to clear.  Because we're
2920      compositing rather than blitting, we need to clear the area under
2921      the image regardless of anything else.  */
2922   if (p->bx >= 0 && !p->overlay_p)
2923     {
2924       clearRect = NSMakeRect (p->bx, p->by, p->nx, p->ny);
2925       clearRect = NSUnionRect (clearRect, imageRect);
2926     }
2927   else
2928     {
2929       clearRect = imageRect;
2930     }
2932   /* Handle partially visible rows.  */
2933   clearRect = NSIntersectionRect (clearRect, rowRect);
2935   /* The visible portion of imageRect will always be contained within
2936      clearRect.  */
2937   if (ns_clip_to_rect (f, &clearRect, 1))
2938     {
2939       if (! NSIsEmptyRect (clearRect))
2940         {
2941           NSTRACE_RECT ("clearRect", clearRect);
2943           [ns_lookup_indexed_color(face->background, f) set];
2944           NSRectFill (clearRect);
2945         }
2947       if (p->which)
2948         {
2949           EmacsImage *img = bimgs[p->which - 1];
2951           if (!img)
2952             {
2953               // Note: For "periodic" images, allocate one EmacsImage for
2954               // the base image, and use it for all dh:s.
2955               unsigned short *bits = p->bits;
2956               int full_height = p->h + p->dh;
2957               int i;
2958               unsigned char *cbits = xmalloc (full_height);
2960               for (i = 0; i < full_height; i++)
2961                 cbits[i] = bits[i];
2962               img = [[EmacsImage alloc] initFromXBM: cbits width: 8
2963                                              height: full_height
2964                                                  fg: 0 bg: 0];
2965               bimgs[p->which - 1] = img;
2966               xfree (cbits);
2967             }
2970           {
2971             NSColor *bm_color;
2972             if (!p->cursor_p)
2973               bm_color = ns_lookup_indexed_color(face->foreground, f);
2974             else if (p->overlay_p)
2975               bm_color = ns_lookup_indexed_color(face->background, f);
2976             else
2977               bm_color = f->output_data.ns->cursor_color;
2978             [img setXBMColor: bm_color];
2979           }
2981 #ifdef NS_IMPL_COCOA
2982           // Note: For periodic images, the full image height is "h + hd".
2983           // By using the height h, a suitable part of the image is used.
2984           NSRect fromRect = NSMakeRect(0, 0, p->wd, p->h);
2986           NSTRACE_RECT ("fromRect", fromRect);
2988           [img drawInRect: imageRect
2989                  fromRect: fromRect
2990                 operation: NSCompositingOperationSourceOver
2991                  fraction: 1.0
2992                respectFlipped: YES
2993                     hints: nil];
2994 #else
2995           {
2996             NSPoint pt = imageRect.origin;
2997             pt.y += p->h;
2998             [img compositeToPoint: pt operation: NSCompositingOperationSourceOver];
2999           }
3000 #endif
3001         }
3002       ns_reset_clipping (f);
3003     }
3007 static void
3008 ns_draw_window_cursor (struct window *w, struct glyph_row *glyph_row,
3009                        int x, int y, enum text_cursor_kinds cursor_type,
3010                        int cursor_width, bool on_p, bool active_p)
3011 /* --------------------------------------------------------------------------
3012      External call (RIF): draw cursor.
3013      Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
3014    -------------------------------------------------------------------------- */
3016   NSRect r, s;
3017   int fx, fy, h, cursor_height;
3018   struct frame *f = WINDOW_XFRAME (w);
3019   struct glyph *phys_cursor_glyph;
3020   struct glyph *cursor_glyph;
3021   struct face *face;
3022   NSColor *hollow_color = FRAME_BACKGROUND_COLOR (f);
3024   /* If cursor is out of bounds, don't draw garbage.  This can happen
3025      in mini-buffer windows when switching between echo area glyphs
3026      and mini-buffer.  */
3028   NSTRACE ("ns_draw_window_cursor");
3030   if (!on_p)
3031     return;
3033   w->phys_cursor_type = cursor_type;
3034   w->phys_cursor_on_p = on_p;
3036   if (cursor_type == NO_CURSOR)
3037     {
3038       w->phys_cursor_width = 0;
3039       return;
3040     }
3042   if ((phys_cursor_glyph = get_phys_cursor_glyph (w)) == NULL)
3043     {
3044       if (glyph_row->exact_window_width_line_p
3045           && w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])
3046         {
3047           glyph_row->cursor_in_fringe_p = 1;
3048           draw_fringe_bitmap (w, glyph_row, 0);
3049         }
3050       return;
3051     }
3053   /* We draw the cursor (with NSRectFill), then draw the glyph on top
3054      (other terminals do it the other way round).  We must set
3055      w->phys_cursor_width to the cursor width.  For bar cursors, that
3056      is CURSOR_WIDTH; for box cursors, it is the glyph width.  */
3057   get_phys_cursor_geometry (w, glyph_row, phys_cursor_glyph, &fx, &fy, &h);
3059   /* The above get_phys_cursor_geometry call set w->phys_cursor_width
3060      to the glyph width; replace with CURSOR_WIDTH for (V)BAR cursors. */
3061   if (cursor_type == BAR_CURSOR)
3062     {
3063       if (cursor_width < 1)
3064         cursor_width = max (FRAME_CURSOR_WIDTH (f), 1);
3066       /* The bar cursor should never be wider than the glyph. */
3067       if (cursor_width < w->phys_cursor_width)
3068         w->phys_cursor_width = cursor_width;
3069     }
3070   /* If we have an HBAR, "cursor_width" MAY specify height. */
3071   else if (cursor_type == HBAR_CURSOR)
3072     {
3073       cursor_height = (cursor_width < 1) ? lrint (0.25 * h) : cursor_width;
3074       if (cursor_height > glyph_row->height)
3075         cursor_height = glyph_row->height;
3076       if (h > cursor_height) // Cursor smaller than line height, move down
3077         fy += h - cursor_height;
3078       h = cursor_height;
3079     }
3081   r.origin.x = fx, r.origin.y = fy;
3082   r.size.height = h;
3083   r.size.width = w->phys_cursor_width;
3085   /* Prevent the cursor from being drawn outside the text area.  */
3086   r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA));
3088   if (ns_clip_to_rect (f, &r, 1))
3089     {
3090       face = FACE_FROM_ID_OR_NULL (f, phys_cursor_glyph->face_id);
3091       if (face && NS_FACE_BACKGROUND (face)
3092           == ns_index_color (FRAME_CURSOR_COLOR (f), f))
3093         {
3094           [ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), f) set];
3095           hollow_color = FRAME_CURSOR_COLOR (f);
3096         }
3097       else
3098         [FRAME_CURSOR_COLOR (f) set];
3100       switch (cursor_type)
3101         {
3102         case DEFAULT_CURSOR:
3103         case NO_CURSOR:
3104           break;
3105         case FILLED_BOX_CURSOR:
3106           NSRectFill (r);
3107           break;
3108         case HOLLOW_BOX_CURSOR:
3109           NSRectFill (r);
3110           [hollow_color set];
3111           NSRectFill (NSInsetRect (r, 1, 1));
3112           [FRAME_CURSOR_COLOR (f) set];
3113           break;
3114         case HBAR_CURSOR:
3115           NSRectFill (r);
3116           break;
3117         case BAR_CURSOR:
3118           s = r;
3119           /* If the character under cursor is R2L, draw the bar cursor
3120              on the right of its glyph, rather than on the left.  */
3121           cursor_glyph = get_phys_cursor_glyph (w);
3122           if ((cursor_glyph->resolved_level & 1) != 0)
3123             s.origin.x += cursor_glyph->pixel_width - s.size.width;
3125           NSRectFill (s);
3126           break;
3127         }
3129       /* draw the character under the cursor */
3130       if (cursor_type != NO_CURSOR)
3131         draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
3133       ns_reset_clipping (f);
3134     }
3135   else if (! redisplaying_p)
3136     {
3137       /* If this function is called outside redisplay, it probably
3138          means we need an immediate update.  */
3139       [FRAME_NS_VIEW (f) display];
3140     }
3144 static void
3145 ns_draw_vertical_window_border (struct window *w, int x, int y0, int y1)
3146 /* --------------------------------------------------------------------------
3147      External (RIF): Draw a vertical line.
3148    -------------------------------------------------------------------------- */
3150   struct frame *f = XFRAME (WINDOW_FRAME (w));
3151   struct face *face;
3152   NSRect r = NSMakeRect (x, y0, 1, y1-y0);
3154   NSTRACE ("ns_draw_vertical_window_border");
3156   face = FACE_FROM_ID_OR_NULL (f, VERTICAL_BORDER_FACE_ID);
3158   if (ns_clip_to_rect (f, &r, 1))
3159     {
3160       if (face)
3161         [ns_lookup_indexed_color(face->foreground, f) set];
3163       NSRectFill(r);
3164       ns_reset_clipping (f);
3165     }
3169 static void
3170 ns_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1)
3171 /* --------------------------------------------------------------------------
3172      External (RIF): Draw a window divider.
3173    -------------------------------------------------------------------------- */
3175   struct frame *f = XFRAME (WINDOW_FRAME (w));
3176   struct face *face = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FACE_ID);
3177   struct face *face_first
3178     = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FIRST_PIXEL_FACE_ID);
3179   struct face *face_last
3180     = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_LAST_PIXEL_FACE_ID);
3181   unsigned long color = face ? face->foreground : FRAME_FOREGROUND_PIXEL (f);
3182   unsigned long color_first = (face_first
3183                                ? face_first->foreground
3184                                : FRAME_FOREGROUND_PIXEL (f));
3185   unsigned long color_last = (face_last
3186                               ? face_last->foreground
3187                               : FRAME_FOREGROUND_PIXEL (f));
3188   NSRect divider = NSMakeRect (x0, y0, x1-x0, y1-y0);
3190   NSTRACE ("ns_draw_window_divider");
3192   if (ns_clip_to_rect (f, &divider, 1))
3193     {
3194       if ((y1 - y0 > x1 - x0) && (x1 - x0 >= 3))
3195         /* A vertical divider, at least three pixels wide: Draw first and
3196            last pixels differently.  */
3197         {
3198           [ns_lookup_indexed_color(color_first, f) set];
3199           NSRectFill(NSMakeRect (x0, y0, 1, y1 - y0));
3200           [ns_lookup_indexed_color(color, f) set];
3201           NSRectFill(NSMakeRect (x0 + 1, y0, x1 - x0 - 2, y1 - y0));
3202           [ns_lookup_indexed_color(color_last, f) set];
3203           NSRectFill(NSMakeRect (x1 - 1, y0, 1, y1 - y0));
3204         }
3205       else if ((x1 - x0 > y1 - y0) && (y1 - y0 >= 3))
3206         /* A horizontal divider, at least three pixels high: Draw first and
3207            last pixels differently.  */
3208         {
3209           [ns_lookup_indexed_color(color_first, f) set];
3210           NSRectFill(NSMakeRect (x0, y0, x1 - x0, 1));
3211           [ns_lookup_indexed_color(color, f) set];
3212           NSRectFill(NSMakeRect (x0, y0 + 1, x1 - x0, y1 - y0 - 2));
3213           [ns_lookup_indexed_color(color_last, f) set];
3214           NSRectFill(NSMakeRect (x0, y1 - 1, x1 - x0, 1));
3215         }
3216       else
3217         {
3218           /* In any other case do not draw the first and last pixels
3219              differently.  */
3220           [ns_lookup_indexed_color(color, f) set];
3221           NSRectFill(divider);
3222         }
3224       ns_reset_clipping (f);
3225     }
3228 static void
3229 ns_show_hourglass (struct frame *f)
3231   /* TODO: add NSProgressIndicator to all frames.  */
3234 static void
3235 ns_hide_hourglass (struct frame *f)
3237   /* TODO: remove NSProgressIndicator from all frames.  */
3240 /* ==========================================================================
3242     Glyph drawing operations
3244    ========================================================================== */
3246 static int
3247 ns_get_glyph_string_clip_rect (struct glyph_string *s, NativeRectangle *nr)
3248 /* --------------------------------------------------------------------------
3249     Wrapper utility to account for internal border width on full-width lines,
3250     and allow top full-width rows to hit the frame top.  nr should be pointer
3251     to two successive NSRects.  Number of rects actually used is returned.
3252    -------------------------------------------------------------------------- */
3254   int n = get_glyph_string_clip_rects (s, nr, 2);
3255   return n;
3258 /* --------------------------------------------------------------------
3259    Draw a wavy line under glyph string s. The wave fills wave_height
3260    pixels from y.
3262                     x          wave_length = 2
3263                                  --
3264                 y    *   *   *   *   *
3265                      |* * * * * * * * *
3266     wave_height = 3  | *   *   *   *
3267   --------------------------------------------------------------------- */
3269 static void
3270 ns_draw_underwave (struct glyph_string *s, EmacsCGFloat width, EmacsCGFloat x)
3272   int wave_height = 3, wave_length = 2;
3273   int y, dx, dy, odd, xmax;
3274   NSPoint a, b;
3275   NSRect waveClip;
3277   dx = wave_length;
3278   dy = wave_height - 1;
3279   y =  s->ybase - wave_height + 3;
3280   xmax = x + width;
3282   /* Find and set clipping rectangle */
3283   waveClip = NSMakeRect (x, y, width, wave_height);
3284   [[NSGraphicsContext currentContext] saveGraphicsState];
3285   NSRectClip (waveClip);
3287   /* Draw the waves */
3288   a.x = x - ((int)(x) % dx) + (EmacsCGFloat) 0.5;
3289   b.x = a.x + dx;
3290   odd = (int)(a.x/dx) % 2;
3291   a.y = b.y = y + 0.5;
3293   if (odd)
3294     a.y += dy;
3295   else
3296     b.y += dy;
3298   while (a.x <= xmax)
3299     {
3300       [NSBezierPath strokeLineFromPoint:a toPoint:b];
3301       a.x = b.x, a.y = b.y;
3302       b.x += dx, b.y = y + 0.5 + odd*dy;
3303       odd = !odd;
3304     }
3306   /* Restore previous clipping rectangle(s) */
3307   [[NSGraphicsContext currentContext] restoreGraphicsState];
3312 static void
3313 ns_draw_text_decoration (struct glyph_string *s, struct face *face,
3314                          NSColor *defaultCol, CGFloat width, CGFloat x)
3315 /* --------------------------------------------------------------------------
3316    Draw underline, overline, and strike-through on glyph string s.
3317    -------------------------------------------------------------------------- */
3319   if (s->for_overlaps)
3320     return;
3322   /* Do underline. */
3323   if (face->underline_p)
3324     {
3325       if (s->face->underline_type == FACE_UNDER_WAVE)
3326         {
3327           if (face->underline_defaulted_p)
3328             [defaultCol set];
3329           else
3330             [ns_lookup_indexed_color (face->underline_color, s->f) set];
3332           ns_draw_underwave (s, width, x);
3333         }
3334       else if (s->face->underline_type == FACE_UNDER_LINE)
3335         {
3337           NSRect r;
3338           unsigned long thickness, position;
3340           /* If the prev was underlined, match its appearance. */
3341           if (s->prev && s->prev->face->underline_p
3342               && s->prev->face->underline_type == FACE_UNDER_LINE
3343               && s->prev->underline_thickness > 0)
3344             {
3345               thickness = s->prev->underline_thickness;
3346               position = s->prev->underline_position;
3347             }
3348           else
3349             {
3350               struct font *font = font_for_underline_metrics (s);
3351               unsigned long descent = s->y + s->height - s->ybase;
3353               /* Use underline thickness of font, defaulting to 1. */
3354               thickness = (font && font->underline_thickness > 0)
3355                 ? font->underline_thickness : 1;
3357               /* Determine the offset of underlining from the baseline. */
3358               if (x_underline_at_descent_line)
3359                 position = descent - thickness;
3360               else if (x_use_underline_position_properties
3361                        && font && font->underline_position >= 0)
3362                 position = font->underline_position;
3363               else if (font)
3364                 position = lround (font->descent / 2);
3365               else
3366                 position = underline_minimum_offset;
3368               position = max (position, underline_minimum_offset);
3370               /* Ensure underlining is not cropped. */
3371               if (descent <= position)
3372                 {
3373                   position = descent - 1;
3374                   thickness = 1;
3375                 }
3376               else if (descent < position + thickness)
3377                 thickness = 1;
3378             }
3380           s->underline_thickness = thickness;
3381           s->underline_position = position;
3383           r = NSMakeRect (x, s->ybase + position, width, thickness);
3385           if (face->underline_defaulted_p)
3386             [defaultCol set];
3387           else
3388             [ns_lookup_indexed_color (face->underline_color, s->f) set];
3389           NSRectFill (r);
3390         }
3391     }
3392   /* Do overline. We follow other terms in using a thickness of 1
3393      and ignoring overline_margin. */
3394   if (face->overline_p)
3395     {
3396       NSRect r;
3397       r = NSMakeRect (x, s->y, width, 1);
3399       if (face->overline_color_defaulted_p)
3400         [defaultCol set];
3401       else
3402         [ns_lookup_indexed_color (face->overline_color, s->f) set];
3403       NSRectFill (r);
3404     }
3406   /* Do strike-through.  We follow other terms for thickness and
3407      vertical position.*/
3408   if (face->strike_through_p)
3409     {
3410       NSRect r;
3411       /* Y-coordinate and height of the glyph string's first glyph.
3412          We cannot use s->y and s->height because those could be
3413          larger if there are taller display elements (e.g., characters
3414          displayed with a larger font) in the same glyph row.  */
3415       int glyph_y = s->ybase - s->first_glyph->ascent;
3416       int glyph_height = s->first_glyph->ascent + s->first_glyph->descent;
3417       /* Strike-through width and offset from the glyph string's
3418          top edge.  */
3419       unsigned long h = 1;
3420       unsigned long dy;
3422       dy = lrint ((glyph_height - h) / 2);
3423       r = NSMakeRect (x, glyph_y + dy, width, 1);
3425       if (face->strike_through_color_defaulted_p)
3426         [defaultCol set];
3427       else
3428         [ns_lookup_indexed_color (face->strike_through_color, s->f) set];
3429       NSRectFill (r);
3430     }
3433 static void
3434 ns_draw_box (NSRect r, CGFloat thickness, NSColor *col,
3435              char left_p, char right_p)
3436 /* --------------------------------------------------------------------------
3437     Draw an unfilled rect inside r, optionally leaving left and/or right open.
3438     Note we can't just use an NSDrawRect command, because of the possibility
3439     of some sides not being drawn, and because the rect will be filled.
3440    -------------------------------------------------------------------------- */
3442   NSRect s = r;
3443   [col set];
3445   /* top, bottom */
3446   s.size.height = thickness;
3447   NSRectFill (s);
3448   s.origin.y += r.size.height - thickness;
3449   NSRectFill (s);
3451   s.size.height = r.size.height;
3452   s.origin.y = r.origin.y;
3454   /* left, right (optional) */
3455   s.size.width = thickness;
3456   if (left_p)
3457     NSRectFill (s);
3458   if (right_p)
3459     {
3460       s.origin.x += r.size.width - thickness;
3461       NSRectFill (s);
3462     }
3466 static void
3467 ns_draw_relief (NSRect r, int thickness, char raised_p,
3468                char top_p, char bottom_p, char left_p, char right_p,
3469                struct glyph_string *s)
3470 /* --------------------------------------------------------------------------
3471     Draw a relief rect inside r, optionally leaving some sides open.
3472     Note we can't just use an NSDrawBezel command, because of the possibility
3473     of some sides not being drawn, and because the rect will be filled.
3474    -------------------------------------------------------------------------- */
3476   static NSColor *baseCol = nil, *lightCol = nil, *darkCol = nil;
3477   NSColor *newBaseCol = nil;
3478   NSRect sr = r;
3480   NSTRACE ("ns_draw_relief");
3482   /* set up colors */
3484   if (s->face->use_box_color_for_shadows_p)
3485     {
3486       newBaseCol = ns_lookup_indexed_color (s->face->box_color, s->f);
3487     }
3488 /*     else if (s->first_glyph->type == IMAGE_GLYPH
3489            && s->img->pixmap
3490            && !IMAGE_BACKGROUND_TRANSPARENT (s->img, s->f, 0))
3491        {
3492          newBaseCol = IMAGE_BACKGROUND  (s->img, s->f, 0);
3493        } */
3494   else
3495     {
3496       newBaseCol = ns_lookup_indexed_color (s->face->background, s->f);
3497     }
3499   if (newBaseCol == nil)
3500     newBaseCol = [NSColor grayColor];
3502   if (newBaseCol != baseCol)  /* TODO: better check */
3503     {
3504       [baseCol release];
3505       baseCol = [newBaseCol retain];
3506       [lightCol release];
3507       lightCol = [[baseCol highlightWithLevel: 0.2] retain];
3508       [darkCol release];
3509       darkCol = [[baseCol shadowWithLevel: 0.3] retain];
3510     }
3512   [(raised_p ? lightCol : darkCol) set];
3514   /* TODO: mitering. Using NSBezierPath doesn't work because of color switch. */
3516   /* top */
3517   sr.size.height = thickness;
3518   if (top_p) NSRectFill (sr);
3520   /* left */
3521   sr.size.height = r.size.height;
3522   sr.size.width = thickness;
3523   if (left_p) NSRectFill (sr);
3525   [(raised_p ? darkCol : lightCol) set];
3527   /* bottom */
3528   sr.size.width = r.size.width;
3529   sr.size.height = thickness;
3530   sr.origin.y += r.size.height - thickness;
3531   if (bottom_p) NSRectFill (sr);
3533   /* right */
3534   sr.size.height = r.size.height;
3535   sr.origin.y = r.origin.y;
3536   sr.size.width = thickness;
3537   sr.origin.x += r.size.width - thickness;
3538   if (right_p) NSRectFill (sr);
3542 static void
3543 ns_dumpglyphs_box_or_relief (struct glyph_string *s)
3544 /* --------------------------------------------------------------------------
3545       Function modeled after x_draw_glyph_string_box ().
3546       Sets up parameters for drawing.
3547    -------------------------------------------------------------------------- */
3549   int right_x, last_x;
3550   char left_p, right_p;
3551   struct glyph *last_glyph;
3552   NSRect r;
3553   int thickness;
3554   struct face *face;
3556   if (s->hl == DRAW_MOUSE_FACE)
3557     {
3558       face = FACE_FROM_ID_OR_NULL (s->f,
3559                                    MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3560       if (!face)
3561         face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3562     }
3563   else
3564     face = s->face;
3566   thickness = face->box_line_width;
3568   NSTRACE ("ns_dumpglyphs_box_or_relief");
3570   last_x = ((s->row->full_width_p && !s->w->pseudo_window_p)
3571             ? WINDOW_RIGHT_EDGE_X (s->w)
3572             : window_box_right (s->w, s->area));
3573   last_glyph = (s->cmp || s->img
3574                 ? s->first_glyph : s->first_glyph + s->nchars-1);
3576   right_x = ((s->row->full_width_p && s->extends_to_end_of_line_p
3577               ? last_x - 1 : min (last_x, s->x + s->background_width) - 1));
3579   left_p = (s->first_glyph->left_box_line_p
3580             || (s->hl == DRAW_MOUSE_FACE
3581                 && (s->prev == NULL || s->prev->hl != s->hl)));
3582   right_p = (last_glyph->right_box_line_p
3583              || (s->hl == DRAW_MOUSE_FACE
3584                  && (s->next == NULL || s->next->hl != s->hl)));
3586   r = NSMakeRect (s->x, s->y, right_x - s->x + 1, s->height);
3588   /* TODO: Sometimes box_color is 0 and this seems wrong; should investigate. */
3589   if (s->face->box == FACE_SIMPLE_BOX && s->face->box_color)
3590     {
3591       ns_draw_box (r, abs (thickness),
3592                    ns_lookup_indexed_color (face->box_color, s->f),
3593                   left_p, right_p);
3594     }
3595   else
3596     {
3597       ns_draw_relief (r, abs (thickness), s->face->box == FACE_RAISED_BOX,
3598                      1, 1, left_p, right_p, s);
3599     }
3603 static void
3604 ns_maybe_dumpglyphs_background (struct glyph_string *s, char force_p)
3605 /* --------------------------------------------------------------------------
3606       Modeled after x_draw_glyph_string_background, which draws BG in
3607       certain cases.  Others are left to the text rendering routine.
3608    -------------------------------------------------------------------------- */
3610   NSTRACE ("ns_maybe_dumpglyphs_background");
3612   if (!s->background_filled_p/* || s->hl == DRAW_MOUSE_FACE*/)
3613     {
3614       int box_line_width = max (s->face->box_line_width, 0);
3615       if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width
3616           /* When xdisp.c ignores FONT_HEIGHT, we cannot trust font
3617              dimensions, since the actual glyphs might be much
3618              smaller.  So in that case we always clear the rectangle
3619              with background color.  */
3620           || FONT_TOO_HIGH (s->font)
3621           || s->font_not_found_p || s->extends_to_end_of_line_p || force_p)
3622         {
3623           struct face *face;
3624           if (s->hl == DRAW_MOUSE_FACE)
3625             {
3626               face
3627                 = FACE_FROM_ID_OR_NULL (s->f,
3628                                         MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3629               if (!face)
3630                 face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3631             }
3632           else
3633             face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3634           if (!face->stipple)
3635             [(NS_FACE_BACKGROUND (face) != 0
3636               ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
3637               : FRAME_BACKGROUND_COLOR (s->f)) set];
3638           else
3639             {
3640               struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
3641               [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set];
3642             }
3644           if (s->hl != DRAW_CURSOR)
3645             {
3646               NSRect r = NSMakeRect (s->x, s->y + box_line_width,
3647                                     s->background_width,
3648                                     s->height-2*box_line_width);
3649               NSRectFill (r);
3650             }
3652           s->background_filled_p = 1;
3653         }
3654     }
3658 static void
3659 ns_dumpglyphs_image (struct glyph_string *s, NSRect r)
3660 /* --------------------------------------------------------------------------
3661       Renders an image and associated borders.
3662    -------------------------------------------------------------------------- */
3664   EmacsImage *img = s->img->pixmap;
3665   int box_line_vwidth = max (s->face->box_line_width, 0);
3666   int x = s->x, y = s->ybase - image_ascent (s->img, s->face, &s->slice);
3667   int bg_x, bg_y, bg_height;
3668   int th;
3669   char raised_p;
3670   NSRect br;
3671   struct face *face;
3672   NSColor *tdCol;
3674   NSTRACE ("ns_dumpglyphs_image");
3676   if (s->face->box != FACE_NO_BOX
3677       && s->first_glyph->left_box_line_p && s->slice.x == 0)
3678     x += abs (s->face->box_line_width);
3680   bg_x = x;
3681   bg_y =  s->slice.y == 0 ? s->y : s->y + box_line_vwidth;
3682   bg_height = s->height;
3683   /* other terms have this, but was causing problems w/tabbar mode */
3684   /* - 2 * box_line_vwidth; */
3686   if (s->slice.x == 0) x += s->img->hmargin;
3687   if (s->slice.y == 0) y += s->img->vmargin;
3689   /* Draw BG: if we need larger area than image itself cleared, do that,
3690      otherwise, since we composite the image under NS (instead of mucking
3691      with its background color), we must clear just the image area. */
3692   if (s->hl == DRAW_MOUSE_FACE)
3693     {
3694       face = FACE_FROM_ID_OR_NULL (s->f,
3695                                    MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3696       if (!face)
3697        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3698     }
3699   else
3700     face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3702   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f) set];
3704   if (bg_height > s->slice.height || s->img->hmargin || s->img->vmargin
3705       || s->img->mask || s->img->pixmap == 0 || s->width != s->background_width)
3706     {
3707       br = NSMakeRect (bg_x, bg_y, s->background_width, bg_height);
3708       s->background_filled_p = 1;
3709     }
3710   else
3711     {
3712       br = NSMakeRect (x, y, s->slice.width, s->slice.height);
3713     }
3715   NSRectFill (br);
3717   /* Draw the image.. do we need to draw placeholder if img ==nil? */
3718   if (img != nil)
3719     {
3720 #ifdef NS_IMPL_COCOA
3721       NSRect dr = NSMakeRect (x, y, s->slice.width, s->slice.height);
3722       NSRect ir = NSMakeRect (s->slice.x,
3723                               s->img->height - s->slice.y - s->slice.height,
3724                               s->slice.width, s->slice.height);
3725       [img drawInRect: dr
3726              fromRect: ir
3727              operation: NSCompositingOperationSourceOver
3728               fraction: 1.0
3729            respectFlipped: YES
3730                 hints: nil];
3731 #else
3732       [img compositeToPoint: NSMakePoint (x, y + s->slice.height)
3733                   operation: NSCompositingOperationSourceOver];
3734 #endif
3735     }
3737   if (s->hl == DRAW_CURSOR)
3738     {
3739     [FRAME_CURSOR_COLOR (s->f) set];
3740     if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
3741       tdCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
3742     else
3743       /* Currently on NS img->mask is always 0. Since
3744          get_window_cursor_type specifies a hollow box cursor when on
3745          a non-masked image we never reach this clause. But we put it
3746          in, in anticipation of better support for image masks on
3747          NS. */
3748       tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3749     }
3750   else
3751     {
3752       tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3753     }
3755   /* Draw underline, overline, strike-through. */
3756   ns_draw_text_decoration (s, face, tdCol, br.size.width, br.origin.x);
3758   /* Draw relief, if requested */
3759   if (s->img->relief || s->hl ==DRAW_IMAGE_RAISED || s->hl ==DRAW_IMAGE_SUNKEN)
3760     {
3761       if (s->hl == DRAW_IMAGE_SUNKEN || s->hl == DRAW_IMAGE_RAISED)
3762         {
3763           th = tool_bar_button_relief >= 0 ?
3764             tool_bar_button_relief : DEFAULT_TOOL_BAR_BUTTON_RELIEF;
3765           raised_p = (s->hl == DRAW_IMAGE_RAISED);
3766         }
3767       else
3768         {
3769           th = abs (s->img->relief);
3770           raised_p = (s->img->relief > 0);
3771         }
3773       r.origin.x = x - th;
3774       r.origin.y = y - th;
3775       r.size.width = s->slice.width + 2*th-1;
3776       r.size.height = s->slice.height + 2*th-1;
3777       ns_draw_relief (r, th, raised_p,
3778                       s->slice.y == 0,
3779                       s->slice.y + s->slice.height == s->img->height,
3780                       s->slice.x == 0,
3781                       s->slice.x + s->slice.width == s->img->width, s);
3782     }
3784   /* If there is no mask, the background won't be seen,
3785      so draw a rectangle on the image for the cursor.
3786      Do this for all images, getting transparency right is not reliable.  */
3787   if (s->hl == DRAW_CURSOR)
3788     {
3789       int thickness = abs (s->img->relief);
3790       if (thickness == 0) thickness = 1;
3791       ns_draw_box (br, thickness, FRAME_CURSOR_COLOR (s->f), 1, 1);
3792     }
3796 static void
3797 ns_dumpglyphs_stretch (struct glyph_string *s)
3799   NSRect r[2];
3800   int n, i;
3801   struct face *face;
3802   NSColor *fgCol, *bgCol;
3804   if (!s->background_filled_p)
3805     {
3806       n = ns_get_glyph_string_clip_rect (s, r);
3807       *r = NSMakeRect (s->x, s->y, s->background_width, s->height);
3809       if (ns_clip_to_rect (s->f, r, n))
3810         {
3811           if (s->hl == DRAW_MOUSE_FACE)
3812             {
3813               face = FACE_FROM_ID_OR_NULL (s->f,
3814                                            MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3815               if (!face)
3816                 face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3817             }
3818           else
3819             face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3821           bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
3822           fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3824           for (i = 0; i < n; ++i)
3825             {
3826               if (!s->row->full_width_p)
3827                 {
3828                   int overrun, leftoverrun;
3830                   /* truncate to avoid overwriting fringe and/or scrollbar */
3831                   overrun = max (0, (s->x + s->background_width)
3832                                  - (WINDOW_BOX_RIGHT_EDGE_X (s->w)
3833                                     - WINDOW_RIGHT_FRINGE_WIDTH (s->w)));
3834                   r[i].size.width -= overrun;
3836                   /* truncate to avoid overwriting to left of the window box */
3837                   leftoverrun = (WINDOW_BOX_LEFT_EDGE_X (s->w)
3838                                  + WINDOW_LEFT_FRINGE_WIDTH (s->w)) - s->x;
3840                     if (leftoverrun > 0)
3841                       {
3842                         r[i].origin.x += leftoverrun;
3843                         r[i].size.width -= leftoverrun;
3844                       }
3846                     /* XXX: Try to work between problem where a stretch glyph on
3847                        a partially-visible bottom row will clear part of the
3848                        modeline, and another where list-buffers headers and similar
3849                        rows erroneously have visible_height set to 0.  Not sure
3850                        where this is coming from as other terms seem not to show.  */
3851                     r[i].size.height = min (s->height, s->row->visible_height);
3852                 }
3854               [bgCol set];
3856               /* NOTE: under NS this is NOT used to draw cursors, but we must avoid
3857                  overwriting cursor (usually when cursor on a tab).  */
3858               if (s->hl == DRAW_CURSOR)
3859                 {
3860                   CGFloat x, width;
3862                   x = r[i].origin.x;
3863                   width = s->w->phys_cursor_width;
3864                   r[i].size.width -= width;
3865                   r[i].origin.x += width;
3867                   NSRectFill (r[i]);
3869                   /* Draw overlining, etc. on the cursor.  */
3870                   if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
3871                     ns_draw_text_decoration (s, face, bgCol, width, x);
3872                   else
3873                     ns_draw_text_decoration (s, face, fgCol, width, x);
3874                 }
3875               else
3876                 {
3877                   NSRectFill (r[i]);
3878                 }
3880               /* Draw overlining, etc. on the stretch glyph (or the part
3881                  of the stretch glyph after the cursor).  */
3882               ns_draw_text_decoration (s, face, fgCol, r[i].size.width,
3883                                        r[i].origin.x);
3884             }
3885           ns_reset_clipping (s->f);
3886         }
3887       s->background_filled_p = 1;
3888     }
3892 static void
3893 ns_draw_glyph_string_foreground (struct glyph_string *s)
3895   int x, flags;
3896   struct font *font = s->font;
3898   /* If first glyph of S has a left box line, start drawing the text
3899      of S to the right of that box line.  */
3900   if (s->face && s->face->box != FACE_NO_BOX
3901       && s->first_glyph->left_box_line_p)
3902     x = s->x + eabs (s->face->box_line_width);
3903   else
3904     x = s->x;
3906   flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
3907     (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
3908      (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
3909       NS_DUMPGLYPH_NORMAL));
3911   font->driver->draw
3912     (s, s->cmp_from, s->nchars, x, s->ybase,
3913      (flags == NS_DUMPGLYPH_NORMAL && !s->background_filled_p)
3914      || flags == NS_DUMPGLYPH_MOUSEFACE);
3918 static void
3919 ns_draw_composite_glyph_string_foreground (struct glyph_string *s)
3921   int i, j, x;
3922   struct font *font = s->font;
3924   /* If first glyph of S has a left box line, start drawing the text
3925      of S to the right of that box line.  */
3926   if (s->face && s->face->box != FACE_NO_BOX
3927       && s->first_glyph->left_box_line_p)
3928     x = s->x + eabs (s->face->box_line_width);
3929   else
3930     x = s->x;
3932   /* S is a glyph string for a composition.  S->cmp_from is the index
3933      of the first character drawn for glyphs of this composition.
3934      S->cmp_from == 0 means we are drawing the very first character of
3935      this composition.  */
3937   /* Draw a rectangle for the composition if the font for the very
3938      first character of the composition could not be loaded.  */
3939   if (s->font_not_found_p)
3940     {
3941       if (s->cmp_from == 0)
3942         {
3943           NSRect r = NSMakeRect (s->x, s->y, s->width-1, s->height -1);
3944           ns_draw_box (r, 1, FRAME_CURSOR_COLOR (s->f), 1, 1);
3945         }
3946     }
3947   else if (! s->first_glyph->u.cmp.automatic)
3948     {
3949       int y = s->ybase;
3951       for (i = 0, j = s->cmp_from; i < s->nchars; i++, j++)
3952         /* TAB in a composition means display glyphs with padding
3953            space on the left or right.  */
3954         if (COMPOSITION_GLYPH (s->cmp, j) != '\t')
3955           {
3956             int xx = x + s->cmp->offsets[j * 2];
3957             int yy = y - s->cmp->offsets[j * 2 + 1];
3959             font->driver->draw (s, j, j + 1, xx, yy, false);
3960             if (s->face->overstrike)
3961               font->driver->draw (s, j, j + 1, xx + 1, yy, false);
3962           }
3963     }
3964   else
3965     {
3966       Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
3967       Lisp_Object glyph;
3968       int y = s->ybase;
3969       int width = 0;
3971       for (i = j = s->cmp_from; i < s->cmp_to; i++)
3972         {
3973           glyph = LGSTRING_GLYPH (gstring, i);
3974           if (NILP (LGLYPH_ADJUSTMENT (glyph)))
3975             width += LGLYPH_WIDTH (glyph);
3976           else
3977             {
3978               int xoff, yoff, wadjust;
3980               if (j < i)
3981                 {
3982                   font->driver->draw (s, j, i, x, y, false);
3983                   if (s->face->overstrike)
3984                     font->driver->draw (s, j, i, x + 1, y, false);
3985                   x += width;
3986                 }
3987               xoff = LGLYPH_XOFF (glyph);
3988               yoff = LGLYPH_YOFF (glyph);
3989               wadjust = LGLYPH_WADJUST (glyph);
3990               font->driver->draw (s, i, i + 1, x + xoff, y + yoff, false);
3991               if (s->face->overstrike)
3992                 font->driver->draw (s, i, i + 1, x + xoff + 1, y + yoff,
3993                                     false);
3994               x += wadjust;
3995               j = i + 1;
3996               width = 0;
3997             }
3998         }
3999       if (j < i)
4000         {
4001           font->driver->draw (s, j, i, x, y, false);
4002           if (s->face->overstrike)
4003             font->driver->draw (s, j, i, x + 1, y, false);
4004         }
4005     }
4008 static void
4009 ns_draw_glyph_string (struct glyph_string *s)
4010 /* --------------------------------------------------------------------------
4011       External (RIF): Main draw-text call.
4012    -------------------------------------------------------------------------- */
4014   /* TODO (optimize): focus for box and contents draw */
4015   NSRect r[2];
4016   int n;
4017   char box_drawn_p = 0;
4018   struct font *font = s->face->font;
4019   if (! font) font = FRAME_FONT (s->f);
4021   NSTRACE_WHEN (NSTRACE_GROUP_GLYPHS, "ns_draw_glyph_string");
4023   if (s->next && s->right_overhang && !s->for_overlaps/*&&s->hl!=DRAW_CURSOR*/)
4024     {
4025       int width;
4026       struct glyph_string *next;
4028       for (width = 0, next = s->next;
4029            next && width < s->right_overhang;
4030            width += next->width, next = next->next)
4031         if (next->first_glyph->type != IMAGE_GLYPH)
4032           {
4033             if (next->first_glyph->type != STRETCH_GLYPH)
4034               {
4035                 n = ns_get_glyph_string_clip_rect (s->next, r);
4036                 if (ns_clip_to_rect (s->f, r, n))
4037                   {
4038                     ns_maybe_dumpglyphs_background (s->next, 1);
4039                     ns_reset_clipping (s->f);
4040                   }
4041               }
4042             else
4043               {
4044                 ns_dumpglyphs_stretch (s->next);
4045               }
4046             next->num_clips = 0;
4047           }
4048     }
4050   if (!s->for_overlaps && s->face->box != FACE_NO_BOX
4051         && (s->first_glyph->type == CHAR_GLYPH
4052             || s->first_glyph->type == COMPOSITE_GLYPH))
4053     {
4054       n = ns_get_glyph_string_clip_rect (s, r);
4055       if (ns_clip_to_rect (s->f, r, n))
4056         {
4057           ns_maybe_dumpglyphs_background (s, 1);
4058           ns_dumpglyphs_box_or_relief (s);
4059           ns_reset_clipping (s->f);
4060         }
4061       box_drawn_p = 1;
4062     }
4064   switch (s->first_glyph->type)
4065     {
4067     case IMAGE_GLYPH:
4068       n = ns_get_glyph_string_clip_rect (s, r);
4069       if (ns_clip_to_rect (s->f, r, n))
4070         {
4071           ns_dumpglyphs_image (s, r[0]);
4072           ns_reset_clipping (s->f);
4073         }
4074       break;
4076     case STRETCH_GLYPH:
4077       ns_dumpglyphs_stretch (s);
4078       break;
4080     case CHAR_GLYPH:
4081     case COMPOSITE_GLYPH:
4082       n = ns_get_glyph_string_clip_rect (s, r);
4083       if (ns_clip_to_rect (s->f, r, n))
4084         {
4085           if (s->for_overlaps || (s->cmp_from > 0
4086                                   && ! s->first_glyph->u.cmp.automatic))
4087             s->background_filled_p = 1;
4088           else
4089             ns_maybe_dumpglyphs_background
4090               (s, s->first_glyph->type == COMPOSITE_GLYPH);
4092           if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
4093             {
4094               unsigned long tmp = NS_FACE_BACKGROUND (s->face);
4095               NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
4096               NS_FACE_FOREGROUND (s->face) = tmp;
4097             }
4099           {
4100             BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
4102             if (isComposite)
4103               ns_draw_composite_glyph_string_foreground (s);
4104             else
4105               ns_draw_glyph_string_foreground (s);
4106           }
4108           {
4109             NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
4110                             ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face),
4111                                                        s->f)
4112                             : FRAME_FOREGROUND_COLOR (s->f));
4113             [col set];
4115             /* Draw underline, overline, strike-through.  */
4116             ns_draw_text_decoration (s, s->face, col, s->width, s->x);
4117           }
4119           if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
4120             {
4121               unsigned long tmp = NS_FACE_BACKGROUND (s->face);
4122               NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
4123               NS_FACE_FOREGROUND (s->face) = tmp;
4124             }
4126           ns_reset_clipping (s->f);
4127         }
4128       break;
4130     case GLYPHLESS_GLYPH:
4131       n = ns_get_glyph_string_clip_rect (s, r);
4132       if (ns_clip_to_rect (s->f, r, n))
4133         {
4134           if (s->for_overlaps || (s->cmp_from > 0
4135                                   && ! s->first_glyph->u.cmp.automatic))
4136             s->background_filled_p = 1;
4137           else
4138             ns_maybe_dumpglyphs_background
4139               (s, s->first_glyph->type == COMPOSITE_GLYPH);
4140           /* ... */
4141           /* Not yet implemented.  */
4142           /* ... */
4143           ns_reset_clipping (s->f);
4144         }
4145       break;
4147     default:
4148       emacs_abort ();
4149     }
4151   /* Draw box if not done already. */
4152   if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX)
4153     {
4154       n = ns_get_glyph_string_clip_rect (s, r);
4155       if (ns_clip_to_rect (s->f, r, n))
4156         {
4157           ns_dumpglyphs_box_or_relief (s);
4158           ns_reset_clipping (s->f);
4159         }
4160     }
4162   s->num_clips = 0;
4167 /* ==========================================================================
4169     Event loop
4171    ========================================================================== */
4174 static void
4175 ns_send_appdefined (int value)
4176 /* --------------------------------------------------------------------------
4177     Internal: post an appdefined event which EmacsApp-sendEvent will
4178               recognize and take as a command to halt the event loop.
4179    -------------------------------------------------------------------------- */
4181   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_send_appdefined(%d)", value);
4183   // GNUstep needs postEvent to happen on the main thread.
4184   // Cocoa needs nextEventMatchingMask to happen on the main thread too.
4185   if (! [[NSThread currentThread] isMainThread])
4186     {
4187       EmacsApp *app = (EmacsApp *)NSApp;
4188       app->nextappdefined = value;
4189       [app performSelectorOnMainThread:@selector (sendFromMainThread:)
4190                             withObject:nil
4191                          waitUntilDone:NO];
4192       return;
4193     }
4195   /* Only post this event if we haven't already posted one.  This will end
4196        the [NXApp run] main loop after having processed all events queued at
4197        this moment.  */
4199 #ifdef NS_IMPL_COCOA
4200   if (! send_appdefined)
4201     {
4202       /* OS X 10.10.1 swallows the AppDefined event we are sending ourselves
4203          in certain situations (rapid incoming events).
4204          So check if we have one, if not add one.  */
4205       NSEvent *appev = [NSApp nextEventMatchingMask:NSEventMaskApplicationDefined
4206                                           untilDate:[NSDate distantPast]
4207                                              inMode:NSDefaultRunLoopMode
4208                                             dequeue:NO];
4209       if (! appev) send_appdefined = YES;
4210     }
4211 #endif
4213   if (send_appdefined)
4214     {
4215       NSEvent *nxev;
4217       /* We only need one NX_APPDEFINED event to stop NXApp from running.  */
4218       send_appdefined = NO;
4220       /* Don't need wakeup timer any more */
4221       if (timed_entry)
4222         {
4223           [timed_entry invalidate];
4224           [timed_entry release];
4225           timed_entry = nil;
4226         }
4228       nxev = [NSEvent otherEventWithType: NSEventTypeApplicationDefined
4229                                 location: NSMakePoint (0, 0)
4230                            modifierFlags: 0
4231                                timestamp: 0
4232                             windowNumber: [[NSApp mainWindow] windowNumber]
4233                                  context: [NSApp context]
4234                                  subtype: 0
4235                                    data1: value
4236                                    data2: 0];
4238       /* Post an application defined event on the event queue.  When this is
4239          received the [NXApp run] will return, thus having processed all
4240          events which are currently queued.  */
4241       [NSApp postEvent: nxev atStart: NO];
4242     }
4245 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4246 static void
4247 check_native_fs ()
4249   Lisp_Object frame, tail;
4251   if (ns_last_use_native_fullscreen == ns_use_native_fullscreen)
4252     return;
4254   ns_last_use_native_fullscreen = ns_use_native_fullscreen;
4256   FOR_EACH_FRAME (tail, frame)
4257     {
4258       struct frame *f = XFRAME (frame);
4259       if (FRAME_NS_P (f))
4260         {
4261           EmacsView *view = FRAME_NS_VIEW (f);
4262           [view updateCollectionBehavior];
4263         }
4264     }
4266 #endif
4268 /* GNUstep does not have cancelTracking.  */
4269 #ifdef NS_IMPL_COCOA
4270 /* Check if menu open should be canceled or continued as normal.  */
4271 void
4272 ns_check_menu_open (NSMenu *menu)
4274   /* Click in menu bar? */
4275   NSArray *a = [[NSApp mainMenu] itemArray];
4276   int i;
4277   BOOL found = NO;
4279   if (menu == nil) // Menu tracking ended.
4280     {
4281       if (menu_will_open_state == MENU_OPENING)
4282         menu_will_open_state = MENU_NONE;
4283       return;
4284     }
4286   for (i = 0; ! found && i < [a count]; i++)
4287     found = menu == [[a objectAtIndex:i] submenu];
4288   if (found)
4289     {
4290       if (menu_will_open_state == MENU_NONE && emacs_event)
4291         {
4292           NSEvent *theEvent = [NSApp currentEvent];
4293           struct frame *emacsframe = SELECTED_FRAME ();
4295           [menu cancelTracking];
4296           menu_will_open_state = MENU_PENDING;
4297           emacs_event->kind = MENU_BAR_ACTIVATE_EVENT;
4298           EV_TRAILER (theEvent);
4300           CGEventRef ourEvent = CGEventCreate (NULL);
4301           menu_mouse_point = CGEventGetLocation (ourEvent);
4302           CFRelease (ourEvent);
4303         }
4304       else if (menu_will_open_state == MENU_OPENING)
4305         {
4306           menu_will_open_state = MENU_NONE;
4307         }
4308     }
4311 /* Redo saved menu click if state is MENU_PENDING.  */
4312 void
4313 ns_check_pending_open_menu ()
4315   if (menu_will_open_state == MENU_PENDING)
4316     {
4317       CGEventSourceRef source
4318         = CGEventSourceCreate (kCGEventSourceStateHIDSystemState);
4320       CGEventRef event = CGEventCreateMouseEvent (source,
4321                                                   kCGEventLeftMouseDown,
4322                                                   menu_mouse_point,
4323                                                   kCGMouseButtonLeft);
4324       CGEventSetType (event, kCGEventLeftMouseDown);
4325       CGEventPost (kCGHIDEventTap, event);
4326       CFRelease (event);
4327       CFRelease (source);
4329       menu_will_open_state = MENU_OPENING;
4330     }
4332 #endif /* NS_IMPL_COCOA */
4334 static int
4335 ns_read_socket (struct terminal *terminal, struct input_event *hold_quit)
4336 /* --------------------------------------------------------------------------
4337      External (hook): Post an event to ourself and keep reading events until
4338      we read it back again.  In effect process all events which were waiting.
4339      From 21+ we have to manage the event buffer ourselves.
4340    -------------------------------------------------------------------------- */
4342   struct input_event ev;
4343   int nevents;
4345   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_read_socket");
4347 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4348   check_native_fs ();
4349 #endif
4351   if ([NSApp modalWindow] != nil)
4352     return -1;
4354   if (hold_event_q.nr > 0)
4355     {
4356       int i;
4357       for (i = 0; i < hold_event_q.nr; ++i)
4358         kbd_buffer_store_event_hold (&hold_event_q.q[i], hold_quit);
4359       hold_event_q.nr = 0;
4360       return i;
4361     }
4363   if ([NSThread isMainThread])
4364     {
4365       block_input ();
4366       n_emacs_events_pending = 0;
4367       ns_init_events (&ev);
4368       q_event_ptr = hold_quit;
4370       /* we manage autorelease pools by allocate/reallocate each time around
4371          the loop; strict nesting is occasionally violated but seems not to
4372          matter.. earlier methods using full nesting caused major memory leaks */
4373       [outerpool release];
4374       outerpool = [[NSAutoreleasePool alloc] init];
4376       /* If have pending open-file requests, attend to the next one of those. */
4377       if (ns_pending_files && [ns_pending_files count] != 0
4378           && [(EmacsApp *)NSApp openFile: [ns_pending_files objectAtIndex: 0]])
4379         {
4380           [ns_pending_files removeObjectAtIndex: 0];
4381         }
4382       /* Deal with pending service requests. */
4383       else if (ns_pending_service_names && [ns_pending_service_names count] != 0
4384                && [(EmacsApp *)
4385                     NSApp fulfillService: [ns_pending_service_names objectAtIndex: 0]
4386                                  withArg: [ns_pending_service_args objectAtIndex: 0]])
4387         {
4388           [ns_pending_service_names removeObjectAtIndex: 0];
4389           [ns_pending_service_args removeObjectAtIndex: 0];
4390         }
4391       else
4392         {
4393           /* Run and wait for events.  We must always send one NX_APPDEFINED event
4394              to ourself, otherwise [NXApp run] will never exit.  */
4395           send_appdefined = YES;
4396           ns_send_appdefined (-1);
4398           [NSApp run];
4399         }
4401       nevents = n_emacs_events_pending;
4402       n_emacs_events_pending = 0;
4403       ns_finish_events ();
4404       q_event_ptr = NULL;
4405       unblock_input ();
4406     }
4407   else
4408     return -1;
4410   return nevents;
4415 ns_select (int nfds, fd_set *readfds, fd_set *writefds,
4416            fd_set *exceptfds, struct timespec *timeout,
4417            sigset_t *sigmask)
4418 /* --------------------------------------------------------------------------
4419      Replacement for select, checking for events
4420    -------------------------------------------------------------------------- */
4422   int result;
4423   int t, k, nr = 0;
4424   struct input_event event;
4425   char c;
4427   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_select");
4429 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4430   check_native_fs ();
4431 #endif
4433   if (hold_event_q.nr > 0)
4434     {
4435       /* We already have events pending. */
4436       raise (SIGIO);
4437       errno = EINTR;
4438       return -1;
4439     }
4441   for (k = 0; k < nfds+1; k++)
4442     {
4443       if (readfds && FD_ISSET(k, readfds)) ++nr;
4444       if (writefds && FD_ISSET(k, writefds)) ++nr;
4445     }
4447   if (NSApp == nil
4448       || ![NSThread isMainThread]
4449       || (timeout && timeout->tv_sec == 0 && timeout->tv_nsec == 0))
4450     return thread_select(pselect, nfds, readfds, writefds,
4451                          exceptfds, timeout, sigmask);
4452   else
4453     {
4454       struct timespec t = {0, 0};
4455       thread_select(pselect, 0, NULL, NULL, NULL, &t, sigmask);
4456     }
4458   [outerpool release];
4459   outerpool = [[NSAutoreleasePool alloc] init];
4462   send_appdefined = YES;
4463   if (nr > 0)
4464     {
4465       pthread_mutex_lock (&select_mutex);
4466       select_nfds = nfds;
4467       select_valid = 0;
4468       if (readfds)
4469         {
4470           select_readfds = *readfds;
4471           select_valid += SELECT_HAVE_READ;
4472         }
4473       if (writefds)
4474         {
4475           select_writefds = *writefds;
4476           select_valid += SELECT_HAVE_WRITE;
4477         }
4479       if (timeout)
4480         {
4481           select_timeout = *timeout;
4482           select_valid += SELECT_HAVE_TMO;
4483         }
4485       pthread_mutex_unlock (&select_mutex);
4487       /* Inform fd_handler that select should be called */
4488       c = 'g';
4489       emacs_write_sig (selfds[1], &c, 1);
4490     }
4491   else if (nr == 0 && timeout)
4492     {
4493       /* No file descriptor, just a timeout, no need to wake fd_handler  */
4494       double time = timespectod (*timeout);
4495       timed_entry = [[NSTimer scheduledTimerWithTimeInterval: time
4496                                                       target: NSApp
4497                                                     selector:
4498                                   @selector (timeout_handler:)
4499                                                     userInfo: 0
4500                                                      repeats: NO]
4501                       retain];
4502     }
4503   else /* No timeout and no file descriptors, can this happen?  */
4504     {
4505       /* Send appdefined so we exit from the loop */
4506       ns_send_appdefined (-1);
4507     }
4509   block_input ();
4510   ns_init_events (&event);
4512   [NSApp run];
4514   ns_finish_events ();
4515   if (nr > 0 && readfds)
4516     {
4517       c = 's';
4518       emacs_write_sig (selfds[1], &c, 1);
4519     }
4520   unblock_input ();
4522   t = last_appdefined_event_data;
4524   if (t != NO_APPDEFINED_DATA)
4525     {
4526       last_appdefined_event_data = NO_APPDEFINED_DATA;
4528       if (t == -2)
4529         {
4530           /* The NX_APPDEFINED event we received was a timeout. */
4531           result = 0;
4532         }
4533       else if (t == -1)
4534         {
4535           /* The NX_APPDEFINED event we received was the result of
4536              at least one real input event arriving.  */
4537           errno = EINTR;
4538           result = -1;
4539         }
4540       else
4541         {
4542           /* Received back from select () in fd_handler; copy the results */
4543           pthread_mutex_lock (&select_mutex);
4544           if (readfds) *readfds = select_readfds;
4545           if (writefds) *writefds = select_writefds;
4546           pthread_mutex_unlock (&select_mutex);
4547           result = t;
4548         }
4549     }
4550   else
4551     {
4552       errno = EINTR;
4553       result = -1;
4554     }
4556   return result;
4559 #ifdef HAVE_PTHREAD
4560 void
4561 ns_run_loop_break ()
4562 /* Break out of the NS run loop in ns_select or ns_read_socket. */
4564   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_run_loop_break");
4566   /* If we don't have a GUI, don't send the event. */
4567   if (NSApp != NULL)
4568     ns_send_appdefined(-1);
4570 #endif
4573 /* ==========================================================================
4575     Scrollbar handling
4577    ========================================================================== */
4580 static void
4581 ns_set_vertical_scroll_bar (struct window *window,
4582                            int portion, int whole, int position)
4583 /* --------------------------------------------------------------------------
4584       External (hook): Update or add scrollbar
4585    -------------------------------------------------------------------------- */
4587   Lisp_Object win;
4588   NSRect r, v;
4589   struct frame *f = XFRAME (WINDOW_FRAME (window));
4590   EmacsView *view = FRAME_NS_VIEW (f);
4591   EmacsScroller *bar;
4592   int window_y, window_height;
4593   int top, left, height, width;
4594   BOOL update_p = YES;
4596   /* optimization; display engine sends WAY too many of these.. */
4597   if (!NILP (window->vertical_scroll_bar))
4598     {
4599       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4600       if ([bar checkSamePosition: position portion: portion whole: whole])
4601         {
4602           if (view->scrollbarsNeedingUpdate == 0)
4603             {
4604               if (!windows_or_buffers_changed)
4605                   return;
4606             }
4607           else
4608             view->scrollbarsNeedingUpdate--;
4609           update_p = NO;
4610         }
4611     }
4613   NSTRACE ("ns_set_vertical_scroll_bar");
4615   /* Get dimensions.  */
4616   window_box (window, ANY_AREA, 0, &window_y, 0, &window_height);
4617   top = window_y;
4618   height = window_height;
4619   width = NS_SCROLL_BAR_WIDTH (f);
4620   left = WINDOW_SCROLL_BAR_AREA_X (window);
4622   r = NSMakeRect (left, top, width, height);
4623   /* the parent view is flipped, so we need to flip y value */
4624   v = [view frame];
4625   r.origin.y = (v.size.height - r.size.height - r.origin.y);
4627   XSETWINDOW (win, window);
4628   block_input ();
4630   /* we want at least 5 lines to display a scrollbar */
4631   if (WINDOW_TOTAL_LINES (window) < 5)
4632     {
4633       if (!NILP (window->vertical_scroll_bar))
4634         {
4635           bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4636           [bar removeFromSuperview];
4637           wset_vertical_scroll_bar (window, Qnil);
4638           [bar release];
4639         }
4640       ns_clear_frame_area (f, left, top, width, height);
4641       unblock_input ();
4642       return;
4643     }
4645   if (NILP (window->vertical_scroll_bar))
4646     {
4647       if (width > 0 && height > 0)
4648         ns_clear_frame_area (f, left, top, width, height);
4650       bar = [[EmacsScroller alloc] initFrame: r window: win];
4651       wset_vertical_scroll_bar (window, make_save_ptr (bar));
4652       update_p = YES;
4653     }
4654   else
4655     {
4656       NSRect oldRect;
4657       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4658       oldRect = [bar frame];
4659       r.size.width = oldRect.size.width;
4660       if (FRAME_LIVE_P (f) && !NSEqualRects (oldRect, r))
4661         {
4662           if (oldRect.origin.x != r.origin.x)
4663               ns_clear_frame_area (f, left, top, width, height);
4664           [bar setFrame: r];
4665         }
4666     }
4668   if (update_p)
4669     [bar setPosition: position portion: portion whole: whole];
4670   unblock_input ();
4674 static void
4675 ns_set_horizontal_scroll_bar (struct window *window,
4676                               int portion, int whole, int position)
4677 /* --------------------------------------------------------------------------
4678       External (hook): Update or add scrollbar
4679    -------------------------------------------------------------------------- */
4681   Lisp_Object win;
4682   NSRect r, v;
4683   struct frame *f = XFRAME (WINDOW_FRAME (window));
4684   EmacsView *view = FRAME_NS_VIEW (f);
4685   EmacsScroller *bar;
4686   int top, height, left, width;
4687   int window_x, window_width;
4688   BOOL update_p = YES;
4690   /* optimization; display engine sends WAY too many of these.. */
4691   if (!NILP (window->horizontal_scroll_bar))
4692     {
4693       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4694       if ([bar checkSamePosition: position portion: portion whole: whole])
4695         {
4696           if (view->scrollbarsNeedingUpdate == 0)
4697             {
4698               if (!windows_or_buffers_changed)
4699                   return;
4700             }
4701           else
4702             view->scrollbarsNeedingUpdate--;
4703           update_p = NO;
4704         }
4705     }
4707   NSTRACE ("ns_set_horizontal_scroll_bar");
4709   /* Get dimensions.  */
4710   window_box (window, ANY_AREA, &window_x, 0, &window_width, 0);
4711   left = window_x;
4712   width = window_width;
4713   height = NS_SCROLL_BAR_HEIGHT (f);
4714   top = WINDOW_SCROLL_BAR_AREA_Y (window);
4716   r = NSMakeRect (left, top, width, height);
4717   /* the parent view is flipped, so we need to flip y value */
4718   v = [view frame];
4719   r.origin.y = (v.size.height - r.size.height - r.origin.y);
4721   XSETWINDOW (win, window);
4722   block_input ();
4724   if (NILP (window->horizontal_scroll_bar))
4725     {
4726       if (width > 0 && height > 0)
4727         ns_clear_frame_area (f, left, top, width, height);
4729       bar = [[EmacsScroller alloc] initFrame: r window: win];
4730       wset_horizontal_scroll_bar (window, make_save_ptr (bar));
4731       update_p = YES;
4732     }
4733   else
4734     {
4735       NSRect oldRect;
4736       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4737       oldRect = [bar frame];
4738       if (FRAME_LIVE_P (f) && !NSEqualRects (oldRect, r))
4739         {
4740           if (oldRect.origin.y != r.origin.y)
4741             ns_clear_frame_area (f, left, top, width, height);
4742           [bar setFrame: r];
4743           update_p = YES;
4744         }
4745     }
4747   /* If there are both horizontal and vertical scroll-bars they leave
4748      a square that belongs to neither. We need to clear it otherwise
4749      it fills with junk. */
4750   if (!NILP (window->vertical_scroll_bar))
4751     ns_clear_frame_area (f, WINDOW_SCROLL_BAR_AREA_X (window), top,
4752                          NS_SCROLL_BAR_HEIGHT (f), height);
4754   if (update_p)
4755     [bar setPosition: position portion: portion whole: whole];
4756   unblock_input ();
4760 static void
4761 ns_condemn_scroll_bars (struct frame *f)
4762 /* --------------------------------------------------------------------------
4763      External (hook): arrange for all frame's scrollbars to be removed
4764      at next call to judge_scroll_bars, except for those redeemed.
4765    -------------------------------------------------------------------------- */
4767   int i;
4768   id view;
4769   NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews];
4771   NSTRACE ("ns_condemn_scroll_bars");
4773   for (i =[subviews count]-1; i >= 0; i--)
4774     {
4775       view = [subviews objectAtIndex: i];
4776       if ([view isKindOfClass: [EmacsScroller class]])
4777         [view condemn];
4778     }
4782 static void
4783 ns_redeem_scroll_bar (struct window *window)
4784 /* --------------------------------------------------------------------------
4785      External (hook): arrange to spare this window's scrollbar
4786      at next call to judge_scroll_bars.
4787    -------------------------------------------------------------------------- */
4789   id bar;
4790   NSTRACE ("ns_redeem_scroll_bar");
4791   if (!NILP (window->vertical_scroll_bar)
4792       && WINDOW_HAS_VERTICAL_SCROLL_BAR (window))
4793     {
4794       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4795       [bar reprieve];
4796     }
4798   if (!NILP (window->horizontal_scroll_bar)
4799       && WINDOW_HAS_HORIZONTAL_SCROLL_BAR (window))
4800     {
4801       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4802       [bar reprieve];
4803     }
4807 static void
4808 ns_judge_scroll_bars (struct frame *f)
4809 /* --------------------------------------------------------------------------
4810      External (hook): destroy all scrollbars on frame that weren't
4811      redeemed after call to condemn_scroll_bars.
4812    -------------------------------------------------------------------------- */
4814   int i;
4815   id view;
4816   EmacsView *eview = FRAME_NS_VIEW (f);
4817   NSArray *subviews = [[eview superview] subviews];
4818   BOOL removed = NO;
4820   NSTRACE ("ns_judge_scroll_bars");
4821   for (i = [subviews count]-1; i >= 0; --i)
4822     {
4823       view = [subviews objectAtIndex: i];
4824       if (![view isKindOfClass: [EmacsScroller class]]) continue;
4825       if ([view judge])
4826         removed = YES;
4827     }
4829   if (removed)
4830     [eview updateFrameSize: NO];
4833 /* ==========================================================================
4835     Initialization
4837    ========================================================================== */
4840 x_display_pixel_height (struct ns_display_info *dpyinfo)
4842   NSArray *screens = [NSScreen screens];
4843   NSEnumerator *enumerator = [screens objectEnumerator];
4844   NSScreen *screen;
4845   NSRect frame;
4847   frame = NSZeroRect;
4848   while ((screen = [enumerator nextObject]) != nil)
4849     frame = NSUnionRect (frame, [screen frame]);
4851   return NSHeight (frame);
4855 x_display_pixel_width (struct ns_display_info *dpyinfo)
4857   NSArray *screens = [NSScreen screens];
4858   NSEnumerator *enumerator = [screens objectEnumerator];
4859   NSScreen *screen;
4860   NSRect frame;
4862   frame = NSZeroRect;
4863   while ((screen = [enumerator nextObject]) != nil)
4864     frame = NSUnionRect (frame, [screen frame]);
4866   return NSWidth (frame);
4870 static Lisp_Object ns_string_to_lispmod (const char *s)
4871 /* --------------------------------------------------------------------------
4872      Convert modifier name to lisp symbol
4873    -------------------------------------------------------------------------- */
4875   if (!strncmp (SSDATA (SYMBOL_NAME (Qmeta)), s, 10))
4876     return Qmeta;
4877   else if (!strncmp (SSDATA (SYMBOL_NAME (Qsuper)), s, 10))
4878     return Qsuper;
4879   else if (!strncmp (SSDATA (SYMBOL_NAME (Qcontrol)), s, 10))
4880     return Qcontrol;
4881   else if (!strncmp (SSDATA (SYMBOL_NAME (Qalt)), s, 10))
4882     return Qalt;
4883   else if (!strncmp (SSDATA (SYMBOL_NAME (Qhyper)), s, 10))
4884     return Qhyper;
4885   else if (!strncmp (SSDATA (SYMBOL_NAME (Qnone)), s, 10))
4886     return Qnone;
4887   else
4888     return Qnil;
4892 static void
4893 ns_default (const char *parameter, Lisp_Object *result,
4894            Lisp_Object yesval, Lisp_Object noval,
4895            BOOL is_float, BOOL is_modstring)
4896 /* --------------------------------------------------------------------------
4897       Check a parameter value in user's preferences
4898    -------------------------------------------------------------------------- */
4900   const char *value = ns_get_defaults_value (parameter);
4902   if (value)
4903     {
4904       double f;
4905       char *pos;
4906       if (c_strcasecmp (value, "YES") == 0)
4907         *result = yesval;
4908       else if (c_strcasecmp (value, "NO") == 0)
4909         *result = noval;
4910       else if (is_float && (f = strtod (value, &pos), pos != value))
4911         *result = make_float (f);
4912       else if (is_modstring && value)
4913         *result = ns_string_to_lispmod (value);
4914       else fprintf (stderr,
4915                    "Bad value for default \"%s\": \"%s\"\n", parameter, value);
4916     }
4920 static void
4921 ns_initialize_display_info (struct ns_display_info *dpyinfo)
4922 /* --------------------------------------------------------------------------
4923       Initialize global info and storage for display.
4924    -------------------------------------------------------------------------- */
4926     NSScreen *screen = [NSScreen mainScreen];
4927     NSWindowDepth depth = [screen depth];
4929     dpyinfo->resx = 72.27; /* used 75.0, but this makes pt == pixel, expected */
4930     dpyinfo->resy = 72.27;
4931     dpyinfo->color_p = ![NSDeviceWhiteColorSpace isEqualToString:
4932                                                   NSColorSpaceFromDepth (depth)]
4933                 && ![NSCalibratedWhiteColorSpace isEqualToString:
4934                                                  NSColorSpaceFromDepth (depth)];
4935     dpyinfo->n_planes = NSBitsPerPixelFromDepth (depth);
4936     dpyinfo->color_table = xmalloc (sizeof *dpyinfo->color_table);
4937     dpyinfo->color_table->colors = NULL;
4938     dpyinfo->root_window = 42; /* a placeholder.. */
4939     dpyinfo->x_highlight_frame = dpyinfo->x_focus_frame = NULL;
4940     dpyinfo->n_fonts = 0;
4941     dpyinfo->smallest_font_height = 1;
4942     dpyinfo->smallest_char_width = 1;
4944     reset_mouse_highlight (&dpyinfo->mouse_highlight);
4948 /* This and next define (many of the) public functions in this file. */
4949 /* x_... are generic versions in xdisp.c that we, and other terms, get away
4950          with using despite presence in the "system dependent" redisplay
4951          interface.  In addition, many of the ns_ methods have code that is
4952          shared with all terms, indicating need for further refactoring. */
4953 extern frame_parm_handler ns_frame_parm_handlers[];
4954 static struct redisplay_interface ns_redisplay_interface =
4956   ns_frame_parm_handlers,
4957   x_produce_glyphs,
4958   x_write_glyphs,
4959   x_insert_glyphs,
4960   x_clear_end_of_line,
4961   ns_scroll_run,
4962   ns_after_update_window_line,
4963   ns_update_window_begin,
4964   ns_update_window_end,
4965   0, /* flush_display */
4966   x_clear_window_mouse_face,
4967   x_get_glyph_overhangs,
4968   x_fix_overlapping_area,
4969   ns_draw_fringe_bitmap,
4970   0, /* define_fringe_bitmap */ /* FIXME: simplify ns_draw_fringe_bitmap */
4971   0, /* destroy_fringe_bitmap */
4972   ns_compute_glyph_string_overhangs,
4973   ns_draw_glyph_string,
4974   ns_define_frame_cursor,
4975   ns_clear_frame_area,
4976   ns_draw_window_cursor,
4977   ns_draw_vertical_window_border,
4978   ns_draw_window_divider,
4979   ns_shift_glyphs_for_insert,
4980   ns_show_hourglass,
4981   ns_hide_hourglass
4985 static void
4986 ns_delete_display (struct ns_display_info *dpyinfo)
4988   /* TODO... */
4992 /* This function is called when the last frame on a display is deleted. */
4993 static void
4994 ns_delete_terminal (struct terminal *terminal)
4996   struct ns_display_info *dpyinfo = terminal->display_info.ns;
4998   NSTRACE ("ns_delete_terminal");
5000   /* Protect against recursive calls.  delete_frame in
5001      delete_terminal calls us back when it deletes our last frame.  */
5002   if (!terminal->name)
5003     return;
5005   block_input ();
5007   x_destroy_all_bitmaps (dpyinfo);
5008   ns_delete_display (dpyinfo);
5009   unblock_input ();
5013 static struct terminal *
5014 ns_create_terminal (struct ns_display_info *dpyinfo)
5015 /* --------------------------------------------------------------------------
5016       Set up use of NS before we make the first connection.
5017    -------------------------------------------------------------------------- */
5019   struct terminal *terminal;
5021   NSTRACE ("ns_create_terminal");
5023   terminal = create_terminal (output_ns, &ns_redisplay_interface);
5025   terminal->display_info.ns = dpyinfo;
5026   dpyinfo->terminal = terminal;
5028   terminal->clear_frame_hook = ns_clear_frame;
5029   terminal->ring_bell_hook = ns_ring_bell;
5030   terminal->update_begin_hook = ns_update_begin;
5031   terminal->update_end_hook = ns_update_end;
5032   terminal->read_socket_hook = ns_read_socket;
5033   terminal->frame_up_to_date_hook = ns_frame_up_to_date;
5034   terminal->mouse_position_hook = ns_mouse_position;
5035   terminal->frame_rehighlight_hook = ns_frame_rehighlight;
5036   terminal->frame_raise_lower_hook = ns_frame_raise_lower;
5037   terminal->fullscreen_hook = ns_fullscreen_hook;
5038   terminal->menu_show_hook = ns_menu_show;
5039   terminal->popup_dialog_hook = ns_popup_dialog;
5040   terminal->set_vertical_scroll_bar_hook = ns_set_vertical_scroll_bar;
5041   terminal->set_horizontal_scroll_bar_hook = ns_set_horizontal_scroll_bar;
5042   terminal->condemn_scroll_bars_hook = ns_condemn_scroll_bars;
5043   terminal->redeem_scroll_bar_hook = ns_redeem_scroll_bar;
5044   terminal->judge_scroll_bars_hook = ns_judge_scroll_bars;
5045   terminal->delete_frame_hook = x_destroy_window;
5046   terminal->delete_terminal_hook = ns_delete_terminal;
5047   /* Other hooks are NULL by default.  */
5049   return terminal;
5053 struct ns_display_info *
5054 ns_term_init (Lisp_Object display_name)
5055 /* --------------------------------------------------------------------------
5056      Start the Application and get things rolling.
5057    -------------------------------------------------------------------------- */
5059   struct terminal *terminal;
5060   struct ns_display_info *dpyinfo;
5061   static int ns_initialized = 0;
5062   Lisp_Object tmp;
5064   if (ns_initialized) return x_display_list;
5065   ns_initialized = 1;
5067   block_input ();
5069   NSTRACE ("ns_term_init");
5071   [outerpool release];
5072   outerpool = [[NSAutoreleasePool alloc] init];
5074   /* count object allocs (About, click icon); on macOS use ObjectAlloc tool */
5075   /*GSDebugAllocationActive (YES); */
5076   block_input ();
5078   baud_rate = 38400;
5079   Fset_input_interrupt_mode (Qnil);
5081   if (selfds[0] == -1)
5082     {
5083       if (emacs_pipe (selfds) != 0)
5084         {
5085           fprintf (stderr, "Failed to create pipe: %s\n",
5086                    emacs_strerror (errno));
5087           emacs_abort ();
5088         }
5090       fcntl (selfds[0], F_SETFL, O_NONBLOCK|fcntl (selfds[0], F_GETFL));
5091       FD_ZERO (&select_readfds);
5092       FD_ZERO (&select_writefds);
5093       pthread_mutex_init (&select_mutex, NULL);
5094     }
5096   ns_pending_files = [[NSMutableArray alloc] init];
5097   ns_pending_service_names = [[NSMutableArray alloc] init];
5098   ns_pending_service_args = [[NSMutableArray alloc] init];
5100 /* Start app and create the main menu, window, view.
5101      Needs to be here because ns_initialize_display_info () uses AppKit classes.
5102      The view will then ask the NSApp to stop and return to Emacs. */
5103   [EmacsApp sharedApplication];
5104   if (NSApp == nil)
5105     return NULL;
5106   [NSApp setDelegate: NSApp];
5108   /* Start the select thread.  */
5109   [NSThread detachNewThreadSelector:@selector (fd_handler:)
5110                            toTarget:NSApp
5111                          withObject:nil];
5113   /* debugging: log all notifications */
5114   /*   [[NSNotificationCenter defaultCenter] addObserver: NSApp
5115                                          selector: @selector (logNotification:)
5116                                              name: nil object: nil]; */
5118   dpyinfo = xzalloc (sizeof *dpyinfo);
5120   ns_initialize_display_info (dpyinfo);
5121   terminal = ns_create_terminal (dpyinfo);
5123   terminal->kboard = allocate_kboard (Qns);
5124   /* Don't let the initial kboard remain current longer than necessary.
5125      That would cause problems if a file loaded on startup tries to
5126      prompt in the mini-buffer.  */
5127   if (current_kboard == initial_kboard)
5128     current_kboard = terminal->kboard;
5129   terminal->kboard->reference_count++;
5131   dpyinfo->next = x_display_list;
5132   x_display_list = dpyinfo;
5134   dpyinfo->name_list_element = Fcons (display_name, Qnil);
5136   terminal->name = xlispstrdup (display_name);
5138   unblock_input ();
5140   if (!inhibit_x_resources)
5141     {
5142       ns_default ("GSFontAntiAlias", &ns_antialias_text,
5143                  Qt, Qnil, NO, NO);
5144       tmp = Qnil;
5145       /* this is a standard variable */
5146       ns_default ("AppleAntiAliasingThreshold", &tmp,
5147                  make_float (10.0), make_float (6.0), YES, NO);
5148       ns_antialias_threshold = NILP (tmp) ? 10.0 : extract_float (tmp);
5149     }
5151   NSTRACE_MSG ("Colors");
5153   {
5154     NSColorList *cl = [NSColorList colorListNamed: @"Emacs"];
5156     if ( cl == nil )
5157       {
5158         Lisp_Object color_file, color_map, color;
5159         unsigned long c;
5160         char *name;
5162         color_file = Fexpand_file_name (build_string ("rgb.txt"),
5163                          Fsymbol_value (intern ("data-directory")));
5165         color_map = Fx_load_color_file (color_file);
5166         if (NILP (color_map))
5167           fatal ("Could not read %s.\n", SDATA (color_file));
5169         cl = [[NSColorList alloc] initWithName: @"Emacs"];
5170         for ( ; CONSP (color_map); color_map = XCDR (color_map))
5171           {
5172             color = XCAR (color_map);
5173             name = SSDATA (XCAR (color));
5174             c = XINT (XCDR (color));
5175             [cl setColor:
5176                   [NSColor colorForEmacsRed: RED_FROM_ULONG (c) / 255.0
5177                                       green: GREEN_FROM_ULONG (c) / 255.0
5178                                        blue: BLUE_FROM_ULONG (c) / 255.0
5179                                       alpha: 1.0]
5180                   forKey: [NSString stringWithUTF8String: name]];
5181           }
5183         /* FIXME: Report any errors writing the color file below.  */
5184 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101100
5185 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101100
5186         if ([cl respondsToSelector:@selector(writeToURL:error:)])
5187 #endif
5188           [cl writeToURL:nil error:nil];
5189 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101100
5190         else
5191 #endif
5192 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 101100 */
5193 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101100 \
5194   || defined (NS_IMPL_GNUSTEP)
5195           [cl writeToFile: nil];
5196 #endif
5197       }
5198   }
5200   NSTRACE_MSG ("Versions");
5202   {
5203 #ifdef NS_IMPL_GNUSTEP
5204     Vwindow_system_version = build_string (gnustep_base_version);
5205 #else
5206     /*PSnextrelease (128, c); */
5207     char c[DBL_BUFSIZE_BOUND];
5208     int len = dtoastr (c, sizeof c, 0, 0, NSAppKitVersionNumber);
5209     Vwindow_system_version = make_unibyte_string (c, len);
5210 #endif
5211   }
5213   delete_keyboard_wait_descriptor (0);
5215   ns_app_name = [[NSProcessInfo processInfo] processName];
5217   /* Set up macOS app menu */
5219   NSTRACE_MSG ("Menu init");
5221 #ifdef NS_IMPL_COCOA
5222   {
5223     NSMenu *appMenu;
5224     NSMenuItem *item;
5225     /* set up the application menu */
5226     svcsMenu = [[EmacsMenu alloc] initWithTitle: @"Services"];
5227     [svcsMenu setAutoenablesItems: NO];
5228     appMenu = [[EmacsMenu alloc] initWithTitle: @"Emacs"];
5229     [appMenu setAutoenablesItems: NO];
5230     mainMenu = [[EmacsMenu alloc] initWithTitle: @""];
5231     dockMenu = [[EmacsMenu alloc] initWithTitle: @""];
5233     [appMenu insertItemWithTitle: @"About Emacs"
5234                           action: @selector (orderFrontStandardAboutPanel:)
5235                    keyEquivalent: @""
5236                          atIndex: 0];
5237     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 1];
5238     [appMenu insertItemWithTitle: @"Preferences..."
5239                           action: @selector (showPreferencesWindow:)
5240                    keyEquivalent: @","
5241                          atIndex: 2];
5242     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 3];
5243     item = [appMenu insertItemWithTitle: @"Services"
5244                                  action: @selector (menuDown:)
5245                           keyEquivalent: @""
5246                                 atIndex: 4];
5247     [appMenu setSubmenu: svcsMenu forItem: item];
5248     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 5];
5249     [appMenu insertItemWithTitle: @"Hide Emacs"
5250                           action: @selector (hide:)
5251                    keyEquivalent: @"h"
5252                          atIndex: 6];
5253     item =  [appMenu insertItemWithTitle: @"Hide Others"
5254                           action: @selector (hideOtherApplications:)
5255                    keyEquivalent: @"h"
5256                          atIndex: 7];
5257     [item setKeyEquivalentModifierMask: NSEventModifierFlagCommand | NSEventModifierFlagOption];
5258     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 8];
5259     [appMenu insertItemWithTitle: @"Quit Emacs"
5260                           action: @selector (terminate:)
5261                    keyEquivalent: @"q"
5262                          atIndex: 9];
5264     item = [mainMenu insertItemWithTitle: ns_app_name
5265                                   action: @selector (menuDown:)
5266                            keyEquivalent: @""
5267                                  atIndex: 0];
5268     [mainMenu setSubmenu: appMenu forItem: item];
5269     [dockMenu insertItemWithTitle: @"New Frame"
5270                            action: @selector (newFrame:)
5271                     keyEquivalent: @""
5272                           atIndex: 0];
5274     [NSApp setMainMenu: mainMenu];
5275     [NSApp setAppleMenu: appMenu];
5276     [NSApp setServicesMenu: svcsMenu];
5277     /* Needed at least on Cocoa, to get dock menu to show windows */
5278     [NSApp setWindowsMenu: [[NSMenu alloc] init]];
5280     [[NSNotificationCenter defaultCenter]
5281       addObserver: mainMenu
5282          selector: @selector (trackingNotification:)
5283              name: NSMenuDidBeginTrackingNotification object: mainMenu];
5284     [[NSNotificationCenter defaultCenter]
5285       addObserver: mainMenu
5286          selector: @selector (trackingNotification:)
5287              name: NSMenuDidEndTrackingNotification object: mainMenu];
5288   }
5289 #endif /* macOS menu setup */
5291   /* Register our external input/output types, used for determining
5292      applicable services and also drag/drop eligibility. */
5294   NSTRACE_MSG ("Input/output types");
5296   ns_send_types = [[NSArray arrayWithObjects: NSStringPboardType, nil] retain];
5297   ns_return_types = [[NSArray arrayWithObjects: NSStringPboardType, nil]
5298                       retain];
5299   ns_drag_types = [[NSArray arrayWithObjects:
5300                             NSStringPboardType,
5301                             NSTabularTextPboardType,
5302                             NSFilenamesPboardType,
5303                             NSURLPboardType, nil] retain];
5305   /* If fullscreen is in init/default-frame-alist, focus isn't set
5306      right for fullscreen windows, so set this.  */
5307   [NSApp activateIgnoringOtherApps:YES];
5309   NSTRACE_MSG ("Call NSApp run");
5311   [NSApp run];
5312   ns_do_open_file = YES;
5314 #ifdef NS_IMPL_GNUSTEP
5315   /* GNUstep steals SIGCHLD for use in NSTask, but we don't use NSTask.
5316      We must re-catch it so subprocess works.  */
5317   catch_child_signal ();
5318 #endif
5320   NSTRACE_MSG ("ns_term_init done");
5322   unblock_input ();
5324   return dpyinfo;
5328 void
5329 ns_term_shutdown (int sig)
5331   [[NSUserDefaults standardUserDefaults] synchronize];
5333   /* code not reached in emacs.c after this is called by shut_down_emacs: */
5334   if (STRINGP (Vauto_save_list_file_name))
5335     unlink (SSDATA (Vauto_save_list_file_name));
5337   if (sig == 0 || sig == SIGTERM)
5338     {
5339       [NSApp terminate: NSApp];
5340     }
5341   else // force a stack trace to happen
5342     {
5343       emacs_abort ();
5344     }
5348 /* ==========================================================================
5350     EmacsApp implementation
5352    ========================================================================== */
5355 @implementation EmacsApp
5357 - (id)init
5359   NSTRACE ("[EmacsApp init]");
5361   if ((self = [super init]))
5362     {
5363 #ifdef NS_IMPL_COCOA
5364       self->isFirst = YES;
5365 #endif
5366 #ifdef NS_IMPL_GNUSTEP
5367       self->applicationDidFinishLaunchingCalled = NO;
5368 #endif
5369     }
5371   return self;
5374 #ifdef NS_IMPL_COCOA
5375 - (void)run
5377   NSTRACE ("[EmacsApp run]");
5379 #ifndef NSAppKitVersionNumber10_9
5380 #define NSAppKitVersionNumber10_9 1265
5381 #endif
5383     if ((int)NSAppKitVersionNumber != NSAppKitVersionNumber10_9)
5384       {
5385         [super run];
5386         return;
5387       }
5389   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5391   if (isFirst) [self finishLaunching];
5392   isFirst = NO;
5394   shouldKeepRunning = YES;
5395   do
5396     {
5397       [pool release];
5398       pool = [[NSAutoreleasePool alloc] init];
5400       NSEvent *event =
5401         [self nextEventMatchingMask:NSEventMaskAny
5402                           untilDate:[NSDate distantFuture]
5403                              inMode:NSDefaultRunLoopMode
5404                             dequeue:YES];
5406       [self sendEvent:event];
5407       [self updateWindows];
5408     } while (shouldKeepRunning);
5410   [pool release];
5413 - (void)stop: (id)sender
5415   NSTRACE ("[EmacsApp stop:]");
5417     shouldKeepRunning = NO;
5418     // Stop possible dialog also.  Noop if no dialog present.
5419     // The file dialog still leaks 7k - 10k on 10.9 though.
5420     [super stop:sender];
5422 #endif /* NS_IMPL_COCOA */
5424 - (void)logNotification: (NSNotification *)notification
5426   NSTRACE ("[EmacsApp logNotification:]");
5428   const char *name = [[notification name] UTF8String];
5429   if (!strstr (name, "Update") && !strstr (name, "NSMenu")
5430       && !strstr (name, "WindowNumber"))
5431     NSLog (@"notification: '%@'", [notification name]);
5435 - (void)sendEvent: (NSEvent *)theEvent
5436 /* --------------------------------------------------------------------------
5437      Called when NSApp is running for each event received.  Used to stop
5438      the loop when we choose, since there's no way to just run one iteration.
5439    -------------------------------------------------------------------------- */
5441   int type = [theEvent type];
5442   NSWindow *window = [theEvent window];
5444   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsApp sendEvent:]");
5445   NSTRACE_MSG ("Type: %d", type);
5447 #ifdef NS_IMPL_GNUSTEP
5448   // Keyboard events aren't propagated to file dialogs for some reason.
5449   if ([NSApp modalWindow] != nil &&
5450       (type == NSEventTypeKeyDown || type == NSEventTypeKeyUp || type == NSEventTypeFlagsChanged))
5451     {
5452       [[NSApp modalWindow] sendEvent: theEvent];
5453       return;
5454     }
5455 #endif
5457   if (represented_filename != nil && represented_frame)
5458     {
5459       NSString *fstr = represented_filename;
5460       NSView *view = FRAME_NS_VIEW (represented_frame);
5461 #ifdef NS_IMPL_COCOA
5462       /* work around a bug observed on 10.3 and later where
5463          setTitleWithRepresentedFilename does not clear out previous state
5464          if given filename does not exist */
5465       if (! [[NSFileManager defaultManager] fileExistsAtPath: fstr])
5466         [[view window] setRepresentedFilename: @""];
5467 #endif
5468       [[view window] setRepresentedFilename: fstr];
5469       [represented_filename release];
5470       represented_filename = nil;
5471       represented_frame = NULL;
5472     }
5474   if (type == NSEventTypeApplicationDefined)
5475     {
5476       switch ([theEvent data2])
5477         {
5478 #ifdef NS_IMPL_COCOA
5479         case NSAPP_DATA2_RUNASSCRIPT:
5480           ns_run_ascript ();
5481           [self stop: self];
5482           return;
5483 #endif
5484         case NSAPP_DATA2_RUNFILEDIALOG:
5485           ns_run_file_dialog ();
5486           [self stop: self];
5487           return;
5488         }
5489     }
5491   if (type == NSEventTypeCursorUpdate && window == nil)
5492     {
5493       fprintf (stderr, "Dropping external cursor update event.\n");
5494       return;
5495     }
5497   if (type == NSEventTypeApplicationDefined)
5498     {
5499       /* Events posted by ns_send_appdefined interrupt the run loop here.
5500          But, if a modal window is up, an appdefined can still come through,
5501          (e.g., from a makeKeyWindow event) but stopping self also stops the
5502          modal loop. Just defer it until later. */
5503       if ([NSApp modalWindow] == nil)
5504         {
5505           last_appdefined_event_data = [theEvent data1];
5506           [self stop: self];
5507         }
5508       else
5509         {
5510           send_appdefined = YES;
5511         }
5512     }
5515 #ifdef NS_IMPL_COCOA
5516   /* If no dialog and none of our frames have focus and it is a move, skip it.
5517      It is a mouse move in an auxiliary menu, i.e. on the top right on macOS,
5518      such as Wifi, sound, date or similar.
5519      This prevents "spooky" highlighting in the frame under the menu.  */
5520   if (type == NSEventTypeMouseMoved && [NSApp modalWindow] == nil)
5521     {
5522       struct ns_display_info *di;
5523       BOOL has_focus = NO;
5524       for (di = x_display_list; ! has_focus && di; di = di->next)
5525         has_focus = di->x_focus_frame != 0;
5526       if (! has_focus)
5527         return;
5528     }
5529 #endif
5531   NSTRACE_UNSILENCE();
5533   [super sendEvent: theEvent];
5537 - (void)showPreferencesWindow: (id)sender
5539   struct frame *emacsframe = SELECTED_FRAME ();
5540   NSEvent *theEvent = [NSApp currentEvent];
5542   if (!emacs_event)
5543     return;
5544   emacs_event->kind = NS_NONKEY_EVENT;
5545   emacs_event->code = KEY_NS_SHOW_PREFS;
5546   emacs_event->modifiers = 0;
5547   EV_TRAILER (theEvent);
5551 - (void)newFrame: (id)sender
5553   NSTRACE ("[EmacsApp newFrame:]");
5555   struct frame *emacsframe = SELECTED_FRAME ();
5556   NSEvent *theEvent = [NSApp currentEvent];
5558   if (!emacs_event)
5559     return;
5560   emacs_event->kind = NS_NONKEY_EVENT;
5561   emacs_event->code = KEY_NS_NEW_FRAME;
5562   emacs_event->modifiers = 0;
5563   EV_TRAILER (theEvent);
5567 /* Open a file (used by below, after going into queue read by ns_read_socket) */
5568 - (BOOL) openFile: (NSString *)fileName
5570   NSTRACE ("[EmacsApp openFile:]");
5572   struct frame *emacsframe = SELECTED_FRAME ();
5573   NSEvent *theEvent = [NSApp currentEvent];
5575   if (!emacs_event)
5576     return NO;
5578   emacs_event->kind = NS_NONKEY_EVENT;
5579   emacs_event->code = KEY_NS_OPEN_FILE_LINE;
5580   ns_input_file = append2 (ns_input_file, build_string ([fileName UTF8String]));
5581   ns_input_line = Qnil; /* can be start or cons start,end */
5582   emacs_event->modifiers =0;
5583   EV_TRAILER (theEvent);
5585   return YES;
5589 /* **************************************************************************
5591       EmacsApp delegate implementation
5593    ************************************************************************** */
5595 - (void)applicationDidFinishLaunching: (NSNotification *)notification
5596 /* --------------------------------------------------------------------------
5597      When application is loaded, terminate event loop in ns_term_init
5598    -------------------------------------------------------------------------- */
5600   NSTRACE ("[EmacsApp applicationDidFinishLaunching:]");
5602 #ifdef NS_IMPL_GNUSTEP
5603   ((EmacsApp *)self)->applicationDidFinishLaunchingCalled = YES;
5604 #endif
5605   [NSApp setServicesProvider: NSApp];
5607   [self antialiasThresholdDidChange:nil];
5608 #ifdef NS_IMPL_COCOA
5609   [[NSNotificationCenter defaultCenter]
5610     addObserver:self
5611        selector:@selector(antialiasThresholdDidChange:)
5612            name:NSAntialiasThresholdChangedNotification
5613          object:nil];
5614 #endif
5616 #ifdef NS_IMPL_COCOA
5617   if ([NSApp activationPolicy] == NSApplicationActivationPolicyProhibited) {
5618     /* Set the app's activation policy to regular when we run outside
5619        of a bundle.  This is already done for us by Info.plist when we
5620        run inside a bundle. */
5621     [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
5622     [NSApp setApplicationIconImage:
5623              [EmacsImage
5624                allocInitFromFile:
5625                  build_string("icons/hicolor/128x128/apps/emacs.png")]];
5626   }
5627 #endif
5629   ns_send_appdefined (-2);
5632 - (void)antialiasThresholdDidChange:(NSNotification *)notification
5634 #ifdef NS_IMPL_COCOA
5635   macfont_update_antialias_threshold ();
5636 #endif
5640 /* Termination sequences:
5641     C-x C-c:
5642     Cmd-Q:
5643     MenuBar | File | Exit:
5644     Select Quit from App menubar:
5645         -terminate
5646         KEY_NS_POWER_OFF, (save-buffers-kill-emacs)
5647         ns_term_shutdown()
5649     Select Quit from Dock menu:
5650     Logout attempt:
5651         -appShouldTerminate
5652           Cancel -> Nothing else
5653           Accept ->
5655           -terminate
5656           KEY_NS_POWER_OFF, (save-buffers-kill-emacs)
5657           ns_term_shutdown()
5661 - (void) terminate: (id)sender
5663   NSTRACE ("[EmacsApp terminate:]");
5665   struct frame *emacsframe = SELECTED_FRAME ();
5667   if (!emacs_event)
5668     return;
5670   emacs_event->kind = NS_NONKEY_EVENT;
5671   emacs_event->code = KEY_NS_POWER_OFF;
5672   emacs_event->arg = Qt; /* mark as non-key event */
5673   EV_TRAILER ((id)nil);
5676 static bool
5677 runAlertPanel(NSString *title,
5678               NSString *msgFormat,
5679               NSString *defaultButton,
5680               NSString *alternateButton)
5682 #ifdef NS_IMPL_GNUSTEP
5683   return NSRunAlertPanel(title, msgFormat, defaultButton, alternateButton, nil)
5684     == NSAlertDefaultReturn;
5685 #else
5686   NSAlert *alert = [[NSAlert alloc] init];
5687   [alert setAlertStyle: NSAlertStyleCritical];
5688   [alert setMessageText: msgFormat];
5689   [alert addButtonWithTitle: defaultButton];
5690   [alert addButtonWithTitle: alternateButton];
5691   NSInteger ret = [alert runModal];
5692   [alert release];
5693   return ret == NSAlertFirstButtonReturn;
5694 #endif
5698 - (NSApplicationTerminateReply)applicationShouldTerminate: (id)sender
5700   NSTRACE ("[EmacsApp applicationShouldTerminate:]");
5702   bool ret;
5704   if (NILP (ns_confirm_quit)) //   || ns_shutdown_properly  --> TO DO
5705     return NSTerminateNow;
5707   ret = runAlertPanel(ns_app_name,
5708                       @"Exit requested.  Would you like to Save Buffers and Exit, or Cancel the request?",
5709                       @"Save Buffers and Exit", @"Cancel");
5711   return ret ? NSTerminateNow : NSTerminateCancel;
5714 static int
5715 not_in_argv (NSString *arg)
5717   int k;
5718   const char *a = [arg UTF8String];
5719   for (k = 1; k < initial_argc; ++k)
5720     if (strcmp (a, initial_argv[k]) == 0) return 0;
5721   return 1;
5724 /*   Notification from the Workspace to open a file */
5725 - (BOOL)application: sender openFile: (NSString *)file
5727   if (ns_do_open_file || not_in_argv (file))
5728     [ns_pending_files addObject: file];
5729   return YES;
5733 /*   Open a file as a temporary file */
5734 - (BOOL)application: sender openTempFile: (NSString *)file
5736   if (ns_do_open_file || not_in_argv (file))
5737     [ns_pending_files addObject: file];
5738   return YES;
5742 /*   Notification from the Workspace to open a file noninteractively (?) */
5743 - (BOOL)application: sender openFileWithoutUI: (NSString *)file
5745   if (ns_do_open_file || not_in_argv (file))
5746     [ns_pending_files addObject: file];
5747   return YES;
5750 /*   Notification from the Workspace to open multiple files */
5751 - (void)application: sender openFiles: (NSArray *)fileList
5753   NSEnumerator *files = [fileList objectEnumerator];
5754   NSString *file;
5755   /* Don't open files from the command line unconditionally,
5756      Cocoa parses the command line wrong, --option value tries to open value
5757      if --option is the last option.  */
5758   while ((file = [files nextObject]) != nil)
5759     if (ns_do_open_file || not_in_argv (file))
5760       [ns_pending_files addObject: file];
5762   [self replyToOpenOrPrint: NSApplicationDelegateReplySuccess];
5767 /* Handle dock menu requests.  */
5768 - (NSMenu *)applicationDockMenu: (NSApplication *) sender
5770   return dockMenu;
5774 /* TODO: these may help w/IO switching btwn terminal and NSApp */
5775 - (void)applicationWillBecomeActive: (NSNotification *)notification
5777   NSTRACE ("[EmacsApp applicationWillBecomeActive:]");
5778   //ns_app_active=YES;
5781 - (void)applicationDidBecomeActive: (NSNotification *)notification
5783   NSTRACE ("[EmacsApp applicationDidBecomeActive:]");
5785 #ifdef NS_IMPL_GNUSTEP
5786   if (! applicationDidFinishLaunchingCalled)
5787     [self applicationDidFinishLaunching:notification];
5788 #endif
5789   //ns_app_active=YES;
5791   ns_update_auto_hide_menu_bar ();
5792   // No constraining takes place when the application is not active.
5793   ns_constrain_all_frames ();
5795 - (void)applicationDidResignActive: (NSNotification *)notification
5797   NSTRACE ("[EmacsApp applicationDidResignActive:]");
5799   //ns_app_active=NO;
5800   ns_send_appdefined (-1);
5805 /* ==========================================================================
5807     EmacsApp aux handlers for managing event loop
5809    ========================================================================== */
5812 - (void)timeout_handler: (NSTimer *)timedEntry
5813 /* --------------------------------------------------------------------------
5814      The timeout specified to ns_select has passed.
5815    -------------------------------------------------------------------------- */
5817   /*NSTRACE ("timeout_handler"); */
5818   ns_send_appdefined (-2);
5821 - (void)sendFromMainThread:(id)unused
5823   ns_send_appdefined (nextappdefined);
5826 - (void)fd_handler:(id)unused
5827 /* --------------------------------------------------------------------------
5828      Check data waiting on file descriptors and terminate if so
5829    -------------------------------------------------------------------------- */
5831   int result;
5832   int waiting = 1, nfds;
5833   char c;
5835   fd_set readfds, writefds, *wfds;
5836   struct timespec timeout, *tmo;
5837   NSAutoreleasePool *pool = nil;
5839   /* NSTRACE ("fd_handler"); */
5841   for (;;)
5842     {
5843       [pool release];
5844       pool = [[NSAutoreleasePool alloc] init];
5846       if (waiting)
5847         {
5848           fd_set fds;
5849           FD_ZERO (&fds);
5850           FD_SET (selfds[0], &fds);
5851           result = select (selfds[0]+1, &fds, NULL, NULL, NULL);
5852           if (result > 0 && read (selfds[0], &c, 1) == 1 && c == 'g')
5853             waiting = 0;
5854         }
5855       else
5856         {
5857           pthread_mutex_lock (&select_mutex);
5858           nfds = select_nfds;
5860           if (select_valid & SELECT_HAVE_READ)
5861             readfds = select_readfds;
5862           else
5863             FD_ZERO (&readfds);
5865           if (select_valid & SELECT_HAVE_WRITE)
5866             {
5867               writefds = select_writefds;
5868               wfds = &writefds;
5869             }
5870           else
5871             wfds = NULL;
5872           if (select_valid & SELECT_HAVE_TMO)
5873             {
5874               timeout = select_timeout;
5875               tmo = &timeout;
5876             }
5877           else
5878             tmo = NULL;
5880           pthread_mutex_unlock (&select_mutex);
5882           FD_SET (selfds[0], &readfds);
5883           if (selfds[0] >= nfds) nfds = selfds[0]+1;
5885           result = pselect (nfds, &readfds, wfds, NULL, tmo, NULL);
5887           if (result == 0)
5888             ns_send_appdefined (-2);
5889           else if (result > 0)
5890             {
5891               if (FD_ISSET (selfds[0], &readfds))
5892                 {
5893                   if (read (selfds[0], &c, 1) == 1 && c == 's')
5894                     waiting = 1;
5895                 }
5896               else
5897                 {
5898                   pthread_mutex_lock (&select_mutex);
5899                   if (select_valid & SELECT_HAVE_READ)
5900                     select_readfds = readfds;
5901                   if (select_valid & SELECT_HAVE_WRITE)
5902                     select_writefds = writefds;
5903                   if (select_valid & SELECT_HAVE_TMO)
5904                     select_timeout = timeout;
5905                   pthread_mutex_unlock (&select_mutex);
5907                   ns_send_appdefined (result);
5908                 }
5909             }
5910           waiting = 1;
5911         }
5912     }
5917 /* ==========================================================================
5919     Service provision
5921    ========================================================================== */
5923 /* called from system: queue for next pass through event loop */
5924 - (void)requestService: (NSPasteboard *)pboard
5925               userData: (NSString *)userData
5926                  error: (NSString **)error
5928   [ns_pending_service_names addObject: userData];
5929   [ns_pending_service_args addObject: [NSString stringWithUTF8String:
5930       SSDATA (ns_string_from_pasteboard (pboard))]];
5934 /* called from ns_read_socket to clear queue */
5935 - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
5937   struct frame *emacsframe = SELECTED_FRAME ();
5938   NSEvent *theEvent = [NSApp currentEvent];
5940   NSTRACE ("[EmacsApp fulfillService:withArg:]");
5942   if (!emacs_event)
5943     return NO;
5945   emacs_event->kind = NS_NONKEY_EVENT;
5946   emacs_event->code = KEY_NS_SPI_SERVICE_CALL;
5947   ns_input_spi_name = build_string ([name UTF8String]);
5948   ns_input_spi_arg = build_string ([arg UTF8String]);
5949   emacs_event->modifiers = EV_MODIFIERS (theEvent);
5950   EV_TRAILER (theEvent);
5952   return YES;
5956 @end  /* EmacsApp */
5960 /* ==========================================================================
5962     EmacsView implementation
5964    ========================================================================== */
5967 @implementation EmacsView
5969 /* needed to inform when window closed from LISP */
5970 - (void) setWindowClosing: (BOOL)closing
5972   NSTRACE ("[EmacsView setWindowClosing:%d]", closing);
5974   windowClosing = closing;
5978 - (void)dealloc
5980   NSTRACE ("[EmacsView dealloc]");
5981   [toolbar release];
5982   if (fs_state == FULLSCREEN_BOTH)
5983     [nonfs_window release];
5984   [super dealloc];
5988 /* called on font panel selection */
5989 - (void)changeFont: (id)sender
5991   NSEvent *e = [[self window] currentEvent];
5992   struct face *face = FACE_FROM_ID (emacsframe, DEFAULT_FACE_ID);
5993   struct font *font = face->font;
5994   id newFont;
5995   CGFloat size;
5996   NSFont *nsfont;
5998   NSTRACE ("[EmacsView changeFont:]");
6000   if (!emacs_event)
6001     return;
6003 #ifdef NS_IMPL_GNUSTEP
6004   nsfont = ((struct nsfont_info *)font)->nsfont;
6005 #endif
6006 #ifdef NS_IMPL_COCOA
6007   nsfont = (NSFont *) macfont_get_nsctfont (font);
6008 #endif
6010   if ((newFont = [sender convertFont: nsfont]))
6011     {
6012       SET_FRAME_GARBAGED (emacsframe); /* now needed as of 2008/10 */
6014       emacs_event->kind = NS_NONKEY_EVENT;
6015       emacs_event->modifiers = 0;
6016       emacs_event->code = KEY_NS_CHANGE_FONT;
6018       size = [newFont pointSize];
6019       ns_input_fontsize = make_number (lrint (size));
6020       ns_input_font = build_string ([[newFont familyName] UTF8String]);
6021       EV_TRAILER (e);
6022     }
6026 - (BOOL)acceptsFirstResponder
6028   NSTRACE ("[EmacsView acceptsFirstResponder]");
6029   return YES;
6033 - (void)resetCursorRects
6035   NSRect visible = [self visibleRect];
6036   NSCursor *currentCursor = FRAME_POINTER_TYPE (emacsframe);
6037   NSTRACE ("[EmacsView resetCursorRects]");
6039   if (currentCursor == nil)
6040     currentCursor = [NSCursor arrowCursor];
6042   if (!NSIsEmptyRect (visible))
6043     [self addCursorRect: visible cursor: currentCursor];
6044   [currentCursor setOnMouseEntered: YES];
6049 /*****************************************************************************/
6050 /* Keyboard handling. */
6051 #define NS_KEYLOG 0
6053 - (void)keyDown: (NSEvent *)theEvent
6055   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
6056   int code;
6057   unsigned fnKeysym = 0;
6058   static NSMutableArray *nsEvArray;
6059   int left_is_none;
6060   unsigned int flags = [theEvent modifierFlags];
6062   NSTRACE ("[EmacsView keyDown:]");
6064   /* Rhapsody and macOS give up and down events for the arrow keys */
6065   if (ns_fake_keydown == YES)
6066     ns_fake_keydown = NO;
6067   else if ([theEvent type] != NSEventTypeKeyDown)
6068     return;
6070   if (!emacs_event)
6071     return;
6073  if (![[self window] isKeyWindow]
6074      && [[theEvent window] isKindOfClass: [EmacsWindow class]]
6075      /* we must avoid an infinite loop here. */
6076      && (EmacsView *)[[theEvent window] delegate] != self)
6077    {
6078      /* XXX: There is an occasional condition in which, when Emacs display
6079          updates a different frame from the current one, and temporarily
6080          selects it, then processes some interrupt-driven input
6081          (dispnew.c:3878), OS will send the event to the correct NSWindow, but
6082          for some reason that window has its first responder set to the NSView
6083          most recently updated (I guess), which is not the correct one. */
6084      [(EmacsView *)[[theEvent window] delegate] keyDown: theEvent];
6085      return;
6086    }
6088   if (nsEvArray == nil)
6089     nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1];
6091   [NSCursor setHiddenUntilMouseMoves: YES];
6093   if (hlinfo->mouse_face_hidden && INTEGERP (Vmouse_highlight))
6094     {
6095       clear_mouse_face (hlinfo);
6096       hlinfo->mouse_face_hidden = 1;
6097     }
6099   if (!processingCompose)
6100     {
6101       /* When using screen sharing, no left or right information is sent,
6102          so use Left key in those cases.  */
6103       int is_left_key, is_right_key;
6105       code = ([[theEvent charactersIgnoringModifiers] length] == 0) ?
6106         0 : [[theEvent charactersIgnoringModifiers] characterAtIndex: 0];
6108       /* (Carbon way: [theEvent keyCode]) */
6110       /* is it a "function key"? */
6111       /* Note: Sometimes a plain key will have the NSEventModifierFlagNumericPad
6112          flag set (this is probably a bug in the OS).
6113       */
6114       if (code < 0x00ff && (flags&NSEventModifierFlagNumericPad))
6115         {
6116           fnKeysym = ns_convert_key ([theEvent keyCode] | NSEventModifierFlagNumericPad);
6117         }
6118       if (fnKeysym == 0)
6119         {
6120           fnKeysym = ns_convert_key (code);
6121         }
6123       if (fnKeysym)
6124         {
6125           /* COUNTERHACK: map 'Delete' on upper-right main KB to 'Backspace',
6126              because Emacs treats Delete and KP-Delete same (in simple.el). */
6127           if ((fnKeysym == 0xFFFF && [theEvent keyCode] == 0x33)
6128 #ifdef NS_IMPL_GNUSTEP
6129               /*  GNUstep uses incompatible keycodes, even for those that are
6130                   supposed to be hardware independent.  Just check for delete.
6131                   Keypad delete does not have keysym 0xFFFF.
6132                   See https://savannah.gnu.org/bugs/?25395
6133               */
6134               || (fnKeysym == 0xFFFF && code == 127)
6135 #endif
6136             )
6137             code = 0xFF08; /* backspace */
6138           else
6139             code = fnKeysym;
6140         }
6142       /* are there modifiers? */
6143       emacs_event->modifiers = 0;
6145       if (flags & NSEventModifierFlagHelp)
6146           emacs_event->modifiers |= hyper_modifier;
6148       if (flags & NSEventModifierFlagShift)
6149         emacs_event->modifiers |= shift_modifier;
6151       is_right_key = (flags & NSRightCommandKeyMask) == NSRightCommandKeyMask;
6152       is_left_key = (flags & NSLeftCommandKeyMask) == NSLeftCommandKeyMask
6153         || (! is_right_key && (flags & NSEventModifierFlagCommand) == NSEventModifierFlagCommand);
6155       if (is_right_key)
6156         emacs_event->modifiers |= parse_solitary_modifier
6157           (EQ (ns_right_command_modifier, Qleft)
6158            ? ns_command_modifier
6159            : ns_right_command_modifier);
6161       if (is_left_key)
6162         {
6163           emacs_event->modifiers |= parse_solitary_modifier
6164             (ns_command_modifier);
6166           /* if super (default), take input manager's word so things like
6167              dvorak / qwerty layout work */
6168           if (EQ (ns_command_modifier, Qsuper)
6169               && !fnKeysym
6170               && [[theEvent characters] length] != 0)
6171             {
6172               /* XXX: the code we get will be unshifted, so if we have
6173                  a shift modifier, must convert ourselves */
6174               if (!(flags & NSEventModifierFlagShift))
6175                 code = [[theEvent characters] characterAtIndex: 0];
6176 #if 0
6177               /* this is ugly and also requires linking w/Carbon framework
6178                  (for LMGetKbdType) so for now leave this rare (?) case
6179                  undealt with.. in future look into CGEvent methods */
6180               else
6181                 {
6182                   long smv = GetScriptManagerVariable (smKeyScript);
6183                   Handle uchrHandle = GetResource
6184                     ('uchr', GetScriptVariable (smv, smScriptKeys));
6185                   UInt32 dummy = 0;
6186                   UCKeyTranslate ((UCKeyboardLayout *) *uchrHandle,
6187                                  [[theEvent characters] characterAtIndex: 0],
6188                                  kUCKeyActionDisplay,
6189                                  (flags & ~NSEventModifierFlagCommand) >> 8,
6190                                  LMGetKbdType (), kUCKeyTranslateNoDeadKeysMask,
6191                                  &dummy, 1, &dummy, &code);
6192                   code &= 0xFF;
6193                 }
6194 #endif
6195             }
6196         }
6198       is_right_key = (flags & NSRightControlKeyMask) == NSRightControlKeyMask;
6199       is_left_key = (flags & NSLeftControlKeyMask) == NSLeftControlKeyMask
6200         || (! is_right_key && (flags & NSEventModifierFlagControl) == NSEventModifierFlagControl);
6202       if (is_right_key)
6203           emacs_event->modifiers |= parse_solitary_modifier
6204               (EQ (ns_right_control_modifier, Qleft)
6205                ? ns_control_modifier
6206                : ns_right_control_modifier);
6208       if (is_left_key)
6209         emacs_event->modifiers |= parse_solitary_modifier
6210           (ns_control_modifier);
6212       if (flags & NS_FUNCTION_KEY_MASK && !fnKeysym)
6213           emacs_event->modifiers |=
6214             parse_solitary_modifier (ns_function_modifier);
6216       left_is_none = NILP (ns_alternate_modifier)
6217         || EQ (ns_alternate_modifier, Qnone);
6219       is_right_key = (flags & NSRightAlternateKeyMask)
6220         == NSRightAlternateKeyMask;
6221       is_left_key = (flags & NSLeftAlternateKeyMask) == NSLeftAlternateKeyMask
6222         || (! is_right_key
6223             && (flags & NSEventModifierFlagOption) == NSEventModifierFlagOption);
6225       if (is_right_key)
6226         {
6227           if ((NILP (ns_right_alternate_modifier)
6228                || EQ (ns_right_alternate_modifier, Qnone)
6229                || (EQ (ns_right_alternate_modifier, Qleft) && left_is_none))
6230               && !fnKeysym)
6231             {   /* accept pre-interp alt comb */
6232               if ([[theEvent characters] length] > 0)
6233                 code = [[theEvent characters] characterAtIndex: 0];
6234               /*HACK: clear lone shift modifier to stop next if from firing */
6235               if (emacs_event->modifiers == shift_modifier)
6236                 emacs_event->modifiers = 0;
6237             }
6238           else
6239             emacs_event->modifiers |= parse_solitary_modifier
6240               (EQ (ns_right_alternate_modifier, Qleft)
6241                ? ns_alternate_modifier
6242                : ns_right_alternate_modifier);
6243         }
6245       if (is_left_key) /* default = meta */
6246         {
6247           if (left_is_none && !fnKeysym)
6248             {   /* accept pre-interp alt comb */
6249               if ([[theEvent characters] length] > 0)
6250                 code = [[theEvent characters] characterAtIndex: 0];
6251               /*HACK: clear lone shift modifier to stop next if from firing */
6252               if (emacs_event->modifiers == shift_modifier)
6253                 emacs_event->modifiers = 0;
6254             }
6255           else
6256               emacs_event->modifiers |=
6257                 parse_solitary_modifier (ns_alternate_modifier);
6258         }
6260   if (NS_KEYLOG)
6261     fprintf (stderr, "keyDown: code =%x\tfnKey =%x\tflags = %x\tmods = %x\n",
6262              (unsigned) code, fnKeysym, flags, emacs_event->modifiers);
6264       /* if it was a function key or had modifiers, pass it directly to emacs */
6265       if (fnKeysym || (emacs_event->modifiers
6266                        && (emacs_event->modifiers != shift_modifier)
6267                        && [[theEvent charactersIgnoringModifiers] length] > 0))
6268 /*[[theEvent characters] length] */
6269         {
6270           emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
6271           if (code < 0x20)
6272             code |= (1<<28)|(3<<16);
6273           else if (code == 0x7f)
6274             code |= (1<<28)|(3<<16);
6275           else if (!fnKeysym)
6276             emacs_event->kind = code > 0xFF
6277               ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT;
6279           emacs_event->code = code;
6280           EV_TRAILER (theEvent);
6281           processingCompose = NO;
6282           return;
6283         }
6284     }
6287   if (NS_KEYLOG && !processingCompose)
6288     fprintf (stderr, "keyDown: Begin compose sequence.\n");
6290   processingCompose = YES;
6291   [nsEvArray addObject: theEvent];
6292   [self interpretKeyEvents: nsEvArray];
6293   [nsEvArray removeObject: theEvent];
6297 /* <NSTextInput> implementation (called through super interpretKeyEvents:]). */
6300 /* <NSTextInput>: called when done composing;
6301    NOTE: also called when we delete over working text, followed immed.
6302          by doCommandBySelector: deleteBackward: */
6303 - (void)insertText: (id)aString
6305   NSString *s;
6306   NSUInteger len;
6308   NSTRACE ("[EmacsView insertText:]");
6310   if ([aString isKindOfClass:[NSAttributedString class]])
6311     s = [aString string];
6312   else
6313     s = aString;
6315   len = [s length];
6317   if (NS_KEYLOG)
6318     NSLog (@"insertText '%@'\tlen = %lu", aString, (unsigned long) len);
6319   processingCompose = NO;
6321   if (!emacs_event)
6322     return;
6324   /* first, clear any working text */
6325   if (workingText != nil)
6326     [self deleteWorkingText];
6328   /* It might be preferable to use getCharacters:range: below,
6329      cf. https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CocoaPerformance/Articles/StringDrawing.html#//apple_ref/doc/uid/TP40001445-112378.
6330      However, we probably can't use SAFE_NALLOCA here because it might
6331      exit nonlocally.  */
6333   /* now insert the string as keystrokes */
6334   for (NSUInteger i = 0; i < len; i++)
6335     {
6336       NSUInteger code = [s characterAtIndex:i];
6337       if (UTF_16_HIGH_SURROGATE_P (code) && i < len - 1)
6338         {
6339           unichar low = [s characterAtIndex:i + 1];
6340           if (UTF_16_LOW_SURROGATE_P (low))
6341             {
6342               code = surrogates_to_codepoint (low, code);
6343               ++i;
6344             }
6345         }
6346       /* TODO: still need this? */
6347       if (code == 0x2DC)
6348         code = '~'; /* 0x7E */
6349       if (code != 32) /* Space */
6350         emacs_event->modifiers = 0;
6351       emacs_event->kind
6352         = code > 0xFF ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT;
6353       emacs_event->code = code;
6354       EV_TRAILER ((id)nil);
6355     }
6359 /* <NSTextInput>: inserts display of composing characters */
6360 - (void)setMarkedText: (id)aString selectedRange: (NSRange)selRange
6362   NSString *str = [aString respondsToSelector: @selector (string)] ?
6363     [aString string] : aString;
6365   NSTRACE ("[EmacsView setMarkedText:selectedRange:]");
6367   if (NS_KEYLOG)
6368     NSLog (@"setMarkedText '%@' len =%lu range %lu from %lu",
6369            str, (unsigned long)[str length],
6370            (unsigned long)selRange.length,
6371            (unsigned long)selRange.location);
6373   if (workingText != nil)
6374     [self deleteWorkingText];
6375   if ([str length] == 0)
6376     return;
6378   if (!emacs_event)
6379     return;
6381   processingCompose = YES;
6382   workingText = [str copy];
6383   ns_working_text = build_string ([workingText UTF8String]);
6385   emacs_event->kind = NS_TEXT_EVENT;
6386   emacs_event->code = KEY_NS_PUT_WORKING_TEXT;
6387   EV_TRAILER ((id)nil);
6391 /* delete display of composing characters [not in <NSTextInput>] */
6392 - (void)deleteWorkingText
6394   NSTRACE ("[EmacsView deleteWorkingText]");
6396   if (workingText == nil)
6397     return;
6398   if (NS_KEYLOG)
6399     NSLog(@"deleteWorkingText len =%lu\n", (unsigned long)[workingText length]);
6400   [workingText release];
6401   workingText = nil;
6402   processingCompose = NO;
6404   if (!emacs_event)
6405     return;
6407   emacs_event->kind = NS_TEXT_EVENT;
6408   emacs_event->code = KEY_NS_UNPUT_WORKING_TEXT;
6409   EV_TRAILER ((id)nil);
6413 - (BOOL)hasMarkedText
6415   NSTRACE ("[EmacsView hasMarkedText]");
6417   return workingText != nil;
6421 - (NSRange)markedRange
6423   NSTRACE ("[EmacsView markedRange]");
6425   NSRange rng = workingText != nil
6426     ? NSMakeRange (0, [workingText length]) : NSMakeRange (NSNotFound, 0);
6427   if (NS_KEYLOG)
6428     NSLog (@"markedRange request");
6429   return rng;
6433 - (void)unmarkText
6435   NSTRACE ("[EmacsView unmarkText]");
6437   if (NS_KEYLOG)
6438     NSLog (@"unmark (accept) text");
6439   [self deleteWorkingText];
6440   processingCompose = NO;
6444 /* used to position char selection windows, etc. */
6445 - (NSRect)firstRectForCharacterRange: (NSRange)theRange
6447   NSRect rect;
6448   NSPoint pt;
6449   struct window *win = XWINDOW (FRAME_SELECTED_WINDOW (emacsframe));
6451   NSTRACE ("[EmacsView firstRectForCharacterRange:]");
6453   if (NS_KEYLOG)
6454     NSLog (@"firstRectForCharRange request");
6456   rect.size.width = theRange.length * FRAME_COLUMN_WIDTH (emacsframe);
6457   rect.size.height = FRAME_LINE_HEIGHT (emacsframe);
6458   pt.x = WINDOW_TEXT_TO_FRAME_PIXEL_X (win, win->phys_cursor.x);
6459   pt.y = WINDOW_TO_FRAME_PIXEL_Y (win, win->phys_cursor.y
6460                                        +FRAME_LINE_HEIGHT (emacsframe));
6462   pt = [self convertPoint: pt toView: nil];
6464 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
6465 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6466   if ([[self window] respondsToSelector: @selector(convertRectToScreen:)])
6467     {
6468 #endif
6469       rect.origin = pt;
6470       rect = [(EmacsWindow *) [self window] convertRectToScreen: rect];
6471 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6472     }
6473   else
6474 #endif
6475 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
6476 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 \
6477   || defined (NS_IMPL_GNUSTEP)
6478     {
6479       pt = [[self window] convertBaseToScreen: pt];
6480       rect.origin = pt;
6481     }
6482 #endif
6484   return rect;
6488 - (NSInteger)conversationIdentifier
6490   return (NSInteger)self;
6494 - (void)doCommandBySelector: (SEL)aSelector
6496   NSTRACE ("[EmacsView doCommandBySelector:]");
6498   if (NS_KEYLOG)
6499     NSLog (@"doCommandBySelector: %@", NSStringFromSelector (aSelector));
6501   processingCompose = NO;
6502   if (aSelector == @selector (deleteBackward:))
6503     {
6504       /* happens when user backspaces over an ongoing composition:
6505          throw a 'delete' into the event queue */
6506       if (!emacs_event)
6507         return;
6508       emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
6509       emacs_event->code = 0xFF08;
6510       EV_TRAILER ((id)nil);
6511     }
6514 - (NSArray *)validAttributesForMarkedText
6516   static NSArray *arr = nil;
6517   if (arr == nil) arr = [NSArray new];
6518  /* [[NSArray arrayWithObject: NSUnderlineStyleAttributeName] retain]; */
6519   return arr;
6522 - (NSRange)selectedRange
6524   if (NS_KEYLOG)
6525     NSLog (@"selectedRange request");
6526   return NSMakeRange (NSNotFound, 0);
6529 #if defined (NS_IMPL_COCOA) || GNUSTEP_GUI_MAJOR_VERSION > 0 || \
6530     GNUSTEP_GUI_MINOR_VERSION > 22
6531 - (NSUInteger)characterIndexForPoint: (NSPoint)thePoint
6532 #else
6533 - (unsigned int)characterIndexForPoint: (NSPoint)thePoint
6534 #endif
6536   if (NS_KEYLOG)
6537     NSLog (@"characterIndexForPoint request");
6538   return 0;
6541 - (NSAttributedString *)attributedSubstringFromRange: (NSRange)theRange
6543   static NSAttributedString *str = nil;
6544   if (str == nil) str = [NSAttributedString new];
6545   if (NS_KEYLOG)
6546     NSLog (@"attributedSubstringFromRange request");
6547   return str;
6550 /* End <NSTextInput> impl. */
6551 /*****************************************************************************/
6554 /* This is what happens when the user presses a mouse button.  */
6555 - (void)mouseDown: (NSEvent *)theEvent
6557   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6558   NSPoint p = [self convertPoint: [theEvent locationInWindow] fromView: nil];
6560   NSTRACE ("[EmacsView mouseDown:]");
6562   [self deleteWorkingText];
6564   if (!emacs_event)
6565     return;
6567   dpyinfo->last_mouse_frame = emacsframe;
6568   /* appears to be needed to prevent spurious movement events generated on
6569      button clicks */
6570   emacsframe->mouse_moved = 0;
6572   if ([theEvent type] == NSEventTypeScrollWheel)
6573     {
6574 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
6575 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6576       if ([theEvent respondsToSelector:@selector(hasPreciseScrollingDeltas)])
6577         {
6578 #endif
6579           /* If the input device is a touchpad or similar, use precise
6580            * scrolling deltas.  These are measured in pixels, so we
6581            * have to add them up until they exceed one line height,
6582            * then we can send a scroll wheel event.
6583            *
6584            * If the device only has coarse scrolling deltas, like a
6585            * real mousewheel, the deltas represent a ratio of whole
6586            * lines, so round up the number of lines.  This means we
6587            * always send one scroll event per click, but can still
6588            * scroll more than one line if the OS tells us to.
6589            */
6590           bool horizontal;
6591           int lines = 0;
6592           int scrollUp = NO;
6594           /* FIXME: At the top or bottom of the buffer we should
6595            * ignore momentum-phase events.  */
6596           if (! ns_use_mwheel_momentum
6597               && [theEvent momentumPhase] != NSEventPhaseNone)
6598             return;
6600           if ([theEvent hasPreciseScrollingDeltas])
6601             {
6602               static int totalDeltaX, totalDeltaY;
6603               int lineHeight;
6605               if (NUMBERP (ns_mwheel_line_height))
6606                 lineHeight = XINT (ns_mwheel_line_height);
6607               else
6608                 {
6609                   /* FIXME: Use actual line height instead of the default.  */
6610                   lineHeight = default_line_pixel_height
6611                     (XWINDOW (FRAME_SELECTED_WINDOW (emacsframe)));
6612                 }
6614               if ([theEvent phase] == NSEventPhaseBegan)
6615                 {
6616                   totalDeltaX = 0;
6617                   totalDeltaY = 0;
6618                 }
6620               totalDeltaX += [theEvent scrollingDeltaX];
6621               totalDeltaY += [theEvent scrollingDeltaY];
6623               /* Calculate the number of lines, if any, to scroll, and
6624                * reset the total delta for the direction we're NOT
6625                * scrolling so that small movements don't add up.  */
6626               if (abs (totalDeltaX) > abs (totalDeltaY)
6627                   && abs (totalDeltaX) > lineHeight)
6628                 {
6629                   horizontal = YES;
6630                   scrollUp = totalDeltaX > 0;
6632                   lines = abs (totalDeltaX / lineHeight);
6633                   totalDeltaX = totalDeltaX % lineHeight;
6634                   totalDeltaY = 0;
6635                 }
6636               else if (abs (totalDeltaY) >= abs (totalDeltaX)
6637                        && abs (totalDeltaY) > lineHeight)
6638                 {
6639                   horizontal = NO;
6640                   scrollUp = totalDeltaY > 0;
6642                   lines = abs (totalDeltaY / lineHeight);
6643                   totalDeltaY = totalDeltaY % lineHeight;
6644                   totalDeltaX = 0;
6645                 }
6647               if (lines > 1 && ! ns_use_mwheel_acceleration)
6648                 lines = 1;
6649             }
6650           else
6651             {
6652               CGFloat delta;
6654               if ([theEvent scrollingDeltaY] == 0)
6655                 {
6656                   horizontal = YES;
6657                   delta = [theEvent scrollingDeltaX];
6658                 }
6659               else
6660                 {
6661                   horizontal = NO;
6662                   delta = [theEvent scrollingDeltaY];
6663                 }
6665               lines = (ns_use_mwheel_acceleration)
6666                 ? ceil (fabs (delta)) : 1;
6668               scrollUp = delta > 0;
6669             }
6671           if (lines == 0)
6672             return;
6674           emacs_event->kind = horizontal ? HORIZ_WHEEL_EVENT : WHEEL_EVENT;
6675           emacs_event->arg = (make_number (lines));
6677           emacs_event->code = 0;
6678           emacs_event->modifiers = EV_MODIFIERS (theEvent) |
6679             (scrollUp ? up_modifier : down_modifier);
6680 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6681         }
6682       else
6683 #endif
6684 #endif /* defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
6685 #if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6686         {
6687           CGFloat delta = [theEvent deltaY];
6688           /* Mac notebooks send wheel events w/delta =0 when trackpad scrolling */
6689           if (delta == 0)
6690             {
6691               delta = [theEvent deltaX];
6692               if (delta == 0)
6693                 {
6694                   NSTRACE_MSG ("deltaIsZero");
6695                   return;
6696                 }
6697               emacs_event->kind = HORIZ_WHEEL_EVENT;
6698             }
6699           else
6700             emacs_event->kind = WHEEL_EVENT;
6702           emacs_event->code = 0;
6703           emacs_event->modifiers = EV_MODIFIERS (theEvent) |
6704             ((delta > 0) ? up_modifier : down_modifier);
6705         }
6706 #endif
6707     }
6708   else
6709     {
6710       emacs_event->kind = MOUSE_CLICK_EVENT;
6711       emacs_event->code = EV_BUTTON (theEvent);
6712       emacs_event->modifiers = EV_MODIFIERS (theEvent)
6713                              | EV_UDMODIFIERS (theEvent);
6714     }
6716   XSETINT (emacs_event->x, lrint (p.x));
6717   XSETINT (emacs_event->y, lrint (p.y));
6718   EV_TRAILER (theEvent);
6719   return;
6723 - (void)rightMouseDown: (NSEvent *)theEvent
6725   NSTRACE ("[EmacsView rightMouseDown:]");
6726   [self mouseDown: theEvent];
6730 - (void)otherMouseDown: (NSEvent *)theEvent
6732   NSTRACE ("[EmacsView otherMouseDown:]");
6733   [self mouseDown: theEvent];
6737 - (void)mouseUp: (NSEvent *)theEvent
6739   NSTRACE ("[EmacsView mouseUp:]");
6740   [self mouseDown: theEvent];
6744 - (void)rightMouseUp: (NSEvent *)theEvent
6746   NSTRACE ("[EmacsView rightMouseUp:]");
6747   [self mouseDown: theEvent];
6751 - (void)otherMouseUp: (NSEvent *)theEvent
6753   NSTRACE ("[EmacsView otherMouseUp:]");
6754   [self mouseDown: theEvent];
6758 - (void) scrollWheel: (NSEvent *)theEvent
6760   NSTRACE ("[EmacsView scrollWheel:]");
6761   [self mouseDown: theEvent];
6765 /* Tell emacs the mouse has moved. */
6766 - (void)mouseMoved: (NSEvent *)e
6768   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
6769   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6770   Lisp_Object frame;
6771   NSPoint pt;
6773   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsView mouseMoved:]");
6775   dpyinfo->last_mouse_movement_time = EV_TIMESTAMP (e);
6776   pt = [self convertPoint: [e locationInWindow] fromView: nil];
6777   dpyinfo->last_mouse_motion_x = pt.x;
6778   dpyinfo->last_mouse_motion_y = pt.y;
6780   /* update any mouse face */
6781   if (hlinfo->mouse_face_hidden)
6782     {
6783       hlinfo->mouse_face_hidden = 0;
6784       clear_mouse_face (hlinfo);
6785     }
6787   /* tooltip handling */
6788   previous_help_echo_string = help_echo_string;
6789   help_echo_string = Qnil;
6791   if (!NILP (Vmouse_autoselect_window))
6792     {
6793       NSTRACE_MSG ("mouse_autoselect_window");
6794       static Lisp_Object last_mouse_window;
6795       Lisp_Object window
6796         = window_from_coordinates (emacsframe, pt.x, pt.y, 0, 0);
6798       if (WINDOWP (window)
6799           && !EQ (window, last_mouse_window)
6800           && !EQ (window, selected_window)
6801           && (!NILP (focus_follows_mouse)
6802               || (EQ (XWINDOW (window)->frame,
6803                       XWINDOW (selected_window)->frame))))
6804         {
6805           NSTRACE_MSG ("in_window");
6806           emacs_event->kind = SELECT_WINDOW_EVENT;
6807           emacs_event->frame_or_window = window;
6808           EV_TRAILER2 (e);
6809         }
6810       /* Remember the last window where we saw the mouse.  */
6811       last_mouse_window = window;
6812     }
6814   if (!note_mouse_movement (emacsframe, pt.x, pt.y))
6815     help_echo_string = previous_help_echo_string;
6817   XSETFRAME (frame, emacsframe);
6818   if (!NILP (help_echo_string) || !NILP (previous_help_echo_string))
6819     {
6820       /* NOTE: help_echo_{window,pos,object} are set in xdisp.c
6821          (note_mouse_highlight), which is called through the
6822          note_mouse_movement () call above */
6823       any_help_event_p = YES;
6824       gen_help_event (help_echo_string, frame, help_echo_window,
6825                       help_echo_object, help_echo_pos);
6826     }
6828   if (emacsframe->mouse_moved && send_appdefined)
6829     ns_send_appdefined (-1);
6833 - (void)mouseDragged: (NSEvent *)e
6835   NSTRACE ("[EmacsView mouseDragged:]");
6836   [self mouseMoved: e];
6840 - (void)rightMouseDragged: (NSEvent *)e
6842   NSTRACE ("[EmacsView rightMouseDragged:]");
6843   [self mouseMoved: e];
6847 - (void)otherMouseDragged: (NSEvent *)e
6849   NSTRACE ("[EmacsView otherMouseDragged:]");
6850   [self mouseMoved: e];
6854 - (BOOL)windowShouldClose: (id)sender
6856   NSEvent *e =[[self window] currentEvent];
6858   NSTRACE ("[EmacsView windowShouldClose:]");
6859   windowClosing = YES;
6860   if (!emacs_event)
6861     return NO;
6862   emacs_event->kind = DELETE_WINDOW_EVENT;
6863   emacs_event->modifiers = 0;
6864   emacs_event->code = 0;
6865   EV_TRAILER (e);
6866   /* Don't close this window, let this be done from lisp code.  */
6867   return NO;
6870 - (void) updateFrameSize: (BOOL) delay
6872   NSWindow *window = [self window];
6873   NSRect wr = [window frame];
6874   int extra = 0;
6875   int oldc = cols, oldr = rows;
6876   int oldw = FRAME_PIXEL_WIDTH (emacsframe);
6877   int oldh = FRAME_PIXEL_HEIGHT (emacsframe);
6878   int neww, newh;
6880   NSTRACE ("[EmacsView updateFrameSize:]");
6881   NSTRACE_SIZE ("Original size", NSMakeSize (oldw, oldh));
6882   NSTRACE_RECT ("Original frame", wr);
6883   NSTRACE_MSG  ("Original columns: %d", cols);
6884   NSTRACE_MSG  ("Original rows: %d", rows);
6886   if (! [self isFullscreen])
6887     {
6888       int toolbar_height;
6889 #ifdef NS_IMPL_GNUSTEP
6890       // GNUstep does not always update the tool bar height.  Force it.
6891       if (toolbar && [toolbar isVisible])
6892           update_frame_tool_bar (emacsframe);
6893 #endif
6895       toolbar_height = FRAME_TOOLBAR_HEIGHT (emacsframe);
6896       if (toolbar_height < 0)
6897         toolbar_height = 35;
6899       extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
6900         + toolbar_height;
6901     }
6903   if (wait_for_tool_bar)
6904     {
6905       /* The toolbar height is always 0 in fullscreen and undecorated
6906          frames, so don't wait for it to become available. */
6907       if (FRAME_TOOLBAR_HEIGHT (emacsframe) == 0
6908           && FRAME_UNDECORATED (emacsframe) == false
6909           && ! [self isFullscreen])
6910         {
6911           NSTRACE_MSG ("Waiting for toolbar");
6912           return;
6913         }
6914       wait_for_tool_bar = NO;
6915     }
6917   neww = (int)wr.size.width - emacsframe->border_width;
6918   newh = (int)wr.size.height - extra;
6920   NSTRACE_SIZE ("New size", NSMakeSize (neww, newh));
6921   NSTRACE_MSG ("FRAME_TOOLBAR_HEIGHT: %d", FRAME_TOOLBAR_HEIGHT (emacsframe));
6922   NSTRACE_MSG ("FRAME_NS_TITLEBAR_HEIGHT: %d", FRAME_NS_TITLEBAR_HEIGHT (emacsframe));
6924   cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, neww);
6925   rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe, newh);
6927   if (cols < MINWIDTH)
6928     cols = MINWIDTH;
6930   if (rows < MINHEIGHT)
6931     rows = MINHEIGHT;
6933   NSTRACE_MSG ("New columns: %d", cols);
6934   NSTRACE_MSG ("New rows: %d", rows);
6936   if (oldr != rows || oldc != cols || neww != oldw || newh != oldh)
6937     {
6938       NSView *view = FRAME_NS_VIEW (emacsframe);
6940       change_frame_size (emacsframe,
6941                          FRAME_PIXEL_TO_TEXT_WIDTH (emacsframe, neww),
6942                          FRAME_PIXEL_TO_TEXT_HEIGHT (emacsframe, newh),
6943                          0, delay, 0, 1);
6944       SET_FRAME_GARBAGED (emacsframe);
6945       cancel_mouse_face (emacsframe);
6947       /* The next two lines set the frame to the same size as we've
6948          already set above.  We need to do this when we switch back
6949          from non-native fullscreen, in other circumstances it appears
6950          to be a noop.  (bug#28872) */
6951       wr = NSMakeRect (0, 0, neww, newh);
6952       [view setFrame: wr];
6954       // to do: consider using [NSNotificationCenter postNotificationName:].
6955       [self windowDidMove: // Update top/left.
6956               [NSNotification notificationWithName:NSWindowDidMoveNotification
6957                                             object:[view window]]];
6958     }
6959   else
6960     {
6961       NSTRACE_MSG ("No change");
6962     }
6965 - (NSSize)windowWillResize: (NSWindow *)sender toSize: (NSSize)frameSize
6966 /* normalize frame to gridded text size */
6968   int extra = 0;
6970   NSTRACE ("[EmacsView windowWillResize:toSize: " NSTRACE_FMT_SIZE "]",
6971            NSTRACE_ARG_SIZE (frameSize));
6972   NSTRACE_RECT   ("[sender frame]", [sender frame]);
6973   NSTRACE_FSTYPE ("fs_state", fs_state);
6975   if (!FRAME_LIVE_P (emacsframe))
6976     return frameSize;
6978   if (fs_state == FULLSCREEN_MAXIMIZED
6979       && (maximized_width != (int)frameSize.width
6980           || maximized_height != (int)frameSize.height))
6981     [self setFSValue: FULLSCREEN_NONE];
6982   else if (fs_state == FULLSCREEN_WIDTH
6983            && maximized_width != (int)frameSize.width)
6984     [self setFSValue: FULLSCREEN_NONE];
6985   else if (fs_state == FULLSCREEN_HEIGHT
6986            && maximized_height != (int)frameSize.height)
6987     [self setFSValue: FULLSCREEN_NONE];
6989   if (fs_state == FULLSCREEN_NONE)
6990     maximized_width = maximized_height = -1;
6992   if (! [self isFullscreen])
6993     {
6994       extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
6995         + FRAME_TOOLBAR_HEIGHT (emacsframe);
6996     }
6998   cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, frameSize.width);
6999   if (cols < MINWIDTH)
7000     cols = MINWIDTH;
7002   rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe,
7003                                            frameSize.height - extra);
7004   if (rows < MINHEIGHT)
7005     rows = MINHEIGHT;
7006 #ifdef NS_IMPL_COCOA
7007   {
7008     /* this sets window title to have size in it; the wm does this under GS */
7009     NSRect r = [[self window] frame];
7010     if (r.size.height == frameSize.height && r.size.width == frameSize.width)
7011       {
7012         if (old_title != 0)
7013           {
7014             xfree (old_title);
7015             old_title = 0;
7016           }
7017       }
7018     else if (fs_state == FULLSCREEN_NONE && ! maximizing_resize
7019              && [[self window] title] != NULL)
7020       {
7021         char *size_title;
7022         NSWindow *window = [self window];
7023         if (old_title == 0)
7024           {
7025             char *t = strdup ([[[self window] title] UTF8String]);
7026             char *pos = strstr (t, "  â€”  ");
7027             if (pos)
7028               *pos = '\0';
7029             old_title = t;
7030           }
7031         size_title = xmalloc (strlen (old_title) + 40);
7032         esprintf (size_title, "%s  â€”  (%d x %d)", old_title, cols, rows);
7033         [window setTitle: [NSString stringWithUTF8String: size_title]];
7034         xfree (size_title);
7035       }
7036   }
7037 #endif /* NS_IMPL_COCOA */
7039   NSTRACE_MSG ("cols: %d  rows: %d", cols, rows);
7041   /* Restrict the new size to the text gird.
7043      Don't restrict the width if the user only adjusted the height, and
7044      vice versa.  (Without this, the frame would shrink, and move
7045      slightly, if the window was resized by dragging one of its
7046      borders.) */
7047   if (!frame_resize_pixelwise)
7048     {
7049       NSRect r = [[self window] frame];
7051       if (r.size.width != frameSize.width)
7052         {
7053           frameSize.width =
7054             FRAME_TEXT_COLS_TO_PIXEL_WIDTH  (emacsframe, cols);
7055         }
7057       if (r.size.height != frameSize.height)
7058         {
7059           frameSize.height =
7060             FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (emacsframe, rows) + extra;
7061         }
7062     }
7064   NSTRACE_RETURN_SIZE (frameSize);
7066   return frameSize;
7070 - (void)windowDidResize: (NSNotification *)notification
7072   NSTRACE ("[EmacsView windowDidResize:]");
7073   if (!FRAME_LIVE_P (emacsframe))
7074     {
7075       NSTRACE_MSG ("Ignored (frame dead)");
7076       return;
7077     }
7078   if (emacsframe->output_data.ns->in_animation)
7079     {
7080       NSTRACE_MSG ("Ignored (in animation)");
7081       return;
7082     }
7084   if (! [self fsIsNative])
7085     {
7086       NSWindow *theWindow = [notification object];
7087       /* We can get notification on the non-FS window when in
7088          fullscreen mode.  */
7089       if ([self window] != theWindow) return;
7090     }
7092   NSTRACE_RECT ("frame", [[notification object] frame]);
7094 #ifdef NS_IMPL_GNUSTEP
7095   NSWindow *theWindow = [notification object];
7097    /* In GNUstep, at least currently, it's possible to get a didResize
7098       without getting a willResize.. therefore we need to act as if we got
7099       the willResize now */
7100   NSSize sz = [theWindow frame].size;
7101   sz = [self windowWillResize: theWindow toSize: sz];
7102 #endif /* NS_IMPL_GNUSTEP */
7104   if (cols > 0 && rows > 0)
7105     {
7106       [self updateFrameSize: YES];
7107     }
7109   ns_send_appdefined (-1);
7112 #ifdef NS_IMPL_COCOA
7113 - (void)viewDidEndLiveResize
7115   NSTRACE ("[EmacsView viewDidEndLiveResize]");
7117   [super viewDidEndLiveResize];
7118   if (old_title != 0)
7119     {
7120       [[self window] setTitle: [NSString stringWithUTF8String: old_title]];
7121       xfree (old_title);
7122       old_title = 0;
7123     }
7124   maximizing_resize = NO;
7126 #endif /* NS_IMPL_COCOA */
7129 - (void)windowDidBecomeKey: (NSNotification *)notification
7130 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
7132   [self windowDidBecomeKey];
7136 - (void)windowDidBecomeKey      /* for direct calls */
7138   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
7139   struct frame *old_focus = dpyinfo->x_focus_frame;
7141   NSTRACE ("[EmacsView windowDidBecomeKey]");
7143   if (emacsframe != old_focus)
7144     dpyinfo->x_focus_frame = emacsframe;
7146   ns_frame_rehighlight (emacsframe);
7148   if (emacs_event)
7149     {
7150       emacs_event->kind = FOCUS_IN_EVENT;
7151       EV_TRAILER ((id)nil);
7152     }
7156 - (void)windowDidResignKey: (NSNotification *)notification
7157 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
7159   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
7160   BOOL is_focus_frame = dpyinfo->x_focus_frame == emacsframe;
7161   NSTRACE ("[EmacsView windowDidResignKey:]");
7163   if (is_focus_frame)
7164     dpyinfo->x_focus_frame = 0;
7166   emacsframe->mouse_moved = 0;
7167   ns_frame_rehighlight (emacsframe);
7169   /* FIXME: for some reason needed on second and subsequent clicks away
7170             from sole-frame Emacs to get hollow box to show */
7171   if (!windowClosing && [[self window] isVisible] == YES)
7172     {
7173       x_update_cursor (emacsframe, 1);
7174       x_set_frame_alpha (emacsframe);
7175     }
7177   if (any_help_event_p)
7178     {
7179       Lisp_Object frame;
7180       XSETFRAME (frame, emacsframe);
7181       help_echo_string = Qnil;
7182       gen_help_event (Qnil, frame, Qnil, Qnil, 0);
7183     }
7185   if (emacs_event && is_focus_frame)
7186     {
7187       [self deleteWorkingText];
7188       emacs_event->kind = FOCUS_OUT_EVENT;
7189       EV_TRAILER ((id)nil);
7190     }
7194 - (void)windowWillMiniaturize: sender
7196   NSTRACE ("[EmacsView windowWillMiniaturize:]");
7200 - (void)setFrame:(NSRect)frameRect
7202   NSTRACE ("[EmacsView setFrame:" NSTRACE_FMT_RECT "]",
7203            NSTRACE_ARG_RECT (frameRect));
7205   [super setFrame:(NSRect)frameRect];
7209 - (BOOL)isFlipped
7211   return YES;
7215 - (BOOL)isOpaque
7217   return NO;
7221 - (void)createToolbar: (struct frame *)f
7223   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
7224   NSWindow *window = [view window];
7226   toolbar = [[EmacsToolbar alloc] initForView: self withIdentifier:
7227                    [NSString stringWithFormat: @"Emacs Frame %d",
7228                              ns_window_num]];
7229   [toolbar setVisible: NO];
7230   [window setToolbar: toolbar];
7232   /* Don't set frame garbaged until tool bar is up to date?
7233      This avoids an extra clear and redraw (flicker) at frame creation.  */
7234   if (FRAME_EXTERNAL_TOOL_BAR (f)) wait_for_tool_bar = YES;
7235   else wait_for_tool_bar = NO;
7238 #ifdef NS_IMPL_COCOA
7239   {
7240     NSButton *toggleButton;
7241     toggleButton = [window standardWindowButton: NSWindowToolbarButton];
7242     [toggleButton setTarget: self];
7243     [toggleButton setAction: @selector (toggleToolbar: )];
7244   }
7245 #endif
7249 - (instancetype) initFrameFromEmacs: (struct frame *)f
7251   NSRect r, wr;
7252   Lisp_Object tem;
7253   NSWindow *win;
7254   NSColor *col;
7255   NSString *name;
7257   NSTRACE ("[EmacsView initFrameFromEmacs:]");
7258   NSTRACE_MSG ("cols:%d lines:%d", f->text_cols, f->text_lines);
7260   windowClosing = NO;
7261   processingCompose = NO;
7262   scrollbarsNeedingUpdate = 0;
7263   fs_state = FULLSCREEN_NONE;
7264   fs_before_fs = next_maximized = -1;
7266   fs_is_native = NO;
7267 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7268 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7269   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7270 #endif
7271     fs_is_native = ns_use_native_fullscreen;
7272 #endif
7274   maximized_width = maximized_height = -1;
7275   nonfs_window = nil;
7277   ns_userRect = NSMakeRect (0, 0, 0, 0);
7278   r = NSMakeRect (0, 0, FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, f->text_cols),
7279                  FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, f->text_lines));
7280   [self initWithFrame: r];
7281   [self setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
7283   FRAME_NS_VIEW (f) = self;
7284   emacsframe = f;
7285 #ifdef NS_IMPL_COCOA
7286   old_title = 0;
7287   maximizing_resize = NO;
7288 #endif
7290   win = [[EmacsWindow alloc]
7291             initWithContentRect: r
7292                       styleMask: (FRAME_UNDECORATED (f)
7293                                   ? FRAME_UNDECORATED_FLAGS
7294                                   : FRAME_DECORATED_FLAGS)
7295                         backing: NSBackingStoreBuffered
7296                           defer: YES];
7298 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7299 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7300   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7301 #endif
7302     [win setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
7303 #endif
7305   wr = [win frame];
7306   bwidth = f->border_width = wr.size.width - r.size.width;
7308   [win setAcceptsMouseMovedEvents: YES];
7309   [win setDelegate: self];
7310 #if !defined (NS_IMPL_COCOA) || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7311 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7312   if ([win respondsToSelector: @selector(useOptimizedDrawing:)])
7313 #endif
7314     [win useOptimizedDrawing: YES];
7315 #endif
7317   [[win contentView] addSubview: self];
7319   if (ns_drag_types)
7320     [self registerForDraggedTypes: ns_drag_types];
7322   tem = f->name;
7323   name = [NSString stringWithUTF8String:
7324                    NILP (tem) ? "Emacs" : SSDATA (tem)];
7325   [win setTitle: name];
7327   /* toolbar support */
7328   if (! FRAME_UNDECORATED (f))
7329     [self createToolbar: f];
7331 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
7332 #ifndef NSAppKitVersionNumber10_10
7333 #define NSAppKitVersionNumber10_10 1343
7334 #endif
7336   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_10
7337       && FRAME_NS_APPEARANCE (f) != ns_appearance_aqua)
7338     win.appearance = [NSAppearance
7339                           appearanceNamed: NSAppearanceNameVibrantDark];
7340 #endif
7342 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
7343   if ([win respondsToSelector: @selector(titlebarAppearsTransparent)])
7344     win.titlebarAppearsTransparent = FRAME_NS_TRANSPARENT_TITLEBAR (f);
7345 #endif
7347   tem = f->icon_name;
7348   if (!NILP (tem))
7349     [win setMiniwindowTitle:
7350            [NSString stringWithUTF8String: SSDATA (tem)]];
7352   if (FRAME_PARENT_FRAME (f) != NULL)
7353     {
7354       NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
7355       [parent addChildWindow: win
7356                      ordered: NSWindowAbove];
7357     }
7359   if (FRAME_Z_GROUP (f) != z_group_none)
7360       win.level = NSNormalWindowLevel
7361         + (FRAME_Z_GROUP_BELOW (f) ? -1 : 1);
7363   {
7364     NSScreen *screen = [win screen];
7366     if (screen != 0)
7367       {
7368         NSPoint pt = NSMakePoint
7369           (IN_BOUND (-SCREENMAX, f->left_pos
7370                      + NS_PARENT_WINDOW_LEFT_POS (f), SCREENMAX),
7371            IN_BOUND (-SCREENMAX,
7372                      NS_PARENT_WINDOW_TOP_POS (f) - f->top_pos,
7373                      SCREENMAX));
7375         [win setFrameTopLeftPoint: pt];
7377         NSTRACE_RECT ("new frame", [win frame]);
7378       }
7379   }
7381   [win makeFirstResponder: self];
7383   col = ns_lookup_indexed_color (NS_FACE_BACKGROUND
7384                                  (FACE_FROM_ID (emacsframe, DEFAULT_FACE_ID)),
7385                                  emacsframe);
7386   [win setBackgroundColor: col];
7387   if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7388     [win setOpaque: NO];
7390 #if !defined (NS_IMPL_COCOA) \
7391   || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7392 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7393   if ([self respondsToSelector: @selector(allocateGState)])
7394 #endif
7395     [self allocateGState];
7396 #endif
7397   [NSApp registerServicesMenuSendTypes: ns_send_types
7398                            returnTypes: [NSArray array]];
7400   /* macOS Sierra automatically enables tabbed windows.  We can't
7401      allow this to be enabled until it's available on a Free system.
7402      Currently it only happens by accident and is buggy anyway. */
7403 #if defined (NS_IMPL_COCOA) \
7404   && MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
7405 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101200
7406   if ([win respondsToSelector: @selector(setTabbingMode:)])
7407 #endif
7408     [win setTabbingMode: NSWindowTabbingModeDisallowed];
7409 #endif
7411   ns_window_num++;
7412   return self;
7416 - (void)windowDidMove: sender
7418   NSWindow *win = [self window];
7419   NSRect r = [win frame];
7420   NSArray *screens = [NSScreen screens];
7421   NSScreen *screen = [screens objectAtIndex: 0];
7423   NSTRACE ("[EmacsView windowDidMove:]");
7425   if (!emacsframe->output_data.ns)
7426     return;
7427   if (screen != nil)
7428     {
7429       emacsframe->left_pos = r.origin.x - NS_PARENT_WINDOW_LEFT_POS (emacsframe);
7430       emacsframe->top_pos =
7431         NS_PARENT_WINDOW_TOP_POS (emacsframe) - (r.origin.y + r.size.height);
7433       if (emacs_event)
7434         {
7435           emacs_event->kind = MOVE_FRAME_EVENT;
7436           EV_TRAILER ((id)nil);
7437         }
7438     }
7442 /* Called AFTER method below, but before our windowWillResize call there leads
7443    to windowDidResize -> x_set_window_size.  Update emacs' notion of frame
7444    location so set_window_size moves the frame. */
7445 - (BOOL)windowShouldZoom: (NSWindow *)sender toFrame: (NSRect)newFrame
7447   NSTRACE (("[EmacsView windowShouldZoom:toFrame:" NSTRACE_FMT_RECT "]"
7448             NSTRACE_FMT_RETURN "YES"),
7449            NSTRACE_ARG_RECT (newFrame));
7451   emacsframe->output_data.ns->zooming = 1;
7452   return YES;
7456 /* Override to do something slightly nonstandard, but nice.  First click on
7457    zoom button will zoom vertically.  Second will zoom completely.  Third
7458    returns to original. */
7459 - (NSRect)windowWillUseStandardFrame:(NSWindow *)sender
7460                         defaultFrame:(NSRect)defaultFrame
7462   // TODO: Rename to "currentFrame" and assign "result" properly in
7463   // all paths.
7464   NSRect result = [sender frame];
7466   NSTRACE (("[EmacsView windowWillUseStandardFrame:defaultFrame:"
7467             NSTRACE_FMT_RECT "]"),
7468            NSTRACE_ARG_RECT (defaultFrame));
7469   NSTRACE_FSTYPE ("fs_state", fs_state);
7470   NSTRACE_FSTYPE ("fs_before_fs", fs_before_fs);
7471   NSTRACE_FSTYPE ("next_maximized", next_maximized);
7472   NSTRACE_RECT   ("ns_userRect", ns_userRect);
7473   NSTRACE_RECT   ("[sender frame]", [sender frame]);
7475   if (fs_before_fs != -1) /* Entering fullscreen */
7476     {
7477       NSTRACE_MSG ("Entering fullscreen");
7478       result = defaultFrame;
7479     }
7480   else
7481     {
7482       // Save the window size and position (frame) before the resize.
7483       if (fs_state != FULLSCREEN_MAXIMIZED
7484           && fs_state != FULLSCREEN_WIDTH)
7485         {
7486           ns_userRect.size.width = result.size.width;
7487           ns_userRect.origin.x   = result.origin.x;
7488         }
7490       if (fs_state != FULLSCREEN_MAXIMIZED
7491           && fs_state != FULLSCREEN_HEIGHT)
7492         {
7493           ns_userRect.size.height = result.size.height;
7494           ns_userRect.origin.y    = result.origin.y;
7495         }
7497       NSTRACE_RECT ("ns_userRect (2)", ns_userRect);
7499       if (next_maximized == FULLSCREEN_HEIGHT
7500           || (next_maximized == -1
7501               && abs ((int)(defaultFrame.size.height - result.size.height))
7502               > FRAME_LINE_HEIGHT (emacsframe)))
7503         {
7504           /* first click */
7505           NSTRACE_MSG ("FULLSCREEN_HEIGHT");
7506           maximized_height = result.size.height = defaultFrame.size.height;
7507           maximized_width = -1;
7508           result.origin.y = defaultFrame.origin.y;
7509           if (ns_userRect.size.height != 0)
7510             {
7511               result.origin.x = ns_userRect.origin.x;
7512               result.size.width = ns_userRect.size.width;
7513             }
7514           [self setFSValue: FULLSCREEN_HEIGHT];
7515 #ifdef NS_IMPL_COCOA
7516           maximizing_resize = YES;
7517 #endif
7518         }
7519       else if (next_maximized == FULLSCREEN_WIDTH)
7520         {
7521           NSTRACE_MSG ("FULLSCREEN_WIDTH");
7522           maximized_width = result.size.width = defaultFrame.size.width;
7523           maximized_height = -1;
7524           result.origin.x = defaultFrame.origin.x;
7525           if (ns_userRect.size.width != 0)
7526             {
7527               result.origin.y = ns_userRect.origin.y;
7528               result.size.height = ns_userRect.size.height;
7529             }
7530           [self setFSValue: FULLSCREEN_WIDTH];
7531         }
7532       else if (next_maximized == FULLSCREEN_MAXIMIZED
7533                || (next_maximized == -1
7534                    && abs ((int)(defaultFrame.size.width - result.size.width))
7535                    > FRAME_COLUMN_WIDTH (emacsframe)))
7536         {
7537           NSTRACE_MSG ("FULLSCREEN_MAXIMIZED");
7539           result = defaultFrame;  /* second click */
7540           maximized_width = result.size.width;
7541           maximized_height = result.size.height;
7542           [self setFSValue: FULLSCREEN_MAXIMIZED];
7543 #ifdef NS_IMPL_COCOA
7544           maximizing_resize = YES;
7545 #endif
7546         }
7547       else
7548         {
7549           /* restore */
7550           NSTRACE_MSG ("Restore");
7551           result = ns_userRect.size.height ? ns_userRect : result;
7552           NSTRACE_RECT ("restore (2)", result);
7553           ns_userRect = NSMakeRect (0, 0, 0, 0);
7554 #ifdef NS_IMPL_COCOA
7555           maximizing_resize = fs_state != FULLSCREEN_NONE;
7556 #endif
7557           [self setFSValue: FULLSCREEN_NONE];
7558           maximized_width = maximized_height = -1;
7559         }
7560     }
7562   if (fs_before_fs == -1) next_maximized = -1;
7564   NSTRACE_RECT   ("Final ns_userRect", ns_userRect);
7565   NSTRACE_MSG    ("Final maximized_width: %d", maximized_width);
7566   NSTRACE_MSG    ("Final maximized_height: %d", maximized_height);
7567   NSTRACE_FSTYPE ("Final next_maximized", next_maximized);
7569   [self windowWillResize: sender toSize: result.size];
7571   NSTRACE_RETURN_RECT (result);
7573   return result;
7577 - (void)windowDidDeminiaturize: sender
7579   NSTRACE ("[EmacsView windowDidDeminiaturize:]");
7580   if (!emacsframe->output_data.ns)
7581     return;
7583   SET_FRAME_ICONIFIED (emacsframe, 0);
7584   SET_FRAME_VISIBLE (emacsframe, 1);
7585   windows_or_buffers_changed = 63;
7587   if (emacs_event)
7588     {
7589       emacs_event->kind = DEICONIFY_EVENT;
7590       EV_TRAILER ((id)nil);
7591     }
7595 - (void)windowDidExpose: sender
7597   NSTRACE ("[EmacsView windowDidExpose:]");
7598   if (!emacsframe->output_data.ns)
7599     return;
7601   SET_FRAME_VISIBLE (emacsframe, 1);
7602   SET_FRAME_GARBAGED (emacsframe);
7604   if (send_appdefined)
7605     ns_send_appdefined (-1);
7609 - (void)windowDidMiniaturize: sender
7611   NSTRACE ("[EmacsView windowDidMiniaturize:]");
7612   if (!emacsframe->output_data.ns)
7613     return;
7615   SET_FRAME_ICONIFIED (emacsframe, 1);
7616   SET_FRAME_VISIBLE (emacsframe, 0);
7618   if (emacs_event)
7619     {
7620       emacs_event->kind = ICONIFY_EVENT;
7621       EV_TRAILER ((id)nil);
7622     }
7625 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7626 - (NSApplicationPresentationOptions)window:(NSWindow *)window
7627       willUseFullScreenPresentationOptions:
7628   (NSApplicationPresentationOptions)proposedOptions
7630   return proposedOptions|NSApplicationPresentationAutoHideToolbar;
7632 #endif
7634 - (void)windowWillEnterFullScreen:(NSNotification *)notification
7636   NSTRACE ("[EmacsView windowWillEnterFullScreen:]");
7637   [self windowWillEnterFullScreen];
7639 - (void)windowWillEnterFullScreen /* provided for direct calls */
7641   NSTRACE ("[EmacsView windowWillEnterFullScreen]");
7642   fs_before_fs = fs_state;
7645 - (void)windowDidEnterFullScreen:(NSNotification *)notification
7647   NSTRACE ("[EmacsView windowDidEnterFullScreen:]");
7648   [self windowDidEnterFullScreen];
7651 - (void)windowDidEnterFullScreen /* provided for direct calls */
7653   NSTRACE ("[EmacsView windowDidEnterFullScreen]");
7654   [self setFSValue: FULLSCREEN_BOTH];
7655   if (! [self fsIsNative])
7656     {
7657       [self windowDidBecomeKey];
7658       [nonfs_window orderOut:self];
7659     }
7660   else
7661     {
7662       BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (emacsframe) ? YES : NO;
7663 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 \
7664   && MAC_OS_X_VERSION_MIN_REQUIRED <= 1070
7665       unsigned val = (unsigned)[NSApp presentationOptions];
7667       // Mac OS X 10.7 bug fix, the menu won't appear without this.
7668       // val is non-zero on other macOS versions.
7669       if (val == 0)
7670         {
7671           NSApplicationPresentationOptions options
7672             = NSApplicationPresentationAutoHideDock
7673             | NSApplicationPresentationAutoHideMenuBar
7674             | NSApplicationPresentationFullScreen
7675             | NSApplicationPresentationAutoHideToolbar;
7677           [NSApp setPresentationOptions: options];
7678         }
7679 #endif
7680       [toolbar setVisible:tbar_visible];
7681     }
7684 - (void)windowWillExitFullScreen:(NSNotification *)notification
7686   NSTRACE ("[EmacsView windowWillExitFullScreen:]");
7687   [self windowWillExitFullScreen];
7690 - (void)windowWillExitFullScreen /* provided for direct calls */
7692   NSTRACE ("[EmacsView windowWillExitFullScreen]");
7693   if (!FRAME_LIVE_P (emacsframe))
7694     {
7695       NSTRACE_MSG ("Ignored (frame dead)");
7696       return;
7697     }
7698   if (next_maximized != -1)
7699     fs_before_fs = next_maximized;
7702 - (void)windowDidExitFullScreen:(NSNotification *)notification
7704   NSTRACE ("[EmacsView windowDidExitFullScreen:]");
7705   [self windowDidExitFullScreen];
7708 - (void)windowDidExitFullScreen /* provided for direct calls */
7710   NSTRACE ("[EmacsView windowDidExitFullScreen]");
7711   if (!FRAME_LIVE_P (emacsframe))
7712     {
7713       NSTRACE_MSG ("Ignored (frame dead)");
7714       return;
7715     }
7716   [self setFSValue: fs_before_fs];
7717   fs_before_fs = -1;
7718 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7719   [self updateCollectionBehavior];
7720 #endif
7721   if (FRAME_EXTERNAL_TOOL_BAR (emacsframe))
7722     {
7723       [toolbar setVisible:YES];
7724       update_frame_tool_bar (emacsframe);
7725       [self updateFrameSize:YES];
7726       [[self window] display];
7727     }
7728   else
7729     [toolbar setVisible:NO];
7731   if (next_maximized != -1)
7732     [[self window] performZoom:self];
7735 - (BOOL)fsIsNative
7737   return fs_is_native;
7740 - (BOOL)isFullscreen
7742   BOOL res;
7744   if (! fs_is_native)
7745     {
7746       res = (nonfs_window != nil);
7747     }
7748   else
7749     {
7750 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7751       res = (([[self window] styleMask] & NSWindowStyleMaskFullScreen) != 0);
7752 #else
7753       res = NO;
7754 #endif
7755     }
7757   NSTRACE ("[EmacsView isFullscreen] " NSTRACE_FMT_RETURN " %d",
7758            (int) res);
7760   return res;
7763 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7764 - (void)updateCollectionBehavior
7766   NSTRACE ("[EmacsView updateCollectionBehavior]");
7768   if (! [self isFullscreen])
7769     {
7770       NSWindow *win = [self window];
7771       NSWindowCollectionBehavior b = [win collectionBehavior];
7772       if (ns_use_native_fullscreen)
7773         b |= NSWindowCollectionBehaviorFullScreenPrimary;
7774       else
7775         b &= ~NSWindowCollectionBehaviorFullScreenPrimary;
7777       [win setCollectionBehavior: b];
7778 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7779       if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7780 #endif
7781         fs_is_native = ns_use_native_fullscreen;
7782     }
7784 #endif
7786 - (void)toggleFullScreen: (id)sender
7788   NSWindow *w, *fw;
7789   BOOL onFirstScreen;
7790   struct frame *f;
7791   NSRect r, wr;
7792   NSColor *col;
7794   NSTRACE ("[EmacsView toggleFullScreen:]");
7796   if (fs_is_native)
7797     {
7798 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7799 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7800       if ([[self window] respondsToSelector: @selector(toggleFullScreen:)])
7801 #endif
7802         [[self window] toggleFullScreen:sender];
7803 #endif
7804       return;
7805     }
7807   w = [self window];
7808   onFirstScreen = [[w screen] isEqual:[[NSScreen screens] objectAtIndex:0]];
7809   f = emacsframe;
7810   wr = [w frame];
7811   col = ns_lookup_indexed_color (NS_FACE_BACKGROUND
7812                                  (FACE_FROM_ID (f, DEFAULT_FACE_ID)),
7813                                  f);
7815   if (fs_state != FULLSCREEN_BOTH)
7816     {
7817       NSScreen *screen = [w screen];
7819 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
7820       /* Hide ghost menu bar on secondary monitor? */
7821       if (! onFirstScreen
7822 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
7823           && [NSScreen respondsToSelector: @selector(screensHaveSeparateSpaces)]
7824 #endif
7825           )
7826         onFirstScreen = [NSScreen screensHaveSeparateSpaces];
7827 #endif
7828       /* Hide dock and menubar if we are on the primary screen.  */
7829       if (onFirstScreen)
7830         {
7831 #ifdef NS_IMPL_COCOA
7832           NSApplicationPresentationOptions options
7833             = NSApplicationPresentationAutoHideDock
7834             | NSApplicationPresentationAutoHideMenuBar;
7836           [NSApp setPresentationOptions: options];
7837 #else
7838           [NSMenu setMenuBarVisible:NO];
7839 #endif
7840         }
7842       fw = [[EmacsFSWindow alloc]
7843                        initWithContentRect:[w contentRectForFrameRect:wr]
7844                                  styleMask:NSWindowStyleMaskBorderless
7845                                    backing:NSBackingStoreBuffered
7846                                      defer:YES
7847                                     screen:screen];
7849       [fw setContentView:[w contentView]];
7850       [fw setTitle:[w title]];
7851       [fw setDelegate:self];
7852       [fw setAcceptsMouseMovedEvents: YES];
7853 #if !defined (NS_IMPL_COCOA) \
7854   || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7855 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7856       if ([fw respondsToSelector: @selector(useOptimizedDrawing:)])
7857 #endif
7858         [fw useOptimizedDrawing: YES];
7859 #endif
7860       [fw setBackgroundColor: col];
7861       if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7862         [fw setOpaque: NO];
7864       f->border_width = 0;
7866       nonfs_window = w;
7868       [self windowWillEnterFullScreen];
7869       [fw makeKeyAndOrderFront:NSApp];
7870       [fw makeFirstResponder:self];
7871       [w orderOut:self];
7872       r = [fw frameRectForContentRect:[screen frame]];
7873       [fw setFrame: r display:YES animate:ns_use_fullscreen_animation];
7874       [self windowDidEnterFullScreen];
7875       [fw display];
7876     }
7877   else
7878     {
7879       fw = w;
7880       w = nonfs_window;
7881       nonfs_window = nil;
7883       if (onFirstScreen)
7884         {
7885 #ifdef NS_IMPL_COCOA
7886           [NSApp setPresentationOptions: NSApplicationPresentationDefault];
7887 #else
7888           [NSMenu setMenuBarVisible:YES];
7889 #endif
7890         }
7892       [w setContentView:[fw contentView]];
7893       [w setBackgroundColor: col];
7894       if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7895         [w setOpaque: NO];
7897       f->border_width = bwidth;
7899       // to do: consider using [NSNotificationCenter postNotificationName:] to send notifications.
7901       [self windowWillExitFullScreen];
7902       [fw setFrame: [w frame] display:YES animate:ns_use_fullscreen_animation];
7903       [fw close];
7904       [w makeKeyAndOrderFront:NSApp];
7905       [self windowDidExitFullScreen];
7906       [self updateFrameSize:YES];
7907     }
7910 - (void)handleFS
7912   NSTRACE ("[EmacsView handleFS]");
7914   if (fs_state != emacsframe->want_fullscreen)
7915     {
7916       if (fs_state == FULLSCREEN_BOTH)
7917         {
7918           NSTRACE_MSG ("fs_state == FULLSCREEN_BOTH");
7919           [self toggleFullScreen:self];
7920         }
7922       switch (emacsframe->want_fullscreen)
7923         {
7924         case FULLSCREEN_BOTH:
7925           NSTRACE_MSG ("FULLSCREEN_BOTH");
7926           [self toggleFullScreen:self];
7927           break;
7928         case FULLSCREEN_WIDTH:
7929           NSTRACE_MSG ("FULLSCREEN_WIDTH");
7930           next_maximized = FULLSCREEN_WIDTH;
7931           if (fs_state != FULLSCREEN_BOTH)
7932             [[self window] performZoom:self];
7933           break;
7934         case FULLSCREEN_HEIGHT:
7935           NSTRACE_MSG ("FULLSCREEN_HEIGHT");
7936           next_maximized = FULLSCREEN_HEIGHT;
7937           if (fs_state != FULLSCREEN_BOTH)
7938             [[self window] performZoom:self];
7939           break;
7940         case FULLSCREEN_MAXIMIZED:
7941           NSTRACE_MSG ("FULLSCREEN_MAXIMIZED");
7942           next_maximized = FULLSCREEN_MAXIMIZED;
7943           if (fs_state != FULLSCREEN_BOTH)
7944             [[self window] performZoom:self];
7945           break;
7946         case FULLSCREEN_NONE:
7947           NSTRACE_MSG ("FULLSCREEN_NONE");
7948           if (fs_state != FULLSCREEN_BOTH)
7949             {
7950               next_maximized = FULLSCREEN_NONE;
7951               [[self window] performZoom:self];
7952             }
7953           break;
7954         }
7956       emacsframe->want_fullscreen = FULLSCREEN_NONE;
7957     }
7961 - (void) setFSValue: (int)value
7963   NSTRACE ("[EmacsView setFSValue:" NSTRACE_FMT_FSTYPE "]",
7964            NSTRACE_ARG_FSTYPE(value));
7966   Lisp_Object lval = Qnil;
7967   switch (value)
7968     {
7969     case FULLSCREEN_BOTH:
7970       lval = Qfullboth;
7971       break;
7972     case FULLSCREEN_WIDTH:
7973       lval = Qfullwidth;
7974       break;
7975     case FULLSCREEN_HEIGHT:
7976       lval = Qfullheight;
7977       break;
7978     case FULLSCREEN_MAXIMIZED:
7979       lval = Qmaximized;
7980       break;
7981     }
7982   store_frame_param (emacsframe, Qfullscreen, lval);
7983   fs_state = value;
7986 - (void)mouseEntered: (NSEvent *)theEvent
7988   NSTRACE ("[EmacsView mouseEntered:]");
7989   if (emacsframe)
7990     FRAME_DISPLAY_INFO (emacsframe)->last_mouse_movement_time
7991       = EV_TIMESTAMP (theEvent);
7995 - (void)mouseExited: (NSEvent *)theEvent
7997   Mouse_HLInfo *hlinfo = emacsframe ? MOUSE_HL_INFO (emacsframe) : NULL;
7999   NSTRACE ("[EmacsView mouseExited:]");
8001   if (!hlinfo)
8002     return;
8004   FRAME_DISPLAY_INFO (emacsframe)->last_mouse_movement_time
8005     = EV_TIMESTAMP (theEvent);
8007   if (emacsframe == hlinfo->mouse_face_mouse_frame)
8008     {
8009       clear_mouse_face (hlinfo);
8010       hlinfo->mouse_face_mouse_frame = 0;
8011     }
8015 - (instancetype)menuDown: sender
8017   NSTRACE ("[EmacsView menuDown:]");
8018   if (context_menu_value == -1)
8019     context_menu_value = [sender tag];
8020   else
8021     {
8022       NSInteger tag = [sender tag];
8023       find_and_call_menu_selection (emacsframe, emacsframe->menu_bar_items_used,
8024                                     emacsframe->menu_bar_vector,
8025                                     (void *)tag);
8026     }
8028   ns_send_appdefined (-1);
8029   return self;
8033 - (EmacsToolbar *)toolbar
8035   return toolbar;
8039 /* this gets called on toolbar button click */
8040 - (instancetype)toolbarClicked: (id)item
8042   NSEvent *theEvent;
8043   int idx = [item tag] * TOOL_BAR_ITEM_NSLOTS;
8045   NSTRACE ("[EmacsView toolbarClicked:]");
8047   if (!emacs_event)
8048     return self;
8050   /* send first event (for some reason two needed) */
8051   theEvent = [[self window] currentEvent];
8052   emacs_event->kind = TOOL_BAR_EVENT;
8053   XSETFRAME (emacs_event->arg, emacsframe);
8054   EV_TRAILER (theEvent);
8056   emacs_event->kind = TOOL_BAR_EVENT;
8057 /*   XSETINT (emacs_event->code, 0); */
8058   emacs_event->arg = AREF (emacsframe->tool_bar_items,
8059                            idx + TOOL_BAR_ITEM_KEY);
8060   emacs_event->modifiers = EV_MODIFIERS (theEvent);
8061   EV_TRAILER (theEvent);
8062   return self;
8066 - (instancetype)toggleToolbar: (id)sender
8068   NSTRACE ("[EmacsView toggleToolbar:]");
8070   if (!emacs_event)
8071     return self;
8073   emacs_event->kind = NS_NONKEY_EVENT;
8074   emacs_event->code = KEY_NS_TOGGLE_TOOLBAR;
8075   EV_TRAILER ((id)nil);
8076   return self;
8080 - (void)viewWillDraw
8082   /* If the frame has been garbaged there's no point in redrawing
8083      anything.  */
8084   if (FRAME_GARBAGED_P (emacsframe))
8085     [self setNeedsDisplay:NO];
8088 - (void)drawRect: (NSRect)rect
8090   const NSRect *rectList;
8091   NSInteger numRects;
8093   NSTRACE ("[EmacsView drawRect:" NSTRACE_FMT_RECT "]",
8094            NSTRACE_ARG_RECT(rect));
8096   if (!emacsframe || !emacsframe->output_data.ns)
8097     return;
8099   block_input ();
8101   /* Get only the precise dirty rectangles to avoid redrawing
8102      potentially large areas of the frame that haven't changed.
8104      I'm not sure this actually provides much of a performance benefit
8105      as it's hard to benchmark, but it certainly doesn't seem to
8106      hurt.  */
8107   [self getRectsBeingDrawn:&rectList count:&numRects];
8108   for (int i = 0 ; i < numRects ; i++)
8109     {
8110       NSRect r = rectList[i];
8112       NSTRACE_RECT ("r", r);
8114       expose_frame (emacsframe,
8115                     NSMinX (r), NSMinY (r),
8116                     NSWidth (r), NSHeight (r));
8117     }
8119   unblock_input ();
8121   /*
8122     drawRect: may be called (at least in Mac OS X 10.5) for invisible
8123     views as well for some reason.  Thus, do not infer visibility
8124     here.
8126     emacsframe->async_visible = 1;
8127     emacsframe->async_iconified = 0;
8128   */
8132 /* NSDraggingDestination protocol methods.  Actually this is not really a
8133    protocol, but a category of Object.  O well...  */
8135 -(NSDragOperation) draggingEntered: (id <NSDraggingInfo>) sender
8137   NSTRACE ("[EmacsView draggingEntered:]");
8138   return NSDragOperationGeneric;
8142 -(BOOL)prepareForDragOperation: (id <NSDraggingInfo>) sender
8144   return YES;
8148 -(BOOL)performDragOperation: (id <NSDraggingInfo>) sender
8150   id pb;
8151   int x, y;
8152   NSString *type;
8153   NSEvent *theEvent = [[self window] currentEvent];
8154   NSPoint position;
8155   NSDragOperation op = [sender draggingSourceOperationMask];
8156   int modifiers = 0;
8158   NSTRACE ("[EmacsView performDragOperation:]");
8160   if (!emacs_event)
8161     return NO;
8163   position = [self convertPoint: [sender draggingLocation] fromView: nil];
8164   x = lrint (position.x);  y = lrint (position.y);
8166   pb = [sender draggingPasteboard];
8167   type = [pb availableTypeFromArray: ns_drag_types];
8169   if (! (op & (NSDragOperationMove|NSDragOperationDelete)) &&
8170       // URL drags contain all operations (0xf), don't allow all to be set.
8171       (op & 0xf) != 0xf)
8172     {
8173       if (op & NSDragOperationLink)
8174         modifiers |= NSEventModifierFlagControl;
8175       if (op & NSDragOperationCopy)
8176         modifiers |= NSEventModifierFlagOption;
8177       if (op & NSDragOperationGeneric)
8178         modifiers |= NSEventModifierFlagCommand;
8179     }
8181   modifiers = EV_MODIFIERS2 (modifiers);
8182   if (type == 0)
8183     {
8184       return NO;
8185     }
8186   else if ([type isEqualToString: NSFilenamesPboardType])
8187     {
8188       NSArray *files;
8189       NSEnumerator *fenum;
8190       NSString *file;
8192       if (!(files = [pb propertyListForType: type]))
8193         return NO;
8195       fenum = [files objectEnumerator];
8196       while ( (file = [fenum nextObject]) )
8197         {
8198           emacs_event->kind = DRAG_N_DROP_EVENT;
8199           XSETINT (emacs_event->x, x);
8200           XSETINT (emacs_event->y, y);
8201           emacs_event->modifiers = modifiers;
8202           emacs_event->arg =  list2 (Qfile, build_string ([file UTF8String]));
8203           EV_TRAILER (theEvent);
8204         }
8205       return YES;
8206     }
8207   else if ([type isEqualToString: NSURLPboardType])
8208     {
8209       NSURL *url = [NSURL URLFromPasteboard: pb];
8210       if (url == nil) return NO;
8212       emacs_event->kind = DRAG_N_DROP_EVENT;
8213       XSETINT (emacs_event->x, x);
8214       XSETINT (emacs_event->y, y);
8215       emacs_event->modifiers = modifiers;
8216       emacs_event->arg =  list2 (Qurl,
8217                                  build_string ([[url absoluteString]
8218                                                  UTF8String]));
8219       EV_TRAILER (theEvent);
8221       if ([url isFileURL] != NO)
8222         {
8223           NSString *file = [url path];
8224           ns_input_file = append2 (ns_input_file,
8225                                    build_string ([file UTF8String]));
8226         }
8227       return YES;
8228     }
8229   else if ([type isEqualToString: NSStringPboardType]
8230            || [type isEqualToString: NSTabularTextPboardType])
8231     {
8232       NSString *data;
8234       if (! (data = [pb stringForType: type]))
8235         return NO;
8237       emacs_event->kind = DRAG_N_DROP_EVENT;
8238       XSETINT (emacs_event->x, x);
8239       XSETINT (emacs_event->y, y);
8240       emacs_event->modifiers = modifiers;
8241       emacs_event->arg =  list2 (Qnil, build_string ([data UTF8String]));
8242       EV_TRAILER (theEvent);
8243       return YES;
8244     }
8245   else
8246     {
8247       fprintf (stderr, "Invalid data type in dragging pasteboard");
8248       return NO;
8249     }
8253 - (id) validRequestorForSendType: (NSString *)typeSent
8254                       returnType: (NSString *)typeReturned
8256   NSTRACE ("[EmacsView validRequestorForSendType:returnType:]");
8257   if (typeSent != nil && [ns_send_types indexOfObject: typeSent] != NSNotFound
8258       && typeReturned == nil)
8259     {
8260       if (! NILP (ns_get_local_selection (QPRIMARY, QUTF8_STRING)))
8261         return self;
8262     }
8264   return [super validRequestorForSendType: typeSent
8265                                returnType: typeReturned];
8269 /* The next two methods are part of NSServicesRequests informal protocol,
8270    supposedly called when a services menu item is chosen from this app.
8271    But this should not happen because we override the services menu with our
8272    own entries which call ns-perform-service.
8273    Nonetheless, it appeared to happen (under strange circumstances): bug#1435.
8274    So let's at least stub them out until further investigation can be done. */
8276 - (BOOL) readSelectionFromPasteboard: (NSPasteboard *)pb
8278   /* we could call ns_string_from_pasteboard(pboard) here but then it should
8279      be written into the buffer in place of the existing selection..
8280      ordinary service calls go through functions defined in ns-win.el */
8281   return NO;
8284 - (BOOL) writeSelectionToPasteboard: (NSPasteboard *)pb types: (NSArray *)types
8286   NSArray *typesDeclared;
8287   Lisp_Object val;
8289   NSTRACE ("[EmacsView writeSelectionToPasteboard:types:]");
8291   /* We only support NSStringPboardType */
8292   if ([types containsObject:NSStringPboardType] == NO) {
8293     return NO;
8294   }
8296   val = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8297   if (CONSP (val) && SYMBOLP (XCAR (val)))
8298     {
8299       val = XCDR (val);
8300       if (CONSP (val) && NILP (XCDR (val)))
8301         val = XCAR (val);
8302     }
8303   if (! STRINGP (val))
8304     return NO;
8306   typesDeclared = [NSArray arrayWithObject:NSStringPboardType];
8307   [pb declareTypes:typesDeclared owner:nil];
8308   ns_string_to_pasteboard (pb, val);
8309   return YES;
8313 /* setMini =YES means set from internal (gives a finder icon), NO means set nil
8314    (gives a miniaturized version of the window); currently we use the latter for
8315    frames whose active buffer doesn't correspond to any file
8316    (e.g., '*scratch*') */
8317 - (instancetype)setMiniwindowImage: (BOOL) setMini
8319   id image = [[self window] miniwindowImage];
8320   NSTRACE ("[EmacsView setMiniwindowImage:%d]", setMini);
8322   /* NOTE: under Cocoa miniwindowImage always returns nil, documentation
8323      about "AppleDockIconEnabled" notwithstanding, however the set message
8324      below has its effect nonetheless. */
8325   if (image != emacsframe->output_data.ns->miniimage)
8326     {
8327       if (image && [image isKindOfClass: [EmacsImage class]])
8328         [image release];
8329       [[self window] setMiniwindowImage:
8330                        setMini ? emacsframe->output_data.ns->miniimage : nil];
8331     }
8333   return self;
8337 - (void) setRows: (int) r andColumns: (int) c
8339   NSTRACE ("[EmacsView setRows:%d andColumns:%d]", r, c);
8340   rows = r;
8341   cols = c;
8344 - (int) fullscreenState
8346   return fs_state;
8349 @end  /* EmacsView */
8353 /* ==========================================================================
8355     EmacsWindow implementation
8357    ========================================================================== */
8359 @implementation EmacsWindow
8361 #ifdef NS_IMPL_COCOA
8362 - (id)accessibilityAttributeValue:(NSString *)attribute
8364   Lisp_Object str = Qnil;
8365   struct frame *f = SELECTED_FRAME ();
8366   struct buffer *curbuf = XBUFFER (XWINDOW (f->selected_window)->contents);
8368   NSTRACE ("[EmacsWindow accessibilityAttributeValue:]");
8370   if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
8371     return NSAccessibilityTextFieldRole;
8373   if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]
8374       && curbuf && ! NILP (BVAR (curbuf, mark_active)))
8375     {
8376       str = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8377     }
8378   else if (curbuf && [attribute isEqualToString:NSAccessibilityValueAttribute])
8379     {
8380       if (! NILP (BVAR (curbuf, mark_active)))
8381           str = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8383       if (NILP (str))
8384         {
8385           ptrdiff_t start_byte = BUF_BEGV_BYTE (curbuf);
8386           ptrdiff_t byte_range = BUF_ZV_BYTE (curbuf) - start_byte;
8387           ptrdiff_t range = BUF_ZV (curbuf) - BUF_BEGV (curbuf);
8389           if (! NILP (BVAR (curbuf, enable_multibyte_characters)))
8390             str = make_uninit_multibyte_string (range, byte_range);
8391           else
8392             str = make_uninit_string (range);
8393           /* To check: This returns emacs-utf-8, which is a superset of utf-8.
8394              Is this a problem?  */
8395           memcpy (SDATA (str), BYTE_POS_ADDR (start_byte), byte_range);
8396         }
8397     }
8400   if (! NILP (str))
8401     {
8402       if (CONSP (str) && SYMBOLP (XCAR (str)))
8403         {
8404           str = XCDR (str);
8405           if (CONSP (str) && NILP (XCDR (str)))
8406             str = XCAR (str);
8407         }
8408       if (STRINGP (str))
8409         {
8410           const char *utfStr = SSDATA (str);
8411           NSString *nsStr = [NSString stringWithUTF8String: utfStr];
8412           return nsStr;
8413         }
8414     }
8416   return [super accessibilityAttributeValue:attribute];
8418 #endif /* NS_IMPL_COCOA */
8420 /* Constrain size and placement of a frame.
8422    By returning the original "frameRect", the frame is not
8423    constrained. This can lead to unwanted situations where, for
8424    example, the menu bar covers the frame.
8426    The default implementation (accessed using "super") constrains the
8427    frame to the visible area of SCREEN, minus the menu bar (if
8428    present) and the Dock.  Note that default implementation also calls
8429    windowWillResize, with the frame it thinks should have.  (This can
8430    make the frame exit maximized mode.)
8432    Note that this should work in situations where multiple monitors
8433    are present.  Common configurations are side-by-side monitors and a
8434    monitor on top of another (e.g. when a laptop is placed under a
8435    large screen). */
8436 - (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
8438   NSTRACE ("[EmacsWindow constrainFrameRect:" NSTRACE_FMT_RECT " toScreen:]",
8439              NSTRACE_ARG_RECT (frameRect));
8441 #ifdef NS_IMPL_COCOA
8442 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
8443   // If separate spaces is on, it is like each screen is independent.  There is
8444   // no spanning of frames across screens.
8445   if (
8446 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
8447       [NSScreen respondsToSelector: @selector(screensHaveSeparateSpaces)] &&
8448 #endif
8449       [NSScreen screensHaveSeparateSpaces])
8450     {
8451       NSTRACE_MSG ("Screens have separate spaces");
8452       frameRect = [super constrainFrameRect:frameRect toScreen:screen];
8453       NSTRACE_RETURN_RECT (frameRect);
8454       return frameRect;
8455     }
8456   else
8457 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 */
8459     // Check that the proposed frameRect is visible in at least one
8460     // screen.  If it is not, ask the system to reposition it (only
8461     // for non-child windows).
8463     if (!FRAME_PARENT_FRAME (((EmacsView *)[self delegate])->emacsframe))
8464     {
8465       NSArray *screens = [NSScreen screens];
8466       NSUInteger nr_screens = [screens count];
8468       int i;
8469       BOOL frame_on_screen = NO;
8471       for (i = 0; i < nr_screens; ++i)
8472         {
8473           NSScreen *s = [screens objectAtIndex: i];
8474           NSRect scrRect = [s frame];
8476           if (NSIntersectsRect(frameRect, scrRect))
8477             {
8478               frame_on_screen = YES;
8479               break;
8480             }
8481         }
8483       if (!frame_on_screen)
8484         {
8485           NSTRACE_MSG ("Frame outside screens; constraining");
8486           frameRect = [super constrainFrameRect:frameRect toScreen:screen];
8487           NSTRACE_RETURN_RECT (frameRect);
8488           return frameRect;
8489         }
8490     }
8491 #endif
8493   return constrain_frame_rect(frameRect,
8494                               [(EmacsView *)[self delegate] isFullscreen]);
8498 - (void)performZoom:(id)sender
8500   NSTRACE ("[EmacsWindow performZoom:]");
8502   return [super performZoom:sender];
8505 - (void)zoom:(id)sender
8507   NSTRACE ("[EmacsWindow zoom:]");
8509   ns_update_auto_hide_menu_bar();
8511   // Below are three zoom implementations.  In the final commit, the
8512   // idea is that the last should be included.
8514 #if 0
8515   // Native zoom done using the standard zoom animation.  Size of the
8516   // resulting frame reduced to accommodate the Dock and, if present,
8517   // the menu-bar.
8518   [super zoom:sender];
8520 #elif 0
8521   // Native zoom done using the standard zoom animation, plus an
8522   // explicit resize to cover the full screen, except the menu-bar and
8523   // dock, if present.
8524   [super zoom:sender];
8526   // After the native zoom, resize the resulting frame to fill the
8527   // entire screen, except the menu-bar.
8528   //
8529   // This works for all practical purposes.  (The only minor oddity is
8530   // when transiting from full-height frame to a maximized, the
8531   // animation reduces the height of the frame slightly (to the 4
8532   // pixels needed to accommodate the Doc) before it snaps back into
8533   // full height.  The user would need a very trained eye to spot
8534   // this.)
8535   NSScreen * screen = [self screen];
8536   if (screen != nil)
8537     {
8538       int fs_state = [(EmacsView *)[self delegate] fullscreenState];
8540       NSTRACE_FSTYPE ("fullscreenState", fs_state);
8542       NSRect sr = [screen frame];
8543       struct EmacsMargins margins
8544         = ns_screen_margins_ignoring_hidden_dock(screen);
8546       NSRect wr = [self frame];
8547       NSTRACE_RECT ("Rect after zoom", wr);
8549       NSRect newWr = wr;
8551       if (fs_state == FULLSCREEN_MAXIMIZED
8552           || fs_state == FULLSCREEN_HEIGHT)
8553         {
8554           newWr.origin.y = sr.origin.y + margins.bottom;
8555           newWr.size.height = sr.size.height - margins.top - margins.bottom;
8556         }
8558       if (fs_state == FULLSCREEN_MAXIMIZED
8559           || fs_state == FULLSCREEN_WIDTH)
8560         {
8561           newWr.origin.x = sr.origin.x + margins.left;
8562           newWr.size.width = sr.size.width - margins.right - margins.left;
8563         }
8565       if (newWr.size.width     != wr.size.width
8566           || newWr.size.height != wr.size.height
8567           || newWr.origin.x    != wr.origin.x
8568           || newWr.origin.y    != wr.origin.y)
8569         {
8570           NSTRACE_MSG ("New frame different");
8571           [self setFrame: newWr display: NO];
8572         }
8573     }
8574 #else
8575   // Non-native zoom which is done instantaneously.  The resulting
8576   // frame covers the entire screen, except the menu-bar and dock, if
8577   // present.
8578   NSScreen * screen = [self screen];
8579   if (screen != nil)
8580     {
8581       NSRect sr = [screen frame];
8582       struct EmacsMargins margins
8583         = ns_screen_margins_ignoring_hidden_dock(screen);
8585       sr.size.height -= (margins.top + margins.bottom);
8586       sr.size.width  -= (margins.left + margins.right);
8587       sr.origin.x += margins.left;
8588       sr.origin.y += margins.bottom;
8590       sr = [[self delegate] windowWillUseStandardFrame:self
8591                                           defaultFrame:sr];
8592       [self setFrame: sr display: NO];
8593     }
8594 #endif
8597 - (void)setFrame:(NSRect)windowFrame
8598          display:(BOOL)displayViews
8600   NSTRACE ("[EmacsWindow setFrame:" NSTRACE_FMT_RECT " display:%d]",
8601            NSTRACE_ARG_RECT (windowFrame), displayViews);
8603   [super setFrame:windowFrame display:displayViews];
8606 - (void)setFrame:(NSRect)windowFrame
8607          display:(BOOL)displayViews
8608          animate:(BOOL)performAnimation
8610   NSTRACE ("[EmacsWindow setFrame:" NSTRACE_FMT_RECT
8611            " display:%d performAnimation:%d]",
8612            NSTRACE_ARG_RECT (windowFrame), displayViews, performAnimation);
8614   [super setFrame:windowFrame display:displayViews animate:performAnimation];
8617 - (void)setFrameTopLeftPoint:(NSPoint)point
8619   NSTRACE ("[EmacsWindow setFrameTopLeftPoint:" NSTRACE_FMT_POINT "]",
8620            NSTRACE_ARG_POINT (point));
8622   [super setFrameTopLeftPoint:point];
8625 - (BOOL)canBecomeKeyWindow
8627   return !FRAME_NO_ACCEPT_FOCUS (((EmacsView *)[self delegate])->emacsframe);
8629 @end /* EmacsWindow */
8632 @implementation EmacsFSWindow
8634 - (BOOL)canBecomeKeyWindow
8636   return YES;
8639 - (BOOL)canBecomeMainWindow
8641   return YES;
8644 @end
8646 /* ==========================================================================
8648     EmacsScroller implementation
8650    ========================================================================== */
8653 @implementation EmacsScroller
8655 /* for repeat button push */
8656 #define SCROLL_BAR_FIRST_DELAY 0.5
8657 #define SCROLL_BAR_CONTINUOUS_DELAY (1.0 / 15)
8659 + (CGFloat) scrollerWidth
8661   /* TODO: if we want to allow variable widths, this is the place to do it,
8662            however neither GNUstep nor Cocoa support it very well */
8663   CGFloat r;
8664 #if defined (NS_IMPL_COCOA) \
8665   && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
8666 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
8667   if ([NSScroller respondsToSelector:
8668                     @selector(scrollerWidthForControlSize:scrollerStyle:)])
8669 #endif
8670     r = [NSScroller scrollerWidthForControlSize: NSControlSizeRegular
8671                                   scrollerStyle: NSScrollerStyleLegacy];
8672 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
8673   else
8674 #endif
8675 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
8676 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 \
8677   || defined (NS_IMPL_GNUSTEP)
8678     r = [NSScroller scrollerWidth];
8679 #endif
8680   return r;
8683 - (instancetype)initFrame: (NSRect )r window: (Lisp_Object)nwin
8685   NSTRACE ("[EmacsScroller initFrame: window:]");
8687   if (r.size.width > r.size.height)
8688       horizontal = YES;
8689   else
8690       horizontal = NO;
8692   [super initWithFrame: r/*NSMakeRect (0, 0, 0, 0)*/];
8693   [self setContinuous: YES];
8694   [self setEnabled: YES];
8696   /* Ensure auto resizing of scrollbars occurs within the emacs frame's view
8697      locked against the top and bottom edges, and right edge on macOS, where
8698      scrollers are on right. */
8699 #ifdef NS_IMPL_GNUSTEP
8700   [self setAutoresizingMask: NSViewMaxXMargin | NSViewHeightSizable];
8701 #else
8702   [self setAutoresizingMask: NSViewMinXMargin | NSViewHeightSizable];
8703 #endif
8705   window = XWINDOW (nwin);
8706   condemned = NO;
8707   if (horizontal)
8708     pixel_length = NSWidth (r);
8709   else
8710     pixel_length = NSHeight (r);
8711   if (pixel_length == 0) pixel_length = 1;
8712   min_portion = 20 / pixel_length;
8714   frame = XFRAME (window->frame);
8715   if (FRAME_LIVE_P (frame))
8716     {
8717       int i;
8718       EmacsView *view = FRAME_NS_VIEW (frame);
8719       NSView *sview = [[view window] contentView];
8720       NSArray *subs = [sview subviews];
8722       /* disable optimization stopping redraw of other scrollbars */
8723       view->scrollbarsNeedingUpdate = 0;
8724       for (i =[subs count]-1; i >= 0; i--)
8725         if ([[subs objectAtIndex: i] isKindOfClass: [EmacsScroller class]])
8726           view->scrollbarsNeedingUpdate++;
8727       [sview addSubview: self];
8728     }
8730 /*  [self setFrame: r]; */
8732   return self;
8736 - (void)setFrame: (NSRect)newRect
8738   NSTRACE ("[EmacsScroller setFrame:]");
8740 /*  block_input (); */
8741   if (horizontal)
8742     pixel_length = NSWidth (newRect);
8743   else
8744     pixel_length = NSHeight (newRect);
8745   if (pixel_length == 0) pixel_length = 1;
8746   min_portion = 20 / pixel_length;
8747   [super setFrame: newRect];
8748 /*  unblock_input (); */
8752 - (void)dealloc
8754   NSTRACE ("[EmacsScroller dealloc]");
8755   if (window)
8756     {
8757       if (horizontal)
8758         wset_horizontal_scroll_bar (window, Qnil);
8759       else
8760         wset_vertical_scroll_bar (window, Qnil);
8761     }
8762   window = 0;
8763   [super dealloc];
8767 - (instancetype)condemn
8769   NSTRACE ("[EmacsScroller condemn]");
8770   condemned =YES;
8771   return self;
8775 - (instancetype)reprieve
8777   NSTRACE ("[EmacsScroller reprieve]");
8778   condemned =NO;
8779   return self;
8783 -(bool)judge
8785   NSTRACE ("[EmacsScroller judge]");
8786   bool ret = condemned;
8787   if (condemned)
8788     {
8789       EmacsView *view;
8790       block_input ();
8791       /* ensure other scrollbar updates after deletion */
8792       view = (EmacsView *)FRAME_NS_VIEW (frame);
8793       if (view != nil)
8794         view->scrollbarsNeedingUpdate++;
8795       if (window)
8796         {
8797           if (horizontal)
8798             wset_horizontal_scroll_bar (window, Qnil);
8799           else
8800             wset_vertical_scroll_bar (window, Qnil);
8801         }
8802       window = 0;
8803       [self removeFromSuperview];
8804       [self release];
8805       unblock_input ();
8806     }
8807   return ret;
8811 - (void)resetCursorRects
8813   NSRect visible = [self visibleRect];
8814   NSTRACE ("[EmacsScroller resetCursorRects]");
8816   if (!NSIsEmptyRect (visible))
8817     [self addCursorRect: visible cursor: [NSCursor arrowCursor]];
8818   [[NSCursor arrowCursor] setOnMouseEntered: YES];
8822 - (int) checkSamePosition: (int) position portion: (int) portion
8823                     whole: (int) whole
8825   return em_position ==position && em_portion ==portion && em_whole ==whole
8826     && portion != whole; /* needed for resize empty buf */
8830 - (instancetype)setPosition: (int)position portion: (int)portion whole: (int)whole
8832   NSTRACE ("[EmacsScroller setPosition:portion:whole:]");
8834   em_position = position;
8835   em_portion = portion;
8836   em_whole = whole;
8838   if (portion >= whole)
8839     {
8840 #ifdef NS_IMPL_COCOA
8841       [self setKnobProportion: 1.0];
8842       [self setDoubleValue: 1.0];
8843 #else
8844       [self setFloatValue: 0.0 knobProportion: 1.0];
8845 #endif
8846     }
8847   else
8848     {
8849       float pos;
8850       CGFloat por;
8851       portion = max ((float)whole*min_portion/pixel_length, portion);
8852       pos = (float)position / (whole - portion);
8853       por = (CGFloat)portion/whole;
8854 #ifdef NS_IMPL_COCOA
8855       [self setKnobProportion: por];
8856       [self setDoubleValue: pos];
8857 #else
8858       [self setFloatValue: pos knobProportion: por];
8859 #endif
8860     }
8862   return self;
8865 /* set up emacs_event */
8866 - (void) sendScrollEventAtLoc: (float)loc fromEvent: (NSEvent *)e
8868   Lisp_Object win;
8870   NSTRACE ("[EmacsScroller sendScrollEventAtLoc:fromEvent:]");
8872   if (!emacs_event)
8873     return;
8875   emacs_event->part = last_hit_part;
8876   emacs_event->code = 0;
8877   emacs_event->modifiers = EV_MODIFIERS (e) | down_modifier;
8878   XSETWINDOW (win, window);
8879   emacs_event->frame_or_window = win;
8880   emacs_event->timestamp = EV_TIMESTAMP (e);
8881   emacs_event->arg = Qnil;
8883   if (horizontal)
8884     {
8885       emacs_event->kind = HORIZONTAL_SCROLL_BAR_CLICK_EVENT;
8886       XSETINT (emacs_event->x, em_whole * loc / pixel_length);
8887       XSETINT (emacs_event->y, em_whole);
8888     }
8889   else
8890     {
8891       emacs_event->kind = SCROLL_BAR_CLICK_EVENT;
8892       XSETINT (emacs_event->x, loc);
8893       XSETINT (emacs_event->y, pixel_length-20);
8894     }
8896   if (q_event_ptr)
8897     {
8898       n_emacs_events_pending++;
8899       kbd_buffer_store_event_hold (emacs_event, q_event_ptr);
8900     }
8901   else
8902     hold_event (emacs_event);
8903   EVENT_INIT (*emacs_event);
8904   ns_send_appdefined (-1);
8908 /* called manually thru timer to implement repeated button action w/hold-down */
8909 - (instancetype)repeatScroll: (NSTimer *)scrollEntry
8911   NSEvent *e = [[self window] currentEvent];
8912   NSPoint p =  [[self window] mouseLocationOutsideOfEventStream];
8913   BOOL inKnob = [self testPart: p] == NSScrollerKnob;
8915   NSTRACE ("[EmacsScroller repeatScroll:]");
8917   /* clear timer if need be */
8918   if (inKnob || [scroll_repeat_entry timeInterval] == SCROLL_BAR_FIRST_DELAY)
8919     {
8920         [scroll_repeat_entry invalidate];
8921         [scroll_repeat_entry release];
8922         scroll_repeat_entry = nil;
8924         if (inKnob)
8925           return self;
8927         scroll_repeat_entry
8928           = [[NSTimer scheduledTimerWithTimeInterval:
8929                         SCROLL_BAR_CONTINUOUS_DELAY
8930                                             target: self
8931                                           selector: @selector (repeatScroll:)
8932                                           userInfo: 0
8933                                            repeats: YES]
8934               retain];
8935     }
8937   [self sendScrollEventAtLoc: 0 fromEvent: e];
8938   return self;
8942 /* Asynchronous mouse tracking for scroller.  This allows us to dispatch
8943    mouseDragged events without going into a modal loop. */
8944 - (void)mouseDown: (NSEvent *)e
8946   NSRect sr, kr;
8947   /* hitPart is only updated AFTER event is passed on */
8948   NSScrollerPart part = [self testPart: [e locationInWindow]];
8949   CGFloat loc, kloc, pos UNINIT;
8950   int edge = 0;
8952   NSTRACE ("[EmacsScroller mouseDown:]");
8954   switch (part)
8955     {
8956     case NSScrollerDecrementPage:
8957       last_hit_part = horizontal ? scroll_bar_before_handle : scroll_bar_above_handle; break;
8958     case NSScrollerIncrementPage:
8959       last_hit_part = horizontal ? scroll_bar_after_handle : scroll_bar_below_handle; break;
8960     case NSScrollerDecrementLine:
8961       last_hit_part = horizontal ? scroll_bar_left_arrow : scroll_bar_up_arrow; break;
8962     case NSScrollerIncrementLine:
8963       last_hit_part = horizontal ? scroll_bar_right_arrow : scroll_bar_down_arrow; break;
8964     case NSScrollerKnob:
8965       last_hit_part = horizontal ? scroll_bar_horizontal_handle : scroll_bar_handle; break;
8966     case NSScrollerKnobSlot:  /* GNUstep-only */
8967       last_hit_part = scroll_bar_move_ratio; break;
8968     default:  /* NSScrollerNoPart? */
8969       fprintf (stderr, "EmacsScroller-mouseDown: unexpected part %ld\n",
8970                (long) part);
8971       return;
8972     }
8974   if (part == NSScrollerKnob || part == NSScrollerKnobSlot)
8975     {
8976       /* handle, or on GNUstep possibly slot */
8977       NSEvent *fake_event;
8978       int length;
8980       /* compute float loc in slot and mouse offset on knob */
8981       sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot]
8982                       toView: nil];
8983       if (horizontal)
8984         {
8985           length = NSWidth (sr);
8986           loc = ([e locationInWindow].x - NSMinX (sr));
8987         }
8988       else
8989         {
8990           length = NSHeight (sr);
8991           loc = length - ([e locationInWindow].y - NSMinY (sr));
8992         }
8994       if (loc <= 0.0)
8995         {
8996           loc = 0.0;
8997           edge = -1;
8998         }
8999       else if (loc >= length)
9000         {
9001           loc = length;
9002           edge = 1;
9003         }
9005       if (edge)
9006         kloc = 0.5 * edge;
9007       else
9008         {
9009           kr = [self convertRect: [self rectForPart: NSScrollerKnob]
9010                           toView: nil];
9011           if (horizontal)
9012             kloc = ([e locationInWindow].x - NSMinX (kr));
9013           else
9014             kloc = NSHeight (kr) - ([e locationInWindow].y - NSMinY (kr));
9015         }
9016       last_mouse_offset = kloc;
9018       /* if knob, tell emacs a location offset by knob pos
9019          (to indicate top of handle) */
9020       if (part == NSScrollerKnob)
9021         pos = (loc - last_mouse_offset);
9022       else
9023         /* else this is a slot click on GNUstep: go straight there */
9024         pos = loc;
9026       /* If there are buttons in the scroller area, we need to
9027          recalculate pos as emacs expects the scroller slot to take up
9028          the entire available length.  */
9029       if (length != pixel_length)
9030         pos = pos * pixel_length / length;
9032       /* send a fake mouse-up to super to preempt modal -trackKnob: mode */
9033       fake_event = [NSEvent mouseEventWithType: NSEventTypeLeftMouseUp
9034                                       location: [e locationInWindow]
9035                                  modifierFlags: [e modifierFlags]
9036                                      timestamp: [e timestamp]
9037                                   windowNumber: [e windowNumber]
9038                                        context: nil
9039                                    eventNumber: [e eventNumber]
9040                                     clickCount: [e clickCount]
9041                                       pressure: [e pressure]];
9042       [super mouseUp: fake_event];
9043     }
9044   else
9045     {
9046       pos = 0;      /* ignored */
9048       /* set a timer to repeat, as we can't let superclass do this modally */
9049       scroll_repeat_entry
9050         = [[NSTimer scheduledTimerWithTimeInterval: SCROLL_BAR_FIRST_DELAY
9051                                             target: self
9052                                           selector: @selector (repeatScroll:)
9053                                           userInfo: 0
9054                                            repeats: YES]
9055             retain];
9056     }
9058   if (part != NSScrollerKnob)
9059     [self sendScrollEventAtLoc: pos fromEvent: e];
9063 /* Called as we manually track scroller drags, rather than superclass. */
9064 - (void)mouseDragged: (NSEvent *)e
9066     NSRect sr;
9067     double loc, pos;
9068     int length;
9070     NSTRACE ("[EmacsScroller mouseDragged:]");
9072       sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot]
9073                       toView: nil];
9075       if (horizontal)
9076         {
9077           length = NSWidth (sr);
9078           loc = ([e locationInWindow].x - NSMinX (sr));
9079         }
9080       else
9081         {
9082           length = NSHeight (sr);
9083           loc = length - ([e locationInWindow].y - NSMinY (sr));
9084         }
9086       if (loc <= 0.0)
9087         {
9088           loc = 0.0;
9089         }
9090       else if (loc >= length + last_mouse_offset)
9091         {
9092           loc = length + last_mouse_offset;
9093         }
9095       pos = (loc - last_mouse_offset);
9097       /* If there are buttons in the scroller area, we need to
9098          recalculate pos as emacs expects the scroller slot to take up
9099          the entire available length.  */
9100       if (length != pixel_length)
9101         pos = pos * pixel_length / length;
9103       [self sendScrollEventAtLoc: pos fromEvent: e];
9107 - (void)mouseUp: (NSEvent *)e
9109   NSTRACE ("[EmacsScroller mouseUp:]");
9111   if (scroll_repeat_entry)
9112     {
9113       [scroll_repeat_entry invalidate];
9114       [scroll_repeat_entry release];
9115       scroll_repeat_entry = nil;
9116     }
9117   last_hit_part = scroll_bar_above_handle;
9121 /* treat scrollwheel events in the bar as though they were in the main window */
9122 - (void) scrollWheel: (NSEvent *)theEvent
9124   NSTRACE ("[EmacsScroller scrollWheel:]");
9126   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (frame);
9127   [view mouseDown: theEvent];
9130 @end  /* EmacsScroller */
9133 #ifdef NS_IMPL_GNUSTEP
9134 /* Dummy class to get rid of startup warnings.  */
9135 @implementation EmacsDocument
9137 @end
9138 #endif
9141 /* ==========================================================================
9143    Font-related functions; these used to be in nsfaces.m
9145    ========================================================================== */
9148 Lisp_Object
9149 x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
9151   struct font *font = XFONT_OBJECT (font_object);
9152   EmacsView *view = FRAME_NS_VIEW (f);
9153   int font_ascent, font_descent;
9155   if (fontset < 0)
9156     fontset = fontset_from_font (font_object);
9157   FRAME_FONTSET (f) = fontset;
9159   if (FRAME_FONT (f) == font)
9160     /* This font is already set in frame F.  There's nothing more to
9161        do.  */
9162     return font_object;
9164   FRAME_FONT (f) = font;
9166   FRAME_BASELINE_OFFSET (f) = font->baseline_offset;
9167   FRAME_COLUMN_WIDTH (f) = font->average_width;
9168   get_font_ascent_descent (font, &font_ascent, &font_descent);
9169   FRAME_LINE_HEIGHT (f) = font_ascent + font_descent;
9171   /* Compute the scroll bar width in character columns.  */
9172   if (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) > 0)
9173     {
9174       int wid = FRAME_COLUMN_WIDTH (f);
9175       FRAME_CONFIG_SCROLL_BAR_COLS (f)
9176         = (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) + wid - 1) / wid;
9177     }
9178   else
9179     {
9180       int wid = FRAME_COLUMN_WIDTH (f);
9181       FRAME_CONFIG_SCROLL_BAR_COLS (f) = (14 + wid - 1) / wid;
9182     }
9184   /* Compute the scroll bar height in character lines.  */
9185   if (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) > 0)
9186     {
9187       int height = FRAME_LINE_HEIGHT (f);
9188       FRAME_CONFIG_SCROLL_BAR_LINES (f)
9189         = (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) + height - 1) / height;
9190     }
9191   else
9192     {
9193       int height = FRAME_LINE_HEIGHT (f);
9194       FRAME_CONFIG_SCROLL_BAR_LINES (f) = (14 + height - 1) / height;
9195     }
9197   /* Now make the frame display the given font.  */
9198   if (FRAME_NS_WINDOW (f) != 0 && ! [view isFullscreen])
9199     adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
9200                        FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3,
9201                        false, Qfont);
9203   return font_object;
9207 /* XLFD: -foundry-family-weight-slant-swidth-adstyle-pxlsz-ptSz-resx-resy-spc-avgWidth-rgstry-encoding */
9208 /* Note: ns_font_to_xlfd and ns_fontname_to_xlfd no longer needed, removed
9209          in 1.43. */
9211 const char *
9212 ns_xlfd_to_fontname (const char *xlfd)
9213 /* --------------------------------------------------------------------------
9214     Convert an X font name (XLFD) to an NS font name.
9215     Only family is used.
9216     The string returned is temporarily allocated.
9217    -------------------------------------------------------------------------- */
9219   char *name = xmalloc (180);
9220   int i, len;
9221   const char *ret;
9223   if (!strncmp (xlfd, "--", 2))
9224     sscanf (xlfd, "--%*[^-]-%179[^-]-", name);
9225   else
9226     sscanf (xlfd, "-%*[^-]-%179[^-]-", name);
9228   /* stopgap for malformed XLFD input */
9229   if (strlen (name) == 0)
9230     strcpy (name, "Monaco");
9232   /* undo hack in ns_fontname_to_xlfd, converting '$' to '-', '_' to ' '
9233      also uppercase after '-' or ' ' */
9234   name[0] = c_toupper (name[0]);
9235   for (len =strlen (name), i =0; i<len; i++)
9236     {
9237       if (name[i] == '$')
9238         {
9239           name[i] = '-';
9240           if (i+1<len)
9241             name[i+1] = c_toupper (name[i+1]);
9242         }
9243       else if (name[i] == '_')
9244         {
9245           name[i] = ' ';
9246           if (i+1<len)
9247             name[i+1] = c_toupper (name[i+1]);
9248         }
9249     }
9250 /*fprintf (stderr, "converted '%s' to '%s'\n",xlfd,name);  */
9251   ret = [[NSString stringWithUTF8String: name] UTF8String];
9252   xfree (name);
9253   return ret;
9257 void
9258 syms_of_nsterm (void)
9260   NSTRACE ("syms_of_nsterm");
9262   ns_antialias_threshold = 10.0;
9264   /* from 23+ we need to tell emacs what modifiers there are.. */
9265   DEFSYM (Qmodifier_value, "modifier-value");
9266   DEFSYM (Qalt, "alt");
9267   DEFSYM (Qhyper, "hyper");
9268   DEFSYM (Qmeta, "meta");
9269   DEFSYM (Qsuper, "super");
9270   DEFSYM (Qcontrol, "control");
9271   DEFSYM (QUTF8_STRING, "UTF8_STRING");
9273   DEFSYM (Qfile, "file");
9274   DEFSYM (Qurl, "url");
9276   Fput (Qalt, Qmodifier_value, make_number (alt_modifier));
9277   Fput (Qhyper, Qmodifier_value, make_number (hyper_modifier));
9278   Fput (Qmeta, Qmodifier_value, make_number (meta_modifier));
9279   Fput (Qsuper, Qmodifier_value, make_number (super_modifier));
9280   Fput (Qcontrol, Qmodifier_value, make_number (ctrl_modifier));
9282   DEFVAR_LISP ("ns-input-file", ns_input_file,
9283               "The file specified in the last NS event.");
9284   ns_input_file =Qnil;
9286   DEFVAR_LISP ("ns-working-text", ns_working_text,
9287               "String for visualizing working composition sequence.");
9288   ns_working_text =Qnil;
9290   DEFVAR_LISP ("ns-input-font", ns_input_font,
9291               "The font specified in the last NS event.");
9292   ns_input_font =Qnil;
9294   DEFVAR_LISP ("ns-input-fontsize", ns_input_fontsize,
9295               "The fontsize specified in the last NS event.");
9296   ns_input_fontsize =Qnil;
9298   DEFVAR_LISP ("ns-input-line", ns_input_line,
9299                "The line specified in the last NS event.");
9300   ns_input_line =Qnil;
9302   DEFVAR_LISP ("ns-input-spi-name", ns_input_spi_name,
9303                "The service name specified in the last NS event.");
9304   ns_input_spi_name =Qnil;
9306   DEFVAR_LISP ("ns-input-spi-arg", ns_input_spi_arg,
9307                "The service argument specified in the last NS event.");
9308   ns_input_spi_arg =Qnil;
9310   DEFVAR_LISP ("ns-alternate-modifier", ns_alternate_modifier,
9311                "This variable describes the behavior of the alternate or option key.\n\
9312 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9313 that key.\n\
9314 Set to none means that the alternate / option key is not interpreted by Emacs\n\
9315 at all, allowing it to be used at a lower level for accented character entry.");
9316   ns_alternate_modifier = Qmeta;
9318   DEFVAR_LISP ("ns-right-alternate-modifier", ns_right_alternate_modifier,
9319                "This variable describes the behavior of the right alternate or option key.\n\
9320 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9321 that key.\n\
9322 Set to left means be the same key as `ns-alternate-modifier'.\n\
9323 Set to none means that the alternate / option key is not interpreted by Emacs\n\
9324 at all, allowing it to be used at a lower level for accented character entry.");
9325   ns_right_alternate_modifier = Qleft;
9327   DEFVAR_LISP ("ns-command-modifier", ns_command_modifier,
9328                "This variable describes the behavior of the command key.\n\
9329 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9330 that key.");
9331   ns_command_modifier = Qsuper;
9333   DEFVAR_LISP ("ns-right-command-modifier", ns_right_command_modifier,
9334                "This variable describes the behavior of the right command key.\n\
9335 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9336 that key.\n\
9337 Set to left means be the same key as `ns-command-modifier'.\n\
9338 Set to none means that the command / option key is not interpreted by Emacs\n\
9339 at all, allowing it to be used at a lower level for accented character entry.");
9340   ns_right_command_modifier = Qleft;
9342   DEFVAR_LISP ("ns-control-modifier", ns_control_modifier,
9343                "This variable describes the behavior of the control key.\n\
9344 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9345 that key.");
9346   ns_control_modifier = Qcontrol;
9348   DEFVAR_LISP ("ns-right-control-modifier", ns_right_control_modifier,
9349                "This variable describes the behavior of the right control key.\n\
9350 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9351 that key.\n\
9352 Set to left means be the same key as `ns-control-modifier'.\n\
9353 Set to none means that the control / option key is not interpreted by Emacs\n\
9354 at all, allowing it to be used at a lower level for accented character entry.");
9355   ns_right_control_modifier = Qleft;
9357   DEFVAR_LISP ("ns-function-modifier", ns_function_modifier,
9358                "This variable describes the behavior of the function key (on laptops).\n\
9359 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9360 that key.\n\
9361 Set to none means that the function key is not interpreted by Emacs at all,\n\
9362 allowing it to be used at a lower level for accented character entry.");
9363   ns_function_modifier = Qnone;
9365   DEFVAR_LISP ("ns-antialias-text", ns_antialias_text,
9366                "Non-nil (the default) means to render text antialiased.");
9367   ns_antialias_text = Qt;
9369   DEFVAR_LISP ("ns-use-thin-smoothing", ns_use_thin_smoothing,
9370                "Non-nil turns on a font smoothing method that produces thinner strokes.");
9371   ns_use_thin_smoothing = Qnil;
9373   DEFVAR_LISP ("ns-confirm-quit", ns_confirm_quit,
9374                "Whether to confirm application quit using dialog.");
9375   ns_confirm_quit = Qnil;
9377   DEFVAR_LISP ("ns-auto-hide-menu-bar", ns_auto_hide_menu_bar,
9378                doc: /* Non-nil means that the menu bar is hidden, but appears when the mouse is near.
9379 Only works on Mac OS X 10.6 or later.  */);
9380   ns_auto_hide_menu_bar = Qnil;
9382   DEFVAR_BOOL ("ns-use-native-fullscreen", ns_use_native_fullscreen,
9383      doc: /*Non-nil means to use native fullscreen on Mac OS X 10.7 and later.
9384 Nil means use fullscreen the old (< 10.7) way.  The old way works better with
9385 multiple monitors, but lacks tool bar.  This variable is ignored on
9386 Mac OS X < 10.7.  Default is t.  */);
9387   ns_use_native_fullscreen = YES;
9388   ns_last_use_native_fullscreen = ns_use_native_fullscreen;
9390   DEFVAR_BOOL ("ns-use-fullscreen-animation", ns_use_fullscreen_animation,
9391      doc: /*Non-nil means use animation on non-native fullscreen.
9392 For native fullscreen, this does nothing.
9393 Default is nil.  */);
9394   ns_use_fullscreen_animation = NO;
9396   DEFVAR_BOOL ("ns-use-srgb-colorspace", ns_use_srgb_colorspace,
9397      doc: /*Non-nil means to use sRGB colorspace on Mac OS X 10.7 and later.
9398 Note that this does not apply to images.
9399 This variable is ignored on Mac OS X < 10.7 and GNUstep.  */);
9400   ns_use_srgb_colorspace = YES;
9402   DEFVAR_BOOL ("ns-use-mwheel-acceleration",
9403                ns_use_mwheel_acceleration,
9404      doc: /*Non-nil means use macOS's standard mouse wheel acceleration.
9405 This variable is ignored on macOS < 10.7 and GNUstep.  Default is t.  */);
9406   ns_use_mwheel_acceleration = YES;
9408   DEFVAR_LISP ("ns-mwheel-line-height", ns_mwheel_line_height,
9409                doc: /*The number of pixels touchpad scrolling considers one line.
9410 Nil or a non-number means use the default frame line height.
9411 This variable is ignored on macOS < 10.7 and GNUstep.  Default is nil.  */);
9412   ns_mwheel_line_height = Qnil;
9414   DEFVAR_BOOL ("ns-use-mwheel-momentum", ns_use_mwheel_momentum,
9415                doc: /*Non-nil means mouse wheel scrolling uses momentum.
9416 This variable is ignored on macOS < 10.7 and GNUstep.  Default is t.  */);
9417   ns_use_mwheel_momentum = YES;
9419   /* TODO: move to common code */
9420   DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars,
9421                doc: /* Which toolkit scroll bars Emacs uses, if any.
9422 A value of nil means Emacs doesn't use toolkit scroll bars.
9423 With the X Window system, the value is a symbol describing the
9424 X toolkit.  Possible values are: gtk, motif, xaw, or xaw3d.
9425 With MS Windows or Nextstep, the value is t.  */);
9426   Vx_toolkit_scroll_bars = Qt;
9428   DEFVAR_BOOL ("x-use-underline-position-properties",
9429                x_use_underline_position_properties,
9430      doc: /*Non-nil means make use of UNDERLINE_POSITION font properties.
9431 A value of nil means ignore them.  If you encounter fonts with bogus
9432 UNDERLINE_POSITION font properties, for example 7x13 on XFree prior
9433 to 4.1, set this to nil. */);
9434   x_use_underline_position_properties = 0;
9436   DEFVAR_BOOL ("x-underline-at-descent-line",
9437                x_underline_at_descent_line,
9438      doc: /* Non-nil means to draw the underline at the same place as the descent line.
9439 (If `line-spacing' is in effect, that moves the underline lower by
9440 that many pixels.)
9441 A value of nil means to draw the underline according to the value of the
9442 variable `x-use-underline-position-properties', which is usually at the
9443 baseline level.  The default value is nil.  */);
9444   x_underline_at_descent_line = 0;
9446   /* Tell Emacs about this window system.  */
9447   Fprovide (Qns, Qnil);
9449   DEFSYM (Qcocoa, "cocoa");
9450   DEFSYM (Qgnustep, "gnustep");
9452 #ifdef NS_IMPL_COCOA
9453   Fprovide (Qcocoa, Qnil);
9454   syms_of_macfont ();
9455 #else
9456   Fprovide (Qgnustep, Qnil);
9457   syms_of_nsfont ();
9458 #endif