Fix xdg timestamp error on 32-bit Emacs
[emacs.git] / src / nsterm.m
blob709e905ec8f4a7a55ed0623c1a1090097bab4e0c
1 /* NeXT/Open/GNUstep / macOS communication module.      -*- coding: utf-8 -*-
3 Copyright (C) 1989, 1993-1994, 2005-2006, 2008-2017 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 struct frame *ns_updating_frame;
280 static NSView *focus_view = NULL;
281 static int ns_window_num = 0;
282 #ifdef NS_IMPL_GNUSTEP
283 static NSRect uRect;            // TODO: This is dead, remove it?
284 #endif
285 static BOOL gsaved = NO;
286 static BOOL ns_fake_keydown = NO;
287 #ifdef NS_IMPL_COCOA
288 static BOOL ns_menu_bar_is_hidden = NO;
289 #endif
290 /*static int debug_lock = 0; */
292 /* event loop */
293 static BOOL send_appdefined = YES;
294 #define NO_APPDEFINED_DATA (-8)
295 static int last_appdefined_event_data = NO_APPDEFINED_DATA;
296 static NSTimer *timed_entry = 0;
297 static NSTimer *scroll_repeat_entry = nil;
298 static fd_set select_readfds, select_writefds;
299 enum { SELECT_HAVE_READ = 1, SELECT_HAVE_WRITE = 2, SELECT_HAVE_TMO = 4 };
300 static int select_nfds = 0, select_valid = 0;
301 static struct timespec select_timeout = { 0, 0 };
302 static int selfds[2] = { -1, -1 };
303 static pthread_mutex_t select_mutex;
304 static NSAutoreleasePool *outerpool;
305 static struct input_event *emacs_event = NULL;
306 static struct input_event *q_event_ptr = NULL;
307 static int n_emacs_events_pending = 0;
308 static NSMutableArray *ns_pending_files, *ns_pending_service_names,
309   *ns_pending_service_args;
310 static BOOL ns_do_open_file = NO;
311 static BOOL ns_last_use_native_fullscreen;
313 /* Non-zero means that a HELP_EVENT has been generated since Emacs
314    start.  */
316 static BOOL any_help_event_p = NO;
318 static struct {
319   struct input_event *q;
320   int nr, cap;
321 } hold_event_q = {
322   NULL, 0, 0
325 static NSString *represented_filename = nil;
326 static struct frame *represented_frame = 0;
328 #ifdef NS_IMPL_COCOA
330  * State for pending menu activation:
331  * MENU_NONE     Normal state
332  * MENU_PENDING  A menu has been clicked on, but has been canceled so we can
333  *               run lisp to update the menu.
334  * MENU_OPENING  Menu is up to date, and the click event is redone so the menu
335  *               will open.
336  */
337 #define MENU_NONE 0
338 #define MENU_PENDING 1
339 #define MENU_OPENING 2
340 static int menu_will_open_state = MENU_NONE;
342 /* Saved position for menu click.  */
343 static CGPoint menu_mouse_point;
344 #endif
346 /* Convert modifiers in a NeXTstep event to emacs style modifiers.  */
347 #define NS_FUNCTION_KEY_MASK 0x800000
348 #define NSLeftControlKeyMask    (0x000001 | NSEventModifierFlagControl)
349 #define NSRightControlKeyMask   (0x002000 | NSEventModifierFlagControl)
350 #define NSLeftCommandKeyMask    (0x000008 | NSEventModifierFlagCommand)
351 #define NSRightCommandKeyMask   (0x000010 | NSEventModifierFlagCommand)
352 #define NSLeftAlternateKeyMask  (0x000020 | NSEventModifierFlagOption)
353 #define NSRightAlternateKeyMask (0x000040 | NSEventModifierFlagOption)
354 #define EV_MODIFIERS2(flags)                          \
355     (((flags & NSEventModifierFlagHelp) ?           \
356            hyper_modifier : 0)                        \
357      | (!EQ (ns_right_alternate_modifier, Qleft) && \
358         ((flags & NSRightAlternateKeyMask) \
359          == NSRightAlternateKeyMask) ? \
360            parse_solitary_modifier (ns_right_alternate_modifier) : 0) \
361      | ((flags & NSEventModifierFlagOption) ?                 \
362            parse_solitary_modifier (ns_alternate_modifier) : 0)   \
363      | ((flags & NSEventModifierFlagShift) ?     \
364            shift_modifier : 0)                        \
365      | (!EQ (ns_right_control_modifier, Qleft) && \
366         ((flags & NSRightControlKeyMask) \
367          == NSRightControlKeyMask) ? \
368            parse_solitary_modifier (ns_right_control_modifier) : 0) \
369      | ((flags & NSEventModifierFlagControl) ?      \
370            parse_solitary_modifier (ns_control_modifier) : 0)     \
371      | ((flags & NS_FUNCTION_KEY_MASK) ?  \
372            parse_solitary_modifier (ns_function_modifier) : 0)    \
373      | (!EQ (ns_right_command_modifier, Qleft) && \
374         ((flags & NSRightCommandKeyMask) \
375          == NSRightCommandKeyMask) ? \
376            parse_solitary_modifier (ns_right_command_modifier) : 0) \
377      | ((flags & NSEventModifierFlagCommand) ?      \
378            parse_solitary_modifier (ns_command_modifier):0))
379 #define EV_MODIFIERS(e) EV_MODIFIERS2 ([e modifierFlags])
381 #define EV_UDMODIFIERS(e)                                      \
382     ((([e type] == NSEventTypeLeftMouseDown) ? down_modifier : 0)       \
383      | (([e type] == NSEventTypeRightMouseDown) ? down_modifier : 0)    \
384      | (([e type] == NSEventTypeOtherMouseDown) ? down_modifier : 0)    \
385      | (([e type] == NSEventTypeLeftMouseDragged) ? down_modifier : 0)  \
386      | (([e type] == NSEventTypeRightMouseDragged) ? down_modifier : 0) \
387      | (([e type] == NSEventTypeOtherMouseDragged) ? down_modifier : 0) \
388      | (([e type] == NSEventTypeLeftMouseUp)   ? up_modifier   : 0)     \
389      | (([e type] == NSEventTypeRightMouseUp)   ? up_modifier   : 0)    \
390      | (([e type] == NSEventTypeOtherMouseUp)   ? up_modifier   : 0))
392 #define EV_BUTTON(e)                                                         \
393     ((([e type] == NSEventTypeLeftMouseDown) || ([e type] == NSEventTypeLeftMouseUp)) ? 0 :    \
394       (([e type] == NSEventTypeRightMouseDown) || ([e type] == NSEventTypeRightMouseUp)) ? 2 : \
395      [e buttonNumber] - 1)
397 /* Convert the time field to a timestamp in milliseconds. */
398 #define EV_TIMESTAMP(e) ([e timestamp] * 1000)
400 /* This is a piece of code which is common to all the event handling
401    methods.  Maybe it should even be a function.  */
402 #define EV_TRAILER(e)                                                   \
403   {                                                                     \
404     XSETFRAME (emacs_event->frame_or_window, emacsframe);               \
405     EV_TRAILER2 (e);                                                    \
406   }
408 #define EV_TRAILER2(e)                                                  \
409   {                                                                     \
410       if (e) emacs_event->timestamp = EV_TIMESTAMP (e);                 \
411       if (q_event_ptr)                                                  \
412         {                                                               \
413           Lisp_Object tem = Vinhibit_quit;                              \
414           Vinhibit_quit = Qt;                                           \
415           n_emacs_events_pending++;                                     \
416           kbd_buffer_store_event_hold (emacs_event, q_event_ptr);       \
417           Vinhibit_quit = tem;                                          \
418         }                                                               \
419       else                                                              \
420         hold_event (emacs_event);                                       \
421       EVENT_INIT (*emacs_event);                                        \
422       ns_send_appdefined (-1);                                          \
423     }
426 /* These flags will be OR'd or XOR'd with the NSWindow's styleMask
427    property depending on what we're doing. */
428 #define FRAME_DECORATED_FLAGS (NSWindowStyleMaskTitled              \
429                                | NSWindowStyleMaskResizable         \
430                                | NSWindowStyleMaskMiniaturizable    \
431                                | NSWindowStyleMaskClosable)
432 #define FRAME_UNDECORATED_FLAGS NSWindowStyleMaskBorderless
434 /* TODO: get rid of need for these forward declarations */
435 static void ns_condemn_scroll_bars (struct frame *f);
436 static void ns_judge_scroll_bars (struct frame *f);
439 /* ==========================================================================
441     Utilities
443    ========================================================================== */
445 void
446 ns_set_represented_filename (NSString *fstr, struct frame *f)
448   represented_filename = [fstr retain];
449   represented_frame = f;
452 void
453 ns_init_events (struct input_event *ev)
455   EVENT_INIT (*ev);
456   emacs_event = ev;
459 void
460 ns_finish_events (void)
462   emacs_event = NULL;
465 static void
466 hold_event (struct input_event *event)
468   if (hold_event_q.nr == hold_event_q.cap)
469     {
470       if (hold_event_q.cap == 0) hold_event_q.cap = 10;
471       else hold_event_q.cap *= 2;
472       hold_event_q.q =
473         xrealloc (hold_event_q.q, hold_event_q.cap * sizeof *hold_event_q.q);
474     }
476   hold_event_q.q[hold_event_q.nr++] = *event;
477   /* Make sure ns_read_socket is called, i.e. we have input.  */
478   raise (SIGIO);
479   send_appdefined = YES;
482 static Lisp_Object
483 append2 (Lisp_Object list, Lisp_Object item)
484 /* --------------------------------------------------------------------------
485    Utility to append to a list
486    -------------------------------------------------------------------------- */
488   return CALLN (Fnconc, list, list1 (item));
492 const char *
493 ns_etc_directory (void)
494 /* If running as a self-contained app bundle, return as a string the
495    filename of the etc directory, if present; else nil.  */
497   NSBundle *bundle = [NSBundle mainBundle];
498   NSString *resourceDir = [bundle resourcePath];
499   NSString *resourcePath;
500   NSFileManager *fileManager = [NSFileManager defaultManager];
501   BOOL isDir;
503   resourcePath = [resourceDir stringByAppendingPathComponent: @"etc"];
504   if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
505     {
506       if (isDir) return [resourcePath UTF8String];
507     }
508   return NULL;
512 const char *
513 ns_exec_path (void)
514 /* If running as a self-contained app bundle, return as a path string
515    the filenames of the libexec and bin directories, ie libexec:bin.
516    Otherwise, return nil.
517    Normally, Emacs does not add its own bin/ directory to the PATH.
518    However, a self-contained NS build has a different layout, with
519    bin/ and libexec/ subdirectories in the directory that contains
520    Emacs.app itself.
521    We put libexec first, because init_callproc_1 uses the first
522    element to initialize exec-directory.  An alternative would be
523    for init_callproc to check for invocation-directory/libexec.
526   NSBundle *bundle = [NSBundle mainBundle];
527   NSString *resourceDir = [bundle resourcePath];
528   NSString *binDir = [bundle bundlePath];
529   NSString *resourcePath, *resourcePaths;
530   NSRange range;
531   NSString *pathSeparator = [NSString stringWithFormat: @"%c", SEPCHAR];
532   NSFileManager *fileManager = [NSFileManager defaultManager];
533   NSArray *paths;
534   NSEnumerator *pathEnum;
535   BOOL isDir;
537   range = [resourceDir rangeOfString: @"Contents"];
538   if (range.location != NSNotFound)
539     {
540       binDir = [binDir stringByAppendingPathComponent: @"Contents"];
541 #ifdef NS_IMPL_COCOA
542       binDir = [binDir stringByAppendingPathComponent: @"MacOS"];
543 #endif
544     }
546   paths = [binDir stringsByAppendingPaths:
547                 [NSArray arrayWithObjects: @"libexec", @"bin", nil]];
548   pathEnum = [paths objectEnumerator];
549   resourcePaths = @"";
551   while ((resourcePath = [pathEnum nextObject]))
552     {
553       if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
554         if (isDir)
555           {
556             if ([resourcePaths length] > 0)
557               resourcePaths
558                 = [resourcePaths stringByAppendingString: pathSeparator];
559             resourcePaths
560               = [resourcePaths stringByAppendingString: resourcePath];
561           }
562     }
563   if ([resourcePaths length] > 0) return [resourcePaths UTF8String];
565   return NULL;
569 const char *
570 ns_load_path (void)
571 /* If running as a self-contained app bundle, return as a path string
572    the filenames of the site-lisp and lisp directories.
573    Ie, site-lisp:lisp.  Otherwise, return nil.  */
575   NSBundle *bundle = [NSBundle mainBundle];
576   NSString *resourceDir = [bundle resourcePath];
577   NSString *resourcePath, *resourcePaths;
578   NSString *pathSeparator = [NSString stringWithFormat: @"%c", SEPCHAR];
579   NSFileManager *fileManager = [NSFileManager defaultManager];
580   BOOL isDir;
581   NSArray *paths = [resourceDir stringsByAppendingPaths:
582                               [NSArray arrayWithObjects:
583                                          @"site-lisp", @"lisp", nil]];
584   NSEnumerator *pathEnum = [paths objectEnumerator];
585   resourcePaths = @"";
587   /* Hack to skip site-lisp.  */
588   if (no_site_lisp) resourcePath = [pathEnum nextObject];
590   while ((resourcePath = [pathEnum nextObject]))
591     {
592       if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
593         if (isDir)
594           {
595             if ([resourcePaths length] > 0)
596               resourcePaths
597                 = [resourcePaths stringByAppendingString: pathSeparator];
598             resourcePaths
599               = [resourcePaths stringByAppendingString: resourcePath];
600           }
601     }
602   if ([resourcePaths length] > 0) return [resourcePaths UTF8String];
604   return NULL;
608 void
609 ns_init_locale (void)
610 /* macOS doesn't set any environment variables for the locale when run
611    from the GUI. Get the locale from the OS and set LANG. */
613   NSLocale *locale = [NSLocale currentLocale];
615   NSTRACE ("ns_init_locale");
617   @try
618     {
619       /* It seems macOS should probably use UTF-8 everywhere.
620          'localeIdentifier' does not specify the encoding, and I can't
621          find any way to get the OS to tell us which encoding to use,
622          so hard-code '.UTF-8'. */
623       NSString *localeID = [NSString stringWithFormat:@"%@.UTF-8",
624                                      [locale localeIdentifier]];
626       /* Set LANG to locale, but not if LANG is already set. */
627       setenv("LANG", [localeID UTF8String], 0);
628     }
629   @catch (NSException *e)
630     {
631       NSLog (@"Locale detection failed: %@: %@", [e name], [e reason]);
632     }
636 void
637 ns_release_object (void *obj)
638 /* --------------------------------------------------------------------------
639     Release an object (callable from C)
640    -------------------------------------------------------------------------- */
642     [(id)obj release];
646 void
647 ns_retain_object (void *obj)
648 /* --------------------------------------------------------------------------
649     Retain an object (callable from C)
650    -------------------------------------------------------------------------- */
652     [(id)obj retain];
656 void *
657 ns_alloc_autorelease_pool (void)
658 /* --------------------------------------------------------------------------
659      Allocate a pool for temporary objects (callable from C)
660    -------------------------------------------------------------------------- */
662   return [[NSAutoreleasePool alloc] init];
666 void
667 ns_release_autorelease_pool (void *pool)
668 /* --------------------------------------------------------------------------
669      Free a pool and temporary objects it refers to (callable from C)
670    -------------------------------------------------------------------------- */
672   ns_release_object (pool);
676 static BOOL
677 ns_menu_bar_should_be_hidden (void)
678 /* True, if the menu bar should be hidden.  */
680   return !NILP (ns_auto_hide_menu_bar)
681     && [NSApp respondsToSelector:@selector(setPresentationOptions:)];
685 struct EmacsMargins
687   CGFloat top;
688   CGFloat bottom;
689   CGFloat left;
690   CGFloat right;
694 static struct EmacsMargins
695 ns_screen_margins (NSScreen *screen)
696 /* The parts of SCREEN used by the operating system.  */
698   NSTRACE ("ns_screen_margins");
700   struct EmacsMargins margins;
702   NSRect screenFrame = [screen frame];
703   NSRect screenVisibleFrame = [screen visibleFrame];
705   /* Sometimes, visibleFrame isn't up-to-date with respect to a hidden
706      menu bar, check this explicitly.  */
707   if (ns_menu_bar_should_be_hidden())
708     {
709       margins.top = 0;
710     }
711   else
712     {
713       CGFloat frameTop = screenFrame.origin.y + screenFrame.size.height;
714       CGFloat visibleFrameTop = (screenVisibleFrame.origin.y
715                                  + screenVisibleFrame.size.height);
717       margins.top = frameTop - visibleFrameTop;
718     }
720   {
721     CGFloat frameRight = screenFrame.origin.x + screenFrame.size.width;
722     CGFloat visibleFrameRight = (screenVisibleFrame.origin.x
723                                  + screenVisibleFrame.size.width);
724     margins.right = frameRight - visibleFrameRight;
725   }
727   margins.bottom = screenVisibleFrame.origin.y - screenFrame.origin.y;
728   margins.left   = screenVisibleFrame.origin.x - screenFrame.origin.x;
730   NSTRACE_MSG ("left:%g right:%g top:%g bottom:%g",
731                margins.left,
732                margins.right,
733                margins.top,
734                margins.bottom);
736   return margins;
740 /* A screen margin between 1 and DOCK_IGNORE_LIMIT (inclusive) is
741    assumed to contain a hidden dock.  macOS currently use 4 pixels for
742    this, however, to be future compatible, a larger value is used.  */
743 #define DOCK_IGNORE_LIMIT 6
745 static struct EmacsMargins
746 ns_screen_margins_ignoring_hidden_dock (NSScreen *screen)
747 /* The parts of SCREEN used by the operating system, excluding the parts
748 reserved for an hidden dock.  */
750   NSTRACE ("ns_screen_margins_ignoring_hidden_dock");
752   struct EmacsMargins margins = ns_screen_margins(screen);
754   /* macOS (currently) reserved 4 pixels along the edge where a hidden
755      dock is located.  Unfortunately, it's not possible to find the
756      location and information about if the dock is hidden.  Instead,
757      it is assumed that if the margin of an edge is less than
758      DOCK_IGNORE_LIMIT, it contains a hidden dock.  */
759   if (margins.left <= DOCK_IGNORE_LIMIT)
760     {
761       margins.left = 0;
762     }
763   if (margins.right <= DOCK_IGNORE_LIMIT)
764     {
765       margins.right = 0;
766     }
767   if (margins.top <= DOCK_IGNORE_LIMIT)
768     {
769       margins.top = 0;
770     }
771   /* Note: This doesn't occur in current versions of macOS, but
772      included for completeness and future compatibility.  */
773   if (margins.bottom <= DOCK_IGNORE_LIMIT)
774     {
775       margins.bottom = 0;
776     }
778   NSTRACE_MSG ("left:%g right:%g top:%g bottom:%g",
779                margins.left,
780                margins.right,
781                margins.top,
782                margins.bottom);
784   return margins;
788 static CGFloat
789 ns_menu_bar_height (NSScreen *screen)
790 /* The height of the menu bar, if visible.
792    Note: Don't use this when fullscreen is enabled -- the screen
793    sometimes includes, sometimes excludes the menu bar area.  */
795   struct EmacsMargins margins = ns_screen_margins(screen);
797   CGFloat res = margins.top;
799   NSTRACE ("ns_menu_bar_height " NSTRACE_FMT_RETURN " %.0f", res);
801   return res;
805 /* ==========================================================================
807     Focus (clipping) and screen update
809    ========================================================================== */
812 // Window constraining
813 // -------------------
815 // To ensure that the windows are not placed under the menu bar, they
816 // are typically moved by the call-back constrainFrameRect. However,
817 // by overriding it, it's possible to inhibit this, leaving the window
818 // in it's original position.
820 // It's possible to hide the menu bar. However, technically, it's only
821 // possible to hide it when the application is active. To ensure that
822 // this work properly, the menu bar and window constraining are
823 // deferred until the application becomes active.
825 // Even though it's not possible to manually move a window above the
826 // top of the screen, it is allowed if it's done programmatically,
827 // when the menu is hidden. This allows the editable area to cover the
828 // full screen height.
830 // Test cases
831 // ----------
833 // Use the following extra files:
835 //    init.el:
836 //       ;; Hide menu and place frame slightly above the top of the screen.
837 //       (setq ns-auto-hide-menu-bar t)
838 //       (set-frame-position (selected-frame) 0 -20)
840 // Test 1:
842 //    emacs -Q -l init.el
844 //    Result: No menu bar, and the title bar should be above the screen.
846 // Test 2:
848 //    emacs -Q
850 //    Result: Menu bar visible, frame placed immediately below the menu.
853 static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
855   NSTRACE ("constrain_frame_rect(" NSTRACE_FMT_RECT ")",
856              NSTRACE_ARG_RECT (frameRect));
858   // --------------------
859   // Collect information about the screen the frame is covering.
860   //
862   NSArray *screens = [NSScreen screens];
863   NSUInteger nr_screens = [screens count];
865   int i;
867   // The height of the menu bar, if present in any screen the frame is
868   // displayed in.
869   int menu_bar_height = 0;
871   // A rectangle covering all the screen the frame is displayed in.
872   NSRect multiscreenRect = NSMakeRect(0, 0, 0, 0);
873   for (i = 0; i < nr_screens; ++i )
874     {
875       NSScreen *s = [screens objectAtIndex: i];
876       NSRect scrRect = [s frame];
878       NSTRACE_MSG ("Screen %d: " NSTRACE_FMT_RECT,
879                    i, NSTRACE_ARG_RECT (scrRect));
881       if (NSIntersectionRect (frameRect, scrRect).size.height != 0)
882         {
883           multiscreenRect = NSUnionRect (multiscreenRect, scrRect);
885           if (!isFullscreen)
886             {
887               CGFloat screen_menu_bar_height = ns_menu_bar_height (s);
888               menu_bar_height = max(menu_bar_height, screen_menu_bar_height);
889             }
890         }
891     }
893   NSTRACE_RECT ("multiscreenRect", multiscreenRect);
895   NSTRACE_MSG ("menu_bar_height: %d", menu_bar_height);
897   if (multiscreenRect.size.width == 0
898       || multiscreenRect.size.height == 0)
899     {
900       // Failed to find any monitor, give up.
901       NSTRACE_MSG ("multiscreenRect empty");
902       NSTRACE_RETURN_RECT (frameRect);
903       return frameRect;
904     }
907   // --------------------
908   // Find a suitable placement.
909   //
911   if (ns_menu_bar_should_be_hidden())
912     {
913       // When the menu bar is hidden, the user may place part of the
914       // frame above the top of the screen, for example to hide the
915       // title bar.
916       //
917       // Hence, keep the original position.
918     }
919   else
920     {
921       // Ensure that the frame is below the menu bar, or below the top
922       // of the screen.
923       //
924       // This assume that the menu bar is placed at the top in the
925       // rectangle that covers the monitors.  (It doesn't have to be,
926       // but if it's not it's hard to do anything useful.)
927       CGFloat topOfWorkArea = (multiscreenRect.origin.y
928                                + multiscreenRect.size.height
929                                - menu_bar_height);
931       CGFloat topOfFrame = frameRect.origin.y + frameRect.size.height;
932       if (topOfFrame > topOfWorkArea)
933         {
934           frameRect.origin.y -= topOfFrame - topOfWorkArea;
935           NSTRACE_RECT ("After placement adjust", frameRect);
936         }
937     }
939   // Include the following section to restrict frame to the screens.
940   // (If so, update it to allow the frame to stretch down below the
941   // screen.)
942 #if 0
943   // --------------------
944   // Ensure frame doesn't stretch below the screens.
945   //
947   CGFloat diff = multiscreenRect.origin.y - frameRect.origin.y;
949   if (diff > 0)
950     {
951       frameRect.origin.y = multiscreenRect.origin.y;
952       frameRect.size.height -= diff;
953     }
954 #endif
956   NSTRACE_RETURN_RECT (frameRect);
957   return frameRect;
961 static void
962 ns_constrain_all_frames (void)
963 /* --------------------------------------------------------------------------
964      Ensure that the menu bar doesn't cover any frames.
965    -------------------------------------------------------------------------- */
967   Lisp_Object tail, frame;
969   NSTRACE ("ns_constrain_all_frames");
971   block_input ();
973   FOR_EACH_FRAME (tail, frame)
974     {
975       struct frame *f = XFRAME (frame);
976       if (FRAME_NS_P (f))
977         {
978           EmacsView *view = FRAME_NS_VIEW (f);
980           if (![view isFullscreen])
981             {
982               [[view window]
983                 setFrame:constrain_frame_rect([[view window] frame], false)
984                  display:NO];
985             }
986         }
987     }
989   unblock_input ();
993 static void
994 ns_update_auto_hide_menu_bar (void)
995 /* --------------------------------------------------------------------------
996      Show or hide the menu bar, based on user setting.
997    -------------------------------------------------------------------------- */
999 #ifdef NS_IMPL_COCOA
1000   NSTRACE ("ns_update_auto_hide_menu_bar");
1002   block_input ();
1004   if (NSApp != nil && [NSApp isActive])
1005     {
1006       // Note, "setPresentationOptions" triggers an error unless the
1007       // application is active.
1008       BOOL menu_bar_should_be_hidden = ns_menu_bar_should_be_hidden ();
1010       if (menu_bar_should_be_hidden != ns_menu_bar_is_hidden)
1011         {
1012           NSApplicationPresentationOptions options
1013             = NSApplicationPresentationDefault;
1015           if (menu_bar_should_be_hidden)
1016             options |= NSApplicationPresentationAutoHideMenuBar
1017               | NSApplicationPresentationAutoHideDock;
1019           [NSApp setPresentationOptions: options];
1021           ns_menu_bar_is_hidden = menu_bar_should_be_hidden;
1023           if (!ns_menu_bar_is_hidden)
1024             {
1025               ns_constrain_all_frames ();
1026             }
1027         }
1028     }
1030   unblock_input ();
1031 #endif
1035 static void
1036 ns_update_begin (struct frame *f)
1037 /* --------------------------------------------------------------------------
1038    Prepare for a grouped sequence of drawing calls
1039    external (RIF) call; whole frame, called before update_window_begin
1040    -------------------------------------------------------------------------- */
1042   EmacsView *view = FRAME_NS_VIEW (f);
1043   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_begin");
1045   ns_update_auto_hide_menu_bar ();
1047 #ifdef NS_IMPL_COCOA
1048   if ([view isFullscreen] && [view fsIsNative])
1049   {
1050     // Fix reappearing tool bar in fullscreen for Mac OS X 10.7
1051     BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (f) ? YES : NO;
1052     NSToolbar *toolbar = [FRAME_NS_VIEW (f) toolbar];
1053     if (! tbar_visible != ! [toolbar isVisible])
1054       [toolbar setVisible: tbar_visible];
1055   }
1056 #endif
1058   ns_updating_frame = f;
1059   [view lockFocus];
1061   /* drawRect may have been called for say the minibuffer, and then clip path
1062      is for the minibuffer.  But the display engine may draw more because
1063      we have set the frame as garbaged.  So reset clip path to the whole
1064      view.  */
1065 #ifdef NS_IMPL_COCOA
1066   {
1067     NSBezierPath *bp;
1068     NSRect r = [view frame];
1069     NSRect cr = [[view window] frame];
1070     /* If a large frame size is set, r may be larger than the window frame
1071        before constrained.  In that case don't change the clip path, as we
1072        will clear in to the tool bar and title bar.  */
1073     if (r.size.height
1074         + FRAME_NS_TITLEBAR_HEIGHT (f)
1075         + FRAME_TOOLBAR_HEIGHT (f) <= cr.size.height)
1076       {
1077         bp = [[NSBezierPath bezierPathWithRect: r] retain];
1078         [bp setClip];
1079         [bp release];
1080       }
1081   }
1082 #endif
1084 #ifdef NS_IMPL_GNUSTEP
1085   uRect = NSMakeRect (0, 0, 0, 0);
1086 #endif
1090 static void
1091 ns_update_window_begin (struct window *w)
1092 /* --------------------------------------------------------------------------
1093    Prepare for a grouped sequence of drawing calls
1094    external (RIF) call; for one window, called after update_begin
1095    -------------------------------------------------------------------------- */
1097   struct frame *f = XFRAME (WINDOW_FRAME (w));
1098   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
1100   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_window_begin");
1101   w->output_cursor = w->cursor;
1103   block_input ();
1105   if (f == hlinfo->mouse_face_mouse_frame)
1106     {
1107       /* Don't do highlighting for mouse motion during the update.  */
1108       hlinfo->mouse_face_defer = 1;
1110         /* If the frame needs to be redrawn,
1111            simply forget about any prior mouse highlighting.  */
1112       if (FRAME_GARBAGED_P (f))
1113         hlinfo->mouse_face_window = Qnil;
1115       /* (further code for mouse faces ifdef'd out in other terms elided) */
1116     }
1118   unblock_input ();
1122 static void
1123 ns_update_window_end (struct window *w, bool cursor_on_p,
1124                       bool mouse_face_overwritten_p)
1125 /* --------------------------------------------------------------------------
1126    Finished a grouped sequence of drawing calls
1127    external (RIF) call; for one window called before update_end
1128    -------------------------------------------------------------------------- */
1130   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_window_end");
1132   /* note: this fn is nearly identical in all terms */
1133   if (!w->pseudo_window_p)
1134     {
1135       block_input ();
1137       if (cursor_on_p)
1138         display_and_set_cursor (w, 1,
1139                                 w->output_cursor.hpos, w->output_cursor.vpos,
1140                                 w->output_cursor.x, w->output_cursor.y);
1142       if (draw_window_fringes (w, 1))
1143         {
1144           if (WINDOW_RIGHT_DIVIDER_WIDTH (w))
1145             x_draw_right_divider (w);
1146           else
1147             x_draw_vertical_border (w);
1148         }
1150       unblock_input ();
1151     }
1153   /* If a row with mouse-face was overwritten, arrange for
1154      frame_up_to_date to redisplay the mouse highlight.  */
1155   if (mouse_face_overwritten_p)
1156     reset_mouse_highlight (MOUSE_HL_INFO (XFRAME (w->frame)));
1160 static void
1161 ns_update_end (struct frame *f)
1162 /* --------------------------------------------------------------------------
1163    Finished a grouped sequence of drawing calls
1164    external (RIF) call; for whole frame, called after update_window_end
1165    -------------------------------------------------------------------------- */
1167   EmacsView *view = FRAME_NS_VIEW (f);
1169   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_end");
1171 /*   if (f == MOUSE_HL_INFO (f)->mouse_face_mouse_frame) */
1172   MOUSE_HL_INFO (f)->mouse_face_defer = 0;
1174   block_input ();
1176   [view unlockFocus];
1177   [[view window] flushWindow];
1179   unblock_input ();
1180   ns_updating_frame = NULL;
1183 static void
1184 ns_focus (struct frame *f, NSRect *r, int n)
1185 /* --------------------------------------------------------------------------
1186    Internal: Focus on given frame.  During small local updates this is used to
1187      draw, however during large updates, ns_update_begin and ns_update_end are
1188      called to wrap the whole thing, in which case these calls are stubbed out.
1189      Except, on GNUstep, we accumulate the rectangle being drawn into, because
1190      the back end won't do this automatically, and will just end up flushing
1191      the entire window.
1192    -------------------------------------------------------------------------- */
1194   NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_focus");
1195   if (r != NULL)
1196     {
1197       NSTRACE_RECT ("r", *r);
1198     }
1200   if (f != ns_updating_frame)
1201     {
1202       NSView *view = FRAME_NS_VIEW (f);
1203       if (view != focus_view)
1204         {
1205           if (focus_view != NULL)
1206             {
1207               [focus_view unlockFocus];
1208               [[focus_view window] flushWindow];
1209 /*debug_lock--; */
1210             }
1212           if (view)
1213             [view lockFocus];
1214           focus_view = view;
1215 /*if (view) debug_lock++; */
1216         }
1217     }
1219   /* clipping */
1220   if (r)
1221     {
1222       [[NSGraphicsContext currentContext] saveGraphicsState];
1223       if (n == 2)
1224         NSRectClipList (r, 2);
1225       else
1226         NSRectClip (*r);
1227       gsaved = YES;
1228     }
1232 static void
1233 ns_unfocus (struct frame *f)
1234 /* --------------------------------------------------------------------------
1235      Internal: Remove focus on given frame
1236    -------------------------------------------------------------------------- */
1238   NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_unfocus");
1240   if (gsaved)
1241     {
1242       [[NSGraphicsContext currentContext] restoreGraphicsState];
1243       gsaved = NO;
1244     }
1246   if (f != ns_updating_frame)
1247     {
1248       if (focus_view != NULL)
1249         {
1250           [focus_view unlockFocus];
1251           [[focus_view window] flushWindow];
1252           focus_view = NULL;
1253 /*debug_lock--; */
1254         }
1255     }
1259 static void
1260 ns_clip_to_row (struct window *w, struct glyph_row *row,
1261                 enum glyph_row_area area, BOOL gc)
1262 /* --------------------------------------------------------------------------
1263      Internal (but parallels other terms): Focus drawing on given row
1264    -------------------------------------------------------------------------- */
1266   struct frame *f = XFRAME (WINDOW_FRAME (w));
1267   NSRect clip_rect;
1268   int window_x, window_y, window_width;
1270   window_box (w, area, &window_x, &window_y, &window_width, 0);
1272   clip_rect.origin.x = window_x;
1273   clip_rect.origin.y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, row->y));
1274   clip_rect.origin.y = max (clip_rect.origin.y, window_y);
1275   clip_rect.size.width = window_width;
1276   clip_rect.size.height = row->visible_height;
1278   ns_focus (f, &clip_rect, 1);
1282 /* ==========================================================================
1284     Visible bell and beep.
1286    ========================================================================== */
1289 // This bell implementation shows the visual bell image asynchronously
1290 // from the rest of Emacs. This is done by adding a NSView to the
1291 // superview of the Emacs window and removing it using a timer.
1293 // Unfortunately, some Emacs operations, like scrolling, is done using
1294 // low-level primitives that copy the content of the window, including
1295 // the bell image. To some extent, this is handled by removing the
1296 // image prior to scrolling and marking that the window is in need for
1297 // redisplay.
1299 // To test this code, make sure that there is no artifacts of the bell
1300 // image in the following situations. Use a non-empty buffer (like the
1301 // tutorial) to ensure that a scroll is performed:
1303 // * Single-window: C-g C-v
1305 // * Side-by-windows: C-x 3 C-g C-v
1307 // * Windows above each other: C-x 2 C-g C-v
1309 @interface EmacsBell : NSImageView
1311   // Number of currently active bell:s.
1312   unsigned int nestCount;
1313   NSView * mView;
1314   bool isAttached;
1316 - (void)show:(NSView *)view;
1317 - (void)hide;
1318 - (void)remove;
1319 @end
1321 @implementation EmacsBell
1323 - (id)init
1325   NSTRACE ("[EmacsBell init]");
1326   if ((self = [super init]))
1327     {
1328       nestCount = 0;
1329       isAttached = false;
1330 #ifdef NS_IMPL_GNUSTEP
1331       // GNUstep doesn't provide named images.  This was reported in
1332       // 2011, see https://savannah.gnu.org/bugs/?33396
1333       //
1334       // As a drop in replacement, a semitransparent gray square is used.
1335       self.image = [[NSImage alloc] initWithSize:NSMakeSize(32 * 5, 32 * 5)];
1336       [self.image lockFocus];
1337       [[NSColor colorForEmacsRed:0.5 green:0.5 blue:0.5 alpha:0.5] set];
1338       NSRectFill(NSMakeRect(0, 0, 32, 32));
1339       [self.image unlockFocus];
1340 #else
1341       self.image = [NSImage imageNamed:NSImageNameCaution];
1342       [self.image setSize:NSMakeSize(self.image.size.width * 5,
1343                                      self.image.size.height * 5)];
1344 #endif
1345     }
1346   return self;
1349 - (void)show:(NSView *)view
1351   NSTRACE ("[EmacsBell show:]");
1352   NSTRACE_MSG ("nestCount: %u", nestCount);
1354   // Show the image, unless it's already shown.
1355   if (nestCount == 0)
1356     {
1357       NSRect rect = [view bounds];
1358       NSPoint pos;
1359       pos.x = rect.origin.x + (rect.size.width  - self.image.size.width )/2;
1360       pos.y = rect.origin.y + (rect.size.height - self.image.size.height)/2;
1362       [self setFrameOrigin:pos];
1363       [self setFrameSize:self.image.size];
1365       isAttached = true;
1366       mView = view;
1367       [[[view window] contentView] addSubview:self
1368                                    positioned:NSWindowAbove
1369                                    relativeTo:nil];
1370     }
1372   ++nestCount;
1374   [self performSelector:@selector(hide) withObject:self afterDelay:0.5];
1378 - (void)hide
1380   // Note: Trace output from this method isn't shown, reason unknown.
1381   // NSTRACE ("[EmacsBell hide]");
1383   if (nestCount > 0)
1384     --nestCount;
1386   // Remove the image once the last bell became inactive.
1387   if (nestCount == 0)
1388     {
1389       [self remove];
1390     }
1394 -(void)remove
1396   NSTRACE ("[EmacsBell remove]");
1397   if (isAttached)
1398     {
1399       NSTRACE_MSG ("removeFromSuperview");
1400       [self removeFromSuperview];
1401       mView.needsDisplay = YES;
1402       isAttached = false;
1403     }
1406 @end
1409 static EmacsBell * bell_view = nil;
1411 static void
1412 ns_ring_bell (struct frame *f)
1413 /* --------------------------------------------------------------------------
1414      "Beep" routine
1415    -------------------------------------------------------------------------- */
1417   NSTRACE ("ns_ring_bell");
1418   if (visible_bell)
1419     {
1420       struct frame *frame = SELECTED_FRAME ();
1421       NSView *view;
1423       if (bell_view == nil)
1424         {
1425           bell_view = [[EmacsBell alloc] init];
1426           [bell_view retain];
1427         }
1429       block_input ();
1431       view = FRAME_NS_VIEW (frame);
1432       if (view != nil)
1433         {
1434           [bell_view show:view];
1435         }
1437       unblock_input ();
1438     }
1439   else
1440     {
1441       NSBeep ();
1442     }
1446 static void
1447 hide_bell (void)
1448 /* --------------------------------------------------------------------------
1449      Ensure the bell is hidden.
1450    -------------------------------------------------------------------------- */
1452   NSTRACE ("hide_bell");
1454   if (bell_view != nil)
1455     {
1456       [bell_view remove];
1457     }
1461 /* ==========================================================================
1463     Frame / window manager related functions
1465    ========================================================================== */
1468 static void
1469 ns_raise_frame (struct frame *f, BOOL make_key)
1470 /* --------------------------------------------------------------------------
1471      Bring window to foreground and if make_key is YES, give it focus.
1472    -------------------------------------------------------------------------- */
1474   NSView *view;
1476   check_window_system (f);
1477   view = FRAME_NS_VIEW (f);
1478   block_input ();
1479   if (FRAME_VISIBLE_P (f))
1480     {
1481       if (make_key)
1482         [[view window] makeKeyAndOrderFront: NSApp];
1483       else
1484         [[view window] orderFront: NSApp];
1485     }
1486   unblock_input ();
1490 static void
1491 ns_lower_frame (struct frame *f)
1492 /* --------------------------------------------------------------------------
1493      Send window to back
1494    -------------------------------------------------------------------------- */
1496   NSView *view;
1498   check_window_system (f);
1499   view = FRAME_NS_VIEW (f);
1500   block_input ();
1501   [[view window] orderBack: NSApp];
1502   unblock_input ();
1506 static void
1507 ns_frame_raise_lower (struct frame *f, bool raise)
1508 /* --------------------------------------------------------------------------
1509      External (hook)
1510    -------------------------------------------------------------------------- */
1512   NSTRACE ("ns_frame_raise_lower");
1514   if (raise)
1515     ns_raise_frame (f, YES);
1516   else
1517     ns_lower_frame (f);
1521 static void
1522 ns_frame_rehighlight (struct frame *frame)
1523 /* --------------------------------------------------------------------------
1524      External (hook): called on things like window switching within frame
1525    -------------------------------------------------------------------------- */
1527   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
1528   struct frame *old_highlight = dpyinfo->x_highlight_frame;
1530   NSTRACE ("ns_frame_rehighlight");
1531   if (dpyinfo->x_focus_frame)
1532     {
1533       dpyinfo->x_highlight_frame
1534         = (FRAMEP (FRAME_FOCUS_FRAME (dpyinfo->x_focus_frame))
1535            ? XFRAME (FRAME_FOCUS_FRAME (dpyinfo->x_focus_frame))
1536            : dpyinfo->x_focus_frame);
1537       if (!FRAME_LIVE_P (dpyinfo->x_highlight_frame))
1538         {
1539           fset_focus_frame (dpyinfo->x_focus_frame, Qnil);
1540           dpyinfo->x_highlight_frame = dpyinfo->x_focus_frame;
1541         }
1542     }
1543   else
1544       dpyinfo->x_highlight_frame = 0;
1546   if (dpyinfo->x_highlight_frame &&
1547          dpyinfo->x_highlight_frame != old_highlight)
1548     {
1549       if (old_highlight)
1550         {
1551           x_update_cursor (old_highlight, 1);
1552           x_set_frame_alpha (old_highlight);
1553         }
1554       if (dpyinfo->x_highlight_frame)
1555         {
1556           x_update_cursor (dpyinfo->x_highlight_frame, 1);
1557           x_set_frame_alpha (dpyinfo->x_highlight_frame);
1558         }
1559     }
1563 void
1564 x_make_frame_visible (struct frame *f)
1565 /* --------------------------------------------------------------------------
1566      External: Show the window (X11 semantics)
1567    -------------------------------------------------------------------------- */
1569   NSTRACE ("x_make_frame_visible");
1570   /* XXX: at some points in past this was not needed, as the only place that
1571      called this (frame.c:Fraise_frame ()) also called raise_lower;
1572      if this ends up the case again, comment this out again. */
1573   if (!FRAME_VISIBLE_P (f))
1574     {
1575       EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
1576       NSWindow *window = [view window];
1578       SET_FRAME_VISIBLE (f, 1);
1579       ns_raise_frame (f, ! FRAME_NO_FOCUS_ON_MAP (f));
1581       /* Making a new frame from a fullscreen frame will make the new frame
1582          fullscreen also.  So skip handleFS as this will print an error.  */
1583       if ([view fsIsNative] && f->want_fullscreen == FULLSCREEN_BOTH
1584           && [view isFullscreen])
1585         return;
1587       if (f->want_fullscreen != FULLSCREEN_NONE)
1588         {
1589           block_input ();
1590           [view handleFS];
1591           unblock_input ();
1592         }
1594       /* Making a frame invisible seems to break the parent->child
1595          relationship, so reinstate it. */
1596       if ([window parentWindow] == nil && FRAME_PARENT_FRAME (f) != NULL)
1597         {
1598           NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
1600           block_input ();
1601           [parent addChildWindow: window
1602                          ordered: NSWindowAbove];
1603           unblock_input ();
1605           /* If the parent frame moved while the child frame was
1606              invisible, the child frame's position won't have been
1607              updated.  Make sure it's in the right place now. */
1608           x_set_offset(f, f->left_pos, f->top_pos, 0);
1609         }
1610     }
1614 void
1615 x_make_frame_invisible (struct frame *f)
1616 /* --------------------------------------------------------------------------
1617      External: Hide the window (X11 semantics)
1618    -------------------------------------------------------------------------- */
1620   NSView *view;
1621   NSTRACE ("x_make_frame_invisible");
1622   check_window_system (f);
1623   view = FRAME_NS_VIEW (f);
1624   [[view window] orderOut: NSApp];
1625   SET_FRAME_VISIBLE (f, 0);
1626   SET_FRAME_ICONIFIED (f, 0);
1630 void
1631 x_iconify_frame (struct frame *f)
1632 /* --------------------------------------------------------------------------
1633      External: Iconify window
1634    -------------------------------------------------------------------------- */
1636   NSView *view;
1637   struct ns_display_info *dpyinfo;
1639   NSTRACE ("x_iconify_frame");
1640   check_window_system (f);
1641   view = FRAME_NS_VIEW (f);
1642   dpyinfo = FRAME_DISPLAY_INFO (f);
1644   if (dpyinfo->x_highlight_frame == f)
1645     dpyinfo->x_highlight_frame = 0;
1647   if ([[view window] windowNumber] <= 0)
1648     {
1649       /* the window is still deferred.  Make it very small, bring it
1650          on screen and order it out. */
1651       NSRect s = { { 100, 100}, {0, 0} };
1652       NSRect t;
1653       t = [[view window] frame];
1654       [[view window] setFrame: s display: NO];
1655       [[view window] orderBack: NSApp];
1656       [[view window] orderOut: NSApp];
1657       [[view window] setFrame: t display: NO];
1658     }
1660   /* Processing input while Emacs is being minimized can cause a
1661      crash, so block it for the duration. */
1662   block_input();
1663   [[view window] miniaturize: NSApp];
1664   unblock_input();
1667 /* Free X resources of frame F.  */
1669 void
1670 x_free_frame_resources (struct frame *f)
1672   NSView *view;
1673   struct ns_display_info *dpyinfo;
1674   Mouse_HLInfo *hlinfo;
1676   NSTRACE ("x_free_frame_resources");
1677   check_window_system (f);
1678   view = FRAME_NS_VIEW (f);
1679   dpyinfo = FRAME_DISPLAY_INFO (f);
1680   hlinfo = MOUSE_HL_INFO (f);
1682   [(EmacsView *)view setWindowClosing: YES]; /* may not have been informed */
1684   block_input ();
1686   free_frame_menubar (f);
1687   free_frame_faces (f);
1689   if (f == dpyinfo->x_focus_frame)
1690     dpyinfo->x_focus_frame = 0;
1691   if (f == dpyinfo->x_highlight_frame)
1692     dpyinfo->x_highlight_frame = 0;
1693   if (f == hlinfo->mouse_face_mouse_frame)
1694     reset_mouse_highlight (hlinfo);
1696   if (f->output_data.ns->miniimage != nil)
1697     [f->output_data.ns->miniimage release];
1699   [[view window] close];
1700   [view release];
1702   xfree (f->output_data.ns);
1704   unblock_input ();
1707 void
1708 x_destroy_window (struct frame *f)
1709 /* --------------------------------------------------------------------------
1710      External: Delete the window
1711    -------------------------------------------------------------------------- */
1713   NSTRACE ("x_destroy_window");
1715   /* If this frame has a parent window, detach it as not doing so can
1716      cause a crash in GNUStep. */
1717   if (FRAME_PARENT_FRAME (f) != NULL)
1718     {
1719       NSWindow *child = [FRAME_NS_VIEW (f) window];
1720       NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
1722       [parent removeChildWindow: child];
1723     }
1725   check_window_system (f);
1726   x_free_frame_resources (f);
1727   ns_window_num--;
1731 void
1732 x_set_offset (struct frame *f, int xoff, int yoff, int change_grav)
1733 /* --------------------------------------------------------------------------
1734      External: Position the window
1735    -------------------------------------------------------------------------- */
1737   NSView *view = FRAME_NS_VIEW (f);
1738   NSArray *screens = [NSScreen screens];
1739   NSScreen *fscreen = [screens objectAtIndex: 0];
1740   NSScreen *screen = [[view window] screen];
1742   NSTRACE ("x_set_offset");
1744   block_input ();
1746   f->left_pos = xoff;
1747   f->top_pos = yoff;
1749   if (view != nil && screen && fscreen)
1750     {
1751       f->left_pos = f->size_hint_flags & XNegative
1752         ? [screen visibleFrame].size.width + f->left_pos - FRAME_PIXEL_WIDTH (f)
1753         : f->left_pos;
1754       /* We use visibleFrame here to take menu bar into account.
1755          Ideally we should also adjust left/top with visibleFrame.origin.  */
1757       f->top_pos = f->size_hint_flags & YNegative
1758         ? ([screen visibleFrame].size.height + f->top_pos
1759            - FRAME_PIXEL_HEIGHT (f) - FRAME_NS_TITLEBAR_HEIGHT (f)
1760            - FRAME_TOOLBAR_HEIGHT (f))
1761         : f->top_pos;
1762 #ifdef NS_IMPL_GNUSTEP
1763       if (FRAME_PARENT_FRAME (f) == NULL)
1764         {
1765           if (f->left_pos < 100)
1766             f->left_pos = 100;  /* don't overlap menu */
1767         }
1768 #endif
1769       /* Constrain the setFrameTopLeftPoint so we don't move behind the
1770          menu bar.  */
1771       NSPoint pt = NSMakePoint (SCREENMAXBOUND (f->left_pos
1772                                                 + NS_PARENT_WINDOW_LEFT_POS (f)),
1773                                 SCREENMAXBOUND (NS_PARENT_WINDOW_TOP_POS (f)
1774                                                 - f->top_pos));
1775       NSTRACE_POINT ("setFrameTopLeftPoint", pt);
1776       [[view window] setFrameTopLeftPoint: pt];
1777       f->size_hint_flags &= ~(XNegative|YNegative);
1778     }
1780   unblock_input ();
1784 void
1785 x_set_window_size (struct frame *f,
1786                    bool change_gravity,
1787                    int width,
1788                    int height,
1789                    bool pixelwise)
1790 /* --------------------------------------------------------------------------
1791      Adjust window pixel size based on given character grid size
1792      Impl is a bit more complex than other terms, need to do some
1793      internal clipping.
1794    -------------------------------------------------------------------------- */
1796   EmacsView *view = FRAME_NS_VIEW (f);
1797   NSWindow *window = [view window];
1798   NSRect wr = [window frame];
1799   int pixelwidth, pixelheight;
1800   int orig_height = wr.size.height;
1802   NSTRACE ("x_set_window_size");
1804   if (view == nil)
1805     return;
1807   NSTRACE_RECT ("current", wr);
1808   NSTRACE_MSG ("Width:%d Height:%d Pixelwise:%d", width, height, pixelwise);
1809   NSTRACE_MSG ("Font %d x %d", FRAME_COLUMN_WIDTH (f), FRAME_LINE_HEIGHT (f));
1811   block_input ();
1813   if (pixelwise)
1814     {
1815       pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
1816       pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
1817     }
1818   else
1819     {
1820       pixelwidth =  FRAME_TEXT_COLS_TO_PIXEL_WIDTH   (f, width);
1821       pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, height);
1822     }
1824   wr.size.width = pixelwidth + f->border_width;
1825   wr.size.height = pixelheight;
1826   if (! [view isFullscreen])
1827     wr.size.height += FRAME_NS_TITLEBAR_HEIGHT (f)
1828       + FRAME_TOOLBAR_HEIGHT (f);
1830   /* Do not try to constrain to this screen.  We may have multiple
1831      screens, and want Emacs to span those.  Constraining to screen
1832      prevents that, and that is not nice to the user.  */
1833  if (f->output_data.ns->zooming)
1834    f->output_data.ns->zooming = 0;
1835  else
1836    wr.origin.y += orig_height - wr.size.height;
1838  frame_size_history_add
1839    (f, Qx_set_window_size_1, width, height,
1840     list5 (Fcons (make_number (pixelwidth), make_number (pixelheight)),
1841            Fcons (make_number (wr.size.width), make_number (wr.size.height)),
1842            make_number (f->border_width),
1843            make_number (FRAME_NS_TITLEBAR_HEIGHT (f)),
1844            make_number (FRAME_TOOLBAR_HEIGHT (f))));
1846   [window setFrame: wr display: YES];
1848   [view updateFrameSize: NO];
1849   unblock_input ();
1852 #ifdef NS_IMPL_COCOA
1853 void
1854 x_set_undecorated (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1855 /* --------------------------------------------------------------------------
1856      Set frame F's `undecorated' parameter.  If non-nil, F's window-system
1857      window is drawn without decorations, title, minimize/maximize boxes
1858      and external borders.  This usually means that the window cannot be
1859      dragged, resized, iconified, maximized or deleted with the mouse.  If
1860      nil, draw the frame with all the elements listed above unless these
1861      have been suspended via window manager settings.
1863      GNUStep cannot change an existing window's style.
1864    -------------------------------------------------------------------------- */
1866   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
1867   NSWindow *window = [view window];
1869   NSTRACE ("x_set_undecorated");
1871   if (!EQ (new_value, old_value))
1872     {
1873       block_input ();
1875       if (NILP (new_value))
1876         {
1877           FRAME_UNDECORATED (f) = false;
1878           [window setStyleMask: ((window.styleMask | FRAME_DECORATED_FLAGS)
1879                                   ^ FRAME_UNDECORATED_FLAGS)];
1881           [view createToolbar: f];
1882         }
1883       else
1884         {
1885           [window setToolbar: nil];
1886           /* Do I need to release the toolbar here? */
1888           FRAME_UNDECORATED (f) = true;
1889           [window setStyleMask: ((window.styleMask | FRAME_UNDECORATED_FLAGS)
1890                                  ^ FRAME_DECORATED_FLAGS)];
1891         }
1893       /* At this point it seems we don't have an active NSResponder,
1894          so some key presses (TAB) are swallowed by the system. */
1895       [window makeFirstResponder: view];
1897       [view updateFrameSize: NO];
1898       unblock_input ();
1899     }
1901 #endif /* NS_IMPL_COCOA */
1903 void
1904 x_set_parent_frame (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1905 /* --------------------------------------------------------------------------
1906      Set frame F's `parent-frame' parameter.  If non-nil, make F a child
1907      frame of the frame specified by that parameter.  Technically, this
1908      makes F's window-system window a child window of the parent frame's
1909      window-system window.  If nil, make F's window-system window a
1910      top-level window--a child of its display's root window.
1912      A child frame's `left' and `top' parameters specify positions
1913      relative to the top-left corner of its parent frame's native
1914      rectangle.  On macOS moving a parent frame moves all its child
1915      frames too, keeping their position relative to the parent
1916      unaltered.  When a parent frame is iconified or made invisible, its
1917      child frames are made invisible.  When a parent frame is deleted,
1918      its child frames are deleted too.
1920      Whether a child frame has a tool bar may be window-system or window
1921      manager dependent.  It's advisable to disable it via the frame
1922      parameter settings.
1924      Some window managers may not honor this parameter.
1925    -------------------------------------------------------------------------- */
1927   struct frame *p = NULL;
1928   NSWindow *parent, *child;
1930   NSTRACE ("x_set_parent_frame");
1932   if (!NILP (new_value)
1933       && (!FRAMEP (new_value)
1934           || !FRAME_LIVE_P (p = XFRAME (new_value))
1935           || !FRAME_X_P (p)))
1936     {
1937       store_frame_param (f, Qparent_frame, old_value);
1938       error ("Invalid specification of `parent-frame'");
1939     }
1941   if (p != FRAME_PARENT_FRAME (f))
1942     {
1943       parent = [FRAME_NS_VIEW (p) window];
1944       child = [FRAME_NS_VIEW (f) window];
1946       block_input ();
1947       [parent addChildWindow: child
1948                      ordered: NSWindowAbove];
1949       unblock_input ();
1951       fset_parent_frame (f, new_value);
1952     }
1955 void
1956 x_set_no_focus_on_map (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1957 /* Set frame F's `no-focus-on-map' parameter which, if non-nil, means
1958  * that F's window-system window does not want to receive input focus
1959  * when it is mapped.  (A frame's window is mapped when the frame is
1960  * displayed for the first time and when the frame changes its state
1961  * from `iconified' or `invisible' to `visible'.)
1963  * Some window managers may not honor this parameter. */
1965   NSTRACE ("x_set_no_focus_on_map");
1967   if (!EQ (new_value, old_value))
1968     {
1969       FRAME_NO_FOCUS_ON_MAP (f) = !NILP (new_value);
1970     }
1973 void
1974 x_set_no_accept_focus (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1975 /*  Set frame F's `no-accept-focus' parameter which, if non-nil, hints
1976  * that F's window-system window does not want to receive input focus
1977  * via mouse clicks or by moving the mouse into it.
1979  * If non-nil, this may have the unwanted side-effect that a user cannot
1980  * scroll a non-selected frame with the mouse.
1982  * Some window managers may not honor this parameter. */
1984   NSTRACE ("x_set_no_accept_focus");
1986   if (!EQ (new_value, old_value))
1987     FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value);
1990 void
1991 x_set_z_group (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1992 /* Set frame F's `z-group' parameter.  If `above', F's window-system
1993    window is displayed above all windows that do not have the `above'
1994    property set.  If nil, F's window is shown below all windows that
1995    have the `above' property set and above all windows that have the
1996    `below' property set.  If `below', F's window is displayed below
1997    all windows that do.
1999    Some window managers may not honor this parameter. */
2001   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2002   NSWindow *window = [view window];
2004   NSTRACE ("x_set_z_group");
2006   if (NILP (new_value))
2007     {
2008       window.level = NSNormalWindowLevel;
2009       FRAME_Z_GROUP (f) = z_group_none;
2010     }
2011   else if (EQ (new_value, Qabove))
2012     {
2013       window.level = NSNormalWindowLevel + 1;
2014       FRAME_Z_GROUP (f) = z_group_above;
2015     }
2016   else if (EQ (new_value, Qabove_suspended))
2017     {
2018       /* Not sure what level this should be. */
2019       window.level = NSNormalWindowLevel + 1;
2020       FRAME_Z_GROUP (f) = z_group_above_suspended;
2021     }
2022   else if (EQ (new_value, Qbelow))
2023     {
2024       window.level = NSNormalWindowLevel - 1;
2025       FRAME_Z_GROUP (f) = z_group_below;
2026     }
2027   else
2028     error ("Invalid z-group specification");
2031 #ifdef NS_IMPL_COCOA
2032 void
2033 ns_set_appearance (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
2035 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
2036   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2037   NSWindow *window = [view window];
2039   NSTRACE ("ns_set_appearance");
2041 #ifndef NSAppKitVersionNumber10_10
2042 #define NSAppKitVersionNumber10_10 1343
2043 #endif
2045   if (NSAppKitVersionNumber < NSAppKitVersionNumber10_10)
2046     return;
2048   if (EQ (new_value, Qdark))
2049     {
2050       window.appearance = [NSAppearance
2051                             appearanceNamed: NSAppearanceNameVibrantDark];
2052       FRAME_NS_APPEARANCE (f) = ns_appearance_vibrant_dark;
2053     }
2054   else
2055     {
2056       window.appearance = [NSAppearance
2057                             appearanceNamed: NSAppearanceNameAqua];
2058       FRAME_NS_APPEARANCE (f) = ns_appearance_aqua;
2059     }
2060 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 */
2063 void
2064 ns_set_transparent_titlebar (struct frame *f, Lisp_Object new_value,
2065                              Lisp_Object old_value)
2067 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
2068   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2069   NSWindow *window = [view window];
2071   NSTRACE ("ns_set_transparent_titlebar");
2073   if ([window respondsToSelector: @selector(titlebarAppearsTransparent)]
2074       && !EQ (new_value, old_value))
2075     {
2076       window.titlebarAppearsTransparent = !NILP (new_value);
2077       FRAME_NS_TRANSPARENT_TITLEBAR (f) = !NILP (new_value);
2078     }
2079 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 */
2081 #endif /* NS_IMPL_COCOA */
2083 static void
2084 ns_fullscreen_hook (struct frame *f)
2086   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2088   NSTRACE ("ns_fullscreen_hook");
2090   if (!FRAME_VISIBLE_P (f))
2091     return;
2093    if (! [view fsIsNative] && f->want_fullscreen == FULLSCREEN_BOTH)
2094     {
2095       /* Old style fs don't initiate correctly if created from
2096          init/default-frame alist, so use a timer (not nice...).
2097       */
2098       [NSTimer scheduledTimerWithTimeInterval: 0.5 target: view
2099                                      selector: @selector (handleFS)
2100                                      userInfo: nil repeats: NO];
2101       return;
2102     }
2104   block_input ();
2105   [view handleFS];
2106   unblock_input ();
2109 /* ==========================================================================
2111     Color management
2113    ========================================================================== */
2116 NSColor *
2117 ns_lookup_indexed_color (unsigned long idx, struct frame *f)
2119   struct ns_color_table *color_table = FRAME_DISPLAY_INFO (f)->color_table;
2120   if (idx < 1 || idx >= color_table->avail)
2121     return nil;
2122   return color_table->colors[idx];
2126 unsigned long
2127 ns_index_color (NSColor *color, struct frame *f)
2129   struct ns_color_table *color_table = FRAME_DISPLAY_INFO (f)->color_table;
2130   ptrdiff_t idx;
2131   ptrdiff_t i;
2133   if (!color_table->colors)
2134     {
2135       color_table->size = NS_COLOR_CAPACITY;
2136       color_table->avail = 1; /* skip idx=0 as marker */
2137       color_table->colors = xmalloc (color_table->size * sizeof (NSColor *));
2138       color_table->colors[0] = nil;
2139       color_table->empty_indices = [[NSMutableSet alloc] init];
2140     }
2142   /* Do we already have this color?  */
2143   for (i = 1; i < color_table->avail; i++)
2144     if (color_table->colors[i] && [color_table->colors[i] isEqual: color])
2145       return i;
2147   if ([color_table->empty_indices count] > 0)
2148     {
2149       NSNumber *index = [color_table->empty_indices anyObject];
2150       [color_table->empty_indices removeObject: index];
2151       idx = [index unsignedLongValue];
2152     }
2153   else
2154     {
2155       if (color_table->avail == color_table->size)
2156         color_table->colors =
2157           xpalloc (color_table->colors, &color_table->size, 1,
2158                    min (ULONG_MAX, PTRDIFF_MAX), sizeof *color_table->colors);
2159       idx = color_table->avail++;
2160     }
2162   color_table->colors[idx] = color;
2163   [color retain];
2164 /*fprintf(stderr, "color_table: allocated %d\n",idx);*/
2165   return idx;
2169 static int
2170 ns_get_color (const char *name, NSColor **col)
2171 /* --------------------------------------------------------------------------
2172      Parse a color name
2173    -------------------------------------------------------------------------- */
2174 /* On *Step, we attempt to mimic the X11 platform here, down to installing an
2175    X11 rgb.txt-compatible color list in Emacs.clr (see ns_term_init()).
2176    See: http://thread.gmane.org/gmane.emacs.devel/113050/focus=113272). */
2178   NSColor *new = nil;
2179   static char hex[20];
2180   int scaling = 0;
2181   float r = -1.0, g, b;
2182   NSString *nsname = [NSString stringWithUTF8String: name];
2184   NSTRACE ("ns_get_color(%s, **)", name);
2186   block_input ();
2188   if ([nsname isEqualToString: @"ns_selection_bg_color"])
2189     {
2190 #ifdef NS_IMPL_COCOA
2191       NSString *defname = [[NSUserDefaults standardUserDefaults]
2192                             stringForKey: @"AppleHighlightColor"];
2193       if (defname != nil)
2194         nsname = defname;
2195       else
2196 #endif
2197       if ((new = [NSColor selectedTextBackgroundColor]) != nil)
2198         {
2199           *col = [new colorUsingDefaultColorSpace];
2200           unblock_input ();
2201           return 0;
2202         }
2203       else
2204         nsname = NS_SELECTION_BG_COLOR_DEFAULT;
2206       name = [nsname UTF8String];
2207     }
2208   else if ([nsname isEqualToString: @"ns_selection_fg_color"])
2209     {
2210       /* NOTE: macOS applications normally don't set foreground
2211          selection, but text may be unreadable if we don't.
2212       */
2213       if ((new = [NSColor selectedTextColor]) != nil)
2214         {
2215           *col = [new colorUsingDefaultColorSpace];
2216           unblock_input ();
2217           return 0;
2218         }
2220       nsname = NS_SELECTION_FG_COLOR_DEFAULT;
2221       name = [nsname UTF8String];
2222     }
2224   /* First, check for some sort of numeric specification. */
2225   hex[0] = '\0';
2227   if (name[0] == '0' || name[0] == '1' || name[0] == '.')  /* RGB decimal */
2228     {
2229       NSScanner *scanner = [NSScanner scannerWithString: nsname];
2230       [scanner scanFloat: &r];
2231       [scanner scanFloat: &g];
2232       [scanner scanFloat: &b];
2233     }
2234   else if (!strncmp(name, "rgb:", 4))  /* A newer X11 format -- rgb:r/g/b */
2235     scaling = (snprintf (hex, sizeof hex, "%s", name + 4) - 2) / 3;
2236   else if (name[0] == '#')        /* An old X11 format; convert to newer */
2237     {
2238       int len = (strlen(name) - 1);
2239       int start = (len % 3 == 0) ? 1 : len / 4 + 1;
2240       int i;
2241       scaling = strlen(name+start) / 3;
2242       for (i = 0; i < 3; i++)
2243         sprintf (hex + i * (scaling + 1), "%.*s/", scaling,
2244                  name + start + i * scaling);
2245       hex[3 * (scaling + 1) - 1] = '\0';
2246     }
2248   if (hex[0])
2249     {
2250       unsigned int rr, gg, bb;
2251       float fscale = scaling == 4 ? 65535.0 : (scaling == 2 ? 255.0 : 15.0);
2252       if (sscanf (hex, "%x/%x/%x", &rr, &gg, &bb))
2253         {
2254           r = rr / fscale;
2255           g = gg / fscale;
2256           b = bb / fscale;
2257         }
2258     }
2260   if (r >= 0.0F)
2261     {
2262       *col = [NSColor colorForEmacsRed: r green: g blue: b alpha: 1.0];
2263       unblock_input ();
2264       return 0;
2265     }
2267   /* Otherwise, color is expected to be from a list */
2268   {
2269     NSEnumerator *lenum, *cenum;
2270     NSString *name;
2271     NSColorList *clist;
2273 #ifdef NS_IMPL_GNUSTEP
2274     /* XXX: who is wrong, the requestor or the implementation? */
2275     if ([nsname compare: @"Highlight" options: NSCaseInsensitiveSearch]
2276         == NSOrderedSame)
2277       nsname = @"highlightColor";
2278 #endif
2280     lenum = [[NSColorList availableColorLists] objectEnumerator];
2281     while ( (clist = [lenum nextObject]) && new == nil)
2282       {
2283         cenum = [[clist allKeys] objectEnumerator];
2284         while ( (name = [cenum nextObject]) && new == nil )
2285           {
2286             if ([name compare: nsname
2287                       options: NSCaseInsensitiveSearch] == NSOrderedSame )
2288               new = [clist colorWithKey: name];
2289           }
2290       }
2291   }
2293   if (new)
2294     *col = [new colorUsingDefaultColorSpace];
2295   unblock_input ();
2296   return new ? 0 : 1;
2301 ns_lisp_to_color (Lisp_Object color, NSColor **col)
2302 /* --------------------------------------------------------------------------
2303      Convert a Lisp string object to a NS color
2304    -------------------------------------------------------------------------- */
2306   NSTRACE ("ns_lisp_to_color");
2307   if (STRINGP (color))
2308     return ns_get_color (SSDATA (color), col);
2309   else if (SYMBOLP (color))
2310     return ns_get_color (SSDATA (SYMBOL_NAME (color)), col);
2311   return 1;
2315 void
2316 ns_query_color(void *col, XColor *color_def, int setPixel)
2317 /* --------------------------------------------------------------------------
2318          Get ARGB values out of NSColor col and put them into color_def.
2319          If setPixel, set the pixel to a concatenated version.
2320          and set color_def pixel to the resulting index.
2321    -------------------------------------------------------------------------- */
2323   EmacsCGFloat r, g, b, a;
2325   [((NSColor *)col) getRed: &r green: &g blue: &b alpha: &a];
2326   color_def->red   = r * 65535;
2327   color_def->green = g * 65535;
2328   color_def->blue  = b * 65535;
2330   if (setPixel == YES)
2331     color_def->pixel
2332       = ARGB_TO_ULONG((int)(a*255),
2333                       (int)(r*255), (int)(g*255), (int)(b*255));
2337 bool
2338 ns_defined_color (struct frame *f,
2339                   const char *name,
2340                   XColor *color_def,
2341                   bool alloc,
2342                   bool makeIndex)
2343 /* --------------------------------------------------------------------------
2344          Return true if named color found, and set color_def rgb accordingly.
2345          If makeIndex and alloc are nonzero put the color in the color_table,
2346          and set color_def pixel to the resulting index.
2347          If makeIndex is zero, set color_def pixel to ARGB.
2348          Return false if not found
2349    -------------------------------------------------------------------------- */
2351   NSColor *col;
2352   NSTRACE_WHEN (NSTRACE_GROUP_COLOR, "ns_defined_color");
2354   block_input ();
2355   if (ns_get_color (name, &col) != 0) /* Color not found  */
2356     {
2357       unblock_input ();
2358       return 0;
2359     }
2360   if (makeIndex && alloc)
2361     color_def->pixel = ns_index_color (col, f);
2362   ns_query_color (col, color_def, !makeIndex);
2363   unblock_input ();
2364   return 1;
2368 void
2369 x_set_frame_alpha (struct frame *f)
2370 /* --------------------------------------------------------------------------
2371      change the entire-frame transparency
2372    -------------------------------------------------------------------------- */
2374   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
2375   double alpha = 1.0;
2376   double alpha_min = 1.0;
2378   NSTRACE ("x_set_frame_alpha");
2380   if (dpyinfo->x_highlight_frame == f)
2381     alpha = f->alpha[0];
2382   else
2383     alpha = f->alpha[1];
2385   if (FLOATP (Vframe_alpha_lower_limit))
2386     alpha_min = XFLOAT_DATA (Vframe_alpha_lower_limit);
2387   else if (INTEGERP (Vframe_alpha_lower_limit))
2388     alpha_min = (XINT (Vframe_alpha_lower_limit)) / 100.0;
2390   if (alpha < 0.0)
2391     return;
2392   else if (1.0 < alpha)
2393     alpha = 1.0;
2394   else if (0.0 <= alpha && alpha < alpha_min && alpha_min <= 1.0)
2395     alpha = alpha_min;
2397 #ifdef NS_IMPL_COCOA
2398   {
2399     EmacsView *view = FRAME_NS_VIEW (f);
2400   [[view window] setAlphaValue: alpha];
2401   }
2402 #endif
2406 /* ==========================================================================
2408     Mouse handling
2410    ========================================================================== */
2413 void
2414 frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y)
2415 /* --------------------------------------------------------------------------
2416      Programmatically reposition mouse pointer in pixel coordinates
2417    -------------------------------------------------------------------------- */
2419   NSTRACE ("frame_set_mouse_pixel_position");
2421   /* FIXME: what about GNUstep? */
2422 #ifdef NS_IMPL_COCOA
2423   CGPoint mouse_pos =
2424     CGPointMake(f->left_pos + pix_x,
2425                 f->top_pos + pix_y +
2426                 FRAME_NS_TITLEBAR_HEIGHT(f) + FRAME_TOOLBAR_HEIGHT(f));
2427   CGWarpMouseCursorPosition (mouse_pos);
2428 #endif
2431 static int
2432 note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y)
2433 /*   ------------------------------------------------------------------------
2434      Called by EmacsView on mouseMovement events.  Passes on
2435      to emacs mainstream code if we moved off of a rect of interest
2436      known as last_mouse_glyph.
2437      ------------------------------------------------------------------------ */
2439   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
2440   NSRect *r;
2442 //  NSTRACE ("note_mouse_movement");
2444   dpyinfo->last_mouse_motion_frame = frame;
2445   r = &dpyinfo->last_mouse_glyph;
2447   /* Note, this doesn't get called for enter/leave, since we don't have a
2448      position.  Those are taken care of in the corresponding NSView methods. */
2450   /* has movement gone beyond last rect we were tracking? */
2451   if (x < r->origin.x || x >= r->origin.x + r->size.width
2452       || y < r->origin.y || y >= r->origin.y + r->size.height)
2453     {
2454       ns_update_begin (frame);
2455       frame->mouse_moved = 1;
2456       note_mouse_highlight (frame, x, y);
2457       remember_mouse_glyph (frame, x, y, r);
2458       ns_update_end (frame);
2459       return 1;
2460     }
2462   return 0;
2466 static void
2467 ns_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window,
2468                    enum scroll_bar_part *part, Lisp_Object *x, Lisp_Object *y,
2469                    Time *time)
2470 /* --------------------------------------------------------------------------
2471     External (hook): inform emacs about mouse position and hit parts.
2472     If a scrollbar is being dragged, set bar_window, part, x, y, time.
2473     x & y should be position in the scrollbar (the whole bar, not the handle)
2474     and length of scrollbar respectively
2475    -------------------------------------------------------------------------- */
2477   id view;
2478   NSPoint position;
2479   Lisp_Object frame, tail;
2480   struct frame *f;
2481   struct ns_display_info *dpyinfo;
2483   NSTRACE ("ns_mouse_position");
2485   if (*fp == NULL)
2486     {
2487       fprintf (stderr, "Warning: ns_mouse_position () called with null *fp.\n");
2488       return;
2489     }
2491   dpyinfo = FRAME_DISPLAY_INFO (*fp);
2493   block_input ();
2495   /* Clear the mouse-moved flag for every frame on this display.  */
2496   FOR_EACH_FRAME (tail, frame)
2497     if (FRAME_NS_P (XFRAME (frame))
2498         && FRAME_NS_DISPLAY (XFRAME (frame)) == FRAME_NS_DISPLAY (*fp))
2499       XFRAME (frame)->mouse_moved = 0;
2501   dpyinfo->last_mouse_scroll_bar = nil;
2502   if (dpyinfo->last_mouse_frame
2503       && FRAME_LIVE_P (dpyinfo->last_mouse_frame))
2504     f = dpyinfo->last_mouse_frame;
2505   else
2506     f = dpyinfo->x_focus_frame ? dpyinfo->x_focus_frame : SELECTED_FRAME ();
2508   if (f && FRAME_NS_P (f))
2509     {
2510       view = FRAME_NS_VIEW (*fp);
2512       position = [[view window] mouseLocationOutsideOfEventStream];
2513       position = [view convertPoint: position fromView: nil];
2514       remember_mouse_glyph (f, position.x, position.y,
2515                             &dpyinfo->last_mouse_glyph);
2516       NSTRACE_POINT ("position", position);
2518       if (bar_window) *bar_window = Qnil;
2519       if (part) *part = scroll_bar_above_handle;
2521       if (x) XSETINT (*x, lrint (position.x));
2522       if (y) XSETINT (*y, lrint (position.y));
2523       if (time)
2524         *time = dpyinfo->last_mouse_movement_time;
2525       *fp = f;
2526     }
2528   unblock_input ();
2532 static void
2533 ns_frame_up_to_date (struct frame *f)
2534 /* --------------------------------------------------------------------------
2535     External (hook): Fix up mouse highlighting right after a full update.
2536     Can't use FRAME_MOUSE_UPDATE due to ns_frame_begin and ns_frame_end calls.
2537    -------------------------------------------------------------------------- */
2539   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_frame_up_to_date");
2541   if (FRAME_NS_P (f))
2542     {
2543       Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
2544       if (f == hlinfo->mouse_face_mouse_frame)
2545         {
2546           block_input ();
2547           ns_update_begin(f);
2548           note_mouse_highlight (hlinfo->mouse_face_mouse_frame,
2549                                 hlinfo->mouse_face_mouse_x,
2550                                 hlinfo->mouse_face_mouse_y);
2551           ns_update_end(f);
2552           unblock_input ();
2553         }
2554     }
2558 static void
2559 ns_define_frame_cursor (struct frame *f, Cursor cursor)
2560 /* --------------------------------------------------------------------------
2561     External (RIF): set frame mouse pointer type.
2562    -------------------------------------------------------------------------- */
2564   NSTRACE ("ns_define_frame_cursor");
2565   if (FRAME_POINTER_TYPE (f) != cursor)
2566     {
2567       EmacsView *view = FRAME_NS_VIEW (f);
2568       FRAME_POINTER_TYPE (f) = cursor;
2569       [[view window] invalidateCursorRectsForView: view];
2570       /* Redisplay assumes this function also draws the changed frame
2571          cursor, but this function doesn't, so do it explicitly.  */
2572       x_update_cursor (f, 1);
2573     }
2578 /* ==========================================================================
2580     Keyboard handling
2582    ========================================================================== */
2585 static unsigned
2586 ns_convert_key (unsigned code)
2587 /* --------------------------------------------------------------------------
2588     Internal call used by NSView-keyDown.
2589    -------------------------------------------------------------------------- */
2591   const unsigned last_keysym = ARRAYELTS (convert_ns_to_X_keysym);
2592   unsigned keysym;
2593   /* An array would be faster, but less easy to read. */
2594   for (keysym = 0; keysym < last_keysym; keysym += 2)
2595     if (code == convert_ns_to_X_keysym[keysym])
2596       return 0xFF00 | convert_ns_to_X_keysym[keysym+1];
2597   return 0;
2598 /* if decide to use keyCode and Carbon table, use this line:
2599      return code > 0xff ? 0 : 0xFF00 | ns_keycode_to_xkeysym_table[code]; */
2603 char *
2604 x_get_keysym_name (int keysym)
2605 /* --------------------------------------------------------------------------
2606     Called by keyboard.c.  Not sure if the return val is important, except
2607     that it be unique.
2608    -------------------------------------------------------------------------- */
2610   static char value[16];
2611   NSTRACE ("x_get_keysym_name");
2612   sprintf (value, "%d", keysym);
2613   return value;
2618 /* ==========================================================================
2620     Block drawing operations
2622    ========================================================================== */
2625 static void
2626 ns_redraw_scroll_bars (struct frame *f)
2628   int i;
2629   id view;
2630   NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews];
2631   NSTRACE ("ns_redraw_scroll_bars");
2632   for (i =[subviews count]-1; i >= 0; i--)
2633     {
2634       view = [subviews objectAtIndex: i];
2635       if (![view isKindOfClass: [EmacsScroller class]]) continue;
2636       [view display];
2637     }
2641 void
2642 ns_clear_frame (struct frame *f)
2643 /* --------------------------------------------------------------------------
2644       External (hook): Erase the entire frame
2645    -------------------------------------------------------------------------- */
2647   NSView *view = FRAME_NS_VIEW (f);
2648   NSRect r;
2650   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_clear_frame");
2652  /* comes on initial frame because we have
2653     after-make-frame-functions = select-frame */
2654  if (!FRAME_DEFAULT_FACE (f))
2655    return;
2657   mark_window_cursors_off (XWINDOW (FRAME_ROOT_WINDOW (f)));
2659   r = [view bounds];
2661   block_input ();
2662   ns_focus (f, &r, 1);
2663   [ns_lookup_indexed_color (NS_FACE_BACKGROUND
2664                             (FACE_FROM_ID (f, DEFAULT_FACE_ID)), f) set];
2665   NSRectFill (r);
2666   ns_unfocus (f);
2668   /* as of 2006/11 or so this is now needed */
2669   ns_redraw_scroll_bars (f);
2670   unblock_input ();
2674 static void
2675 ns_clear_frame_area (struct frame *f, int x, int y, int width, int height)
2676 /* --------------------------------------------------------------------------
2677     External (RIF):  Clear section of frame
2678    -------------------------------------------------------------------------- */
2680   NSRect r = NSMakeRect (x, y, width, height);
2681   NSView *view = FRAME_NS_VIEW (f);
2682   struct face *face = FRAME_DEFAULT_FACE (f);
2684   if (!view || !face)
2685     return;
2687   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_clear_frame_area");
2689   r = NSIntersectionRect (r, [view frame]);
2690   ns_focus (f, &r, 1);
2691   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), f) set];
2693   NSRectFill (r);
2695   ns_unfocus (f);
2696   return;
2699 static void
2700 ns_copy_bits (struct frame *f, NSRect src, NSRect dest)
2702   NSTRACE ("ns_copy_bits");
2704   if (FRAME_NS_VIEW (f))
2705     {
2706       hide_bell();              // Ensure the bell image isn't scrolled.
2708       ns_focus (f, &dest, 1);
2709       [FRAME_NS_VIEW (f) scrollRect: src
2710                                  by: NSMakeSize (dest.origin.x - src.origin.x,
2711                                                  dest.origin.y - src.origin.y)];
2712       ns_unfocus (f);
2713     }
2716 static void
2717 ns_scroll_run (struct window *w, struct run *run)
2718 /* --------------------------------------------------------------------------
2719     External (RIF):  Insert or delete n lines at line vpos
2720    -------------------------------------------------------------------------- */
2722   struct frame *f = XFRAME (w->frame);
2723   int x, y, width, height, from_y, to_y, bottom_y;
2725   NSTRACE ("ns_scroll_run");
2727   /* begin copy from other terms */
2728   /* Get frame-relative bounding box of the text display area of W,
2729      without mode lines.  Include in this box the left and right
2730      fringe of W.  */
2731   window_box (w, ANY_AREA, &x, &y, &width, &height);
2733   from_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->current_y);
2734   to_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->desired_y);
2735   bottom_y = y + height;
2737   if (to_y < from_y)
2738     {
2739       /* Scrolling up.  Make sure we don't copy part of the mode
2740          line at the bottom.  */
2741       if (from_y + run->height > bottom_y)
2742         height = bottom_y - from_y;
2743       else
2744         height = run->height;
2745     }
2746   else
2747     {
2748       /* Scrolling down.  Make sure we don't copy over the mode line.
2749          at the bottom.  */
2750       if (to_y + run->height > bottom_y)
2751         height = bottom_y - to_y;
2752       else
2753         height = run->height;
2754     }
2755   /* end copy from other terms */
2757   if (height == 0)
2758       return;
2760   block_input ();
2762   x_clear_cursor (w);
2764   {
2765     NSRect srcRect = NSMakeRect (x, from_y, width, height);
2766     NSRect dstRect = NSMakeRect (x, to_y, width, height);
2768     ns_copy_bits (f, srcRect , dstRect);
2769   }
2771   unblock_input ();
2775 static void
2776 ns_after_update_window_line (struct window *w, struct glyph_row *desired_row)
2777 /* --------------------------------------------------------------------------
2778     External (RIF): preparatory to fringe update after text was updated
2779    -------------------------------------------------------------------------- */
2781   struct frame *f;
2782   int width, height;
2784   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_after_update_window_line");
2786   /* begin copy from other terms */
2787   eassert (w);
2789   if (!desired_row->mode_line_p && !w->pseudo_window_p)
2790     desired_row->redraw_fringe_bitmaps_p = 1;
2792   /* When a window has disappeared, make sure that no rest of
2793      full-width rows stays visible in the internal border.  */
2794   if (windows_or_buffers_changed
2795       && desired_row->full_width_p
2796       && (f = XFRAME (w->frame),
2797           width = FRAME_INTERNAL_BORDER_WIDTH (f),
2798           width != 0)
2799       && (height = desired_row->visible_height,
2800           height > 0))
2801     {
2802       int y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, desired_row->y));
2804       block_input ();
2805       ns_clear_frame_area (f, 0, y, width, height);
2806       ns_clear_frame_area (f,
2807                            FRAME_PIXEL_WIDTH (f) - width,
2808                            y, width, height);
2809       unblock_input ();
2810     }
2814 static void
2815 ns_shift_glyphs_for_insert (struct frame *f,
2816                            int x, int y, int width, int height,
2817                            int shift_by)
2818 /* --------------------------------------------------------------------------
2819     External (RIF): copy an area horizontally, don't worry about clearing src
2820    -------------------------------------------------------------------------- */
2822   NSRect srcRect = NSMakeRect (x, y, width, height);
2823   NSRect dstRect = NSMakeRect (x+shift_by, y, width, height);
2825   NSTRACE ("ns_shift_glyphs_for_insert");
2827   ns_copy_bits (f, srcRect, dstRect);
2832 /* ==========================================================================
2834     Character encoding and metrics
2836    ========================================================================== */
2839 static void
2840 ns_compute_glyph_string_overhangs (struct glyph_string *s)
2841 /* --------------------------------------------------------------------------
2842      External (RIF); compute left/right overhang of whole string and set in s
2843    -------------------------------------------------------------------------- */
2845   struct font *font = s->font;
2847   if (s->char2b)
2848     {
2849       struct font_metrics metrics;
2850       unsigned int codes[2];
2851       codes[0] = *(s->char2b);
2852       codes[1] = *(s->char2b + s->nchars - 1);
2854       font->driver->text_extents (font, codes, 2, &metrics);
2855       s->left_overhang = -metrics.lbearing;
2856       s->right_overhang
2857         = metrics.rbearing > metrics.width
2858         ? metrics.rbearing - metrics.width : 0;
2859     }
2860   else
2861     {
2862       s->left_overhang = 0;
2863       if (EQ (font->driver->type, Qns))
2864         s->right_overhang = ((struct nsfont_info *)font)->ital ?
2865           FONT_HEIGHT (font) * 0.2 : 0;
2866       else
2867         s->right_overhang = 0;
2868     }
2873 /* ==========================================================================
2875     Fringe and cursor drawing
2877    ========================================================================== */
2880 extern int max_used_fringe_bitmap;
2881 static void
2882 ns_draw_fringe_bitmap (struct window *w, struct glyph_row *row,
2883                       struct draw_fringe_bitmap_params *p)
2884 /* --------------------------------------------------------------------------
2885     External (RIF); fringe-related
2886    -------------------------------------------------------------------------- */
2888   /* Fringe bitmaps comes in two variants, normal and periodic.  A
2889      periodic bitmap is used to create a continuous pattern.  Since a
2890      bitmap is rendered one text line at a time, the start offset (dh)
2891      of the bitmap varies.  Concretely, this is used for the empty
2892      line indicator.
2894      For a bitmap, "h + dh" is the full height and is always
2895      invariant.  For a normal bitmap "dh" is zero.
2897      For example, when the period is three and the full height is 72
2898      the following combinations exists:
2900        h=72 dh=0
2901        h=71 dh=1
2902        h=70 dh=2 */
2904   struct frame *f = XFRAME (WINDOW_FRAME (w));
2905   struct face *face = p->face;
2906   static EmacsImage **bimgs = NULL;
2907   static int nBimgs = 0;
2909   NSTRACE_WHEN (NSTRACE_GROUP_FRINGE, "ns_draw_fringe_bitmap");
2910   NSTRACE_MSG ("which:%d cursor:%d overlay:%d width:%d height:%d period:%d",
2911                p->which, p->cursor_p, p->overlay_p, p->wd, p->h, p->dh);
2913   /* grow bimgs if needed */
2914   if (nBimgs < max_used_fringe_bitmap)
2915     {
2916       bimgs = xrealloc (bimgs, max_used_fringe_bitmap * sizeof *bimgs);
2917       memset (bimgs + nBimgs, 0,
2918               (max_used_fringe_bitmap - nBimgs) * sizeof *bimgs);
2919       nBimgs = max_used_fringe_bitmap;
2920     }
2922   /* Must clip because of partially visible lines.  */
2923   ns_clip_to_row (w, row, ANY_AREA, YES);
2925   if (!p->overlay_p)
2926     {
2927       int bx = p->bx, by = p->by, nx = p->nx, ny = p->ny;
2929       if (bx >= 0 && nx > 0)
2930         {
2931           NSRect r = NSMakeRect (bx, by, nx, ny);
2932           NSRectClip (r);
2933           [ns_lookup_indexed_color (face->background, f) set];
2934           NSRectFill (r);
2935         }
2936     }
2938   if (p->which)
2939     {
2940       NSRect r = NSMakeRect (p->x, p->y, p->wd, p->h);
2941       EmacsImage *img = bimgs[p->which - 1];
2943       if (!img)
2944         {
2945           // Note: For "periodic" images, allocate one EmacsImage for
2946           // the base image, and use it for all dh:s.
2947           unsigned short *bits = p->bits;
2948           int full_height = p->h + p->dh;
2949           int i;
2950           unsigned char *cbits = xmalloc (full_height);
2952           for (i = 0; i < full_height; i++)
2953             cbits[i] = bits[i];
2954           img = [[EmacsImage alloc] initFromXBM: cbits width: 8
2955                                          height: full_height
2956                                              fg: 0 bg: 0];
2957           bimgs[p->which - 1] = img;
2958           xfree (cbits);
2959         }
2961       NSTRACE_RECT ("r", r);
2963       NSRectClip (r);
2964       /* Since we composite the bitmap instead of just blitting it, we need
2965          to erase the whole background. */
2966       [ns_lookup_indexed_color(face->background, f) set];
2967       NSRectFill (r);
2969       {
2970         NSColor *bm_color;
2971         if (!p->cursor_p)
2972           bm_color = ns_lookup_indexed_color(face->foreground, f);
2973         else if (p->overlay_p)
2974           bm_color = ns_lookup_indexed_color(face->background, f);
2975         else
2976           bm_color = f->output_data.ns->cursor_color;
2977         [img setXBMColor: bm_color];
2978       }
2980 #ifdef NS_IMPL_COCOA
2981       // Note: For periodic images, the full image height is "h + hd".
2982       // By using the height h, a suitable part of the image is used.
2983       NSRect fromRect = NSMakeRect(0, 0, p->wd, p->h);
2985       NSTRACE_RECT ("fromRect", fromRect);
2987       [img drawInRect: r
2988               fromRect: fromRect
2989              operation: NSCompositingOperationSourceOver
2990               fraction: 1.0
2991            respectFlipped: YES
2992                 hints: nil];
2993 #else
2994       {
2995         NSPoint pt = r.origin;
2996         pt.y += p->h;
2997         [img compositeToPoint: pt operation: NSCompositingOperationSourceOver];
2998       }
2999 #endif
3000     }
3001   ns_unfocus (f);
3005 static void
3006 ns_draw_window_cursor (struct window *w, struct glyph_row *glyph_row,
3007                        int x, int y, enum text_cursor_kinds cursor_type,
3008                        int cursor_width, bool on_p, bool active_p)
3009 /* --------------------------------------------------------------------------
3010      External call (RIF): draw cursor.
3011      Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
3012    -------------------------------------------------------------------------- */
3014   NSRect r, s;
3015   int fx, fy, h, cursor_height;
3016   struct frame *f = WINDOW_XFRAME (w);
3017   struct glyph *phys_cursor_glyph;
3018   struct glyph *cursor_glyph;
3019   struct face *face;
3020   NSColor *hollow_color = FRAME_BACKGROUND_COLOR (f);
3022   /* If cursor is out of bounds, don't draw garbage.  This can happen
3023      in mini-buffer windows when switching between echo area glyphs
3024      and mini-buffer.  */
3026   NSTRACE ("ns_draw_window_cursor");
3028   if (!on_p)
3029     return;
3031   w->phys_cursor_type = cursor_type;
3032   w->phys_cursor_on_p = on_p;
3034   if (cursor_type == NO_CURSOR)
3035     {
3036       w->phys_cursor_width = 0;
3037       return;
3038     }
3040   if ((phys_cursor_glyph = get_phys_cursor_glyph (w)) == NULL)
3041     {
3042       if (glyph_row->exact_window_width_line_p
3043           && w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])
3044         {
3045           glyph_row->cursor_in_fringe_p = 1;
3046           draw_fringe_bitmap (w, glyph_row, 0);
3047         }
3048       return;
3049     }
3051   /* We draw the cursor (with NSRectFill), then draw the glyph on top
3052      (other terminals do it the other way round).  We must set
3053      w->phys_cursor_width to the cursor width.  For bar cursors, that
3054      is CURSOR_WIDTH; for box cursors, it is the glyph width.  */
3055   get_phys_cursor_geometry (w, glyph_row, phys_cursor_glyph, &fx, &fy, &h);
3057   /* The above get_phys_cursor_geometry call set w->phys_cursor_width
3058      to the glyph width; replace with CURSOR_WIDTH for (V)BAR cursors. */
3059   if (cursor_type == BAR_CURSOR)
3060     {
3061       if (cursor_width < 1)
3062         cursor_width = max (FRAME_CURSOR_WIDTH (f), 1);
3064       /* The bar cursor should never be wider than the glyph. */
3065       if (cursor_width < w->phys_cursor_width)
3066         w->phys_cursor_width = cursor_width;
3067     }
3068   /* If we have an HBAR, "cursor_width" MAY specify height. */
3069   else if (cursor_type == HBAR_CURSOR)
3070     {
3071       cursor_height = (cursor_width < 1) ? lrint (0.25 * h) : cursor_width;
3072       if (cursor_height > glyph_row->height)
3073         cursor_height = glyph_row->height;
3074       if (h > cursor_height) // Cursor smaller than line height, move down
3075         fy += h - cursor_height;
3076       h = cursor_height;
3077     }
3079   r.origin.x = fx, r.origin.y = fy;
3080   r.size.height = h;
3081   r.size.width = w->phys_cursor_width;
3083   /* Prevent the cursor from being drawn outside the text area. */
3084   ns_clip_to_row (w, glyph_row, TEXT_AREA, NO); /* do ns_focus(f, &r, 1); if remove */
3087   face = FACE_FROM_ID_OR_NULL (f, phys_cursor_glyph->face_id);
3088   if (face && NS_FACE_BACKGROUND (face)
3089       == ns_index_color (FRAME_CURSOR_COLOR (f), f))
3090     {
3091       [ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), f) set];
3092       hollow_color = FRAME_CURSOR_COLOR (f);
3093     }
3094   else
3095     [FRAME_CURSOR_COLOR (f) set];
3097 #ifdef NS_IMPL_COCOA
3098   /* TODO: This makes drawing of cursor plus that of phys_cursor_glyph
3099            atomic.  Cleaner ways of doing this should be investigated.
3100            One way would be to set a global variable DRAWING_CURSOR
3101            when making the call to draw_phys..(), don't focus in that
3102            case, then move the ns_unfocus() here after that call. */
3103   NSDisableScreenUpdates ();
3104 #endif
3106   switch (cursor_type)
3107     {
3108     case DEFAULT_CURSOR:
3109     case NO_CURSOR:
3110       break;
3111     case FILLED_BOX_CURSOR:
3112       NSRectFill (r);
3113       break;
3114     case HOLLOW_BOX_CURSOR:
3115       NSRectFill (r);
3116       [hollow_color set];
3117       NSRectFill (NSInsetRect (r, 1, 1));
3118       [FRAME_CURSOR_COLOR (f) set];
3119       break;
3120     case HBAR_CURSOR:
3121       NSRectFill (r);
3122       break;
3123     case BAR_CURSOR:
3124       s = r;
3125       /* If the character under cursor is R2L, draw the bar cursor
3126          on the right of its glyph, rather than on the left.  */
3127       cursor_glyph = get_phys_cursor_glyph (w);
3128       if ((cursor_glyph->resolved_level & 1) != 0)
3129         s.origin.x += cursor_glyph->pixel_width - s.size.width;
3131       NSRectFill (s);
3132       break;
3133     }
3134   ns_unfocus (f);
3136   /* draw the character under the cursor */
3137   if (cursor_type != NO_CURSOR)
3138     draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
3140 #ifdef NS_IMPL_COCOA
3141   NSEnableScreenUpdates ();
3142 #endif
3147 static void
3148 ns_draw_vertical_window_border (struct window *w, int x, int y0, int y1)
3149 /* --------------------------------------------------------------------------
3150      External (RIF): Draw a vertical line.
3151    -------------------------------------------------------------------------- */
3153   struct frame *f = XFRAME (WINDOW_FRAME (w));
3154   struct face *face;
3155   NSRect r = NSMakeRect (x, y0, 1, y1-y0);
3157   NSTRACE ("ns_draw_vertical_window_border");
3159   face = FACE_FROM_ID_OR_NULL (f, VERTICAL_BORDER_FACE_ID);
3161   ns_focus (f, &r, 1);
3162   if (face)
3163     [ns_lookup_indexed_color(face->foreground, f) set];
3165   NSRectFill(r);
3166   ns_unfocus (f);
3170 static void
3171 ns_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1)
3172 /* --------------------------------------------------------------------------
3173      External (RIF): Draw a window divider.
3174    -------------------------------------------------------------------------- */
3176   struct frame *f = XFRAME (WINDOW_FRAME (w));
3177   struct face *face;
3178   NSRect r = NSMakeRect (x0, y0, x1-x0, y1-y0);
3180   NSTRACE ("ns_draw_window_divider");
3182   face = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FACE_ID);
3184   ns_focus (f, &r, 1);
3185   if (face)
3186     [ns_lookup_indexed_color(face->foreground, f) set];
3188   NSRectFill(r);
3189   ns_unfocus (f);
3192 static void
3193 ns_show_hourglass (struct frame *f)
3195   /* TODO: add NSProgressIndicator to all frames.  */
3198 static void
3199 ns_hide_hourglass (struct frame *f)
3201   /* TODO: remove NSProgressIndicator from all frames.  */
3204 /* ==========================================================================
3206     Glyph drawing operations
3208    ========================================================================== */
3210 static int
3211 ns_get_glyph_string_clip_rect (struct glyph_string *s, NativeRectangle *nr)
3212 /* --------------------------------------------------------------------------
3213     Wrapper utility to account for internal border width on full-width lines,
3214     and allow top full-width rows to hit the frame top.  nr should be pointer
3215     to two successive NSRects.  Number of rects actually used is returned.
3216    -------------------------------------------------------------------------- */
3218   int n = get_glyph_string_clip_rects (s, nr, 2);
3219   return n;
3222 /* --------------------------------------------------------------------
3223    Draw a wavy line under glyph string s. The wave fills wave_height
3224    pixels from y.
3226                     x          wave_length = 2
3227                                  --
3228                 y    *   *   *   *   *
3229                      |* * * * * * * * *
3230     wave_height = 3  | *   *   *   *
3231   --------------------------------------------------------------------- */
3233 static void
3234 ns_draw_underwave (struct glyph_string *s, EmacsCGFloat width, EmacsCGFloat x)
3236   int wave_height = 3, wave_length = 2;
3237   int y, dx, dy, odd, xmax;
3238   NSPoint a, b;
3239   NSRect waveClip;
3241   dx = wave_length;
3242   dy = wave_height - 1;
3243   y =  s->ybase - wave_height + 3;
3244   xmax = x + width;
3246   /* Find and set clipping rectangle */
3247   waveClip = NSMakeRect (x, y, width, wave_height);
3248   [[NSGraphicsContext currentContext] saveGraphicsState];
3249   NSRectClip (waveClip);
3251   /* Draw the waves */
3252   a.x = x - ((int)(x) % dx) + (EmacsCGFloat) 0.5;
3253   b.x = a.x + dx;
3254   odd = (int)(a.x/dx) % 2;
3255   a.y = b.y = y + 0.5;
3257   if (odd)
3258     a.y += dy;
3259   else
3260     b.y += dy;
3262   while (a.x <= xmax)
3263     {
3264       [NSBezierPath strokeLineFromPoint:a toPoint:b];
3265       a.x = b.x, a.y = b.y;
3266       b.x += dx, b.y = y + 0.5 + odd*dy;
3267       odd = !odd;
3268     }
3270   /* Restore previous clipping rectangle(s) */
3271   [[NSGraphicsContext currentContext] restoreGraphicsState];
3276 static void
3277 ns_draw_text_decoration (struct glyph_string *s, struct face *face,
3278                          NSColor *defaultCol, CGFloat width, CGFloat x)
3279 /* --------------------------------------------------------------------------
3280    Draw underline, overline, and strike-through on glyph string s.
3281    -------------------------------------------------------------------------- */
3283   if (s->for_overlaps)
3284     return;
3286   /* Do underline. */
3287   if (face->underline_p)
3288     {
3289       if (s->face->underline_type == FACE_UNDER_WAVE)
3290         {
3291           if (face->underline_defaulted_p)
3292             [defaultCol set];
3293           else
3294             [ns_lookup_indexed_color (face->underline_color, s->f) set];
3296           ns_draw_underwave (s, width, x);
3297         }
3298       else if (s->face->underline_type == FACE_UNDER_LINE)
3299         {
3301           NSRect r;
3302           unsigned long thickness, position;
3304           /* If the prev was underlined, match its appearance. */
3305           if (s->prev && s->prev->face->underline_p
3306               && s->prev->face->underline_type == FACE_UNDER_LINE
3307               && s->prev->underline_thickness > 0)
3308             {
3309               thickness = s->prev->underline_thickness;
3310               position = s->prev->underline_position;
3311             }
3312           else
3313             {
3314               struct font *font = font_for_underline_metrics (s);
3315               unsigned long descent = s->y + s->height - s->ybase;
3317               /* Use underline thickness of font, defaulting to 1. */
3318               thickness = (font && font->underline_thickness > 0)
3319                 ? font->underline_thickness : 1;
3321               /* Determine the offset of underlining from the baseline. */
3322               if (x_underline_at_descent_line)
3323                 position = descent - thickness;
3324               else if (x_use_underline_position_properties
3325                        && font && font->underline_position >= 0)
3326                 position = font->underline_position;
3327               else if (font)
3328                 position = lround (font->descent / 2);
3329               else
3330                 position = underline_minimum_offset;
3332               position = max (position, underline_minimum_offset);
3334               /* Ensure underlining is not cropped. */
3335               if (descent <= position)
3336                 {
3337                   position = descent - 1;
3338                   thickness = 1;
3339                 }
3340               else if (descent < position + thickness)
3341                 thickness = 1;
3342             }
3344           s->underline_thickness = thickness;
3345           s->underline_position = position;
3347           r = NSMakeRect (x, s->ybase + position, width, thickness);
3349           if (face->underline_defaulted_p)
3350             [defaultCol set];
3351           else
3352             [ns_lookup_indexed_color (face->underline_color, s->f) set];
3353           NSRectFill (r);
3354         }
3355     }
3356   /* Do overline. We follow other terms in using a thickness of 1
3357      and ignoring overline_margin. */
3358   if (face->overline_p)
3359     {
3360       NSRect r;
3361       r = NSMakeRect (x, s->y, width, 1);
3363       if (face->overline_color_defaulted_p)
3364         [defaultCol set];
3365       else
3366         [ns_lookup_indexed_color (face->overline_color, s->f) set];
3367       NSRectFill (r);
3368     }
3370   /* Do strike-through.  We follow other terms for thickness and
3371      vertical position.*/
3372   if (face->strike_through_p)
3373     {
3374       NSRect r;
3375       /* Y-coordinate and height of the glyph string's first glyph.
3376          We cannot use s->y and s->height because those could be
3377          larger if there are taller display elements (e.g., characters
3378          displayed with a larger font) in the same glyph row.  */
3379       int glyph_y = s->ybase - s->first_glyph->ascent;
3380       int glyph_height = s->first_glyph->ascent + s->first_glyph->descent;
3381       /* Strike-through width and offset from the glyph string's
3382          top edge.  */
3383       unsigned long h = 1;
3384       unsigned long dy;
3386       dy = lrint ((glyph_height - h) / 2);
3387       r = NSMakeRect (x, glyph_y + dy, width, 1);
3389       if (face->strike_through_color_defaulted_p)
3390         [defaultCol set];
3391       else
3392         [ns_lookup_indexed_color (face->strike_through_color, s->f) set];
3393       NSRectFill (r);
3394     }
3397 static void
3398 ns_draw_box (NSRect r, CGFloat thickness, NSColor *col,
3399              char left_p, char right_p)
3400 /* --------------------------------------------------------------------------
3401     Draw an unfilled rect inside r, optionally leaving left and/or right open.
3402     Note we can't just use an NSDrawRect command, because of the possibility
3403     of some sides not being drawn, and because the rect will be filled.
3404    -------------------------------------------------------------------------- */
3406   NSRect s = r;
3407   [col set];
3409   /* top, bottom */
3410   s.size.height = thickness;
3411   NSRectFill (s);
3412   s.origin.y += r.size.height - thickness;
3413   NSRectFill (s);
3415   s.size.height = r.size.height;
3416   s.origin.y = r.origin.y;
3418   /* left, right (optional) */
3419   s.size.width = thickness;
3420   if (left_p)
3421     NSRectFill (s);
3422   if (right_p)
3423     {
3424       s.origin.x += r.size.width - thickness;
3425       NSRectFill (s);
3426     }
3430 static void
3431 ns_draw_relief (NSRect r, int thickness, char raised_p,
3432                char top_p, char bottom_p, char left_p, char right_p,
3433                struct glyph_string *s)
3434 /* --------------------------------------------------------------------------
3435     Draw a relief rect inside r, optionally leaving some sides open.
3436     Note we can't just use an NSDrawBezel command, because of the possibility
3437     of some sides not being drawn, and because the rect will be filled.
3438    -------------------------------------------------------------------------- */
3440   static NSColor *baseCol = nil, *lightCol = nil, *darkCol = nil;
3441   NSColor *newBaseCol = nil;
3442   NSRect sr = r;
3444   NSTRACE ("ns_draw_relief");
3446   /* set up colors */
3448   if (s->face->use_box_color_for_shadows_p)
3449     {
3450       newBaseCol = ns_lookup_indexed_color (s->face->box_color, s->f);
3451     }
3452 /*     else if (s->first_glyph->type == IMAGE_GLYPH
3453            && s->img->pixmap
3454            && !IMAGE_BACKGROUND_TRANSPARENT (s->img, s->f, 0))
3455        {
3456          newBaseCol = IMAGE_BACKGROUND  (s->img, s->f, 0);
3457        } */
3458   else
3459     {
3460       newBaseCol = ns_lookup_indexed_color (s->face->background, s->f);
3461     }
3463   if (newBaseCol == nil)
3464     newBaseCol = [NSColor grayColor];
3466   if (newBaseCol != baseCol)  /* TODO: better check */
3467     {
3468       [baseCol release];
3469       baseCol = [newBaseCol retain];
3470       [lightCol release];
3471       lightCol = [[baseCol highlightWithLevel: 0.2] retain];
3472       [darkCol release];
3473       darkCol = [[baseCol shadowWithLevel: 0.3] retain];
3474     }
3476   [(raised_p ? lightCol : darkCol) set];
3478   /* TODO: mitering. Using NSBezierPath doesn't work because of color switch. */
3480   /* top */
3481   sr.size.height = thickness;
3482   if (top_p) NSRectFill (sr);
3484   /* left */
3485   sr.size.height = r.size.height;
3486   sr.size.width = thickness;
3487   if (left_p) NSRectFill (sr);
3489   [(raised_p ? darkCol : lightCol) set];
3491   /* bottom */
3492   sr.size.width = r.size.width;
3493   sr.size.height = thickness;
3494   sr.origin.y += r.size.height - thickness;
3495   if (bottom_p) NSRectFill (sr);
3497   /* right */
3498   sr.size.height = r.size.height;
3499   sr.origin.y = r.origin.y;
3500   sr.size.width = thickness;
3501   sr.origin.x += r.size.width - thickness;
3502   if (right_p) NSRectFill (sr);
3506 static void
3507 ns_dumpglyphs_box_or_relief (struct glyph_string *s)
3508 /* --------------------------------------------------------------------------
3509       Function modeled after x_draw_glyph_string_box ().
3510       Sets up parameters for drawing.
3511    -------------------------------------------------------------------------- */
3513   int right_x, last_x;
3514   char left_p, right_p;
3515   struct glyph *last_glyph;
3516   NSRect r;
3517   int thickness;
3518   struct face *face;
3520   if (s->hl == DRAW_MOUSE_FACE)
3521     {
3522       face = FACE_FROM_ID_OR_NULL (s->f,
3523                                    MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3524       if (!face)
3525         face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3526     }
3527   else
3528     face = s->face;
3530   thickness = face->box_line_width;
3532   NSTRACE ("ns_dumpglyphs_box_or_relief");
3534   last_x = ((s->row->full_width_p && !s->w->pseudo_window_p)
3535             ? WINDOW_RIGHT_EDGE_X (s->w)
3536             : window_box_right (s->w, s->area));
3537   last_glyph = (s->cmp || s->img
3538                 ? s->first_glyph : s->first_glyph + s->nchars-1);
3540   right_x = ((s->row->full_width_p && s->extends_to_end_of_line_p
3541               ? last_x - 1 : min (last_x, s->x + s->background_width) - 1));
3543   left_p = (s->first_glyph->left_box_line_p
3544             || (s->hl == DRAW_MOUSE_FACE
3545                 && (s->prev == NULL || s->prev->hl != s->hl)));
3546   right_p = (last_glyph->right_box_line_p
3547              || (s->hl == DRAW_MOUSE_FACE
3548                  && (s->next == NULL || s->next->hl != s->hl)));
3550   r = NSMakeRect (s->x, s->y, right_x - s->x + 1, s->height);
3552   /* TODO: Sometimes box_color is 0 and this seems wrong; should investigate. */
3553   if (s->face->box == FACE_SIMPLE_BOX && s->face->box_color)
3554     {
3555       ns_draw_box (r, abs (thickness),
3556                    ns_lookup_indexed_color (face->box_color, s->f),
3557                   left_p, right_p);
3558     }
3559   else
3560     {
3561       ns_draw_relief (r, abs (thickness), s->face->box == FACE_RAISED_BOX,
3562                      1, 1, left_p, right_p, s);
3563     }
3567 static void
3568 ns_maybe_dumpglyphs_background (struct glyph_string *s, char force_p)
3569 /* --------------------------------------------------------------------------
3570       Modeled after x_draw_glyph_string_background, which draws BG in
3571       certain cases.  Others are left to the text rendering routine.
3572    -------------------------------------------------------------------------- */
3574   NSTRACE ("ns_maybe_dumpglyphs_background");
3576   if (!s->background_filled_p/* || s->hl == DRAW_MOUSE_FACE*/)
3577     {
3578       int box_line_width = max (s->face->box_line_width, 0);
3579       if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width
3580           /* When xdisp.c ignores FONT_HEIGHT, we cannot trust font
3581              dimensions, since the actual glyphs might be much
3582              smaller.  So in that case we always clear the rectangle
3583              with background color.  */
3584           || FONT_TOO_HIGH (s->font)
3585           || s->font_not_found_p || s->extends_to_end_of_line_p || force_p)
3586         {
3587           struct face *face;
3588           if (s->hl == DRAW_MOUSE_FACE)
3589             {
3590               face
3591                 = FACE_FROM_ID_OR_NULL (s->f,
3592                                         MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3593               if (!face)
3594                 face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3595             }
3596           else
3597             face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3598           if (!face->stipple)
3599             [(NS_FACE_BACKGROUND (face) != 0
3600               ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
3601               : FRAME_BACKGROUND_COLOR (s->f)) set];
3602           else
3603             {
3604               struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
3605               [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set];
3606             }
3608           if (s->hl != DRAW_CURSOR)
3609             {
3610               NSRect r = NSMakeRect (s->x, s->y + box_line_width,
3611                                     s->background_width,
3612                                     s->height-2*box_line_width);
3613               NSRectFill (r);
3614             }
3616           s->background_filled_p = 1;
3617         }
3618     }
3622 static void
3623 ns_dumpglyphs_image (struct glyph_string *s, NSRect r)
3624 /* --------------------------------------------------------------------------
3625       Renders an image and associated borders.
3626    -------------------------------------------------------------------------- */
3628   EmacsImage *img = s->img->pixmap;
3629   int box_line_vwidth = max (s->face->box_line_width, 0);
3630   int x = s->x, y = s->ybase - image_ascent (s->img, s->face, &s->slice);
3631   int bg_x, bg_y, bg_height;
3632   int th;
3633   char raised_p;
3634   NSRect br;
3635   struct face *face;
3636   NSColor *tdCol;
3638   NSTRACE ("ns_dumpglyphs_image");
3640   if (s->face->box != FACE_NO_BOX
3641       && s->first_glyph->left_box_line_p && s->slice.x == 0)
3642     x += abs (s->face->box_line_width);
3644   bg_x = x;
3645   bg_y =  s->slice.y == 0 ? s->y : s->y + box_line_vwidth;
3646   bg_height = s->height;
3647   /* other terms have this, but was causing problems w/tabbar mode */
3648   /* - 2 * box_line_vwidth; */
3650   if (s->slice.x == 0) x += s->img->hmargin;
3651   if (s->slice.y == 0) y += s->img->vmargin;
3653   /* Draw BG: if we need larger area than image itself cleared, do that,
3654      otherwise, since we composite the image under NS (instead of mucking
3655      with its background color), we must clear just the image area. */
3656   if (s->hl == DRAW_MOUSE_FACE)
3657     {
3658       face = FACE_FROM_ID_OR_NULL (s->f,
3659                                    MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3660       if (!face)
3661        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3662     }
3663   else
3664     face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3666   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f) set];
3668   if (bg_height > s->slice.height || s->img->hmargin || s->img->vmargin
3669       || s->img->mask || s->img->pixmap == 0 || s->width != s->background_width)
3670     {
3671       br = NSMakeRect (bg_x, bg_y, s->background_width, bg_height);
3672       s->background_filled_p = 1;
3673     }
3674   else
3675     {
3676       br = NSMakeRect (x, y, s->slice.width, s->slice.height);
3677     }
3679   NSRectFill (br);
3681   /* Draw the image.. do we need to draw placeholder if img ==nil? */
3682   if (img != nil)
3683     {
3684 #ifdef NS_IMPL_COCOA
3685       NSRect dr = NSMakeRect (x, y, s->slice.width, s->slice.height);
3686       NSRect ir = NSMakeRect (s->slice.x,
3687                               s->img->height - s->slice.y - s->slice.height,
3688                               s->slice.width, s->slice.height);
3689       [img drawInRect: dr
3690              fromRect: ir
3691              operation: NSCompositingOperationSourceOver
3692               fraction: 1.0
3693            respectFlipped: YES
3694                 hints: nil];
3695 #else
3696       [img compositeToPoint: NSMakePoint (x, y + s->slice.height)
3697                   operation: NSCompositingOperationSourceOver];
3698 #endif
3699     }
3701   if (s->hl == DRAW_CURSOR)
3702     {
3703     [FRAME_CURSOR_COLOR (s->f) set];
3704     if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
3705       tdCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
3706     else
3707       /* Currently on NS img->mask is always 0. Since
3708          get_window_cursor_type specifies a hollow box cursor when on
3709          a non-masked image we never reach this clause. But we put it
3710          in, in anticipation of better support for image masks on
3711          NS. */
3712       tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3713     }
3714   else
3715     {
3716       tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3717     }
3719   /* Draw underline, overline, strike-through. */
3720   ns_draw_text_decoration (s, face, tdCol, br.size.width, br.origin.x);
3722   /* Draw relief, if requested */
3723   if (s->img->relief || s->hl ==DRAW_IMAGE_RAISED || s->hl ==DRAW_IMAGE_SUNKEN)
3724     {
3725       if (s->hl == DRAW_IMAGE_SUNKEN || s->hl == DRAW_IMAGE_RAISED)
3726         {
3727           th = tool_bar_button_relief >= 0 ?
3728             tool_bar_button_relief : DEFAULT_TOOL_BAR_BUTTON_RELIEF;
3729           raised_p = (s->hl == DRAW_IMAGE_RAISED);
3730         }
3731       else
3732         {
3733           th = abs (s->img->relief);
3734           raised_p = (s->img->relief > 0);
3735         }
3737       r.origin.x = x - th;
3738       r.origin.y = y - th;
3739       r.size.width = s->slice.width + 2*th-1;
3740       r.size.height = s->slice.height + 2*th-1;
3741       ns_draw_relief (r, th, raised_p,
3742                       s->slice.y == 0,
3743                       s->slice.y + s->slice.height == s->img->height,
3744                       s->slice.x == 0,
3745                       s->slice.x + s->slice.width == s->img->width, s);
3746     }
3748   /* If there is no mask, the background won't be seen,
3749      so draw a rectangle on the image for the cursor.
3750      Do this for all images, getting transparency right is not reliable.  */
3751   if (s->hl == DRAW_CURSOR)
3752     {
3753       int thickness = abs (s->img->relief);
3754       if (thickness == 0) thickness = 1;
3755       ns_draw_box (br, thickness, FRAME_CURSOR_COLOR (s->f), 1, 1);
3756     }
3760 static void
3761 ns_dumpglyphs_stretch (struct glyph_string *s)
3763   NSRect r[2];
3764   int n, i;
3765   struct face *face;
3766   NSColor *fgCol, *bgCol;
3768   if (!s->background_filled_p)
3769     {
3770       n = ns_get_glyph_string_clip_rect (s, r);
3771       *r = NSMakeRect (s->x, s->y, s->background_width, s->height);
3773       ns_focus (s->f, r, n);
3775       if (s->hl == DRAW_MOUSE_FACE)
3776        {
3777          face = FACE_FROM_ID_OR_NULL (s->f,
3778                                       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3779          if (!face)
3780            face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3781        }
3782       else
3783        face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3785       bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
3786       fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3788       for (i = 0; i < n; ++i)
3789         {
3790           if (!s->row->full_width_p)
3791             {
3792               int overrun, leftoverrun;
3794               /* truncate to avoid overwriting fringe and/or scrollbar */
3795               overrun = max (0, (s->x + s->background_width)
3796                              - (WINDOW_BOX_RIGHT_EDGE_X (s->w)
3797                                 - WINDOW_RIGHT_FRINGE_WIDTH (s->w)));
3798               r[i].size.width -= overrun;
3800               /* truncate to avoid overwriting to left of the window box */
3801               leftoverrun = (WINDOW_BOX_LEFT_EDGE_X (s->w)
3802                              + WINDOW_LEFT_FRINGE_WIDTH (s->w)) - s->x;
3804               if (leftoverrun > 0)
3805                 {
3806                   r[i].origin.x += leftoverrun;
3807                   r[i].size.width -= leftoverrun;
3808                 }
3810               /* XXX: Try to work between problem where a stretch glyph on
3811                  a partially-visible bottom row will clear part of the
3812                  modeline, and another where list-buffers headers and similar
3813                  rows erroneously have visible_height set to 0.  Not sure
3814                  where this is coming from as other terms seem not to show. */
3815               r[i].size.height = min (s->height, s->row->visible_height);
3816             }
3818           [bgCol set];
3820           /* NOTE: under NS this is NOT used to draw cursors, but we must avoid
3821              overwriting cursor (usually when cursor on a tab) */
3822           if (s->hl == DRAW_CURSOR)
3823             {
3824               CGFloat x, width;
3826               x = r[i].origin.x;
3827               width = s->w->phys_cursor_width;
3828               r[i].size.width -= width;
3829               r[i].origin.x += width;
3831               NSRectFill (r[i]);
3833               /* Draw overlining, etc. on the cursor. */
3834               if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
3835                 ns_draw_text_decoration (s, face, bgCol, width, x);
3836               else
3837                 ns_draw_text_decoration (s, face, fgCol, width, x);
3838             }
3839           else
3840             {
3841               NSRectFill (r[i]);
3842             }
3844           /* Draw overlining, etc. on the stretch glyph (or the part
3845              of the stretch glyph after the cursor). */
3846           ns_draw_text_decoration (s, face, fgCol, r[i].size.width,
3847                                    r[i].origin.x);
3848         }
3849       ns_unfocus (s->f);
3850       s->background_filled_p = 1;
3851     }
3855 static void
3856 ns_draw_glyph_string_foreground (struct glyph_string *s)
3858   int x, flags;
3859   struct font *font = s->font;
3861   /* If first glyph of S has a left box line, start drawing the text
3862      of S to the right of that box line.  */
3863   if (s->face && s->face->box != FACE_NO_BOX
3864       && s->first_glyph->left_box_line_p)
3865     x = s->x + eabs (s->face->box_line_width);
3866   else
3867     x = s->x;
3869   flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
3870     (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
3871      (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
3872       NS_DUMPGLYPH_NORMAL));
3874   font->driver->draw
3875     (s, s->cmp_from, s->nchars, x, s->ybase,
3876      (flags == NS_DUMPGLYPH_NORMAL && !s->background_filled_p)
3877      || flags == NS_DUMPGLYPH_MOUSEFACE);
3881 static void
3882 ns_draw_composite_glyph_string_foreground (struct glyph_string *s)
3884   int i, j, x;
3885   struct font *font = s->font;
3887   /* If first glyph of S has a left box line, start drawing the text
3888      of S to the right of that box line.  */
3889   if (s->face && s->face->box != FACE_NO_BOX
3890       && s->first_glyph->left_box_line_p)
3891     x = s->x + eabs (s->face->box_line_width);
3892   else
3893     x = s->x;
3895   /* S is a glyph string for a composition.  S->cmp_from is the index
3896      of the first character drawn for glyphs of this composition.
3897      S->cmp_from == 0 means we are drawing the very first character of
3898      this composition.  */
3900   /* Draw a rectangle for the composition if the font for the very
3901      first character of the composition could not be loaded.  */
3902   if (s->font_not_found_p)
3903     {
3904       if (s->cmp_from == 0)
3905         {
3906           NSRect r = NSMakeRect (s->x, s->y, s->width-1, s->height -1);
3907           ns_draw_box (r, 1, FRAME_CURSOR_COLOR (s->f), 1, 1);
3908         }
3909     }
3910   else if (! s->first_glyph->u.cmp.automatic)
3911     {
3912       int y = s->ybase;
3914       for (i = 0, j = s->cmp_from; i < s->nchars; i++, j++)
3915         /* TAB in a composition means display glyphs with padding
3916            space on the left or right.  */
3917         if (COMPOSITION_GLYPH (s->cmp, j) != '\t')
3918           {
3919             int xx = x + s->cmp->offsets[j * 2];
3920             int yy = y - s->cmp->offsets[j * 2 + 1];
3922             font->driver->draw (s, j, j + 1, xx, yy, false);
3923             if (s->face->overstrike)
3924               font->driver->draw (s, j, j + 1, xx + 1, yy, false);
3925           }
3926     }
3927   else
3928     {
3929       Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
3930       Lisp_Object glyph;
3931       int y = s->ybase;
3932       int width = 0;
3934       for (i = j = s->cmp_from; i < s->cmp_to; i++)
3935         {
3936           glyph = LGSTRING_GLYPH (gstring, i);
3937           if (NILP (LGLYPH_ADJUSTMENT (glyph)))
3938             width += LGLYPH_WIDTH (glyph);
3939           else
3940             {
3941               int xoff, yoff, wadjust;
3943               if (j < i)
3944                 {
3945                   font->driver->draw (s, j, i, x, y, false);
3946                   if (s->face->overstrike)
3947                     font->driver->draw (s, j, i, x + 1, y, false);
3948                   x += width;
3949                 }
3950               xoff = LGLYPH_XOFF (glyph);
3951               yoff = LGLYPH_YOFF (glyph);
3952               wadjust = LGLYPH_WADJUST (glyph);
3953               font->driver->draw (s, i, i + 1, x + xoff, y + yoff, false);
3954               if (s->face->overstrike)
3955                 font->driver->draw (s, i, i + 1, x + xoff + 1, y + yoff,
3956                                     false);
3957               x += wadjust;
3958               j = i + 1;
3959               width = 0;
3960             }
3961         }
3962       if (j < i)
3963         {
3964           font->driver->draw (s, j, i, x, y, false);
3965           if (s->face->overstrike)
3966             font->driver->draw (s, j, i, x + 1, y, false);
3967         }
3968     }
3971 static void
3972 ns_draw_glyph_string (struct glyph_string *s)
3973 /* --------------------------------------------------------------------------
3974       External (RIF): Main draw-text call.
3975    -------------------------------------------------------------------------- */
3977   /* TODO (optimize): focus for box and contents draw */
3978   NSRect r[2];
3979   int n;
3980   char box_drawn_p = 0;
3981   struct font *font = s->face->font;
3982   if (! font) font = FRAME_FONT (s->f);
3984   NSTRACE_WHEN (NSTRACE_GROUP_GLYPHS, "ns_draw_glyph_string");
3986   if (s->next && s->right_overhang && !s->for_overlaps/*&&s->hl!=DRAW_CURSOR*/)
3987     {
3988       int width;
3989       struct glyph_string *next;
3991       for (width = 0, next = s->next;
3992            next && width < s->right_overhang;
3993            width += next->width, next = next->next)
3994         if (next->first_glyph->type != IMAGE_GLYPH)
3995           {
3996             if (next->first_glyph->type != STRETCH_GLYPH)
3997               {
3998                 n = ns_get_glyph_string_clip_rect (s->next, r);
3999                 ns_focus (s->f, r, n);
4000                 ns_maybe_dumpglyphs_background (s->next, 1);
4001                 ns_unfocus (s->f);
4002               }
4003             else
4004               {
4005                 ns_dumpglyphs_stretch (s->next);
4006               }
4007             next->num_clips = 0;
4008           }
4009     }
4011   if (!s->for_overlaps && s->face->box != FACE_NO_BOX
4012         && (s->first_glyph->type == CHAR_GLYPH
4013             || s->first_glyph->type == COMPOSITE_GLYPH))
4014     {
4015       n = ns_get_glyph_string_clip_rect (s, r);
4016       ns_focus (s->f, r, n);
4017       ns_maybe_dumpglyphs_background (s, 1);
4018       ns_dumpglyphs_box_or_relief (s);
4019       ns_unfocus (s->f);
4020       box_drawn_p = 1;
4021     }
4023   switch (s->first_glyph->type)
4024     {
4026     case IMAGE_GLYPH:
4027       n = ns_get_glyph_string_clip_rect (s, r);
4028       ns_focus (s->f, r, n);
4029       ns_dumpglyphs_image (s, r[0]);
4030       ns_unfocus (s->f);
4031       break;
4033     case STRETCH_GLYPH:
4034       ns_dumpglyphs_stretch (s);
4035       break;
4037     case CHAR_GLYPH:
4038     case COMPOSITE_GLYPH:
4039       n = ns_get_glyph_string_clip_rect (s, r);
4040       ns_focus (s->f, r, n);
4042       if (s->for_overlaps || (s->cmp_from > 0
4043                               && ! s->first_glyph->u.cmp.automatic))
4044         s->background_filled_p = 1;
4045       else
4046         ns_maybe_dumpglyphs_background
4047           (s, s->first_glyph->type == COMPOSITE_GLYPH);
4049       if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
4050         {
4051           unsigned long tmp = NS_FACE_BACKGROUND (s->face);
4052           NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
4053           NS_FACE_FOREGROUND (s->face) = tmp;
4054         }
4056       {
4057         BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
4059         if (isComposite)
4060           ns_draw_composite_glyph_string_foreground (s);
4061         else
4062           ns_draw_glyph_string_foreground (s);
4063       }
4065       {
4066         NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
4067                         ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face),
4068                                                    s->f)
4069                         : FRAME_FOREGROUND_COLOR (s->f));
4070         [col set];
4072         /* Draw underline, overline, strike-through. */
4073         ns_draw_text_decoration (s, s->face, col, s->width, s->x);
4074       }
4076       if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
4077         {
4078           unsigned long tmp = NS_FACE_BACKGROUND (s->face);
4079           NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
4080           NS_FACE_FOREGROUND (s->face) = tmp;
4081         }
4083       ns_unfocus (s->f);
4084       break;
4086     case GLYPHLESS_GLYPH:
4087       n = ns_get_glyph_string_clip_rect (s, r);
4088       ns_focus (s->f, r, n);
4090       if (s->for_overlaps || (s->cmp_from > 0
4091                               && ! s->first_glyph->u.cmp.automatic))
4092         s->background_filled_p = 1;
4093       else
4094         ns_maybe_dumpglyphs_background
4095           (s, s->first_glyph->type == COMPOSITE_GLYPH);
4096       /* ... */
4097       /* Not yet implemented.  */
4098       /* ... */
4099       ns_unfocus (s->f);
4100       break;
4102     default:
4103       emacs_abort ();
4104     }
4106   /* Draw box if not done already. */
4107   if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX)
4108     {
4109       n = ns_get_glyph_string_clip_rect (s, r);
4110       ns_focus (s->f, r, n);
4111       ns_dumpglyphs_box_or_relief (s);
4112       ns_unfocus (s->f);
4113     }
4115   s->num_clips = 0;
4120 /* ==========================================================================
4122     Event loop
4124    ========================================================================== */
4127 static void
4128 ns_send_appdefined (int value)
4129 /* --------------------------------------------------------------------------
4130     Internal: post an appdefined event which EmacsApp-sendEvent will
4131               recognize and take as a command to halt the event loop.
4132    -------------------------------------------------------------------------- */
4134   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_send_appdefined(%d)", value);
4136   // GNUstep needs postEvent to happen on the main thread.
4137   // Cocoa needs nextEventMatchingMask to happen on the main thread too.
4138   if (! [[NSThread currentThread] isMainThread])
4139     {
4140       EmacsApp *app = (EmacsApp *)NSApp;
4141       app->nextappdefined = value;
4142       [app performSelectorOnMainThread:@selector (sendFromMainThread:)
4143                             withObject:nil
4144                          waitUntilDone:NO];
4145       return;
4146     }
4148   /* Only post this event if we haven't already posted one.  This will end
4149        the [NXApp run] main loop after having processed all events queued at
4150        this moment.  */
4152 #ifdef NS_IMPL_COCOA
4153   if (! send_appdefined)
4154     {
4155       /* OS X 10.10.1 swallows the AppDefined event we are sending ourselves
4156          in certain situations (rapid incoming events).
4157          So check if we have one, if not add one.  */
4158       NSEvent *appev = [NSApp nextEventMatchingMask:NSEventMaskApplicationDefined
4159                                           untilDate:[NSDate distantPast]
4160                                              inMode:NSDefaultRunLoopMode
4161                                             dequeue:NO];
4162       if (! appev) send_appdefined = YES;
4163     }
4164 #endif
4166   if (send_appdefined)
4167     {
4168       NSEvent *nxev;
4170       /* We only need one NX_APPDEFINED event to stop NXApp from running.  */
4171       send_appdefined = NO;
4173       /* Don't need wakeup timer any more */
4174       if (timed_entry)
4175         {
4176           [timed_entry invalidate];
4177           [timed_entry release];
4178           timed_entry = nil;
4179         }
4181       nxev = [NSEvent otherEventWithType: NSEventTypeApplicationDefined
4182                                 location: NSMakePoint (0, 0)
4183                            modifierFlags: 0
4184                                timestamp: 0
4185                             windowNumber: [[NSApp mainWindow] windowNumber]
4186                                  context: [NSApp context]
4187                                  subtype: 0
4188                                    data1: value
4189                                    data2: 0];
4191       /* Post an application defined event on the event queue.  When this is
4192          received the [NXApp run] will return, thus having processed all
4193          events which are currently queued.  */
4194       [NSApp postEvent: nxev atStart: NO];
4195     }
4198 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4199 static void
4200 check_native_fs ()
4202   Lisp_Object frame, tail;
4204   if (ns_last_use_native_fullscreen == ns_use_native_fullscreen)
4205     return;
4207   ns_last_use_native_fullscreen = ns_use_native_fullscreen;
4209   FOR_EACH_FRAME (tail, frame)
4210     {
4211       struct frame *f = XFRAME (frame);
4212       if (FRAME_NS_P (f))
4213         {
4214           EmacsView *view = FRAME_NS_VIEW (f);
4215           [view updateCollectionBehavior];
4216         }
4217     }
4219 #endif
4221 /* GNUstep does not have cancelTracking.  */
4222 #ifdef NS_IMPL_COCOA
4223 /* Check if menu open should be canceled or continued as normal.  */
4224 void
4225 ns_check_menu_open (NSMenu *menu)
4227   /* Click in menu bar? */
4228   NSArray *a = [[NSApp mainMenu] itemArray];
4229   int i;
4230   BOOL found = NO;
4232   if (menu == nil) // Menu tracking ended.
4233     {
4234       if (menu_will_open_state == MENU_OPENING)
4235         menu_will_open_state = MENU_NONE;
4236       return;
4237     }
4239   for (i = 0; ! found && i < [a count]; i++)
4240     found = menu == [[a objectAtIndex:i] submenu];
4241   if (found)
4242     {
4243       if (menu_will_open_state == MENU_NONE && emacs_event)
4244         {
4245           NSEvent *theEvent = [NSApp currentEvent];
4246           struct frame *emacsframe = SELECTED_FRAME ();
4248           [menu cancelTracking];
4249           menu_will_open_state = MENU_PENDING;
4250           emacs_event->kind = MENU_BAR_ACTIVATE_EVENT;
4251           EV_TRAILER (theEvent);
4253           CGEventRef ourEvent = CGEventCreate (NULL);
4254           menu_mouse_point = CGEventGetLocation (ourEvent);
4255           CFRelease (ourEvent);
4256         }
4257       else if (menu_will_open_state == MENU_OPENING)
4258         {
4259           menu_will_open_state = MENU_NONE;
4260         }
4261     }
4264 /* Redo saved menu click if state is MENU_PENDING.  */
4265 void
4266 ns_check_pending_open_menu ()
4268   if (menu_will_open_state == MENU_PENDING)
4269     {
4270       CGEventSourceRef source
4271         = CGEventSourceCreate (kCGEventSourceStateHIDSystemState);
4273       CGEventRef event = CGEventCreateMouseEvent (source,
4274                                                   kCGEventLeftMouseDown,
4275                                                   menu_mouse_point,
4276                                                   kCGMouseButtonLeft);
4277       CGEventSetType (event, kCGEventLeftMouseDown);
4278       CGEventPost (kCGHIDEventTap, event);
4279       CFRelease (event);
4280       CFRelease (source);
4282       menu_will_open_state = MENU_OPENING;
4283     }
4285 #endif /* NS_IMPL_COCOA */
4287 static int
4288 ns_read_socket (struct terminal *terminal, struct input_event *hold_quit)
4289 /* --------------------------------------------------------------------------
4290      External (hook): Post an event to ourself and keep reading events until
4291      we read it back again.  In effect process all events which were waiting.
4292      From 21+ we have to manage the event buffer ourselves.
4293    -------------------------------------------------------------------------- */
4295   struct input_event ev;
4296   int nevents;
4298   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_read_socket");
4300 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4301   check_native_fs ();
4302 #endif
4304   if ([NSApp modalWindow] != nil)
4305     return -1;
4307   if (hold_event_q.nr > 0)
4308     {
4309       int i;
4310       for (i = 0; i < hold_event_q.nr; ++i)
4311         kbd_buffer_store_event_hold (&hold_event_q.q[i], hold_quit);
4312       hold_event_q.nr = 0;
4313       return i;
4314     }
4316   if ([NSThread isMainThread])
4317     {
4318       block_input ();
4319       n_emacs_events_pending = 0;
4320       ns_init_events (&ev);
4321       q_event_ptr = hold_quit;
4323       /* we manage autorelease pools by allocate/reallocate each time around
4324          the loop; strict nesting is occasionally violated but seems not to
4325          matter.. earlier methods using full nesting caused major memory leaks */
4326       [outerpool release];
4327       outerpool = [[NSAutoreleasePool alloc] init];
4329       /* If have pending open-file requests, attend to the next one of those. */
4330       if (ns_pending_files && [ns_pending_files count] != 0
4331           && [(EmacsApp *)NSApp openFile: [ns_pending_files objectAtIndex: 0]])
4332         {
4333           [ns_pending_files removeObjectAtIndex: 0];
4334         }
4335       /* Deal with pending service requests. */
4336       else if (ns_pending_service_names && [ns_pending_service_names count] != 0
4337                && [(EmacsApp *)
4338                     NSApp fulfillService: [ns_pending_service_names objectAtIndex: 0]
4339                                  withArg: [ns_pending_service_args objectAtIndex: 0]])
4340         {
4341           [ns_pending_service_names removeObjectAtIndex: 0];
4342           [ns_pending_service_args removeObjectAtIndex: 0];
4343         }
4344       else
4345         {
4346           /* Run and wait for events.  We must always send one NX_APPDEFINED event
4347              to ourself, otherwise [NXApp run] will never exit.  */
4348           send_appdefined = YES;
4349           ns_send_appdefined (-1);
4351           [NSApp run];
4352         }
4354       nevents = n_emacs_events_pending;
4355       n_emacs_events_pending = 0;
4356       ns_finish_events ();
4357       q_event_ptr = NULL;
4358       unblock_input ();
4359     }
4360   else
4361     return -1;
4363   return nevents;
4368 ns_select (int nfds, fd_set *readfds, fd_set *writefds,
4369            fd_set *exceptfds, struct timespec *timeout,
4370            sigset_t *sigmask)
4371 /* --------------------------------------------------------------------------
4372      Replacement for select, checking for events
4373    -------------------------------------------------------------------------- */
4375   int result;
4376   int t, k, nr = 0;
4377   struct input_event event;
4378   char c;
4380   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_select");
4382 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4383   check_native_fs ();
4384 #endif
4386   if (hold_event_q.nr > 0)
4387     {
4388       /* We already have events pending. */
4389       raise (SIGIO);
4390       errno = EINTR;
4391       return -1;
4392     }
4394   for (k = 0; k < nfds+1; k++)
4395     {
4396       if (readfds && FD_ISSET(k, readfds)) ++nr;
4397       if (writefds && FD_ISSET(k, writefds)) ++nr;
4398     }
4400   if (NSApp == nil
4401       || ![NSThread isMainThread]
4402       || (timeout && timeout->tv_sec == 0 && timeout->tv_nsec == 0))
4403     return thread_select(pselect, nfds, readfds, writefds,
4404                          exceptfds, timeout, sigmask);
4405   else
4406     {
4407       struct timespec t = {0, 0};
4408       thread_select(pselect, 0, NULL, NULL, NULL, &t, sigmask);
4409     }
4411   [outerpool release];
4412   outerpool = [[NSAutoreleasePool alloc] init];
4415   send_appdefined = YES;
4416   if (nr > 0)
4417     {
4418       pthread_mutex_lock (&select_mutex);
4419       select_nfds = nfds;
4420       select_valid = 0;
4421       if (readfds)
4422         {
4423           select_readfds = *readfds;
4424           select_valid += SELECT_HAVE_READ;
4425         }
4426       if (writefds)
4427         {
4428           select_writefds = *writefds;
4429           select_valid += SELECT_HAVE_WRITE;
4430         }
4432       if (timeout)
4433         {
4434           select_timeout = *timeout;
4435           select_valid += SELECT_HAVE_TMO;
4436         }
4438       pthread_mutex_unlock (&select_mutex);
4440       /* Inform fd_handler that select should be called */
4441       c = 'g';
4442       emacs_write_sig (selfds[1], &c, 1);
4443     }
4444   else if (nr == 0 && timeout)
4445     {
4446       /* No file descriptor, just a timeout, no need to wake fd_handler  */
4447       double time = timespectod (*timeout);
4448       timed_entry = [[NSTimer scheduledTimerWithTimeInterval: time
4449                                                       target: NSApp
4450                                                     selector:
4451                                   @selector (timeout_handler:)
4452                                                     userInfo: 0
4453                                                      repeats: NO]
4454                       retain];
4455     }
4456   else /* No timeout and no file descriptors, can this happen?  */
4457     {
4458       /* Send appdefined so we exit from the loop */
4459       ns_send_appdefined (-1);
4460     }
4462   block_input ();
4463   ns_init_events (&event);
4465   [NSApp run];
4467   ns_finish_events ();
4468   if (nr > 0 && readfds)
4469     {
4470       c = 's';
4471       emacs_write_sig (selfds[1], &c, 1);
4472     }
4473   unblock_input ();
4475   t = last_appdefined_event_data;
4477   if (t != NO_APPDEFINED_DATA)
4478     {
4479       last_appdefined_event_data = NO_APPDEFINED_DATA;
4481       if (t == -2)
4482         {
4483           /* The NX_APPDEFINED event we received was a timeout. */
4484           result = 0;
4485         }
4486       else if (t == -1)
4487         {
4488           /* The NX_APPDEFINED event we received was the result of
4489              at least one real input event arriving.  */
4490           errno = EINTR;
4491           result = -1;
4492         }
4493       else
4494         {
4495           /* Received back from select () in fd_handler; copy the results */
4496           pthread_mutex_lock (&select_mutex);
4497           if (readfds) *readfds = select_readfds;
4498           if (writefds) *writefds = select_writefds;
4499           pthread_mutex_unlock (&select_mutex);
4500           result = t;
4501         }
4502     }
4503   else
4504     {
4505       errno = EINTR;
4506       result = -1;
4507     }
4509   return result;
4512 #ifdef HAVE_PTHREAD
4513 void
4514 ns_run_loop_break ()
4515 /* Break out of the NS run loop in ns_select or ns_read_socket. */
4517   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_run_loop_break");
4519   /* If we don't have a GUI, don't send the event. */
4520   if (NSApp != NULL)
4521     ns_send_appdefined(-1);
4523 #endif
4526 /* ==========================================================================
4528     Scrollbar handling
4530    ========================================================================== */
4533 static void
4534 ns_set_vertical_scroll_bar (struct window *window,
4535                            int portion, int whole, int position)
4536 /* --------------------------------------------------------------------------
4537       External (hook): Update or add scrollbar
4538    -------------------------------------------------------------------------- */
4540   Lisp_Object win;
4541   NSRect r, v;
4542   struct frame *f = XFRAME (WINDOW_FRAME (window));
4543   EmacsView *view = FRAME_NS_VIEW (f);
4544   EmacsScroller *bar;
4545   int window_y, window_height;
4546   int top, left, height, width;
4547   BOOL update_p = YES;
4549   /* optimization; display engine sends WAY too many of these.. */
4550   if (!NILP (window->vertical_scroll_bar))
4551     {
4552       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4553       if ([bar checkSamePosition: position portion: portion whole: whole])
4554         {
4555           if (view->scrollbarsNeedingUpdate == 0)
4556             {
4557               if (!windows_or_buffers_changed)
4558                   return;
4559             }
4560           else
4561             view->scrollbarsNeedingUpdate--;
4562           update_p = NO;
4563         }
4564     }
4566   NSTRACE ("ns_set_vertical_scroll_bar");
4568   /* Get dimensions.  */
4569   window_box (window, ANY_AREA, 0, &window_y, 0, &window_height);
4570   top = window_y;
4571   height = window_height;
4572   width = NS_SCROLL_BAR_WIDTH (f);
4573   left = WINDOW_SCROLL_BAR_AREA_X (window);
4575   r = NSMakeRect (left, top, width, height);
4576   /* the parent view is flipped, so we need to flip y value */
4577   v = [view frame];
4578   r.origin.y = (v.size.height - r.size.height - r.origin.y);
4580   XSETWINDOW (win, window);
4581   block_input ();
4583   /* we want at least 5 lines to display a scrollbar */
4584   if (WINDOW_TOTAL_LINES (window) < 5)
4585     {
4586       if (!NILP (window->vertical_scroll_bar))
4587         {
4588           bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4589           [bar removeFromSuperview];
4590           wset_vertical_scroll_bar (window, Qnil);
4591           [bar release];
4592         }
4593       ns_clear_frame_area (f, left, top, width, height);
4594       unblock_input ();
4595       return;
4596     }
4598   if (NILP (window->vertical_scroll_bar))
4599     {
4600       if (width > 0 && height > 0)
4601         ns_clear_frame_area (f, left, top, width, height);
4603       bar = [[EmacsScroller alloc] initFrame: r window: win];
4604       wset_vertical_scroll_bar (window, make_save_ptr (bar));
4605       update_p = YES;
4606     }
4607   else
4608     {
4609       NSRect oldRect;
4610       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4611       oldRect = [bar frame];
4612       r.size.width = oldRect.size.width;
4613       if (FRAME_LIVE_P (f) && !NSEqualRects (oldRect, r))
4614         {
4615           if (oldRect.origin.x != r.origin.x)
4616               ns_clear_frame_area (f, left, top, width, height);
4617           [bar setFrame: r];
4618         }
4619     }
4621   if (update_p)
4622     [bar setPosition: position portion: portion whole: whole];
4623   unblock_input ();
4627 static void
4628 ns_set_horizontal_scroll_bar (struct window *window,
4629                               int portion, int whole, int position)
4630 /* --------------------------------------------------------------------------
4631       External (hook): Update or add scrollbar
4632    -------------------------------------------------------------------------- */
4634   Lisp_Object win;
4635   NSRect r, v;
4636   struct frame *f = XFRAME (WINDOW_FRAME (window));
4637   EmacsView *view = FRAME_NS_VIEW (f);
4638   EmacsScroller *bar;
4639   int top, height, left, width;
4640   int window_x, window_width;
4641   BOOL update_p = YES;
4643   /* optimization; display engine sends WAY too many of these.. */
4644   if (!NILP (window->horizontal_scroll_bar))
4645     {
4646       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4647       if ([bar checkSamePosition: position portion: portion whole: whole])
4648         {
4649           if (view->scrollbarsNeedingUpdate == 0)
4650             {
4651               if (!windows_or_buffers_changed)
4652                   return;
4653             }
4654           else
4655             view->scrollbarsNeedingUpdate--;
4656           update_p = NO;
4657         }
4658     }
4660   NSTRACE ("ns_set_horizontal_scroll_bar");
4662   /* Get dimensions.  */
4663   window_box (window, ANY_AREA, &window_x, 0, &window_width, 0);
4664   left = window_x;
4665   width = window_width;
4666   height = NS_SCROLL_BAR_HEIGHT (f);
4667   top = WINDOW_SCROLL_BAR_AREA_Y (window);
4669   r = NSMakeRect (left, top, width, height);
4670   /* the parent view is flipped, so we need to flip y value */
4671   v = [view frame];
4672   r.origin.y = (v.size.height - r.size.height - r.origin.y);
4674   XSETWINDOW (win, window);
4675   block_input ();
4677   if (NILP (window->horizontal_scroll_bar))
4678     {
4679       if (width > 0 && height > 0)
4680         ns_clear_frame_area (f, left, top, width, height);
4682       bar = [[EmacsScroller alloc] initFrame: r window: win];
4683       wset_horizontal_scroll_bar (window, make_save_ptr (bar));
4684       update_p = YES;
4685     }
4686   else
4687     {
4688       NSRect oldRect;
4689       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4690       oldRect = [bar frame];
4691       if (FRAME_LIVE_P (f) && !NSEqualRects (oldRect, r))
4692         {
4693           if (oldRect.origin.y != r.origin.y)
4694             ns_clear_frame_area (f, left, top, width, height);
4695           [bar setFrame: r];
4696           update_p = YES;
4697         }
4698     }
4700   /* If there are both horizontal and vertical scroll-bars they leave
4701      a square that belongs to neither. We need to clear it otherwise
4702      it fills with junk. */
4703   if (!NILP (window->vertical_scroll_bar))
4704     ns_clear_frame_area (f, WINDOW_SCROLL_BAR_AREA_X (window), top,
4705                          NS_SCROLL_BAR_HEIGHT (f), height);
4707   if (update_p)
4708     [bar setPosition: position portion: portion whole: whole];
4709   unblock_input ();
4713 static void
4714 ns_condemn_scroll_bars (struct frame *f)
4715 /* --------------------------------------------------------------------------
4716      External (hook): arrange for all frame's scrollbars to be removed
4717      at next call to judge_scroll_bars, except for those redeemed.
4718    -------------------------------------------------------------------------- */
4720   int i;
4721   id view;
4722   NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews];
4724   NSTRACE ("ns_condemn_scroll_bars");
4726   for (i =[subviews count]-1; i >= 0; i--)
4727     {
4728       view = [subviews objectAtIndex: i];
4729       if ([view isKindOfClass: [EmacsScroller class]])
4730         [view condemn];
4731     }
4735 static void
4736 ns_redeem_scroll_bar (struct window *window)
4737 /* --------------------------------------------------------------------------
4738      External (hook): arrange to spare this window's scrollbar
4739      at next call to judge_scroll_bars.
4740    -------------------------------------------------------------------------- */
4742   id bar;
4743   NSTRACE ("ns_redeem_scroll_bar");
4744   if (!NILP (window->vertical_scroll_bar)
4745       && WINDOW_HAS_VERTICAL_SCROLL_BAR (window))
4746     {
4747       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4748       [bar reprieve];
4749     }
4751   if (!NILP (window->horizontal_scroll_bar)
4752       && WINDOW_HAS_HORIZONTAL_SCROLL_BAR (window))
4753     {
4754       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4755       [bar reprieve];
4756     }
4760 static void
4761 ns_judge_scroll_bars (struct frame *f)
4762 /* --------------------------------------------------------------------------
4763      External (hook): destroy all scrollbars on frame that weren't
4764      redeemed after call to condemn_scroll_bars.
4765    -------------------------------------------------------------------------- */
4767   int i;
4768   id view;
4769   EmacsView *eview = FRAME_NS_VIEW (f);
4770   NSArray *subviews = [[eview superview] subviews];
4771   BOOL removed = NO;
4773   NSTRACE ("ns_judge_scroll_bars");
4774   for (i = [subviews count]-1; i >= 0; --i)
4775     {
4776       view = [subviews objectAtIndex: i];
4777       if (![view isKindOfClass: [EmacsScroller class]]) continue;
4778       if ([view judge])
4779         removed = YES;
4780     }
4782   if (removed)
4783     [eview updateFrameSize: NO];
4786 /* ==========================================================================
4788     Initialization
4790    ========================================================================== */
4793 x_display_pixel_height (struct ns_display_info *dpyinfo)
4795   NSArray *screens = [NSScreen screens];
4796   NSEnumerator *enumerator = [screens objectEnumerator];
4797   NSScreen *screen;
4798   NSRect frame;
4800   frame = NSZeroRect;
4801   while ((screen = [enumerator nextObject]) != nil)
4802     frame = NSUnionRect (frame, [screen frame]);
4804   return NSHeight (frame);
4808 x_display_pixel_width (struct ns_display_info *dpyinfo)
4810   NSArray *screens = [NSScreen screens];
4811   NSEnumerator *enumerator = [screens objectEnumerator];
4812   NSScreen *screen;
4813   NSRect frame;
4815   frame = NSZeroRect;
4816   while ((screen = [enumerator nextObject]) != nil)
4817     frame = NSUnionRect (frame, [screen frame]);
4819   return NSWidth (frame);
4823 static Lisp_Object ns_string_to_lispmod (const char *s)
4824 /* --------------------------------------------------------------------------
4825      Convert modifier name to lisp symbol
4826    -------------------------------------------------------------------------- */
4828   if (!strncmp (SSDATA (SYMBOL_NAME (Qmeta)), s, 10))
4829     return Qmeta;
4830   else if (!strncmp (SSDATA (SYMBOL_NAME (Qsuper)), s, 10))
4831     return Qsuper;
4832   else if (!strncmp (SSDATA (SYMBOL_NAME (Qcontrol)), s, 10))
4833     return Qcontrol;
4834   else if (!strncmp (SSDATA (SYMBOL_NAME (Qalt)), s, 10))
4835     return Qalt;
4836   else if (!strncmp (SSDATA (SYMBOL_NAME (Qhyper)), s, 10))
4837     return Qhyper;
4838   else if (!strncmp (SSDATA (SYMBOL_NAME (Qnone)), s, 10))
4839     return Qnone;
4840   else
4841     return Qnil;
4845 static void
4846 ns_default (const char *parameter, Lisp_Object *result,
4847            Lisp_Object yesval, Lisp_Object noval,
4848            BOOL is_float, BOOL is_modstring)
4849 /* --------------------------------------------------------------------------
4850       Check a parameter value in user's preferences
4851    -------------------------------------------------------------------------- */
4853   const char *value = ns_get_defaults_value (parameter);
4855   if (value)
4856     {
4857       double f;
4858       char *pos;
4859       if (c_strcasecmp (value, "YES") == 0)
4860         *result = yesval;
4861       else if (c_strcasecmp (value, "NO") == 0)
4862         *result = noval;
4863       else if (is_float && (f = strtod (value, &pos), pos != value))
4864         *result = make_float (f);
4865       else if (is_modstring && value)
4866         *result = ns_string_to_lispmod (value);
4867       else fprintf (stderr,
4868                    "Bad value for default \"%s\": \"%s\"\n", parameter, value);
4869     }
4873 static void
4874 ns_initialize_display_info (struct ns_display_info *dpyinfo)
4875 /* --------------------------------------------------------------------------
4876       Initialize global info and storage for display.
4877    -------------------------------------------------------------------------- */
4879     NSScreen *screen = [NSScreen mainScreen];
4880     NSWindowDepth depth = [screen depth];
4882     dpyinfo->resx = 72.27; /* used 75.0, but this makes pt == pixel, expected */
4883     dpyinfo->resy = 72.27;
4884     dpyinfo->color_p = ![NSDeviceWhiteColorSpace isEqualToString:
4885                                                   NSColorSpaceFromDepth (depth)]
4886                 && ![NSCalibratedWhiteColorSpace isEqualToString:
4887                                                  NSColorSpaceFromDepth (depth)];
4888     dpyinfo->n_planes = NSBitsPerPixelFromDepth (depth);
4889     dpyinfo->color_table = xmalloc (sizeof *dpyinfo->color_table);
4890     dpyinfo->color_table->colors = NULL;
4891     dpyinfo->root_window = 42; /* a placeholder.. */
4892     dpyinfo->x_highlight_frame = dpyinfo->x_focus_frame = NULL;
4893     dpyinfo->n_fonts = 0;
4894     dpyinfo->smallest_font_height = 1;
4895     dpyinfo->smallest_char_width = 1;
4897     reset_mouse_highlight (&dpyinfo->mouse_highlight);
4901 /* This and next define (many of the) public functions in this file. */
4902 /* x_... are generic versions in xdisp.c that we, and other terms, get away
4903          with using despite presence in the "system dependent" redisplay
4904          interface.  In addition, many of the ns_ methods have code that is
4905          shared with all terms, indicating need for further refactoring. */
4906 extern frame_parm_handler ns_frame_parm_handlers[];
4907 static struct redisplay_interface ns_redisplay_interface =
4909   ns_frame_parm_handlers,
4910   x_produce_glyphs,
4911   x_write_glyphs,
4912   x_insert_glyphs,
4913   x_clear_end_of_line,
4914   ns_scroll_run,
4915   ns_after_update_window_line,
4916   ns_update_window_begin,
4917   ns_update_window_end,
4918   0, /* flush_display */
4919   x_clear_window_mouse_face,
4920   x_get_glyph_overhangs,
4921   x_fix_overlapping_area,
4922   ns_draw_fringe_bitmap,
4923   0, /* define_fringe_bitmap */ /* FIXME: simplify ns_draw_fringe_bitmap */
4924   0, /* destroy_fringe_bitmap */
4925   ns_compute_glyph_string_overhangs,
4926   ns_draw_glyph_string,
4927   ns_define_frame_cursor,
4928   ns_clear_frame_area,
4929   ns_draw_window_cursor,
4930   ns_draw_vertical_window_border,
4931   ns_draw_window_divider,
4932   ns_shift_glyphs_for_insert,
4933   ns_show_hourglass,
4934   ns_hide_hourglass
4938 static void
4939 ns_delete_display (struct ns_display_info *dpyinfo)
4941   /* TODO... */
4945 /* This function is called when the last frame on a display is deleted. */
4946 static void
4947 ns_delete_terminal (struct terminal *terminal)
4949   struct ns_display_info *dpyinfo = terminal->display_info.ns;
4951   NSTRACE ("ns_delete_terminal");
4953   /* Protect against recursive calls.  delete_frame in
4954      delete_terminal calls us back when it deletes our last frame.  */
4955   if (!terminal->name)
4956     return;
4958   block_input ();
4960   x_destroy_all_bitmaps (dpyinfo);
4961   ns_delete_display (dpyinfo);
4962   unblock_input ();
4966 static struct terminal *
4967 ns_create_terminal (struct ns_display_info *dpyinfo)
4968 /* --------------------------------------------------------------------------
4969       Set up use of NS before we make the first connection.
4970    -------------------------------------------------------------------------- */
4972   struct terminal *terminal;
4974   NSTRACE ("ns_create_terminal");
4976   terminal = create_terminal (output_ns, &ns_redisplay_interface);
4978   terminal->display_info.ns = dpyinfo;
4979   dpyinfo->terminal = terminal;
4981   terminal->clear_frame_hook = ns_clear_frame;
4982   terminal->ring_bell_hook = ns_ring_bell;
4983   terminal->update_begin_hook = ns_update_begin;
4984   terminal->update_end_hook = ns_update_end;
4985   terminal->read_socket_hook = ns_read_socket;
4986   terminal->frame_up_to_date_hook = ns_frame_up_to_date;
4987   terminal->mouse_position_hook = ns_mouse_position;
4988   terminal->frame_rehighlight_hook = ns_frame_rehighlight;
4989   terminal->frame_raise_lower_hook = ns_frame_raise_lower;
4990   terminal->fullscreen_hook = ns_fullscreen_hook;
4991   terminal->menu_show_hook = ns_menu_show;
4992   terminal->popup_dialog_hook = ns_popup_dialog;
4993   terminal->set_vertical_scroll_bar_hook = ns_set_vertical_scroll_bar;
4994   terminal->set_horizontal_scroll_bar_hook = ns_set_horizontal_scroll_bar;
4995   terminal->condemn_scroll_bars_hook = ns_condemn_scroll_bars;
4996   terminal->redeem_scroll_bar_hook = ns_redeem_scroll_bar;
4997   terminal->judge_scroll_bars_hook = ns_judge_scroll_bars;
4998   terminal->delete_frame_hook = x_destroy_window;
4999   terminal->delete_terminal_hook = ns_delete_terminal;
5000   /* Other hooks are NULL by default.  */
5002   return terminal;
5006 struct ns_display_info *
5007 ns_term_init (Lisp_Object display_name)
5008 /* --------------------------------------------------------------------------
5009      Start the Application and get things rolling.
5010    -------------------------------------------------------------------------- */
5012   struct terminal *terminal;
5013   struct ns_display_info *dpyinfo;
5014   static int ns_initialized = 0;
5015   Lisp_Object tmp;
5017   if (ns_initialized) return x_display_list;
5018   ns_initialized = 1;
5020   block_input ();
5022   NSTRACE ("ns_term_init");
5024   [outerpool release];
5025   outerpool = [[NSAutoreleasePool alloc] init];
5027   /* count object allocs (About, click icon); on macOS use ObjectAlloc tool */
5028   /*GSDebugAllocationActive (YES); */
5029   block_input ();
5031   baud_rate = 38400;
5032   Fset_input_interrupt_mode (Qnil);
5034   if (selfds[0] == -1)
5035     {
5036       if (emacs_pipe (selfds) != 0)
5037         {
5038           fprintf (stderr, "Failed to create pipe: %s\n",
5039                    emacs_strerror (errno));
5040           emacs_abort ();
5041         }
5043       fcntl (selfds[0], F_SETFL, O_NONBLOCK|fcntl (selfds[0], F_GETFL));
5044       FD_ZERO (&select_readfds);
5045       FD_ZERO (&select_writefds);
5046       pthread_mutex_init (&select_mutex, NULL);
5047     }
5049   ns_pending_files = [[NSMutableArray alloc] init];
5050   ns_pending_service_names = [[NSMutableArray alloc] init];
5051   ns_pending_service_args = [[NSMutableArray alloc] init];
5053 /* Start app and create the main menu, window, view.
5054      Needs to be here because ns_initialize_display_info () uses AppKit classes.
5055      The view will then ask the NSApp to stop and return to Emacs. */
5056   [EmacsApp sharedApplication];
5057   if (NSApp == nil)
5058     return NULL;
5059   [NSApp setDelegate: NSApp];
5061   /* Start the select thread.  */
5062   [NSThread detachNewThreadSelector:@selector (fd_handler:)
5063                            toTarget:NSApp
5064                          withObject:nil];
5066   /* debugging: log all notifications */
5067   /*   [[NSNotificationCenter defaultCenter] addObserver: NSApp
5068                                          selector: @selector (logNotification:)
5069                                              name: nil object: nil]; */
5071   dpyinfo = xzalloc (sizeof *dpyinfo);
5073   ns_initialize_display_info (dpyinfo);
5074   terminal = ns_create_terminal (dpyinfo);
5076   terminal->kboard = allocate_kboard (Qns);
5077   /* Don't let the initial kboard remain current longer than necessary.
5078      That would cause problems if a file loaded on startup tries to
5079      prompt in the mini-buffer.  */
5080   if (current_kboard == initial_kboard)
5081     current_kboard = terminal->kboard;
5082   terminal->kboard->reference_count++;
5084   dpyinfo->next = x_display_list;
5085   x_display_list = dpyinfo;
5087   dpyinfo->name_list_element = Fcons (display_name, Qnil);
5089   terminal->name = xlispstrdup (display_name);
5091   unblock_input ();
5093   if (!inhibit_x_resources)
5094     {
5095       ns_default ("GSFontAntiAlias", &ns_antialias_text,
5096                  Qt, Qnil, NO, NO);
5097       tmp = Qnil;
5098       /* this is a standard variable */
5099       ns_default ("AppleAntiAliasingThreshold", &tmp,
5100                  make_float (10.0), make_float (6.0), YES, NO);
5101       ns_antialias_threshold = NILP (tmp) ? 10.0 : extract_float (tmp);
5102     }
5104   NSTRACE_MSG ("Colors");
5106   {
5107     NSColorList *cl = [NSColorList colorListNamed: @"Emacs"];
5109     if ( cl == nil )
5110       {
5111         Lisp_Object color_file, color_map, color;
5112         unsigned long c;
5113         char *name;
5115         color_file = Fexpand_file_name (build_string ("rgb.txt"),
5116                          Fsymbol_value (intern ("data-directory")));
5118         color_map = Fx_load_color_file (color_file);
5119         if (NILP (color_map))
5120           fatal ("Could not read %s.\n", SDATA (color_file));
5122         cl = [[NSColorList alloc] initWithName: @"Emacs"];
5123         for ( ; CONSP (color_map); color_map = XCDR (color_map))
5124           {
5125             color = XCAR (color_map);
5126             name = SSDATA (XCAR (color));
5127             c = XINT (XCDR (color));
5128             [cl setColor:
5129                   [NSColor colorForEmacsRed: RED_FROM_ULONG (c) / 255.0
5130                                       green: GREEN_FROM_ULONG (c) / 255.0
5131                                        blue: BLUE_FROM_ULONG (c) / 255.0
5132                                       alpha: 1.0]
5133                   forKey: [NSString stringWithUTF8String: name]];
5134           }
5135         [cl writeToFile: nil];
5136       }
5137   }
5139   NSTRACE_MSG ("Versions");
5141   {
5142 #ifdef NS_IMPL_GNUSTEP
5143     Vwindow_system_version = build_string (gnustep_base_version);
5144 #else
5145     /*PSnextrelease (128, c); */
5146     char c[DBL_BUFSIZE_BOUND];
5147     int len = dtoastr (c, sizeof c, 0, 0, NSAppKitVersionNumber);
5148     Vwindow_system_version = make_unibyte_string (c, len);
5149 #endif
5150   }
5152   delete_keyboard_wait_descriptor (0);
5154   ns_app_name = [[NSProcessInfo processInfo] processName];
5156   /* Set up macOS app menu */
5158   NSTRACE_MSG ("Menu init");
5160 #ifdef NS_IMPL_COCOA
5161   {
5162     NSMenu *appMenu;
5163     NSMenuItem *item;
5164     /* set up the application menu */
5165     svcsMenu = [[EmacsMenu alloc] initWithTitle: @"Services"];
5166     [svcsMenu setAutoenablesItems: NO];
5167     appMenu = [[EmacsMenu alloc] initWithTitle: @"Emacs"];
5168     [appMenu setAutoenablesItems: NO];
5169     mainMenu = [[EmacsMenu alloc] initWithTitle: @""];
5170     dockMenu = [[EmacsMenu alloc] initWithTitle: @""];
5172     [appMenu insertItemWithTitle: @"About Emacs"
5173                           action: @selector (orderFrontStandardAboutPanel:)
5174                    keyEquivalent: @""
5175                          atIndex: 0];
5176     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 1];
5177     [appMenu insertItemWithTitle: @"Preferences..."
5178                           action: @selector (showPreferencesWindow:)
5179                    keyEquivalent: @","
5180                          atIndex: 2];
5181     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 3];
5182     item = [appMenu insertItemWithTitle: @"Services"
5183                                  action: @selector (menuDown:)
5184                           keyEquivalent: @""
5185                                 atIndex: 4];
5186     [appMenu setSubmenu: svcsMenu forItem: item];
5187     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 5];
5188     [appMenu insertItemWithTitle: @"Hide Emacs"
5189                           action: @selector (hide:)
5190                    keyEquivalent: @"h"
5191                          atIndex: 6];
5192     item =  [appMenu insertItemWithTitle: @"Hide Others"
5193                           action: @selector (hideOtherApplications:)
5194                    keyEquivalent: @"h"
5195                          atIndex: 7];
5196     [item setKeyEquivalentModifierMask: NSEventModifierFlagCommand | NSEventModifierFlagOption];
5197     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 8];
5198     [appMenu insertItemWithTitle: @"Quit Emacs"
5199                           action: @selector (terminate:)
5200                    keyEquivalent: @"q"
5201                          atIndex: 9];
5203     item = [mainMenu insertItemWithTitle: ns_app_name
5204                                   action: @selector (menuDown:)
5205                            keyEquivalent: @""
5206                                  atIndex: 0];
5207     [mainMenu setSubmenu: appMenu forItem: item];
5208     [dockMenu insertItemWithTitle: @"New Frame"
5209                            action: @selector (newFrame:)
5210                     keyEquivalent: @""
5211                           atIndex: 0];
5213     [NSApp setMainMenu: mainMenu];
5214     [NSApp setAppleMenu: appMenu];
5215     [NSApp setServicesMenu: svcsMenu];
5216     /* Needed at least on Cocoa, to get dock menu to show windows */
5217     [NSApp setWindowsMenu: [[NSMenu alloc] init]];
5219     [[NSNotificationCenter defaultCenter]
5220       addObserver: mainMenu
5221          selector: @selector (trackingNotification:)
5222              name: NSMenuDidBeginTrackingNotification object: mainMenu];
5223     [[NSNotificationCenter defaultCenter]
5224       addObserver: mainMenu
5225          selector: @selector (trackingNotification:)
5226              name: NSMenuDidEndTrackingNotification object: mainMenu];
5227   }
5228 #endif /* macOS menu setup */
5230   /* Register our external input/output types, used for determining
5231      applicable services and also drag/drop eligibility. */
5233   NSTRACE_MSG ("Input/output types");
5235   ns_send_types = [[NSArray arrayWithObjects: NSStringPboardType, nil] retain];
5236   ns_return_types = [[NSArray arrayWithObjects: NSStringPboardType, nil]
5237                       retain];
5238   ns_drag_types = [[NSArray arrayWithObjects:
5239                             NSStringPboardType,
5240                             NSTabularTextPboardType,
5241                             NSFilenamesPboardType,
5242                             NSURLPboardType, nil] retain];
5244   /* If fullscreen is in init/default-frame-alist, focus isn't set
5245      right for fullscreen windows, so set this.  */
5246   [NSApp activateIgnoringOtherApps:YES];
5248   NSTRACE_MSG ("Call NSApp run");
5250   [NSApp run];
5251   ns_do_open_file = YES;
5253 #ifdef NS_IMPL_GNUSTEP
5254   /* GNUstep steals SIGCHLD for use in NSTask, but we don't use NSTask.
5255      We must re-catch it so subprocess works.  */
5256   catch_child_signal ();
5257 #endif
5259   NSTRACE_MSG ("ns_term_init done");
5261   unblock_input ();
5263   return dpyinfo;
5267 void
5268 ns_term_shutdown (int sig)
5270   [[NSUserDefaults standardUserDefaults] synchronize];
5272   /* code not reached in emacs.c after this is called by shut_down_emacs: */
5273   if (STRINGP (Vauto_save_list_file_name))
5274     unlink (SSDATA (Vauto_save_list_file_name));
5276   if (sig == 0 || sig == SIGTERM)
5277     {
5278       [NSApp terminate: NSApp];
5279     }
5280   else // force a stack trace to happen
5281     {
5282       emacs_abort ();
5283     }
5287 /* ==========================================================================
5289     EmacsApp implementation
5291    ========================================================================== */
5294 @implementation EmacsApp
5296 - (id)init
5298   NSTRACE ("[EmacsApp init]");
5300   if ((self = [super init]))
5301     {
5302 #ifdef NS_IMPL_COCOA
5303       self->isFirst = YES;
5304 #endif
5305 #ifdef NS_IMPL_GNUSTEP
5306       self->applicationDidFinishLaunchingCalled = NO;
5307 #endif
5308     }
5310   return self;
5313 #ifdef NS_IMPL_COCOA
5314 - (void)run
5316   NSTRACE ("[EmacsApp run]");
5318 #ifndef NSAppKitVersionNumber10_9
5319 #define NSAppKitVersionNumber10_9 1265
5320 #endif
5322     if ((int)NSAppKitVersionNumber != NSAppKitVersionNumber10_9)
5323       {
5324         [super run];
5325         return;
5326       }
5328   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5330   if (isFirst) [self finishLaunching];
5331   isFirst = NO;
5333   shouldKeepRunning = YES;
5334   do
5335     {
5336       [pool release];
5337       pool = [[NSAutoreleasePool alloc] init];
5339       NSEvent *event =
5340         [self nextEventMatchingMask:NSEventMaskAny
5341                           untilDate:[NSDate distantFuture]
5342                              inMode:NSDefaultRunLoopMode
5343                             dequeue:YES];
5345       [self sendEvent:event];
5346       [self updateWindows];
5347     } while (shouldKeepRunning);
5349   [pool release];
5352 - (void)stop: (id)sender
5354   NSTRACE ("[EmacsApp stop:]");
5356     shouldKeepRunning = NO;
5357     // Stop possible dialog also.  Noop if no dialog present.
5358     // The file dialog still leaks 7k - 10k on 10.9 though.
5359     [super stop:sender];
5361 #endif /* NS_IMPL_COCOA */
5363 - (void)logNotification: (NSNotification *)notification
5365   NSTRACE ("[EmacsApp logNotification:]");
5367   const char *name = [[notification name] UTF8String];
5368   if (!strstr (name, "Update") && !strstr (name, "NSMenu")
5369       && !strstr (name, "WindowNumber"))
5370     NSLog (@"notification: '%@'", [notification name]);
5374 - (void)sendEvent: (NSEvent *)theEvent
5375 /* --------------------------------------------------------------------------
5376      Called when NSApp is running for each event received.  Used to stop
5377      the loop when we choose, since there's no way to just run one iteration.
5378    -------------------------------------------------------------------------- */
5380   int type = [theEvent type];
5381   NSWindow *window = [theEvent window];
5383   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsApp sendEvent:]");
5384   NSTRACE_MSG ("Type: %d", type);
5386 #ifdef NS_IMPL_GNUSTEP
5387   // Keyboard events aren't propagated to file dialogs for some reason.
5388   if ([NSApp modalWindow] != nil &&
5389       (type == NSEventTypeKeyDown || type == NSEventTypeKeyUp || type == NSEventTypeFlagsChanged))
5390     {
5391       [[NSApp modalWindow] sendEvent: theEvent];
5392       return;
5393     }
5394 #endif
5396   if (represented_filename != nil && represented_frame)
5397     {
5398       NSString *fstr = represented_filename;
5399       NSView *view = FRAME_NS_VIEW (represented_frame);
5400 #ifdef NS_IMPL_COCOA
5401       /* work around a bug observed on 10.3 and later where
5402          setTitleWithRepresentedFilename does not clear out previous state
5403          if given filename does not exist */
5404       if (! [[NSFileManager defaultManager] fileExistsAtPath: fstr])
5405         [[view window] setRepresentedFilename: @""];
5406 #endif
5407       [[view window] setRepresentedFilename: fstr];
5408       [represented_filename release];
5409       represented_filename = nil;
5410       represented_frame = NULL;
5411     }
5413   if (type == NSEventTypeApplicationDefined)
5414     {
5415       switch ([theEvent data2])
5416         {
5417 #ifdef NS_IMPL_COCOA
5418         case NSAPP_DATA2_RUNASSCRIPT:
5419           ns_run_ascript ();
5420           [self stop: self];
5421           return;
5422 #endif
5423         case NSAPP_DATA2_RUNFILEDIALOG:
5424           ns_run_file_dialog ();
5425           [self stop: self];
5426           return;
5427         }
5428     }
5430   if (type == NSEventTypeCursorUpdate && window == nil)
5431     {
5432       fprintf (stderr, "Dropping external cursor update event.\n");
5433       return;
5434     }
5436   if (type == NSEventTypeApplicationDefined)
5437     {
5438       /* Events posted by ns_send_appdefined interrupt the run loop here.
5439          But, if a modal window is up, an appdefined can still come through,
5440          (e.g., from a makeKeyWindow event) but stopping self also stops the
5441          modal loop. Just defer it until later. */
5442       if ([NSApp modalWindow] == nil)
5443         {
5444           last_appdefined_event_data = [theEvent data1];
5445           [self stop: self];
5446         }
5447       else
5448         {
5449           send_appdefined = YES;
5450         }
5451     }
5454 #ifdef NS_IMPL_COCOA
5455   /* If no dialog and none of our frames have focus and it is a move, skip it.
5456      It is a mouse move in an auxiliary menu, i.e. on the top right on macOS,
5457      such as Wifi, sound, date or similar.
5458      This prevents "spooky" highlighting in the frame under the menu.  */
5459   if (type == NSEventTypeMouseMoved && [NSApp modalWindow] == nil)
5460     {
5461       struct ns_display_info *di;
5462       BOOL has_focus = NO;
5463       for (di = x_display_list; ! has_focus && di; di = di->next)
5464         has_focus = di->x_focus_frame != 0;
5465       if (! has_focus)
5466         return;
5467     }
5468 #endif
5470   NSTRACE_UNSILENCE();
5472   [super sendEvent: theEvent];
5476 - (void)showPreferencesWindow: (id)sender
5478   struct frame *emacsframe = SELECTED_FRAME ();
5479   NSEvent *theEvent = [NSApp currentEvent];
5481   if (!emacs_event)
5482     return;
5483   emacs_event->kind = NS_NONKEY_EVENT;
5484   emacs_event->code = KEY_NS_SHOW_PREFS;
5485   emacs_event->modifiers = 0;
5486   EV_TRAILER (theEvent);
5490 - (void)newFrame: (id)sender
5492   NSTRACE ("[EmacsApp newFrame:]");
5494   struct frame *emacsframe = SELECTED_FRAME ();
5495   NSEvent *theEvent = [NSApp currentEvent];
5497   if (!emacs_event)
5498     return;
5499   emacs_event->kind = NS_NONKEY_EVENT;
5500   emacs_event->code = KEY_NS_NEW_FRAME;
5501   emacs_event->modifiers = 0;
5502   EV_TRAILER (theEvent);
5506 /* Open a file (used by below, after going into queue read by ns_read_socket) */
5507 - (BOOL) openFile: (NSString *)fileName
5509   NSTRACE ("[EmacsApp openFile:]");
5511   struct frame *emacsframe = SELECTED_FRAME ();
5512   NSEvent *theEvent = [NSApp currentEvent];
5514   if (!emacs_event)
5515     return NO;
5517   emacs_event->kind = NS_NONKEY_EVENT;
5518   emacs_event->code = KEY_NS_OPEN_FILE_LINE;
5519   ns_input_file = append2 (ns_input_file, build_string ([fileName UTF8String]));
5520   ns_input_line = Qnil; /* can be start or cons start,end */
5521   emacs_event->modifiers =0;
5522   EV_TRAILER (theEvent);
5524   return YES;
5528 /* **************************************************************************
5530       EmacsApp delegate implementation
5532    ************************************************************************** */
5534 - (void)applicationDidFinishLaunching: (NSNotification *)notification
5535 /* --------------------------------------------------------------------------
5536      When application is loaded, terminate event loop in ns_term_init
5537    -------------------------------------------------------------------------- */
5539   NSTRACE ("[EmacsApp applicationDidFinishLaunching:]");
5541 #ifdef NS_IMPL_GNUSTEP
5542   ((EmacsApp *)self)->applicationDidFinishLaunchingCalled = YES;
5543 #endif
5544   [NSApp setServicesProvider: NSApp];
5546   [self antialiasThresholdDidChange:nil];
5547 #ifdef NS_IMPL_COCOA
5548   [[NSNotificationCenter defaultCenter]
5549     addObserver:self
5550        selector:@selector(antialiasThresholdDidChange:)
5551            name:NSAntialiasThresholdChangedNotification
5552          object:nil];
5553 #endif
5555 #ifdef NS_IMPL_COCOA
5556   if ([NSApp activationPolicy] == NSApplicationActivationPolicyProhibited) {
5557     /* Set the app's activation policy to regular when we run outside
5558        of a bundle.  This is already done for us by Info.plist when we
5559        run inside a bundle. */
5560     [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
5561     [NSApp setApplicationIconImage:
5562              [EmacsImage
5563                allocInitFromFile:
5564                  build_string("icons/hicolor/128x128/apps/emacs.png")]];
5565   }
5566 #endif
5568   ns_send_appdefined (-2);
5571 - (void)antialiasThresholdDidChange:(NSNotification *)notification
5573 #ifdef NS_IMPL_COCOA
5574   macfont_update_antialias_threshold ();
5575 #endif
5579 /* Termination sequences:
5580     C-x C-c:
5581     Cmd-Q:
5582     MenuBar | File | Exit:
5583     Select Quit from App menubar:
5584         -terminate
5585         KEY_NS_POWER_OFF, (save-buffers-kill-emacs)
5586         ns_term_shutdown()
5588     Select Quit from Dock menu:
5589     Logout attempt:
5590         -appShouldTerminate
5591           Cancel -> Nothing else
5592           Accept ->
5594           -terminate
5595           KEY_NS_POWER_OFF, (save-buffers-kill-emacs)
5596           ns_term_shutdown()
5600 - (void) terminate: (id)sender
5602   NSTRACE ("[EmacsApp terminate:]");
5604   struct frame *emacsframe = SELECTED_FRAME ();
5606   if (!emacs_event)
5607     return;
5609   emacs_event->kind = NS_NONKEY_EVENT;
5610   emacs_event->code = KEY_NS_POWER_OFF;
5611   emacs_event->arg = Qt; /* mark as non-key event */
5612   EV_TRAILER ((id)nil);
5615 static bool
5616 runAlertPanel(NSString *title,
5617               NSString *msgFormat,
5618               NSString *defaultButton,
5619               NSString *alternateButton)
5621 #ifdef NS_IMPL_GNUSTEP
5622   return NSRunAlertPanel(title, msgFormat, defaultButton, alternateButton, nil)
5623     == NSAlertDefaultReturn;
5624 #else
5625   NSAlert *alert = [[NSAlert alloc] init];
5626   [alert setAlertStyle: NSAlertStyleCritical];
5627   [alert setMessageText: msgFormat];
5628   [alert addButtonWithTitle: defaultButton];
5629   [alert addButtonWithTitle: alternateButton];
5630   NSInteger ret = [alert runModal];
5631   [alert release];
5632   return ret == NSAlertFirstButtonReturn;
5633 #endif
5637 - (NSApplicationTerminateReply)applicationShouldTerminate: (id)sender
5639   NSTRACE ("[EmacsApp applicationShouldTerminate:]");
5641   bool ret;
5643   if (NILP (ns_confirm_quit)) //   || ns_shutdown_properly  --> TO DO
5644     return NSTerminateNow;
5646   ret = runAlertPanel(ns_app_name,
5647                       @"Exit requested.  Would you like to Save Buffers and Exit, or Cancel the request?",
5648                       @"Save Buffers and Exit", @"Cancel");
5650   return ret ? NSTerminateNow : NSTerminateCancel;
5653 static int
5654 not_in_argv (NSString *arg)
5656   int k;
5657   const char *a = [arg UTF8String];
5658   for (k = 1; k < initial_argc; ++k)
5659     if (strcmp (a, initial_argv[k]) == 0) return 0;
5660   return 1;
5663 /*   Notification from the Workspace to open a file */
5664 - (BOOL)application: sender openFile: (NSString *)file
5666   if (ns_do_open_file || not_in_argv (file))
5667     [ns_pending_files addObject: file];
5668   return YES;
5672 /*   Open a file as a temporary file */
5673 - (BOOL)application: sender openTempFile: (NSString *)file
5675   if (ns_do_open_file || not_in_argv (file))
5676     [ns_pending_files addObject: file];
5677   return YES;
5681 /*   Notification from the Workspace to open a file noninteractively (?) */
5682 - (BOOL)application: sender openFileWithoutUI: (NSString *)file
5684   if (ns_do_open_file || not_in_argv (file))
5685     [ns_pending_files addObject: file];
5686   return YES;
5689 /*   Notification from the Workspace to open multiple files */
5690 - (void)application: sender openFiles: (NSArray *)fileList
5692   NSEnumerator *files = [fileList objectEnumerator];
5693   NSString *file;
5694   /* Don't open files from the command line unconditionally,
5695      Cocoa parses the command line wrong, --option value tries to open value
5696      if --option is the last option.  */
5697   while ((file = [files nextObject]) != nil)
5698     if (ns_do_open_file || not_in_argv (file))
5699       [ns_pending_files addObject: file];
5701   [self replyToOpenOrPrint: NSApplicationDelegateReplySuccess];
5706 /* Handle dock menu requests.  */
5707 - (NSMenu *)applicationDockMenu: (NSApplication *) sender
5709   return dockMenu;
5713 /* TODO: these may help w/IO switching btwn terminal and NSApp */
5714 - (void)applicationWillBecomeActive: (NSNotification *)notification
5716   NSTRACE ("[EmacsApp applicationWillBecomeActive:]");
5717   //ns_app_active=YES;
5720 - (void)applicationDidBecomeActive: (NSNotification *)notification
5722   NSTRACE ("[EmacsApp applicationDidBecomeActive:]");
5724 #ifdef NS_IMPL_GNUSTEP
5725   if (! applicationDidFinishLaunchingCalled)
5726     [self applicationDidFinishLaunching:notification];
5727 #endif
5728   //ns_app_active=YES;
5730   ns_update_auto_hide_menu_bar ();
5731   // No constraining takes place when the application is not active.
5732   ns_constrain_all_frames ();
5734 - (void)applicationDidResignActive: (NSNotification *)notification
5736   NSTRACE ("[EmacsApp applicationDidResignActive:]");
5738   //ns_app_active=NO;
5739   ns_send_appdefined (-1);
5744 /* ==========================================================================
5746     EmacsApp aux handlers for managing event loop
5748    ========================================================================== */
5751 - (void)timeout_handler: (NSTimer *)timedEntry
5752 /* --------------------------------------------------------------------------
5753      The timeout specified to ns_select has passed.
5754    -------------------------------------------------------------------------- */
5756   /*NSTRACE ("timeout_handler"); */
5757   ns_send_appdefined (-2);
5760 - (void)sendFromMainThread:(id)unused
5762   ns_send_appdefined (nextappdefined);
5765 - (void)fd_handler:(id)unused
5766 /* --------------------------------------------------------------------------
5767      Check data waiting on file descriptors and terminate if so
5768    -------------------------------------------------------------------------- */
5770   int result;
5771   int waiting = 1, nfds;
5772   char c;
5774   fd_set readfds, writefds, *wfds;
5775   struct timespec timeout, *tmo;
5776   NSAutoreleasePool *pool = nil;
5778   /* NSTRACE ("fd_handler"); */
5780   for (;;)
5781     {
5782       [pool release];
5783       pool = [[NSAutoreleasePool alloc] init];
5785       if (waiting)
5786         {
5787           fd_set fds;
5788           FD_ZERO (&fds);
5789           FD_SET (selfds[0], &fds);
5790           result = select (selfds[0]+1, &fds, NULL, NULL, NULL);
5791           if (result > 0 && read (selfds[0], &c, 1) == 1 && c == 'g')
5792             waiting = 0;
5793         }
5794       else
5795         {
5796           pthread_mutex_lock (&select_mutex);
5797           nfds = select_nfds;
5799           if (select_valid & SELECT_HAVE_READ)
5800             readfds = select_readfds;
5801           else
5802             FD_ZERO (&readfds);
5804           if (select_valid & SELECT_HAVE_WRITE)
5805             {
5806               writefds = select_writefds;
5807               wfds = &writefds;
5808             }
5809           else
5810             wfds = NULL;
5811           if (select_valid & SELECT_HAVE_TMO)
5812             {
5813               timeout = select_timeout;
5814               tmo = &timeout;
5815             }
5816           else
5817             tmo = NULL;
5819           pthread_mutex_unlock (&select_mutex);
5821           FD_SET (selfds[0], &readfds);
5822           if (selfds[0] >= nfds) nfds = selfds[0]+1;
5824           result = pselect (nfds, &readfds, wfds, NULL, tmo, NULL);
5826           if (result == 0)
5827             ns_send_appdefined (-2);
5828           else if (result > 0)
5829             {
5830               if (FD_ISSET (selfds[0], &readfds))
5831                 {
5832                   if (read (selfds[0], &c, 1) == 1 && c == 's')
5833                     waiting = 1;
5834                 }
5835               else
5836                 {
5837                   pthread_mutex_lock (&select_mutex);
5838                   if (select_valid & SELECT_HAVE_READ)
5839                     select_readfds = readfds;
5840                   if (select_valid & SELECT_HAVE_WRITE)
5841                     select_writefds = writefds;
5842                   if (select_valid & SELECT_HAVE_TMO)
5843                     select_timeout = timeout;
5844                   pthread_mutex_unlock (&select_mutex);
5846                   ns_send_appdefined (result);
5847                 }
5848             }
5849           waiting = 1;
5850         }
5851     }
5856 /* ==========================================================================
5858     Service provision
5860    ========================================================================== */
5862 /* called from system: queue for next pass through event loop */
5863 - (void)requestService: (NSPasteboard *)pboard
5864               userData: (NSString *)userData
5865                  error: (NSString **)error
5867   [ns_pending_service_names addObject: userData];
5868   [ns_pending_service_args addObject: [NSString stringWithUTF8String:
5869       SSDATA (ns_string_from_pasteboard (pboard))]];
5873 /* called from ns_read_socket to clear queue */
5874 - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
5876   struct frame *emacsframe = SELECTED_FRAME ();
5877   NSEvent *theEvent = [NSApp currentEvent];
5879   NSTRACE ("[EmacsApp fulfillService:withArg:]");
5881   if (!emacs_event)
5882     return NO;
5884   emacs_event->kind = NS_NONKEY_EVENT;
5885   emacs_event->code = KEY_NS_SPI_SERVICE_CALL;
5886   ns_input_spi_name = build_string ([name UTF8String]);
5887   ns_input_spi_arg = build_string ([arg UTF8String]);
5888   emacs_event->modifiers = EV_MODIFIERS (theEvent);
5889   EV_TRAILER (theEvent);
5891   return YES;
5895 @end  /* EmacsApp */
5899 /* ==========================================================================
5901     EmacsView implementation
5903    ========================================================================== */
5906 @implementation EmacsView
5908 /* needed to inform when window closed from LISP */
5909 - (void) setWindowClosing: (BOOL)closing
5911   NSTRACE ("[EmacsView setWindowClosing:%d]", closing);
5913   windowClosing = closing;
5917 - (void)dealloc
5919   NSTRACE ("[EmacsView dealloc]");
5920   [toolbar release];
5921   if (fs_state == FULLSCREEN_BOTH)
5922     [nonfs_window release];
5923   [super dealloc];
5927 /* called on font panel selection */
5928 - (void)changeFont: (id)sender
5930   NSEvent *e = [[self window] currentEvent];
5931   struct face *face = FACE_FROM_ID (emacsframe, DEFAULT_FACE_ID);
5932   struct font *font = face->font;
5933   id newFont;
5934   CGFloat size;
5935   NSFont *nsfont;
5937   NSTRACE ("[EmacsView changeFont:]");
5939   if (!emacs_event)
5940     return;
5942 #ifdef NS_IMPL_GNUSTEP
5943   nsfont = ((struct nsfont_info *)font)->nsfont;
5944 #endif
5945 #ifdef NS_IMPL_COCOA
5946   nsfont = (NSFont *) macfont_get_nsctfont (font);
5947 #endif
5949   if ((newFont = [sender convertFont: nsfont]))
5950     {
5951       SET_FRAME_GARBAGED (emacsframe); /* now needed as of 2008/10 */
5953       emacs_event->kind = NS_NONKEY_EVENT;
5954       emacs_event->modifiers = 0;
5955       emacs_event->code = KEY_NS_CHANGE_FONT;
5957       size = [newFont pointSize];
5958       ns_input_fontsize = make_number (lrint (size));
5959       ns_input_font = build_string ([[newFont familyName] UTF8String]);
5960       EV_TRAILER (e);
5961     }
5965 - (BOOL)acceptsFirstResponder
5967   NSTRACE ("[EmacsView acceptsFirstResponder]");
5968   return YES;
5972 - (void)resetCursorRects
5974   NSRect visible = [self visibleRect];
5975   NSCursor *currentCursor = FRAME_POINTER_TYPE (emacsframe);
5976   NSTRACE ("[EmacsView resetCursorRects]");
5978   if (currentCursor == nil)
5979     currentCursor = [NSCursor arrowCursor];
5981   if (!NSIsEmptyRect (visible))
5982     [self addCursorRect: visible cursor: currentCursor];
5983   [currentCursor setOnMouseEntered: YES];
5988 /*****************************************************************************/
5989 /* Keyboard handling. */
5990 #define NS_KEYLOG 0
5992 - (void)keyDown: (NSEvent *)theEvent
5994   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
5995   int code;
5996   unsigned fnKeysym = 0;
5997   static NSMutableArray *nsEvArray;
5998   int left_is_none;
5999   unsigned int flags = [theEvent modifierFlags];
6001   NSTRACE ("[EmacsView keyDown:]");
6003   /* Rhapsody and macOS give up and down events for the arrow keys */
6004   if (ns_fake_keydown == YES)
6005     ns_fake_keydown = NO;
6006   else if ([theEvent type] != NSEventTypeKeyDown)
6007     return;
6009   if (!emacs_event)
6010     return;
6012  if (![[self window] isKeyWindow]
6013      && [[theEvent window] isKindOfClass: [EmacsWindow class]]
6014      /* we must avoid an infinite loop here. */
6015      && (EmacsView *)[[theEvent window] delegate] != self)
6016    {
6017      /* XXX: There is an occasional condition in which, when Emacs display
6018          updates a different frame from the current one, and temporarily
6019          selects it, then processes some interrupt-driven input
6020          (dispnew.c:3878), OS will send the event to the correct NSWindow, but
6021          for some reason that window has its first responder set to the NSView
6022          most recently updated (I guess), which is not the correct one. */
6023      [(EmacsView *)[[theEvent window] delegate] keyDown: theEvent];
6024      return;
6025    }
6027   if (nsEvArray == nil)
6028     nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1];
6030   [NSCursor setHiddenUntilMouseMoves: YES];
6032   if (hlinfo->mouse_face_hidden && INTEGERP (Vmouse_highlight))
6033     {
6034       clear_mouse_face (hlinfo);
6035       hlinfo->mouse_face_hidden = 1;
6036     }
6038   if (!processingCompose)
6039     {
6040       /* When using screen sharing, no left or right information is sent,
6041          so use Left key in those cases.  */
6042       int is_left_key, is_right_key;
6044       code = ([[theEvent charactersIgnoringModifiers] length] == 0) ?
6045         0 : [[theEvent charactersIgnoringModifiers] characterAtIndex: 0];
6047       /* (Carbon way: [theEvent keyCode]) */
6049       /* is it a "function key"? */
6050       /* Note: Sometimes a plain key will have the NSEventModifierFlagNumericPad
6051          flag set (this is probably a bug in the OS).
6052       */
6053       if (code < 0x00ff && (flags&NSEventModifierFlagNumericPad))
6054         {
6055           fnKeysym = ns_convert_key ([theEvent keyCode] | NSEventModifierFlagNumericPad);
6056         }
6057       if (fnKeysym == 0)
6058         {
6059           fnKeysym = ns_convert_key (code);
6060         }
6062       if (fnKeysym)
6063         {
6064           /* COUNTERHACK: map 'Delete' on upper-right main KB to 'Backspace',
6065              because Emacs treats Delete and KP-Delete same (in simple.el). */
6066           if ((fnKeysym == 0xFFFF && [theEvent keyCode] == 0x33)
6067 #ifdef NS_IMPL_GNUSTEP
6068               /*  GNUstep uses incompatible keycodes, even for those that are
6069                   supposed to be hardware independent.  Just check for delete.
6070                   Keypad delete does not have keysym 0xFFFF.
6071                   See https://savannah.gnu.org/bugs/?25395
6072               */
6073               || (fnKeysym == 0xFFFF && code == 127)
6074 #endif
6075             )
6076             code = 0xFF08; /* backspace */
6077           else
6078             code = fnKeysym;
6079         }
6081       /* are there modifiers? */
6082       emacs_event->modifiers = 0;
6084       if (flags & NSEventModifierFlagHelp)
6085           emacs_event->modifiers |= hyper_modifier;
6087       if (flags & NSEventModifierFlagShift)
6088         emacs_event->modifiers |= shift_modifier;
6090       is_right_key = (flags & NSRightCommandKeyMask) == NSRightCommandKeyMask;
6091       is_left_key = (flags & NSLeftCommandKeyMask) == NSLeftCommandKeyMask
6092         || (! is_right_key && (flags & NSEventModifierFlagCommand) == NSEventModifierFlagCommand);
6094       if (is_right_key)
6095         emacs_event->modifiers |= parse_solitary_modifier
6096           (EQ (ns_right_command_modifier, Qleft)
6097            ? ns_command_modifier
6098            : ns_right_command_modifier);
6100       if (is_left_key)
6101         {
6102           emacs_event->modifiers |= parse_solitary_modifier
6103             (ns_command_modifier);
6105           /* if super (default), take input manager's word so things like
6106              dvorak / qwerty layout work */
6107           if (EQ (ns_command_modifier, Qsuper)
6108               && !fnKeysym
6109               && [[theEvent characters] length] != 0)
6110             {
6111               /* XXX: the code we get will be unshifted, so if we have
6112                  a shift modifier, must convert ourselves */
6113               if (!(flags & NSEventModifierFlagShift))
6114                 code = [[theEvent characters] characterAtIndex: 0];
6115 #if 0
6116               /* this is ugly and also requires linking w/Carbon framework
6117                  (for LMGetKbdType) so for now leave this rare (?) case
6118                  undealt with.. in future look into CGEvent methods */
6119               else
6120                 {
6121                   long smv = GetScriptManagerVariable (smKeyScript);
6122                   Handle uchrHandle = GetResource
6123                     ('uchr', GetScriptVariable (smv, smScriptKeys));
6124                   UInt32 dummy = 0;
6125                   UCKeyTranslate ((UCKeyboardLayout *) *uchrHandle,
6126                                  [[theEvent characters] characterAtIndex: 0],
6127                                  kUCKeyActionDisplay,
6128                                  (flags & ~NSEventModifierFlagCommand) >> 8,
6129                                  LMGetKbdType (), kUCKeyTranslateNoDeadKeysMask,
6130                                  &dummy, 1, &dummy, &code);
6131                   code &= 0xFF;
6132                 }
6133 #endif
6134             }
6135         }
6137       is_right_key = (flags & NSRightControlKeyMask) == NSRightControlKeyMask;
6138       is_left_key = (flags & NSLeftControlKeyMask) == NSLeftControlKeyMask
6139         || (! is_right_key && (flags & NSEventModifierFlagControl) == NSEventModifierFlagControl);
6141       if (is_right_key)
6142           emacs_event->modifiers |= parse_solitary_modifier
6143               (EQ (ns_right_control_modifier, Qleft)
6144                ? ns_control_modifier
6145                : ns_right_control_modifier);
6147       if (is_left_key)
6148         emacs_event->modifiers |= parse_solitary_modifier
6149           (ns_control_modifier);
6151       if (flags & NS_FUNCTION_KEY_MASK && !fnKeysym)
6152           emacs_event->modifiers |=
6153             parse_solitary_modifier (ns_function_modifier);
6155       left_is_none = NILP (ns_alternate_modifier)
6156         || EQ (ns_alternate_modifier, Qnone);
6158       is_right_key = (flags & NSRightAlternateKeyMask)
6159         == NSRightAlternateKeyMask;
6160       is_left_key = (flags & NSLeftAlternateKeyMask) == NSLeftAlternateKeyMask
6161         || (! is_right_key
6162             && (flags & NSEventModifierFlagOption) == NSEventModifierFlagOption);
6164       if (is_right_key)
6165         {
6166           if ((NILP (ns_right_alternate_modifier)
6167                || EQ (ns_right_alternate_modifier, Qnone)
6168                || (EQ (ns_right_alternate_modifier, Qleft) && left_is_none))
6169               && !fnKeysym)
6170             {   /* accept pre-interp alt comb */
6171               if ([[theEvent characters] length] > 0)
6172                 code = [[theEvent characters] characterAtIndex: 0];
6173               /*HACK: clear lone shift modifier to stop next if from firing */
6174               if (emacs_event->modifiers == shift_modifier)
6175                 emacs_event->modifiers = 0;
6176             }
6177           else
6178             emacs_event->modifiers |= parse_solitary_modifier
6179               (EQ (ns_right_alternate_modifier, Qleft)
6180                ? ns_alternate_modifier
6181                : ns_right_alternate_modifier);
6182         }
6184       if (is_left_key) /* default = meta */
6185         {
6186           if (left_is_none && !fnKeysym)
6187             {   /* accept pre-interp alt comb */
6188               if ([[theEvent characters] length] > 0)
6189                 code = [[theEvent characters] characterAtIndex: 0];
6190               /*HACK: clear lone shift modifier to stop next if from firing */
6191               if (emacs_event->modifiers == shift_modifier)
6192                 emacs_event->modifiers = 0;
6193             }
6194           else
6195               emacs_event->modifiers |=
6196                 parse_solitary_modifier (ns_alternate_modifier);
6197         }
6199   if (NS_KEYLOG)
6200     fprintf (stderr, "keyDown: code =%x\tfnKey =%x\tflags = %x\tmods = %x\n",
6201              (unsigned) code, fnKeysym, flags, emacs_event->modifiers);
6203       /* if it was a function key or had modifiers, pass it directly to emacs */
6204       if (fnKeysym || (emacs_event->modifiers
6205                        && (emacs_event->modifiers != shift_modifier)
6206                        && [[theEvent charactersIgnoringModifiers] length] > 0))
6207 /*[[theEvent characters] length] */
6208         {
6209           emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
6210           if (code < 0x20)
6211             code |= (1<<28)|(3<<16);
6212           else if (code == 0x7f)
6213             code |= (1<<28)|(3<<16);
6214           else if (!fnKeysym)
6215             emacs_event->kind = code > 0xFF
6216               ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT;
6218           emacs_event->code = code;
6219           EV_TRAILER (theEvent);
6220           processingCompose = NO;
6221           return;
6222         }
6223     }
6226   if (NS_KEYLOG && !processingCompose)
6227     fprintf (stderr, "keyDown: Begin compose sequence.\n");
6229   processingCompose = YES;
6230   [nsEvArray addObject: theEvent];
6231   [self interpretKeyEvents: nsEvArray];
6232   [nsEvArray removeObject: theEvent];
6236 /* <NSTextInput> implementation (called through super interpretKeyEvents:]). */
6239 /* <NSTextInput>: called when done composing;
6240    NOTE: also called when we delete over working text, followed immed.
6241          by doCommandBySelector: deleteBackward: */
6242 - (void)insertText: (id)aString
6244   int code;
6245   int len = [(NSString *)aString length];
6246   int i;
6248   NSTRACE ("[EmacsView insertText:]");
6250   if (NS_KEYLOG)
6251     NSLog (@"insertText '%@'\tlen = %d", aString, len);
6252   processingCompose = NO;
6254   if (!emacs_event)
6255     return;
6257   /* first, clear any working text */
6258   if (workingText != nil)
6259     [self deleteWorkingText];
6261   /* now insert the string as keystrokes */
6262   for (i =0; i<len; i++)
6263     {
6264       code = [aString characterAtIndex: i];
6265       /* TODO: still need this? */
6266       if (code == 0x2DC)
6267         code = '~'; /* 0x7E */
6268       if (code != 32) /* Space */
6269         emacs_event->modifiers = 0;
6270       emacs_event->kind
6271         = code > 0xFF ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT;
6272       emacs_event->code = code;
6273       EV_TRAILER ((id)nil);
6274     }
6278 /* <NSTextInput>: inserts display of composing characters */
6279 - (void)setMarkedText: (id)aString selectedRange: (NSRange)selRange
6281   NSString *str = [aString respondsToSelector: @selector (string)] ?
6282     [aString string] : aString;
6284   NSTRACE ("[EmacsView setMarkedText:selectedRange:]");
6286   if (NS_KEYLOG)
6287     NSLog (@"setMarkedText '%@' len =%lu range %lu from %lu",
6288            str, (unsigned long)[str length],
6289            (unsigned long)selRange.length,
6290            (unsigned long)selRange.location);
6292   if (workingText != nil)
6293     [self deleteWorkingText];
6294   if ([str length] == 0)
6295     return;
6297   if (!emacs_event)
6298     return;
6300   processingCompose = YES;
6301   workingText = [str copy];
6302   ns_working_text = build_string ([workingText UTF8String]);
6304   emacs_event->kind = NS_TEXT_EVENT;
6305   emacs_event->code = KEY_NS_PUT_WORKING_TEXT;
6306   EV_TRAILER ((id)nil);
6310 /* delete display of composing characters [not in <NSTextInput>] */
6311 - (void)deleteWorkingText
6313   NSTRACE ("[EmacsView deleteWorkingText]");
6315   if (workingText == nil)
6316     return;
6317   if (NS_KEYLOG)
6318     NSLog(@"deleteWorkingText len =%lu\n", (unsigned long)[workingText length]);
6319   [workingText release];
6320   workingText = nil;
6321   processingCompose = NO;
6323   if (!emacs_event)
6324     return;
6326   emacs_event->kind = NS_TEXT_EVENT;
6327   emacs_event->code = KEY_NS_UNPUT_WORKING_TEXT;
6328   EV_TRAILER ((id)nil);
6332 - (BOOL)hasMarkedText
6334   NSTRACE ("[EmacsView hasMarkedText]");
6336   return workingText != nil;
6340 - (NSRange)markedRange
6342   NSTRACE ("[EmacsView markedRange]");
6344   NSRange rng = workingText != nil
6345     ? NSMakeRange (0, [workingText length]) : NSMakeRange (NSNotFound, 0);
6346   if (NS_KEYLOG)
6347     NSLog (@"markedRange request");
6348   return rng;
6352 - (void)unmarkText
6354   NSTRACE ("[EmacsView unmarkText]");
6356   if (NS_KEYLOG)
6357     NSLog (@"unmark (accept) text");
6358   [self deleteWorkingText];
6359   processingCompose = NO;
6363 /* used to position char selection windows, etc. */
6364 - (NSRect)firstRectForCharacterRange: (NSRange)theRange
6366   NSRect rect;
6367   NSPoint pt;
6368   struct window *win = XWINDOW (FRAME_SELECTED_WINDOW (emacsframe));
6370   NSTRACE ("[EmacsView firstRectForCharacterRange:]");
6372   if (NS_KEYLOG)
6373     NSLog (@"firstRectForCharRange request");
6375   rect.size.width = theRange.length * FRAME_COLUMN_WIDTH (emacsframe);
6376   rect.size.height = FRAME_LINE_HEIGHT (emacsframe);
6377   pt.x = WINDOW_TEXT_TO_FRAME_PIXEL_X (win, win->phys_cursor.x);
6378   pt.y = WINDOW_TO_FRAME_PIXEL_Y (win, win->phys_cursor.y
6379                                        +FRAME_LINE_HEIGHT (emacsframe));
6381   pt = [self convertPoint: pt toView: nil];
6383 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
6384 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6385   if ([[self window] respondsToSelector: @selector(convertRectToScreen:)])
6386     {
6387 #endif
6388       rect.origin = pt;
6389       rect = [(EmacsWindow *) [self window] convertRectToScreen: rect];
6390 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6391     }
6392   else
6393 #endif
6394 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
6395 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 \
6396   || defined (NS_IMPL_GNUSTEP)
6397     {
6398       pt = [[self window] convertBaseToScreen: pt];
6399       rect.origin = pt;
6400     }
6401 #endif
6403   return rect;
6407 - (NSInteger)conversationIdentifier
6409   return (NSInteger)self;
6413 - (void)doCommandBySelector: (SEL)aSelector
6415   NSTRACE ("[EmacsView doCommandBySelector:]");
6417   if (NS_KEYLOG)
6418     NSLog (@"doCommandBySelector: %@", NSStringFromSelector (aSelector));
6420   processingCompose = NO;
6421   if (aSelector == @selector (deleteBackward:))
6422     {
6423       /* happens when user backspaces over an ongoing composition:
6424          throw a 'delete' into the event queue */
6425       if (!emacs_event)
6426         return;
6427       emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
6428       emacs_event->code = 0xFF08;
6429       EV_TRAILER ((id)nil);
6430     }
6433 - (NSArray *)validAttributesForMarkedText
6435   static NSArray *arr = nil;
6436   if (arr == nil) arr = [NSArray new];
6437  /* [[NSArray arrayWithObject: NSUnderlineStyleAttributeName] retain]; */
6438   return arr;
6441 - (NSRange)selectedRange
6443   if (NS_KEYLOG)
6444     NSLog (@"selectedRange request");
6445   return NSMakeRange (NSNotFound, 0);
6448 #if defined (NS_IMPL_COCOA) || GNUSTEP_GUI_MAJOR_VERSION > 0 || \
6449     GNUSTEP_GUI_MINOR_VERSION > 22
6450 - (NSUInteger)characterIndexForPoint: (NSPoint)thePoint
6451 #else
6452 - (unsigned int)characterIndexForPoint: (NSPoint)thePoint
6453 #endif
6455   if (NS_KEYLOG)
6456     NSLog (@"characterIndexForPoint request");
6457   return 0;
6460 - (NSAttributedString *)attributedSubstringFromRange: (NSRange)theRange
6462   static NSAttributedString *str = nil;
6463   if (str == nil) str = [NSAttributedString new];
6464   if (NS_KEYLOG)
6465     NSLog (@"attributedSubstringFromRange request");
6466   return str;
6469 /* End <NSTextInput> impl. */
6470 /*****************************************************************************/
6473 /* This is what happens when the user presses a mouse button.  */
6474 - (void)mouseDown: (NSEvent *)theEvent
6476   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6477   NSPoint p = [self convertPoint: [theEvent locationInWindow] fromView: nil];
6479   NSTRACE ("[EmacsView mouseDown:]");
6481   [self deleteWorkingText];
6483   if (!emacs_event)
6484     return;
6486   dpyinfo->last_mouse_frame = emacsframe;
6487   /* appears to be needed to prevent spurious movement events generated on
6488      button clicks */
6489   emacsframe->mouse_moved = 0;
6491   if ([theEvent type] == NSEventTypeScrollWheel)
6492     {
6493 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
6494 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6495       if ([theEvent respondsToSelector:@selector(hasPreciseScrollingDeltas)])
6496         {
6497 #endif
6498           /* If the input device is a touchpad or similar, use precise
6499            * scrolling deltas.  These are measured in pixels, so we
6500            * have to add them up until they exceed one line height,
6501            * then we can send a scroll wheel event.
6502            *
6503            * If the device only has coarse scrolling deltas, like a
6504            * real mousewheel, the deltas represent a ratio of whole
6505            * lines, so round up the number of lines.  This means we
6506            * always send one scroll event per click, but can still
6507            * scroll more than one line if the OS tells us to.
6508            */
6509           bool horizontal;
6510           int lines = 0;
6511           int scrollUp = NO;
6513           /* FIXME: At the top or bottom of the buffer we should
6514            * ignore momentum-phase events.  */
6515           if (! ns_use_mwheel_momentum
6516               && [theEvent momentumPhase] != NSEventPhaseNone)
6517             return;
6519           if ([theEvent hasPreciseScrollingDeltas])
6520             {
6521               static int totalDeltaX, totalDeltaY;
6522               int lineHeight;
6524               if (NUMBERP (ns_mwheel_line_height))
6525                 lineHeight = XINT (ns_mwheel_line_height);
6526               else
6527                 {
6528                   /* FIXME: Use actual line height instead of the default.  */
6529                   lineHeight = default_line_pixel_height
6530                     (XWINDOW (FRAME_SELECTED_WINDOW (emacsframe)));
6531                 }
6533               if ([theEvent phase] == NSEventPhaseBegan)
6534                 {
6535                   totalDeltaX = 0;
6536                   totalDeltaY = 0;
6537                 }
6539               totalDeltaX += [theEvent scrollingDeltaX];
6540               totalDeltaY += [theEvent scrollingDeltaY];
6542               /* Calculate the number of lines, if any, to scroll, and
6543                * reset the total delta for the direction we're NOT
6544                * scrolling so that small movements don't add up.  */
6545               if (abs (totalDeltaX) > abs (totalDeltaY)
6546                   && abs (totalDeltaX) > lineHeight)
6547                 {
6548                   horizontal = YES;
6549                   scrollUp = totalDeltaX > 0;
6551                   lines = abs (totalDeltaX / lineHeight);
6552                   totalDeltaX = totalDeltaX % lineHeight;
6553                   totalDeltaY = 0;
6554                 }
6555               else if (abs (totalDeltaY) >= abs (totalDeltaX)
6556                        && abs (totalDeltaY) > lineHeight)
6557                 {
6558                   horizontal = NO;
6559                   scrollUp = totalDeltaY > 0;
6561                   lines = abs (totalDeltaY / lineHeight);
6562                   totalDeltaY = totalDeltaY % lineHeight;
6563                   totalDeltaX = 0;
6564                 }
6566               if (lines > 1 && ! ns_use_mwheel_acceleration)
6567                 lines = 1;
6568             }
6569           else
6570             {
6571               CGFloat delta;
6573               if ([theEvent scrollingDeltaY] == 0)
6574                 {
6575                   horizontal = YES;
6576                   delta = [theEvent scrollingDeltaX];
6577                 }
6578               else
6579                 {
6580                   horizontal = NO;
6581                   delta = [theEvent scrollingDeltaY];
6582                 }
6584               lines = (ns_use_mwheel_acceleration)
6585                 ? ceil (fabs (delta)) : 1;
6587               scrollUp = delta > 0;
6588             }
6590           if (lines == 0)
6591             return;
6593           emacs_event->kind = horizontal ? HORIZ_WHEEL_EVENT : WHEEL_EVENT;
6594           emacs_event->arg = (make_number (lines));
6596           emacs_event->code = 0;
6597           emacs_event->modifiers = EV_MODIFIERS (theEvent) |
6598             (scrollUp ? up_modifier : down_modifier);
6599 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6600         }
6601       else
6602 #endif
6603 #endif /* defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
6604 #if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6605         {
6606           CGFloat delta = [theEvent deltaY];
6607           /* Mac notebooks send wheel events w/delta =0 when trackpad scrolling */
6608           if (delta == 0)
6609             {
6610               delta = [theEvent deltaX];
6611               if (delta == 0)
6612                 {
6613                   NSTRACE_MSG ("deltaIsZero");
6614                   return;
6615                 }
6616               emacs_event->kind = HORIZ_WHEEL_EVENT;
6617             }
6618           else
6619             emacs_event->kind = WHEEL_EVENT;
6621           emacs_event->code = 0;
6622           emacs_event->modifiers = EV_MODIFIERS (theEvent) |
6623             ((delta > 0) ? up_modifier : down_modifier);
6624         }
6625 #endif
6626     }
6627   else
6628     {
6629       emacs_event->kind = MOUSE_CLICK_EVENT;
6630       emacs_event->code = EV_BUTTON (theEvent);
6631       emacs_event->modifiers = EV_MODIFIERS (theEvent)
6632                              | EV_UDMODIFIERS (theEvent);
6633     }
6635   XSETINT (emacs_event->x, lrint (p.x));
6636   XSETINT (emacs_event->y, lrint (p.y));
6637   EV_TRAILER (theEvent);
6638   return;
6642 - (void)rightMouseDown: (NSEvent *)theEvent
6644   NSTRACE ("[EmacsView rightMouseDown:]");
6645   [self mouseDown: theEvent];
6649 - (void)otherMouseDown: (NSEvent *)theEvent
6651   NSTRACE ("[EmacsView otherMouseDown:]");
6652   [self mouseDown: theEvent];
6656 - (void)mouseUp: (NSEvent *)theEvent
6658   NSTRACE ("[EmacsView mouseUp:]");
6659   [self mouseDown: theEvent];
6663 - (void)rightMouseUp: (NSEvent *)theEvent
6665   NSTRACE ("[EmacsView rightMouseUp:]");
6666   [self mouseDown: theEvent];
6670 - (void)otherMouseUp: (NSEvent *)theEvent
6672   NSTRACE ("[EmacsView otherMouseUp:]");
6673   [self mouseDown: theEvent];
6677 - (void) scrollWheel: (NSEvent *)theEvent
6679   NSTRACE ("[EmacsView scrollWheel:]");
6680   [self mouseDown: theEvent];
6684 /* Tell emacs the mouse has moved. */
6685 - (void)mouseMoved: (NSEvent *)e
6687   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
6688   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6689   Lisp_Object frame;
6690   NSPoint pt;
6692   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsView mouseMoved:]");
6694   dpyinfo->last_mouse_movement_time = EV_TIMESTAMP (e);
6695   pt = [self convertPoint: [e locationInWindow] fromView: nil];
6696   dpyinfo->last_mouse_motion_x = pt.x;
6697   dpyinfo->last_mouse_motion_y = pt.y;
6699   /* update any mouse face */
6700   if (hlinfo->mouse_face_hidden)
6701     {
6702       hlinfo->mouse_face_hidden = 0;
6703       clear_mouse_face (hlinfo);
6704     }
6706   /* tooltip handling */
6707   previous_help_echo_string = help_echo_string;
6708   help_echo_string = Qnil;
6710   if (!NILP (Vmouse_autoselect_window))
6711     {
6712       NSTRACE_MSG ("mouse_autoselect_window");
6713       static Lisp_Object last_mouse_window;
6714       Lisp_Object window
6715         = window_from_coordinates (emacsframe, pt.x, pt.y, 0, 0);
6717       if (WINDOWP (window)
6718           && !EQ (window, last_mouse_window)
6719           && !EQ (window, selected_window)
6720           && (!NILP (focus_follows_mouse)
6721               || (EQ (XWINDOW (window)->frame,
6722                       XWINDOW (selected_window)->frame))))
6723         {
6724           NSTRACE_MSG ("in_window");
6725           emacs_event->kind = SELECT_WINDOW_EVENT;
6726           emacs_event->frame_or_window = window;
6727           EV_TRAILER2 (e);
6728         }
6729       /* Remember the last window where we saw the mouse.  */
6730       last_mouse_window = window;
6731     }
6733   if (!note_mouse_movement (emacsframe, pt.x, pt.y))
6734     help_echo_string = previous_help_echo_string;
6736   XSETFRAME (frame, emacsframe);
6737   if (!NILP (help_echo_string) || !NILP (previous_help_echo_string))
6738     {
6739       /* NOTE: help_echo_{window,pos,object} are set in xdisp.c
6740          (note_mouse_highlight), which is called through the
6741          note_mouse_movement () call above */
6742       any_help_event_p = YES;
6743       gen_help_event (help_echo_string, frame, help_echo_window,
6744                       help_echo_object, help_echo_pos);
6745     }
6747   if (emacsframe->mouse_moved && send_appdefined)
6748     ns_send_appdefined (-1);
6752 - (void)mouseDragged: (NSEvent *)e
6754   NSTRACE ("[EmacsView mouseDragged:]");
6755   [self mouseMoved: e];
6759 - (void)rightMouseDragged: (NSEvent *)e
6761   NSTRACE ("[EmacsView rightMouseDragged:]");
6762   [self mouseMoved: e];
6766 - (void)otherMouseDragged: (NSEvent *)e
6768   NSTRACE ("[EmacsView otherMouseDragged:]");
6769   [self mouseMoved: e];
6773 - (BOOL)windowShouldClose: (id)sender
6775   NSEvent *e =[[self window] currentEvent];
6777   NSTRACE ("[EmacsView windowShouldClose:]");
6778   windowClosing = YES;
6779   if (!emacs_event)
6780     return NO;
6781   emacs_event->kind = DELETE_WINDOW_EVENT;
6782   emacs_event->modifiers = 0;
6783   emacs_event->code = 0;
6784   EV_TRAILER (e);
6785   /* Don't close this window, let this be done from lisp code.  */
6786   return NO;
6789 - (void) updateFrameSize: (BOOL) delay
6791   NSWindow *window = [self window];
6792   NSRect wr = [window frame];
6793   int extra = 0;
6794   int oldc = cols, oldr = rows;
6795   int oldw = FRAME_PIXEL_WIDTH (emacsframe);
6796   int oldh = FRAME_PIXEL_HEIGHT (emacsframe);
6797   int neww, newh;
6799   NSTRACE ("[EmacsView updateFrameSize:]");
6800   NSTRACE_SIZE ("Original size", NSMakeSize (oldw, oldh));
6801   NSTRACE_RECT ("Original frame", wr);
6802   NSTRACE_MSG  ("Original columns: %d", cols);
6803   NSTRACE_MSG  ("Original rows: %d", rows);
6805   if (! [self isFullscreen])
6806     {
6807 #ifdef NS_IMPL_GNUSTEP
6808       // GNUstep does not always update the tool bar height.  Force it.
6809       if (toolbar && [toolbar isVisible])
6810           update_frame_tool_bar (emacsframe);
6811 #endif
6813       extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
6814         + FRAME_TOOLBAR_HEIGHT (emacsframe);
6815     }
6817   if (wait_for_tool_bar)
6818     {
6819       /* The toolbar height is always 0 in fullscreen and undecorated
6820          frames, so don't wait for it to become available. */
6821       if (FRAME_TOOLBAR_HEIGHT (emacsframe) == 0
6822           && FRAME_UNDECORATED (emacsframe) == false
6823           && ! [self isFullscreen])
6824         {
6825           NSTRACE_MSG ("Waiting for toolbar");
6826           return;
6827         }
6828       wait_for_tool_bar = NO;
6829     }
6831   neww = (int)wr.size.width - emacsframe->border_width;
6832   newh = (int)wr.size.height - extra;
6834   NSTRACE_SIZE ("New size", NSMakeSize (neww, newh));
6835   NSTRACE_MSG ("FRAME_TOOLBAR_HEIGHT: %d", FRAME_TOOLBAR_HEIGHT (emacsframe));
6836   NSTRACE_MSG ("FRAME_NS_TITLEBAR_HEIGHT: %d", FRAME_NS_TITLEBAR_HEIGHT (emacsframe));
6838   cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, neww);
6839   rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe, newh);
6841   if (cols < MINWIDTH)
6842     cols = MINWIDTH;
6844   if (rows < MINHEIGHT)
6845     rows = MINHEIGHT;
6847   NSTRACE_MSG ("New columns: %d", cols);
6848   NSTRACE_MSG ("New rows: %d", rows);
6850   if (oldr != rows || oldc != cols || neww != oldw || newh != oldh)
6851     {
6852       NSView *view = FRAME_NS_VIEW (emacsframe);
6854       change_frame_size (emacsframe,
6855                          FRAME_PIXEL_TO_TEXT_WIDTH (emacsframe, neww),
6856                          FRAME_PIXEL_TO_TEXT_HEIGHT (emacsframe, newh),
6857                          0, delay, 0, 1);
6858       SET_FRAME_GARBAGED (emacsframe);
6859       cancel_mouse_face (emacsframe);
6861       /* The next two lines appear to be setting the frame to the same
6862          size as it already is.  Why are they there? */
6863       // wr = NSMakeRect (0, 0, neww, newh);
6865       // [view setFrame: wr];
6867       // to do: consider using [NSNotificationCenter postNotificationName:].
6868       [self windowDidMove: // Update top/left.
6869               [NSNotification notificationWithName:NSWindowDidMoveNotification
6870                                             object:[view window]]];
6871     }
6872   else
6873     {
6874       NSTRACE_MSG ("No change");
6875     }
6878 - (NSSize)windowWillResize: (NSWindow *)sender toSize: (NSSize)frameSize
6879 /* normalize frame to gridded text size */
6881   int extra = 0;
6883   NSTRACE ("[EmacsView windowWillResize:toSize: " NSTRACE_FMT_SIZE "]",
6884            NSTRACE_ARG_SIZE (frameSize));
6885   NSTRACE_RECT   ("[sender frame]", [sender frame]);
6886   NSTRACE_FSTYPE ("fs_state", fs_state);
6888   if (!FRAME_LIVE_P (emacsframe))
6889     return frameSize;
6891   if (fs_state == FULLSCREEN_MAXIMIZED
6892       && (maximized_width != (int)frameSize.width
6893           || maximized_height != (int)frameSize.height))
6894     [self setFSValue: FULLSCREEN_NONE];
6895   else if (fs_state == FULLSCREEN_WIDTH
6896            && maximized_width != (int)frameSize.width)
6897     [self setFSValue: FULLSCREEN_NONE];
6898   else if (fs_state == FULLSCREEN_HEIGHT
6899            && maximized_height != (int)frameSize.height)
6900     [self setFSValue: FULLSCREEN_NONE];
6902   if (fs_state == FULLSCREEN_NONE)
6903     maximized_width = maximized_height = -1;
6905   if (! [self isFullscreen])
6906     {
6907       extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
6908         + FRAME_TOOLBAR_HEIGHT (emacsframe);
6909     }
6911   cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, frameSize.width);
6912   if (cols < MINWIDTH)
6913     cols = MINWIDTH;
6915   rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe,
6916                                            frameSize.height - extra);
6917   if (rows < MINHEIGHT)
6918     rows = MINHEIGHT;
6919 #ifdef NS_IMPL_COCOA
6920   {
6921     /* this sets window title to have size in it; the wm does this under GS */
6922     NSRect r = [[self window] frame];
6923     if (r.size.height == frameSize.height && r.size.width == frameSize.width)
6924       {
6925         if (old_title != 0)
6926           {
6927             xfree (old_title);
6928             old_title = 0;
6929           }
6930       }
6931     else if (fs_state == FULLSCREEN_NONE && ! maximizing_resize
6932              && [[self window] title] != NULL)
6933       {
6934         char *size_title;
6935         NSWindow *window = [self window];
6936         if (old_title == 0)
6937           {
6938             char *t = strdup ([[[self window] title] UTF8String]);
6939             char *pos = strstr (t, "  â€”  ");
6940             if (pos)
6941               *pos = '\0';
6942             old_title = t;
6943           }
6944         size_title = xmalloc (strlen (old_title) + 40);
6945         esprintf (size_title, "%s  â€”  (%d x %d)", old_title, cols, rows);
6946         [window setTitle: [NSString stringWithUTF8String: size_title]];
6947         [window display];
6948         xfree (size_title);
6949       }
6950   }
6951 #endif /* NS_IMPL_COCOA */
6953   NSTRACE_MSG ("cols: %d  rows: %d", cols, rows);
6955   /* Restrict the new size to the text gird.
6957      Don't restrict the width if the user only adjusted the height, and
6958      vice versa.  (Without this, the frame would shrink, and move
6959      slightly, if the window was resized by dragging one of its
6960      borders.) */
6961   if (!frame_resize_pixelwise)
6962     {
6963       NSRect r = [[self window] frame];
6965       if (r.size.width != frameSize.width)
6966         {
6967           frameSize.width =
6968             FRAME_TEXT_COLS_TO_PIXEL_WIDTH  (emacsframe, cols);
6969         }
6971       if (r.size.height != frameSize.height)
6972         {
6973           frameSize.height =
6974             FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (emacsframe, rows) + extra;
6975         }
6976     }
6978   NSTRACE_RETURN_SIZE (frameSize);
6980   return frameSize;
6984 - (void)windowDidResize: (NSNotification *)notification
6986   NSTRACE ("[EmacsView windowDidResize:]");
6987   if (!FRAME_LIVE_P (emacsframe))
6988     {
6989       NSTRACE_MSG ("Ignored (frame dead)");
6990       return;
6991     }
6992   if (emacsframe->output_data.ns->in_animation)
6993     {
6994       NSTRACE_MSG ("Ignored (in animation)");
6995       return;
6996     }
6998   if (! [self fsIsNative])
6999     {
7000       NSWindow *theWindow = [notification object];
7001       /* We can get notification on the non-FS window when in
7002          fullscreen mode.  */
7003       if ([self window] != theWindow) return;
7004     }
7006   NSTRACE_RECT ("frame", [[notification object] frame]);
7008 #ifdef NS_IMPL_GNUSTEP
7009   NSWindow *theWindow = [notification object];
7011    /* In GNUstep, at least currently, it's possible to get a didResize
7012       without getting a willResize.. therefore we need to act as if we got
7013       the willResize now */
7014   NSSize sz = [theWindow frame].size;
7015   sz = [self windowWillResize: theWindow toSize: sz];
7016 #endif /* NS_IMPL_GNUSTEP */
7018   if (cols > 0 && rows > 0)
7019     {
7020       [self updateFrameSize: YES];
7021     }
7023   ns_send_appdefined (-1);
7026 #ifdef NS_IMPL_COCOA
7027 - (void)viewDidEndLiveResize
7029   NSTRACE ("[EmacsView viewDidEndLiveResize]");
7031   [super viewDidEndLiveResize];
7032   if (old_title != 0)
7033     {
7034       [[self window] setTitle: [NSString stringWithUTF8String: old_title]];
7035       xfree (old_title);
7036       old_title = 0;
7037     }
7038   maximizing_resize = NO;
7040 #endif /* NS_IMPL_COCOA */
7043 - (void)windowDidBecomeKey: (NSNotification *)notification
7044 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
7046   [self windowDidBecomeKey];
7050 - (void)windowDidBecomeKey      /* for direct calls */
7052   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
7053   struct frame *old_focus = dpyinfo->x_focus_frame;
7055   NSTRACE ("[EmacsView windowDidBecomeKey]");
7057   if (emacsframe != old_focus)
7058     dpyinfo->x_focus_frame = emacsframe;
7060   ns_frame_rehighlight (emacsframe);
7062   if (emacs_event)
7063     {
7064       emacs_event->kind = FOCUS_IN_EVENT;
7065       EV_TRAILER ((id)nil);
7066     }
7070 - (void)windowDidResignKey: (NSNotification *)notification
7071 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
7073   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
7074   BOOL is_focus_frame = dpyinfo->x_focus_frame == emacsframe;
7075   NSTRACE ("[EmacsView windowDidResignKey:]");
7077   if (is_focus_frame)
7078     dpyinfo->x_focus_frame = 0;
7080   emacsframe->mouse_moved = 0;
7081   ns_frame_rehighlight (emacsframe);
7083   /* FIXME: for some reason needed on second and subsequent clicks away
7084             from sole-frame Emacs to get hollow box to show */
7085   if (!windowClosing && [[self window] isVisible] == YES)
7086     {
7087       x_update_cursor (emacsframe, 1);
7088       x_set_frame_alpha (emacsframe);
7089     }
7091   if (any_help_event_p)
7092     {
7093       Lisp_Object frame;
7094       XSETFRAME (frame, emacsframe);
7095       help_echo_string = Qnil;
7096       gen_help_event (Qnil, frame, Qnil, Qnil, 0);
7097     }
7099   if (emacs_event && is_focus_frame)
7100     {
7101       [self deleteWorkingText];
7102       emacs_event->kind = FOCUS_OUT_EVENT;
7103       EV_TRAILER ((id)nil);
7104     }
7108 - (void)windowWillMiniaturize: sender
7110   NSTRACE ("[EmacsView windowWillMiniaturize:]");
7114 - (void)setFrame:(NSRect)frameRect
7116   NSTRACE ("[EmacsView setFrame:" NSTRACE_FMT_RECT "]",
7117            NSTRACE_ARG_RECT (frameRect));
7119   [super setFrame:(NSRect)frameRect];
7123 - (BOOL)isFlipped
7125   return YES;
7129 - (BOOL)isOpaque
7131   return NO;
7135 - (void)createToolbar: (struct frame *)f
7137   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
7138   NSWindow *window = [view window];
7140   toolbar = [[EmacsToolbar alloc] initForView: self withIdentifier:
7141                    [NSString stringWithFormat: @"Emacs Frame %d",
7142                              ns_window_num]];
7143   [toolbar setVisible: NO];
7144   [window setToolbar: toolbar];
7146   /* Don't set frame garbaged until tool bar is up to date?
7147      This avoids an extra clear and redraw (flicker) at frame creation.  */
7148   if (FRAME_EXTERNAL_TOOL_BAR (f)) wait_for_tool_bar = YES;
7149   else wait_for_tool_bar = NO;
7152 #ifdef NS_IMPL_COCOA
7153   {
7154     NSButton *toggleButton;
7155     toggleButton = [window standardWindowButton: NSWindowToolbarButton];
7156     [toggleButton setTarget: self];
7157     [toggleButton setAction: @selector (toggleToolbar: )];
7158   }
7159 #endif
7163 - (instancetype) initFrameFromEmacs: (struct frame *)f
7165   NSRect r, wr;
7166   Lisp_Object tem;
7167   NSWindow *win;
7168   NSColor *col;
7169   NSString *name;
7171   NSTRACE ("[EmacsView initFrameFromEmacs:]");
7172   NSTRACE_MSG ("cols:%d lines:%d", f->text_cols, f->text_lines);
7174   windowClosing = NO;
7175   processingCompose = NO;
7176   scrollbarsNeedingUpdate = 0;
7177   fs_state = FULLSCREEN_NONE;
7178   fs_before_fs = next_maximized = -1;
7180   fs_is_native = NO;
7181 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7182 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7183   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7184 #endif
7185     fs_is_native = ns_use_native_fullscreen;
7186 #endif
7188   maximized_width = maximized_height = -1;
7189   nonfs_window = nil;
7191   ns_userRect = NSMakeRect (0, 0, 0, 0);
7192   r = NSMakeRect (0, 0, FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, f->text_cols),
7193                  FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, f->text_lines));
7194   [self initWithFrame: r];
7195   [self setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
7197   FRAME_NS_VIEW (f) = self;
7198   emacsframe = f;
7199 #ifdef NS_IMPL_COCOA
7200   old_title = 0;
7201   maximizing_resize = NO;
7202 #endif
7204   win = [[EmacsWindow alloc]
7205             initWithContentRect: r
7206                       styleMask: (FRAME_UNDECORATED (f)
7207                                   ? FRAME_UNDECORATED_FLAGS
7208                                   : FRAME_DECORATED_FLAGS)
7209                         backing: NSBackingStoreBuffered
7210                           defer: YES];
7212 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7213 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7214   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7215 #endif
7216     [win setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
7217 #endif
7219   wr = [win frame];
7220   bwidth = f->border_width = wr.size.width - r.size.width;
7222   [win setAcceptsMouseMovedEvents: YES];
7223   [win setDelegate: self];
7224 #if !defined (NS_IMPL_COCOA) || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7225 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7226   if ([win respondsToSelector: @selector(useOptimizedDrawing:)])
7227 #endif
7228     [win useOptimizedDrawing: YES];
7229 #endif
7231   [[win contentView] addSubview: self];
7233   if (ns_drag_types)
7234     [self registerForDraggedTypes: ns_drag_types];
7236   tem = f->name;
7237   name = [NSString stringWithUTF8String:
7238                    NILP (tem) ? "Emacs" : SSDATA (tem)];
7239   [win setTitle: name];
7241   /* toolbar support */
7242   if (! FRAME_UNDECORATED (f))
7243     [self createToolbar: f];
7245 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
7246 #ifndef NSAppKitVersionNumber10_10
7247 #define NSAppKitVersionNumber10_10 1343
7248 #endif
7250   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_10
7251       && FRAME_NS_APPEARANCE (f) != ns_appearance_aqua)
7252     win.appearance = [NSAppearance
7253                           appearanceNamed: NSAppearanceNameVibrantDark];
7254 #endif
7256 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
7257   if ([win respondsToSelector: @selector(titlebarAppearsTransparent)])
7258     win.titlebarAppearsTransparent = FRAME_NS_TRANSPARENT_TITLEBAR (f);
7259 #endif
7261   tem = f->icon_name;
7262   if (!NILP (tem))
7263     [win setMiniwindowTitle:
7264            [NSString stringWithUTF8String: SSDATA (tem)]];
7266   if (FRAME_PARENT_FRAME (f) != NULL)
7267     {
7268       NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
7269       [parent addChildWindow: win
7270                      ordered: NSWindowAbove];
7271     }
7273   if (FRAME_Z_GROUP (f) != z_group_none)
7274       win.level = NSNormalWindowLevel
7275         + (FRAME_Z_GROUP_BELOW (f) ? -1 : 1);
7277   {
7278     NSScreen *screen = [win screen];
7280     if (screen != 0)
7281       {
7282         NSPoint pt = NSMakePoint
7283           (IN_BOUND (-SCREENMAX, f->left_pos
7284                      + NS_PARENT_WINDOW_LEFT_POS (f), SCREENMAX),
7285            IN_BOUND (-SCREENMAX,
7286                      NS_PARENT_WINDOW_TOP_POS (f) - f->top_pos,
7287                      SCREENMAX));
7289         [win setFrameTopLeftPoint: pt];
7291         NSTRACE_RECT ("new frame", [win frame]);
7292       }
7293   }
7295   [win makeFirstResponder: self];
7297   col = ns_lookup_indexed_color (NS_FACE_BACKGROUND
7298                                  (FACE_FROM_ID (emacsframe, DEFAULT_FACE_ID)),
7299                                  emacsframe);
7300   [win setBackgroundColor: col];
7301   if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7302     [win setOpaque: NO];
7304 #if !defined (NS_IMPL_COCOA) \
7305   || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7306 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7307   if ([self respondsToSelector: @selector(allocateGState)])
7308 #endif
7309     [self allocateGState];
7310 #endif
7311   [NSApp registerServicesMenuSendTypes: ns_send_types
7312                            returnTypes: [NSArray array]];
7314   /* macOS Sierra automatically enables tabbed windows.  We can't
7315      allow this to be enabled until it's available on a Free system.
7316      Currently it only happens by accident and is buggy anyway. */
7317 #if defined (NS_IMPL_COCOA) \
7318   && MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
7319 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101200
7320   if ([win respondsToSelector: @selector(setTabbingMode:)])
7321 #endif
7322     [win setTabbingMode: NSWindowTabbingModeDisallowed];
7323 #endif
7325   ns_window_num++;
7326   return self;
7330 - (void)windowDidMove: sender
7332   NSWindow *win = [self window];
7333   NSRect r = [win frame];
7334   NSArray *screens = [NSScreen screens];
7335   NSScreen *screen = [screens objectAtIndex: 0];
7337   NSTRACE ("[EmacsView windowDidMove:]");
7339   if (!emacsframe->output_data.ns)
7340     return;
7341   if (screen != nil)
7342     {
7343       emacsframe->left_pos = r.origin.x - NS_PARENT_WINDOW_LEFT_POS (emacsframe);
7344       emacsframe->top_pos =
7345         NS_PARENT_WINDOW_TOP_POS (emacsframe) - (r.origin.y + r.size.height);
7347       if (emacs_event)
7348         {
7349           emacs_event->kind = MOVE_FRAME_EVENT;
7350           EV_TRAILER ((id)nil);
7351         }
7352     }
7356 /* Called AFTER method below, but before our windowWillResize call there leads
7357    to windowDidResize -> x_set_window_size.  Update emacs' notion of frame
7358    location so set_window_size moves the frame. */
7359 - (BOOL)windowShouldZoom: (NSWindow *)sender toFrame: (NSRect)newFrame
7361   NSTRACE (("[EmacsView windowShouldZoom:toFrame:" NSTRACE_FMT_RECT "]"
7362             NSTRACE_FMT_RETURN "YES"),
7363            NSTRACE_ARG_RECT (newFrame));
7365   emacsframe->output_data.ns->zooming = 1;
7366   return YES;
7370 /* Override to do something slightly nonstandard, but nice.  First click on
7371    zoom button will zoom vertically.  Second will zoom completely.  Third
7372    returns to original. */
7373 - (NSRect)windowWillUseStandardFrame:(NSWindow *)sender
7374                         defaultFrame:(NSRect)defaultFrame
7376   // TODO: Rename to "currentFrame" and assign "result" properly in
7377   // all paths.
7378   NSRect result = [sender frame];
7380   NSTRACE (("[EmacsView windowWillUseStandardFrame:defaultFrame:"
7381             NSTRACE_FMT_RECT "]"),
7382            NSTRACE_ARG_RECT (defaultFrame));
7383   NSTRACE_FSTYPE ("fs_state", fs_state);
7384   NSTRACE_FSTYPE ("fs_before_fs", fs_before_fs);
7385   NSTRACE_FSTYPE ("next_maximized", next_maximized);
7386   NSTRACE_RECT   ("ns_userRect", ns_userRect);
7387   NSTRACE_RECT   ("[sender frame]", [sender frame]);
7389   if (fs_before_fs != -1) /* Entering fullscreen */
7390     {
7391       NSTRACE_MSG ("Entering fullscreen");
7392       result = defaultFrame;
7393     }
7394   else
7395     {
7396       // Save the window size and position (frame) before the resize.
7397       if (fs_state != FULLSCREEN_MAXIMIZED
7398           && fs_state != FULLSCREEN_WIDTH)
7399         {
7400           ns_userRect.size.width = result.size.width;
7401           ns_userRect.origin.x   = result.origin.x;
7402         }
7404       if (fs_state != FULLSCREEN_MAXIMIZED
7405           && fs_state != FULLSCREEN_HEIGHT)
7406         {
7407           ns_userRect.size.height = result.size.height;
7408           ns_userRect.origin.y    = result.origin.y;
7409         }
7411       NSTRACE_RECT ("ns_userRect (2)", ns_userRect);
7413       if (next_maximized == FULLSCREEN_HEIGHT
7414           || (next_maximized == -1
7415               && abs ((int)(defaultFrame.size.height - result.size.height))
7416               > FRAME_LINE_HEIGHT (emacsframe)))
7417         {
7418           /* first click */
7419           NSTRACE_MSG ("FULLSCREEN_HEIGHT");
7420           maximized_height = result.size.height = defaultFrame.size.height;
7421           maximized_width = -1;
7422           result.origin.y = defaultFrame.origin.y;
7423           if (ns_userRect.size.height != 0)
7424             {
7425               result.origin.x = ns_userRect.origin.x;
7426               result.size.width = ns_userRect.size.width;
7427             }
7428           [self setFSValue: FULLSCREEN_HEIGHT];
7429 #ifdef NS_IMPL_COCOA
7430           maximizing_resize = YES;
7431 #endif
7432         }
7433       else if (next_maximized == FULLSCREEN_WIDTH)
7434         {
7435           NSTRACE_MSG ("FULLSCREEN_WIDTH");
7436           maximized_width = result.size.width = defaultFrame.size.width;
7437           maximized_height = -1;
7438           result.origin.x = defaultFrame.origin.x;
7439           if (ns_userRect.size.width != 0)
7440             {
7441               result.origin.y = ns_userRect.origin.y;
7442               result.size.height = ns_userRect.size.height;
7443             }
7444           [self setFSValue: FULLSCREEN_WIDTH];
7445         }
7446       else if (next_maximized == FULLSCREEN_MAXIMIZED
7447                || (next_maximized == -1
7448                    && abs ((int)(defaultFrame.size.width - result.size.width))
7449                    > FRAME_COLUMN_WIDTH (emacsframe)))
7450         {
7451           NSTRACE_MSG ("FULLSCREEN_MAXIMIZED");
7453           result = defaultFrame;  /* second click */
7454           maximized_width = result.size.width;
7455           maximized_height = result.size.height;
7456           [self setFSValue: FULLSCREEN_MAXIMIZED];
7457 #ifdef NS_IMPL_COCOA
7458           maximizing_resize = YES;
7459 #endif
7460         }
7461       else
7462         {
7463           /* restore */
7464           NSTRACE_MSG ("Restore");
7465           result = ns_userRect.size.height ? ns_userRect : result;
7466           NSTRACE_RECT ("restore (2)", result);
7467           ns_userRect = NSMakeRect (0, 0, 0, 0);
7468 #ifdef NS_IMPL_COCOA
7469           maximizing_resize = fs_state != FULLSCREEN_NONE;
7470 #endif
7471           [self setFSValue: FULLSCREEN_NONE];
7472           maximized_width = maximized_height = -1;
7473         }
7474     }
7476   if (fs_before_fs == -1) next_maximized = -1;
7478   NSTRACE_RECT   ("Final ns_userRect", ns_userRect);
7479   NSTRACE_MSG    ("Final maximized_width: %d", maximized_width);
7480   NSTRACE_MSG    ("Final maximized_height: %d", maximized_height);
7481   NSTRACE_FSTYPE ("Final next_maximized", next_maximized);
7483   [self windowWillResize: sender toSize: result.size];
7485   NSTRACE_RETURN_RECT (result);
7487   return result;
7491 - (void)windowDidDeminiaturize: sender
7493   NSTRACE ("[EmacsView windowDidDeminiaturize:]");
7494   if (!emacsframe->output_data.ns)
7495     return;
7497   SET_FRAME_ICONIFIED (emacsframe, 0);
7498   SET_FRAME_VISIBLE (emacsframe, 1);
7499   windows_or_buffers_changed = 63;
7501   if (emacs_event)
7502     {
7503       emacs_event->kind = DEICONIFY_EVENT;
7504       EV_TRAILER ((id)nil);
7505     }
7509 - (void)windowDidExpose: sender
7511   NSTRACE ("[EmacsView windowDidExpose:]");
7512   if (!emacsframe->output_data.ns)
7513     return;
7515   SET_FRAME_VISIBLE (emacsframe, 1);
7516   SET_FRAME_GARBAGED (emacsframe);
7518   if (send_appdefined)
7519     ns_send_appdefined (-1);
7523 - (void)windowDidMiniaturize: sender
7525   NSTRACE ("[EmacsView windowDidMiniaturize:]");
7526   if (!emacsframe->output_data.ns)
7527     return;
7529   SET_FRAME_ICONIFIED (emacsframe, 1);
7530   SET_FRAME_VISIBLE (emacsframe, 0);
7532   if (emacs_event)
7533     {
7534       emacs_event->kind = ICONIFY_EVENT;
7535       EV_TRAILER ((id)nil);
7536     }
7539 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7540 - (NSApplicationPresentationOptions)window:(NSWindow *)window
7541       willUseFullScreenPresentationOptions:
7542   (NSApplicationPresentationOptions)proposedOptions
7544   return proposedOptions|NSApplicationPresentationAutoHideToolbar;
7546 #endif
7548 - (void)windowWillEnterFullScreen:(NSNotification *)notification
7550   NSTRACE ("[EmacsView windowWillEnterFullScreen:]");
7551   [self windowWillEnterFullScreen];
7553 - (void)windowWillEnterFullScreen /* provided for direct calls */
7555   NSTRACE ("[EmacsView windowWillEnterFullScreen]");
7556   fs_before_fs = fs_state;
7559 - (void)windowDidEnterFullScreen:(NSNotification *)notification
7561   NSTRACE ("[EmacsView windowDidEnterFullScreen:]");
7562   [self windowDidEnterFullScreen];
7565 - (void)windowDidEnterFullScreen /* provided for direct calls */
7567   NSTRACE ("[EmacsView windowDidEnterFullScreen]");
7568   [self setFSValue: FULLSCREEN_BOTH];
7569   if (! [self fsIsNative])
7570     {
7571       [self windowDidBecomeKey];
7572       [nonfs_window orderOut:self];
7573     }
7574   else
7575     {
7576       BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (emacsframe) ? YES : NO;
7577 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 \
7578   && MAC_OS_X_VERSION_MIN_REQUIRED <= 1070
7579       unsigned val = (unsigned)[NSApp presentationOptions];
7581       // Mac OS X 10.7 bug fix, the menu won't appear without this.
7582       // val is non-zero on other macOS versions.
7583       if (val == 0)
7584         {
7585           NSApplicationPresentationOptions options
7586             = NSApplicationPresentationAutoHideDock
7587             | NSApplicationPresentationAutoHideMenuBar
7588             | NSApplicationPresentationFullScreen
7589             | NSApplicationPresentationAutoHideToolbar;
7591           [NSApp setPresentationOptions: options];
7592         }
7593 #endif
7594       [toolbar setVisible:tbar_visible];
7595     }
7598 - (void)windowWillExitFullScreen:(NSNotification *)notification
7600   NSTRACE ("[EmacsView windowWillExitFullScreen:]");
7601   [self windowWillExitFullScreen];
7604 - (void)windowWillExitFullScreen /* provided for direct calls */
7606   NSTRACE ("[EmacsView windowWillExitFullScreen]");
7607   if (!FRAME_LIVE_P (emacsframe))
7608     {
7609       NSTRACE_MSG ("Ignored (frame dead)");
7610       return;
7611     }
7612   if (next_maximized != -1)
7613     fs_before_fs = next_maximized;
7616 - (void)windowDidExitFullScreen:(NSNotification *)notification
7618   NSTRACE ("[EmacsView windowDidExitFullScreen:]");
7619   [self windowDidExitFullScreen];
7622 - (void)windowDidExitFullScreen /* provided for direct calls */
7624   NSTRACE ("[EmacsView windowDidExitFullScreen]");
7625   if (!FRAME_LIVE_P (emacsframe))
7626     {
7627       NSTRACE_MSG ("Ignored (frame dead)");
7628       return;
7629     }
7630   [self setFSValue: fs_before_fs];
7631   fs_before_fs = -1;
7632 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7633   [self updateCollectionBehavior];
7634 #endif
7635   if (FRAME_EXTERNAL_TOOL_BAR (emacsframe))
7636     {
7637       [toolbar setVisible:YES];
7638       update_frame_tool_bar (emacsframe);
7639       [self updateFrameSize:YES];
7640       [[self window] display];
7641     }
7642   else
7643     [toolbar setVisible:NO];
7645   if (next_maximized != -1)
7646     [[self window] performZoom:self];
7649 - (BOOL)fsIsNative
7651   return fs_is_native;
7654 - (BOOL)isFullscreen
7656   BOOL res;
7658   if (! fs_is_native)
7659     {
7660       res = (nonfs_window != nil);
7661     }
7662   else
7663     {
7664 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7665       res = (([[self window] styleMask] & NSWindowStyleMaskFullScreen) != 0);
7666 #else
7667       res = NO;
7668 #endif
7669     }
7671   NSTRACE ("[EmacsView isFullscreen] " NSTRACE_FMT_RETURN " %d",
7672            (int) res);
7674   return res;
7677 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7678 - (void)updateCollectionBehavior
7680   NSTRACE ("[EmacsView updateCollectionBehavior]");
7682   if (! [self isFullscreen])
7683     {
7684       NSWindow *win = [self window];
7685       NSWindowCollectionBehavior b = [win collectionBehavior];
7686       if (ns_use_native_fullscreen)
7687         b |= NSWindowCollectionBehaviorFullScreenPrimary;
7688       else
7689         b &= ~NSWindowCollectionBehaviorFullScreenPrimary;
7691       [win setCollectionBehavior: b];
7692 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7693       if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7694 #endif
7695         fs_is_native = ns_use_native_fullscreen;
7696     }
7698 #endif
7700 - (void)toggleFullScreen: (id)sender
7702   NSWindow *w, *fw;
7703   BOOL onFirstScreen;
7704   struct frame *f;
7705   NSRect r, wr;
7706   NSColor *col;
7708   NSTRACE ("[EmacsView toggleFullScreen:]");
7710   if (fs_is_native)
7711     {
7712 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7713 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7714       if ([[self window] respondsToSelector: @selector(toggleFullScreen:)])
7715 #endif
7716         [[self window] toggleFullScreen:sender];
7717 #endif
7718       return;
7719     }
7721   w = [self window];
7722   onFirstScreen = [[w screen] isEqual:[[NSScreen screens] objectAtIndex:0]];
7723   f = emacsframe;
7724   wr = [w frame];
7725   col = ns_lookup_indexed_color (NS_FACE_BACKGROUND
7726                                  (FACE_FROM_ID (f, DEFAULT_FACE_ID)),
7727                                  f);
7729   if (fs_state != FULLSCREEN_BOTH)
7730     {
7731       NSScreen *screen = [w screen];
7733 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
7734       /* Hide ghost menu bar on secondary monitor? */
7735       if (! onFirstScreen
7736 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
7737           && [NSScreen respondsToSelector: @selector(screensHaveSeparateSpaces)]
7738 #endif
7739           )
7740         onFirstScreen = [NSScreen screensHaveSeparateSpaces];
7741 #endif
7742       /* Hide dock and menubar if we are on the primary screen.  */
7743       if (onFirstScreen)
7744         {
7745 #ifdef NS_IMPL_COCOA
7746           NSApplicationPresentationOptions options
7747             = NSApplicationPresentationAutoHideDock
7748             | NSApplicationPresentationAutoHideMenuBar;
7750           [NSApp setPresentationOptions: options];
7751 #else
7752           [NSMenu setMenuBarVisible:NO];
7753 #endif
7754         }
7756       fw = [[EmacsFSWindow alloc]
7757                        initWithContentRect:[w contentRectForFrameRect:wr]
7758                                  styleMask:NSWindowStyleMaskBorderless
7759                                    backing:NSBackingStoreBuffered
7760                                      defer:YES
7761                                     screen:screen];
7763       [fw setContentView:[w contentView]];
7764       [fw setTitle:[w title]];
7765       [fw setDelegate:self];
7766       [fw setAcceptsMouseMovedEvents: YES];
7767 #if !defined (NS_IMPL_COCOA) \
7768   || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7769 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7770       if ([fw respondsToSelector: @selector(useOptimizedDrawing:)])
7771 #endif
7772         [fw useOptimizedDrawing: YES];
7773 #endif
7774       [fw setBackgroundColor: col];
7775       if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7776         [fw setOpaque: NO];
7778       f->border_width = 0;
7780       nonfs_window = w;
7782       [self windowWillEnterFullScreen];
7783       [fw makeKeyAndOrderFront:NSApp];
7784       [fw makeFirstResponder:self];
7785       [w orderOut:self];
7786       r = [fw frameRectForContentRect:[screen frame]];
7787       [fw setFrame: r display:YES animate:ns_use_fullscreen_animation];
7788       [self windowDidEnterFullScreen];
7789       [fw display];
7790     }
7791   else
7792     {
7793       fw = w;
7794       w = nonfs_window;
7795       nonfs_window = nil;
7797       if (onFirstScreen)
7798         {
7799 #ifdef NS_IMPL_COCOA
7800           [NSApp setPresentationOptions: NSApplicationPresentationDefault];
7801 #else
7802           [NSMenu setMenuBarVisible:YES];
7803 #endif
7804         }
7806       [w setContentView:[fw contentView]];
7807       [w setBackgroundColor: col];
7808       if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7809         [w setOpaque: NO];
7811       f->border_width = bwidth;
7813       // to do: consider using [NSNotificationCenter postNotificationName:] to send notifications.
7815       [self windowWillExitFullScreen];
7816       [fw setFrame: [w frame] display:YES animate:ns_use_fullscreen_animation];
7817       [fw close];
7818       [w makeKeyAndOrderFront:NSApp];
7819       [self windowDidExitFullScreen];
7820       [self updateFrameSize:YES];
7821     }
7824 - (void)handleFS
7826   NSTRACE ("[EmacsView handleFS]");
7828   if (fs_state != emacsframe->want_fullscreen)
7829     {
7830       if (fs_state == FULLSCREEN_BOTH)
7831         {
7832           NSTRACE_MSG ("fs_state == FULLSCREEN_BOTH");
7833           [self toggleFullScreen:self];
7834         }
7836       switch (emacsframe->want_fullscreen)
7837         {
7838         case FULLSCREEN_BOTH:
7839           NSTRACE_MSG ("FULLSCREEN_BOTH");
7840           [self toggleFullScreen:self];
7841           break;
7842         case FULLSCREEN_WIDTH:
7843           NSTRACE_MSG ("FULLSCREEN_WIDTH");
7844           next_maximized = FULLSCREEN_WIDTH;
7845           if (fs_state != FULLSCREEN_BOTH)
7846             [[self window] performZoom:self];
7847           break;
7848         case FULLSCREEN_HEIGHT:
7849           NSTRACE_MSG ("FULLSCREEN_HEIGHT");
7850           next_maximized = FULLSCREEN_HEIGHT;
7851           if (fs_state != FULLSCREEN_BOTH)
7852             [[self window] performZoom:self];
7853           break;
7854         case FULLSCREEN_MAXIMIZED:
7855           NSTRACE_MSG ("FULLSCREEN_MAXIMIZED");
7856           next_maximized = FULLSCREEN_MAXIMIZED;
7857           if (fs_state != FULLSCREEN_BOTH)
7858             [[self window] performZoom:self];
7859           break;
7860         case FULLSCREEN_NONE:
7861           NSTRACE_MSG ("FULLSCREEN_NONE");
7862           if (fs_state != FULLSCREEN_BOTH)
7863             {
7864               next_maximized = FULLSCREEN_NONE;
7865               [[self window] performZoom:self];
7866             }
7867           break;
7868         }
7870       emacsframe->want_fullscreen = FULLSCREEN_NONE;
7871     }
7875 - (void) setFSValue: (int)value
7877   NSTRACE ("[EmacsView setFSValue:" NSTRACE_FMT_FSTYPE "]",
7878            NSTRACE_ARG_FSTYPE(value));
7880   Lisp_Object lval = Qnil;
7881   switch (value)
7882     {
7883     case FULLSCREEN_BOTH:
7884       lval = Qfullboth;
7885       break;
7886     case FULLSCREEN_WIDTH:
7887       lval = Qfullwidth;
7888       break;
7889     case FULLSCREEN_HEIGHT:
7890       lval = Qfullheight;
7891       break;
7892     case FULLSCREEN_MAXIMIZED:
7893       lval = Qmaximized;
7894       break;
7895     }
7896   store_frame_param (emacsframe, Qfullscreen, lval);
7897   fs_state = value;
7900 - (void)mouseEntered: (NSEvent *)theEvent
7902   NSTRACE ("[EmacsView mouseEntered:]");
7903   if (emacsframe)
7904     FRAME_DISPLAY_INFO (emacsframe)->last_mouse_movement_time
7905       = EV_TIMESTAMP (theEvent);
7909 - (void)mouseExited: (NSEvent *)theEvent
7911   Mouse_HLInfo *hlinfo = emacsframe ? MOUSE_HL_INFO (emacsframe) : NULL;
7913   NSTRACE ("[EmacsView mouseExited:]");
7915   if (!hlinfo)
7916     return;
7918   FRAME_DISPLAY_INFO (emacsframe)->last_mouse_movement_time
7919     = EV_TIMESTAMP (theEvent);
7921   if (emacsframe == hlinfo->mouse_face_mouse_frame)
7922     {
7923       clear_mouse_face (hlinfo);
7924       hlinfo->mouse_face_mouse_frame = 0;
7925     }
7929 - (instancetype)menuDown: sender
7931   NSTRACE ("[EmacsView menuDown:]");
7932   if (context_menu_value == -1)
7933     context_menu_value = [sender tag];
7934   else
7935     {
7936       NSInteger tag = [sender tag];
7937       find_and_call_menu_selection (emacsframe, emacsframe->menu_bar_items_used,
7938                                     emacsframe->menu_bar_vector,
7939                                     (void *)tag);
7940     }
7942   ns_send_appdefined (-1);
7943   return self;
7947 - (EmacsToolbar *)toolbar
7949   return toolbar;
7953 /* this gets called on toolbar button click */
7954 - (instancetype)toolbarClicked: (id)item
7956   NSEvent *theEvent;
7957   int idx = [item tag] * TOOL_BAR_ITEM_NSLOTS;
7959   NSTRACE ("[EmacsView toolbarClicked:]");
7961   if (!emacs_event)
7962     return self;
7964   /* send first event (for some reason two needed) */
7965   theEvent = [[self window] currentEvent];
7966   emacs_event->kind = TOOL_BAR_EVENT;
7967   XSETFRAME (emacs_event->arg, emacsframe);
7968   EV_TRAILER (theEvent);
7970   emacs_event->kind = TOOL_BAR_EVENT;
7971 /*   XSETINT (emacs_event->code, 0); */
7972   emacs_event->arg = AREF (emacsframe->tool_bar_items,
7973                            idx + TOOL_BAR_ITEM_KEY);
7974   emacs_event->modifiers = EV_MODIFIERS (theEvent);
7975   EV_TRAILER (theEvent);
7976   return self;
7980 - (instancetype)toggleToolbar: (id)sender
7982   NSTRACE ("[EmacsView toggleToolbar:]");
7984   if (!emacs_event)
7985     return self;
7987   emacs_event->kind = NS_NONKEY_EVENT;
7988   emacs_event->code = KEY_NS_TOGGLE_TOOLBAR;
7989   EV_TRAILER ((id)nil);
7990   return self;
7994 - (void)drawRect: (NSRect)rect
7996   int x = NSMinX (rect), y = NSMinY (rect);
7997   int width = NSWidth (rect), height = NSHeight (rect);
7999   NSTRACE ("[EmacsView drawRect:" NSTRACE_FMT_RECT "]",
8000            NSTRACE_ARG_RECT(rect));
8002   if (!emacsframe || !emacsframe->output_data.ns)
8003     return;
8005   ns_clear_frame_area (emacsframe, x, y, width, height);
8006   block_input ();
8007   expose_frame (emacsframe, x, y, width, height);
8008   unblock_input ();
8010   /*
8011     drawRect: may be called (at least in Mac OS X 10.5) for invisible
8012     views as well for some reason.  Thus, do not infer visibility
8013     here.
8015     emacsframe->async_visible = 1;
8016     emacsframe->async_iconified = 0;
8017   */
8021 /* NSDraggingDestination protocol methods.  Actually this is not really a
8022    protocol, but a category of Object.  O well...  */
8024 -(NSDragOperation) draggingEntered: (id <NSDraggingInfo>) sender
8026   NSTRACE ("[EmacsView draggingEntered:]");
8027   return NSDragOperationGeneric;
8031 -(BOOL)prepareForDragOperation: (id <NSDraggingInfo>) sender
8033   return YES;
8037 -(BOOL)performDragOperation: (id <NSDraggingInfo>) sender
8039   id pb;
8040   int x, y;
8041   NSString *type;
8042   NSEvent *theEvent = [[self window] currentEvent];
8043   NSPoint position;
8044   NSDragOperation op = [sender draggingSourceOperationMask];
8045   int modifiers = 0;
8047   NSTRACE ("[EmacsView performDragOperation:]");
8049   if (!emacs_event)
8050     return NO;
8052   position = [self convertPoint: [sender draggingLocation] fromView: nil];
8053   x = lrint (position.x);  y = lrint (position.y);
8055   pb = [sender draggingPasteboard];
8056   type = [pb availableTypeFromArray: ns_drag_types];
8058   if (! (op & (NSDragOperationMove|NSDragOperationDelete)) &&
8059       // URL drags contain all operations (0xf), don't allow all to be set.
8060       (op & 0xf) != 0xf)
8061     {
8062       if (op & NSDragOperationLink)
8063         modifiers |= NSEventModifierFlagControl;
8064       if (op & NSDragOperationCopy)
8065         modifiers |= NSEventModifierFlagOption;
8066       if (op & NSDragOperationGeneric)
8067         modifiers |= NSEventModifierFlagCommand;
8068     }
8070   modifiers = EV_MODIFIERS2 (modifiers);
8071   if (type == 0)
8072     {
8073       return NO;
8074     }
8075   else if ([type isEqualToString: NSFilenamesPboardType])
8076     {
8077       NSArray *files;
8078       NSEnumerator *fenum;
8079       NSString *file;
8081       if (!(files = [pb propertyListForType: type]))
8082         return NO;
8084       fenum = [files objectEnumerator];
8085       while ( (file = [fenum nextObject]) )
8086         {
8087           emacs_event->kind = DRAG_N_DROP_EVENT;
8088           XSETINT (emacs_event->x, x);
8089           XSETINT (emacs_event->y, y);
8090           ns_input_file = append2 (ns_input_file,
8091                                    build_string ([file UTF8String]));
8092           emacs_event->modifiers = modifiers;
8093           emacs_event->arg =  list2 (Qfile, build_string ([file UTF8String]));
8094           EV_TRAILER (theEvent);
8095         }
8096       return YES;
8097     }
8098   else if ([type isEqualToString: NSURLPboardType])
8099     {
8100       NSURL *url = [NSURL URLFromPasteboard: pb];
8101       if (url == nil) return NO;
8103       emacs_event->kind = DRAG_N_DROP_EVENT;
8104       XSETINT (emacs_event->x, x);
8105       XSETINT (emacs_event->y, y);
8106       emacs_event->modifiers = modifiers;
8107       emacs_event->arg =  list2 (Qurl,
8108                                  build_string ([[url absoluteString]
8109                                                  UTF8String]));
8110       EV_TRAILER (theEvent);
8112       if ([url isFileURL] != NO)
8113         {
8114           NSString *file = [url path];
8115           ns_input_file = append2 (ns_input_file,
8116                                    build_string ([file UTF8String]));
8117         }
8118       return YES;
8119     }
8120   else if ([type isEqualToString: NSStringPboardType]
8121            || [type isEqualToString: NSTabularTextPboardType])
8122     {
8123       NSString *data;
8125       if (! (data = [pb stringForType: type]))
8126         return NO;
8128       emacs_event->kind = DRAG_N_DROP_EVENT;
8129       XSETINT (emacs_event->x, x);
8130       XSETINT (emacs_event->y, y);
8131       emacs_event->modifiers = modifiers;
8132       emacs_event->arg =  list2 (Qnil, build_string ([data UTF8String]));
8133       EV_TRAILER (theEvent);
8134       return YES;
8135     }
8136   else
8137     {
8138       fprintf (stderr, "Invalid data type in dragging pasteboard");
8139       return NO;
8140     }
8144 - (id) validRequestorForSendType: (NSString *)typeSent
8145                       returnType: (NSString *)typeReturned
8147   NSTRACE ("[EmacsView validRequestorForSendType:returnType:]");
8148   if (typeSent != nil && [ns_send_types indexOfObject: typeSent] != NSNotFound
8149       && typeReturned == nil)
8150     {
8151       if (! NILP (ns_get_local_selection (QPRIMARY, QUTF8_STRING)))
8152         return self;
8153     }
8155   return [super validRequestorForSendType: typeSent
8156                                returnType: typeReturned];
8160 /* The next two methods are part of NSServicesRequests informal protocol,
8161    supposedly called when a services menu item is chosen from this app.
8162    But this should not happen because we override the services menu with our
8163    own entries which call ns-perform-service.
8164    Nonetheless, it appeared to happen (under strange circumstances): bug#1435.
8165    So let's at least stub them out until further investigation can be done. */
8167 - (BOOL) readSelectionFromPasteboard: (NSPasteboard *)pb
8169   /* we could call ns_string_from_pasteboard(pboard) here but then it should
8170      be written into the buffer in place of the existing selection..
8171      ordinary service calls go through functions defined in ns-win.el */
8172   return NO;
8175 - (BOOL) writeSelectionToPasteboard: (NSPasteboard *)pb types: (NSArray *)types
8177   NSArray *typesDeclared;
8178   Lisp_Object val;
8180   NSTRACE ("[EmacsView writeSelectionToPasteboard:types:]");
8182   /* We only support NSStringPboardType */
8183   if ([types containsObject:NSStringPboardType] == NO) {
8184     return NO;
8185   }
8187   val = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8188   if (CONSP (val) && SYMBOLP (XCAR (val)))
8189     {
8190       val = XCDR (val);
8191       if (CONSP (val) && NILP (XCDR (val)))
8192         val = XCAR (val);
8193     }
8194   if (! STRINGP (val))
8195     return NO;
8197   typesDeclared = [NSArray arrayWithObject:NSStringPboardType];
8198   [pb declareTypes:typesDeclared owner:nil];
8199   ns_string_to_pasteboard (pb, val);
8200   return YES;
8204 /* setMini =YES means set from internal (gives a finder icon), NO means set nil
8205    (gives a miniaturized version of the window); currently we use the latter for
8206    frames whose active buffer doesn't correspond to any file
8207    (e.g., '*scratch*') */
8208 - (instancetype)setMiniwindowImage: (BOOL) setMini
8210   id image = [[self window] miniwindowImage];
8211   NSTRACE ("[EmacsView setMiniwindowImage:%d]", setMini);
8213   /* NOTE: under Cocoa miniwindowImage always returns nil, documentation
8214      about "AppleDockIconEnabled" notwithstanding, however the set message
8215      below has its effect nonetheless. */
8216   if (image != emacsframe->output_data.ns->miniimage)
8217     {
8218       if (image && [image isKindOfClass: [EmacsImage class]])
8219         [image release];
8220       [[self window] setMiniwindowImage:
8221                        setMini ? emacsframe->output_data.ns->miniimage : nil];
8222     }
8224   return self;
8228 - (void) setRows: (int) r andColumns: (int) c
8230   NSTRACE ("[EmacsView setRows:%d andColumns:%d]", r, c);
8231   rows = r;
8232   cols = c;
8235 - (int) fullscreenState
8237   return fs_state;
8240 @end  /* EmacsView */
8244 /* ==========================================================================
8246     EmacsWindow implementation
8248    ========================================================================== */
8250 @implementation EmacsWindow
8252 #ifdef NS_IMPL_COCOA
8253 - (id)accessibilityAttributeValue:(NSString *)attribute
8255   Lisp_Object str = Qnil;
8256   struct frame *f = SELECTED_FRAME ();
8257   struct buffer *curbuf = XBUFFER (XWINDOW (f->selected_window)->contents);
8259   NSTRACE ("[EmacsWindow accessibilityAttributeValue:]");
8261   if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
8262     return NSAccessibilityTextFieldRole;
8264   if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]
8265       && curbuf && ! NILP (BVAR (curbuf, mark_active)))
8266     {
8267       str = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8268     }
8269   else if (curbuf && [attribute isEqualToString:NSAccessibilityValueAttribute])
8270     {
8271       if (! NILP (BVAR (curbuf, mark_active)))
8272           str = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8274       if (NILP (str))
8275         {
8276           ptrdiff_t start_byte = BUF_BEGV_BYTE (curbuf);
8277           ptrdiff_t byte_range = BUF_ZV_BYTE (curbuf) - start_byte;
8278           ptrdiff_t range = BUF_ZV (curbuf) - BUF_BEGV (curbuf);
8280           if (! NILP (BVAR (curbuf, enable_multibyte_characters)))
8281             str = make_uninit_multibyte_string (range, byte_range);
8282           else
8283             str = make_uninit_string (range);
8284           /* To check: This returns emacs-utf-8, which is a superset of utf-8.
8285              Is this a problem?  */
8286           memcpy (SDATA (str), BYTE_POS_ADDR (start_byte), byte_range);
8287         }
8288     }
8291   if (! NILP (str))
8292     {
8293       if (CONSP (str) && SYMBOLP (XCAR (str)))
8294         {
8295           str = XCDR (str);
8296           if (CONSP (str) && NILP (XCDR (str)))
8297             str = XCAR (str);
8298         }
8299       if (STRINGP (str))
8300         {
8301           const char *utfStr = SSDATA (str);
8302           NSString *nsStr = [NSString stringWithUTF8String: utfStr];
8303           return nsStr;
8304         }
8305     }
8307   return [super accessibilityAttributeValue:attribute];
8309 #endif /* NS_IMPL_COCOA */
8311 /* Constrain size and placement of a frame.
8313    By returning the original "frameRect", the frame is not
8314    constrained. This can lead to unwanted situations where, for
8315    example, the menu bar covers the frame.
8317    The default implementation (accessed using "super") constrains the
8318    frame to the visible area of SCREEN, minus the menu bar (if
8319    present) and the Dock.  Note that default implementation also calls
8320    windowWillResize, with the frame it thinks should have.  (This can
8321    make the frame exit maximized mode.)
8323    Note that this should work in situations where multiple monitors
8324    are present.  Common configurations are side-by-side monitors and a
8325    monitor on top of another (e.g. when a laptop is placed under a
8326    large screen). */
8327 - (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
8329   NSTRACE ("[EmacsWindow constrainFrameRect:" NSTRACE_FMT_RECT " toScreen:]",
8330              NSTRACE_ARG_RECT (frameRect));
8332 #ifdef NS_IMPL_COCOA
8333 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
8334   // If separate spaces is on, it is like each screen is independent.  There is
8335   // no spanning of frames across screens.
8336   if (
8337 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
8338       [NSScreen respondsToSelector: @selector(screensHaveSeparateSpaces)] &&
8339 #endif
8340       [NSScreen screensHaveSeparateSpaces])
8341     {
8342       NSTRACE_MSG ("Screens have separate spaces");
8343       frameRect = [super constrainFrameRect:frameRect toScreen:screen];
8344       NSTRACE_RETURN_RECT (frameRect);
8345       return frameRect;
8346     }
8347   else
8348 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 */
8350     // Check that the proposed frameRect is visible in at least one
8351     // screen.  If it is not, ask the system to reposition it (only
8352     // for non-child windows).
8354     if (!FRAME_PARENT_FRAME (((EmacsView *)[self delegate])->emacsframe))
8355     {
8356       NSArray *screens = [NSScreen screens];
8357       NSUInteger nr_screens = [screens count];
8359       int i;
8360       BOOL frame_on_screen = NO;
8362       for (i = 0; i < nr_screens; ++i)
8363         {
8364           NSScreen *s = [screens objectAtIndex: i];
8365           NSRect scrRect = [s frame];
8367           if (NSIntersectsRect(frameRect, scrRect))
8368             {
8369               frame_on_screen = YES;
8370               break;
8371             }
8372         }
8374       if (!frame_on_screen)
8375         {
8376           NSTRACE_MSG ("Frame outside screens; constraining");
8377           frameRect = [super constrainFrameRect:frameRect toScreen:screen];
8378           NSTRACE_RETURN_RECT (frameRect);
8379           return frameRect;
8380         }
8381     }
8382 #endif
8384   return constrain_frame_rect(frameRect,
8385                               [(EmacsView *)[self delegate] isFullscreen]);
8389 - (void)performZoom:(id)sender
8391   NSTRACE ("[EmacsWindow performZoom:]");
8393   return [super performZoom:sender];
8396 - (void)zoom:(id)sender
8398   NSTRACE ("[EmacsWindow zoom:]");
8400   ns_update_auto_hide_menu_bar();
8402   // Below are three zoom implementations.  In the final commit, the
8403   // idea is that the last should be included.
8405 #if 0
8406   // Native zoom done using the standard zoom animation.  Size of the
8407   // resulting frame reduced to accommodate the Dock and, if present,
8408   // the menu-bar.
8409   [super zoom:sender];
8411 #elif 0
8412   // Native zoom done using the standard zoom animation, plus an
8413   // explicit resize to cover the full screen, except the menu-bar and
8414   // dock, if present.
8415   [super zoom:sender];
8417   // After the native zoom, resize the resulting frame to fill the
8418   // entire screen, except the menu-bar.
8419   //
8420   // This works for all practical purposes.  (The only minor oddity is
8421   // when transiting from full-height frame to a maximized, the
8422   // animation reduces the height of the frame slightly (to the 4
8423   // pixels needed to accommodate the Doc) before it snaps back into
8424   // full height.  The user would need a very trained eye to spot
8425   // this.)
8426   NSScreen * screen = [self screen];
8427   if (screen != nil)
8428     {
8429       int fs_state = [(EmacsView *)[self delegate] fullscreenState];
8431       NSTRACE_FSTYPE ("fullscreenState", fs_state);
8433       NSRect sr = [screen frame];
8434       struct EmacsMargins margins
8435         = ns_screen_margins_ignoring_hidden_dock(screen);
8437       NSRect wr = [self frame];
8438       NSTRACE_RECT ("Rect after zoom", wr);
8440       NSRect newWr = wr;
8442       if (fs_state == FULLSCREEN_MAXIMIZED
8443           || fs_state == FULLSCREEN_HEIGHT)
8444         {
8445           newWr.origin.y = sr.origin.y + margins.bottom;
8446           newWr.size.height = sr.size.height - margins.top - margins.bottom;
8447         }
8449       if (fs_state == FULLSCREEN_MAXIMIZED
8450           || fs_state == FULLSCREEN_WIDTH)
8451         {
8452           newWr.origin.x = sr.origin.x + margins.left;
8453           newWr.size.width = sr.size.width - margins.right - margins.left;
8454         }
8456       if (newWr.size.width     != wr.size.width
8457           || newWr.size.height != wr.size.height
8458           || newWr.origin.x    != wr.origin.x
8459           || newWr.origin.y    != wr.origin.y)
8460         {
8461           NSTRACE_MSG ("New frame different");
8462           [self setFrame: newWr display: NO];
8463         }
8464     }
8465 #else
8466   // Non-native zoom which is done instantaneously.  The resulting
8467   // frame covers the entire screen, except the menu-bar and dock, if
8468   // present.
8469   NSScreen * screen = [self screen];
8470   if (screen != nil)
8471     {
8472       NSRect sr = [screen frame];
8473       struct EmacsMargins margins
8474         = ns_screen_margins_ignoring_hidden_dock(screen);
8476       sr.size.height -= (margins.top + margins.bottom);
8477       sr.size.width  -= (margins.left + margins.right);
8478       sr.origin.x += margins.left;
8479       sr.origin.y += margins.bottom;
8481       sr = [[self delegate] windowWillUseStandardFrame:self
8482                                           defaultFrame:sr];
8483       [self setFrame: sr display: NO];
8484     }
8485 #endif
8488 - (void)setFrame:(NSRect)windowFrame
8489          display:(BOOL)displayViews
8491   NSTRACE ("[EmacsWindow setFrame:" NSTRACE_FMT_RECT " display:%d]",
8492            NSTRACE_ARG_RECT (windowFrame), displayViews);
8494   [super setFrame:windowFrame display:displayViews];
8497 - (void)setFrame:(NSRect)windowFrame
8498          display:(BOOL)displayViews
8499          animate:(BOOL)performAnimation
8501   NSTRACE ("[EmacsWindow setFrame:" NSTRACE_FMT_RECT
8502            " display:%d performAnimation:%d]",
8503            NSTRACE_ARG_RECT (windowFrame), displayViews, performAnimation);
8505   [super setFrame:windowFrame display:displayViews animate:performAnimation];
8508 - (void)setFrameTopLeftPoint:(NSPoint)point
8510   NSTRACE ("[EmacsWindow setFrameTopLeftPoint:" NSTRACE_FMT_POINT "]",
8511            NSTRACE_ARG_POINT (point));
8513   [super setFrameTopLeftPoint:point];
8516 - (BOOL)canBecomeKeyWindow
8518   return !FRAME_NO_ACCEPT_FOCUS (((EmacsView *)[self delegate])->emacsframe);
8520 @end /* EmacsWindow */
8523 @implementation EmacsFSWindow
8525 - (BOOL)canBecomeKeyWindow
8527   return YES;
8530 - (BOOL)canBecomeMainWindow
8532   return YES;
8535 @end
8537 /* ==========================================================================
8539     EmacsScroller implementation
8541    ========================================================================== */
8544 @implementation EmacsScroller
8546 /* for repeat button push */
8547 #define SCROLL_BAR_FIRST_DELAY 0.5
8548 #define SCROLL_BAR_CONTINUOUS_DELAY (1.0 / 15)
8550 + (CGFloat) scrollerWidth
8552   /* TODO: if we want to allow variable widths, this is the place to do it,
8553            however neither GNUstep nor Cocoa support it very well */
8554   CGFloat r;
8555 #if defined (NS_IMPL_COCOA) \
8556   && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
8557 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
8558   if ([NSScroller respondsToSelector:
8559                     @selector(scrollerWidthForControlSize:scrollerStyle:)])
8560 #endif
8561     r = [NSScroller scrollerWidthForControlSize: NSControlSizeRegular
8562                                   scrollerStyle: NSScrollerStyleLegacy];
8563 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
8564   else
8565 #endif
8566 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
8567 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 \
8568   || defined (NS_IMPL_GNUSTEP)
8569     r = [NSScroller scrollerWidth];
8570 #endif
8571   return r;
8574 - (instancetype)initFrame: (NSRect )r window: (Lisp_Object)nwin
8576   NSTRACE ("[EmacsScroller initFrame: window:]");
8578   if (r.size.width > r.size.height)
8579       horizontal = YES;
8580   else
8581       horizontal = NO;
8583   [super initWithFrame: r/*NSMakeRect (0, 0, 0, 0)*/];
8584   [self setContinuous: YES];
8585   [self setEnabled: YES];
8587   /* Ensure auto resizing of scrollbars occurs within the emacs frame's view
8588      locked against the top and bottom edges, and right edge on macOS, where
8589      scrollers are on right. */
8590 #ifdef NS_IMPL_GNUSTEP
8591   [self setAutoresizingMask: NSViewMaxXMargin | NSViewHeightSizable];
8592 #else
8593   [self setAutoresizingMask: NSViewMinXMargin | NSViewHeightSizable];
8594 #endif
8596   window = XWINDOW (nwin);
8597   condemned = NO;
8598   if (horizontal)
8599     pixel_length = NSWidth (r);
8600   else
8601     pixel_length = NSHeight (r);
8602   if (pixel_length == 0) pixel_length = 1;
8603   min_portion = 20 / pixel_length;
8605   frame = XFRAME (window->frame);
8606   if (FRAME_LIVE_P (frame))
8607     {
8608       int i;
8609       EmacsView *view = FRAME_NS_VIEW (frame);
8610       NSView *sview = [[view window] contentView];
8611       NSArray *subs = [sview subviews];
8613       /* disable optimization stopping redraw of other scrollbars */
8614       view->scrollbarsNeedingUpdate = 0;
8615       for (i =[subs count]-1; i >= 0; i--)
8616         if ([[subs objectAtIndex: i] isKindOfClass: [EmacsScroller class]])
8617           view->scrollbarsNeedingUpdate++;
8618       [sview addSubview: self];
8619     }
8621 /*  [self setFrame: r]; */
8623   return self;
8627 - (void)setFrame: (NSRect)newRect
8629   NSTRACE ("[EmacsScroller setFrame:]");
8631 /*  block_input (); */
8632   if (horizontal)
8633     pixel_length = NSWidth (newRect);
8634   else
8635     pixel_length = NSHeight (newRect);
8636   if (pixel_length == 0) pixel_length = 1;
8637   min_portion = 20 / pixel_length;
8638   [super setFrame: newRect];
8639 /*  unblock_input (); */
8643 - (void)dealloc
8645   NSTRACE ("[EmacsScroller dealloc]");
8646   if (window)
8647     {
8648       if (horizontal)
8649         wset_horizontal_scroll_bar (window, Qnil);
8650       else
8651         wset_vertical_scroll_bar (window, Qnil);
8652     }
8653   window = 0;
8654   [super dealloc];
8658 - (instancetype)condemn
8660   NSTRACE ("[EmacsScroller condemn]");
8661   condemned =YES;
8662   return self;
8666 - (instancetype)reprieve
8668   NSTRACE ("[EmacsScroller reprieve]");
8669   condemned =NO;
8670   return self;
8674 -(bool)judge
8676   NSTRACE ("[EmacsScroller judge]");
8677   bool ret = condemned;
8678   if (condemned)
8679     {
8680       EmacsView *view;
8681       block_input ();
8682       /* ensure other scrollbar updates after deletion */
8683       view = (EmacsView *)FRAME_NS_VIEW (frame);
8684       if (view != nil)
8685         view->scrollbarsNeedingUpdate++;
8686       if (window)
8687         {
8688           if (horizontal)
8689             wset_horizontal_scroll_bar (window, Qnil);
8690           else
8691             wset_vertical_scroll_bar (window, Qnil);
8692         }
8693       window = 0;
8694       [self removeFromSuperview];
8695       [self release];
8696       unblock_input ();
8697     }
8698   return ret;
8702 - (void)resetCursorRects
8704   NSRect visible = [self visibleRect];
8705   NSTRACE ("[EmacsScroller resetCursorRects]");
8707   if (!NSIsEmptyRect (visible))
8708     [self addCursorRect: visible cursor: [NSCursor arrowCursor]];
8709   [[NSCursor arrowCursor] setOnMouseEntered: YES];
8713 - (int) checkSamePosition: (int) position portion: (int) portion
8714                     whole: (int) whole
8716   return em_position ==position && em_portion ==portion && em_whole ==whole
8717     && portion != whole; /* needed for resize empty buf */
8721 - (instancetype)setPosition: (int)position portion: (int)portion whole: (int)whole
8723   NSTRACE ("[EmacsScroller setPosition:portion:whole:]");
8725   em_position = position;
8726   em_portion = portion;
8727   em_whole = whole;
8729   if (portion >= whole)
8730     {
8731 #ifdef NS_IMPL_COCOA
8732       [self setKnobProportion: 1.0];
8733       [self setDoubleValue: 1.0];
8734 #else
8735       [self setFloatValue: 0.0 knobProportion: 1.0];
8736 #endif
8737     }
8738   else
8739     {
8740       float pos;
8741       CGFloat por;
8742       portion = max ((float)whole*min_portion/pixel_length, portion);
8743       pos = (float)position / (whole - portion);
8744       por = (CGFloat)portion/whole;
8745 #ifdef NS_IMPL_COCOA
8746       [self setKnobProportion: por];
8747       [self setDoubleValue: pos];
8748 #else
8749       [self setFloatValue: pos knobProportion: por];
8750 #endif
8751     }
8753   return self;
8756 /* set up emacs_event */
8757 - (void) sendScrollEventAtLoc: (float)loc fromEvent: (NSEvent *)e
8759   Lisp_Object win;
8761   NSTRACE ("[EmacsScroller sendScrollEventAtLoc:fromEvent:]");
8763   if (!emacs_event)
8764     return;
8766   emacs_event->part = last_hit_part;
8767   emacs_event->code = 0;
8768   emacs_event->modifiers = EV_MODIFIERS (e) | down_modifier;
8769   XSETWINDOW (win, window);
8770   emacs_event->frame_or_window = win;
8771   emacs_event->timestamp = EV_TIMESTAMP (e);
8772   emacs_event->arg = Qnil;
8774   if (horizontal)
8775     {
8776       emacs_event->kind = HORIZONTAL_SCROLL_BAR_CLICK_EVENT;
8777       XSETINT (emacs_event->x, em_whole * loc / pixel_length);
8778       XSETINT (emacs_event->y, em_whole);
8779     }
8780   else
8781     {
8782       emacs_event->kind = SCROLL_BAR_CLICK_EVENT;
8783       XSETINT (emacs_event->x, loc);
8784       XSETINT (emacs_event->y, pixel_length-20);
8785     }
8787   if (q_event_ptr)
8788     {
8789       n_emacs_events_pending++;
8790       kbd_buffer_store_event_hold (emacs_event, q_event_ptr);
8791     }
8792   else
8793     hold_event (emacs_event);
8794   EVENT_INIT (*emacs_event);
8795   ns_send_appdefined (-1);
8799 /* called manually thru timer to implement repeated button action w/hold-down */
8800 - (instancetype)repeatScroll: (NSTimer *)scrollEntry
8802   NSEvent *e = [[self window] currentEvent];
8803   NSPoint p =  [[self window] mouseLocationOutsideOfEventStream];
8804   BOOL inKnob = [self testPart: p] == NSScrollerKnob;
8806   NSTRACE ("[EmacsScroller repeatScroll:]");
8808   /* clear timer if need be */
8809   if (inKnob || [scroll_repeat_entry timeInterval] == SCROLL_BAR_FIRST_DELAY)
8810     {
8811         [scroll_repeat_entry invalidate];
8812         [scroll_repeat_entry release];
8813         scroll_repeat_entry = nil;
8815         if (inKnob)
8816           return self;
8818         scroll_repeat_entry
8819           = [[NSTimer scheduledTimerWithTimeInterval:
8820                         SCROLL_BAR_CONTINUOUS_DELAY
8821                                             target: self
8822                                           selector: @selector (repeatScroll:)
8823                                           userInfo: 0
8824                                            repeats: YES]
8825               retain];
8826     }
8828   [self sendScrollEventAtLoc: 0 fromEvent: e];
8829   return self;
8833 /* Asynchronous mouse tracking for scroller.  This allows us to dispatch
8834    mouseDragged events without going into a modal loop. */
8835 - (void)mouseDown: (NSEvent *)e
8837   NSRect sr, kr;
8838   /* hitPart is only updated AFTER event is passed on */
8839   NSScrollerPart part = [self testPart: [e locationInWindow]];
8840   CGFloat loc, kloc, pos UNINIT;
8841   int edge = 0;
8843   NSTRACE ("[EmacsScroller mouseDown:]");
8845   switch (part)
8846     {
8847     case NSScrollerDecrementPage:
8848       last_hit_part = horizontal ? scroll_bar_before_handle : scroll_bar_above_handle; break;
8849     case NSScrollerIncrementPage:
8850       last_hit_part = horizontal ? scroll_bar_after_handle : scroll_bar_below_handle; break;
8851     case NSScrollerDecrementLine:
8852       last_hit_part = horizontal ? scroll_bar_left_arrow : scroll_bar_up_arrow; break;
8853     case NSScrollerIncrementLine:
8854       last_hit_part = horizontal ? scroll_bar_right_arrow : scroll_bar_down_arrow; break;
8855     case NSScrollerKnob:
8856       last_hit_part = horizontal ? scroll_bar_horizontal_handle : scroll_bar_handle; break;
8857     case NSScrollerKnobSlot:  /* GNUstep-only */
8858       last_hit_part = scroll_bar_move_ratio; break;
8859     default:  /* NSScrollerNoPart? */
8860       fprintf (stderr, "EmacsScroller-mouseDown: unexpected part %ld\n",
8861                (long) part);
8862       return;
8863     }
8865   if (part == NSScrollerKnob || part == NSScrollerKnobSlot)
8866     {
8867       /* handle, or on GNUstep possibly slot */
8868       NSEvent *fake_event;
8869       int length;
8871       /* compute float loc in slot and mouse offset on knob */
8872       sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot]
8873                       toView: nil];
8874       if (horizontal)
8875         {
8876           length = NSWidth (sr);
8877           loc = ([e locationInWindow].x - NSMinX (sr));
8878         }
8879       else
8880         {
8881           length = NSHeight (sr);
8882           loc = length - ([e locationInWindow].y - NSMinY (sr));
8883         }
8885       if (loc <= 0.0)
8886         {
8887           loc = 0.0;
8888           edge = -1;
8889         }
8890       else if (loc >= length)
8891         {
8892           loc = length;
8893           edge = 1;
8894         }
8896       if (edge)
8897         kloc = 0.5 * edge;
8898       else
8899         {
8900           kr = [self convertRect: [self rectForPart: NSScrollerKnob]
8901                           toView: nil];
8902           if (horizontal)
8903             kloc = ([e locationInWindow].x - NSMinX (kr));
8904           else
8905             kloc = NSHeight (kr) - ([e locationInWindow].y - NSMinY (kr));
8906         }
8907       last_mouse_offset = kloc;
8909       if (part != NSScrollerKnob)
8910         /* this is a slot click on GNUstep: go straight there */
8911         pos = loc;
8913       /* send a fake mouse-up to super to preempt modal -trackKnob: mode */
8914       fake_event = [NSEvent mouseEventWithType: NSEventTypeLeftMouseUp
8915                                       location: [e locationInWindow]
8916                                  modifierFlags: [e modifierFlags]
8917                                      timestamp: [e timestamp]
8918                                   windowNumber: [e windowNumber]
8919                                        context: nil
8920                                    eventNumber: [e eventNumber]
8921                                     clickCount: [e clickCount]
8922                                       pressure: [e pressure]];
8923       [super mouseUp: fake_event];
8924     }
8925   else
8926     {
8927       pos = 0;      /* ignored */
8929       /* set a timer to repeat, as we can't let superclass do this modally */
8930       scroll_repeat_entry
8931         = [[NSTimer scheduledTimerWithTimeInterval: SCROLL_BAR_FIRST_DELAY
8932                                             target: self
8933                                           selector: @selector (repeatScroll:)
8934                                           userInfo: 0
8935                                            repeats: YES]
8936             retain];
8937     }
8939   if (part != NSScrollerKnob)
8940     [self sendScrollEventAtLoc: pos fromEvent: e];
8944 /* Called as we manually track scroller drags, rather than superclass. */
8945 - (void)mouseDragged: (NSEvent *)e
8947     NSRect sr;
8948     double loc, pos;
8949     int length;
8951     NSTRACE ("[EmacsScroller mouseDragged:]");
8953       sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot]
8954                       toView: nil];
8956       if (horizontal)
8957         {
8958           length = NSWidth (sr);
8959           loc = ([e locationInWindow].x - NSMinX (sr));
8960         }
8961       else
8962         {
8963           length = NSHeight (sr);
8964           loc = length - ([e locationInWindow].y - NSMinY (sr));
8965         }
8967       if (loc <= 0.0)
8968         {
8969           loc = 0.0;
8970         }
8971       else if (loc >= length + last_mouse_offset)
8972         {
8973           loc = length + last_mouse_offset;
8974         }
8976       pos = (loc - last_mouse_offset);
8977       [self sendScrollEventAtLoc: pos fromEvent: e];
8981 - (void)mouseUp: (NSEvent *)e
8983   NSTRACE ("[EmacsScroller mouseUp:]");
8985   if (scroll_repeat_entry)
8986     {
8987       [scroll_repeat_entry invalidate];
8988       [scroll_repeat_entry release];
8989       scroll_repeat_entry = nil;
8990     }
8991   last_hit_part = scroll_bar_above_handle;
8995 /* treat scrollwheel events in the bar as though they were in the main window */
8996 - (void) scrollWheel: (NSEvent *)theEvent
8998   NSTRACE ("[EmacsScroller scrollWheel:]");
9000   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (frame);
9001   [view mouseDown: theEvent];
9004 @end  /* EmacsScroller */
9007 #ifdef NS_IMPL_GNUSTEP
9008 /* Dummy class to get rid of startup warnings.  */
9009 @implementation EmacsDocument
9011 @end
9012 #endif
9015 /* ==========================================================================
9017    Font-related functions; these used to be in nsfaces.m
9019    ========================================================================== */
9022 Lisp_Object
9023 x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
9025   struct font *font = XFONT_OBJECT (font_object);
9026   EmacsView *view = FRAME_NS_VIEW (f);
9027   int font_ascent, font_descent;
9029   if (fontset < 0)
9030     fontset = fontset_from_font (font_object);
9031   FRAME_FONTSET (f) = fontset;
9033   if (FRAME_FONT (f) == font)
9034     /* This font is already set in frame F.  There's nothing more to
9035        do.  */
9036     return font_object;
9038   FRAME_FONT (f) = font;
9040   FRAME_BASELINE_OFFSET (f) = font->baseline_offset;
9041   FRAME_COLUMN_WIDTH (f) = font->average_width;
9042   get_font_ascent_descent (font, &font_ascent, &font_descent);
9043   FRAME_LINE_HEIGHT (f) = font_ascent + font_descent;
9045   /* Compute the scroll bar width in character columns.  */
9046   if (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) > 0)
9047     {
9048       int wid = FRAME_COLUMN_WIDTH (f);
9049       FRAME_CONFIG_SCROLL_BAR_COLS (f)
9050         = (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) + wid - 1) / wid;
9051     }
9052   else
9053     {
9054       int wid = FRAME_COLUMN_WIDTH (f);
9055       FRAME_CONFIG_SCROLL_BAR_COLS (f) = (14 + wid - 1) / wid;
9056     }
9058   /* Compute the scroll bar height in character lines.  */
9059   if (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) > 0)
9060     {
9061       int height = FRAME_LINE_HEIGHT (f);
9062       FRAME_CONFIG_SCROLL_BAR_LINES (f)
9063         = (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) + height - 1) / height;
9064     }
9065   else
9066     {
9067       int height = FRAME_LINE_HEIGHT (f);
9068       FRAME_CONFIG_SCROLL_BAR_LINES (f) = (14 + height - 1) / height;
9069     }
9071   /* Now make the frame display the given font.  */
9072   if (FRAME_NS_WINDOW (f) != 0 && ! [view isFullscreen])
9073     adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
9074                        FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3,
9075                        false, Qfont);
9077   return font_object;
9081 /* XLFD: -foundry-family-weight-slant-swidth-adstyle-pxlsz-ptSz-resx-resy-spc-avgWidth-rgstry-encoding */
9082 /* Note: ns_font_to_xlfd and ns_fontname_to_xlfd no longer needed, removed
9083          in 1.43. */
9085 const char *
9086 ns_xlfd_to_fontname (const char *xlfd)
9087 /* --------------------------------------------------------------------------
9088     Convert an X font name (XLFD) to an NS font name.
9089     Only family is used.
9090     The string returned is temporarily allocated.
9091    -------------------------------------------------------------------------- */
9093   char *name = xmalloc (180);
9094   int i, len;
9095   const char *ret;
9097   if (!strncmp (xlfd, "--", 2))
9098     sscanf (xlfd, "--%*[^-]-%[^-]179-", name);
9099   else
9100     sscanf (xlfd, "-%*[^-]-%[^-]179-", name);
9102   /* stopgap for malformed XLFD input */
9103   if (strlen (name) == 0)
9104     strcpy (name, "Monaco");
9106   /* undo hack in ns_fontname_to_xlfd, converting '$' to '-', '_' to ' '
9107      also uppercase after '-' or ' ' */
9108   name[0] = c_toupper (name[0]);
9109   for (len =strlen (name), i =0; i<len; i++)
9110     {
9111       if (name[i] == '$')
9112         {
9113           name[i] = '-';
9114           if (i+1<len)
9115             name[i+1] = c_toupper (name[i+1]);
9116         }
9117       else if (name[i] == '_')
9118         {
9119           name[i] = ' ';
9120           if (i+1<len)
9121             name[i+1] = c_toupper (name[i+1]);
9122         }
9123     }
9124 /*fprintf (stderr, "converted '%s' to '%s'\n",xlfd,name);  */
9125   ret = [[NSString stringWithUTF8String: name] UTF8String];
9126   xfree (name);
9127   return ret;
9131 void
9132 syms_of_nsterm (void)
9134   NSTRACE ("syms_of_nsterm");
9136   ns_antialias_threshold = 10.0;
9138   /* from 23+ we need to tell emacs what modifiers there are.. */
9139   DEFSYM (Qmodifier_value, "modifier-value");
9140   DEFSYM (Qalt, "alt");
9141   DEFSYM (Qhyper, "hyper");
9142   DEFSYM (Qmeta, "meta");
9143   DEFSYM (Qsuper, "super");
9144   DEFSYM (Qcontrol, "control");
9145   DEFSYM (QUTF8_STRING, "UTF8_STRING");
9147   DEFSYM (Qfile, "file");
9148   DEFSYM (Qurl, "url");
9150   Fput (Qalt, Qmodifier_value, make_number (alt_modifier));
9151   Fput (Qhyper, Qmodifier_value, make_number (hyper_modifier));
9152   Fput (Qmeta, Qmodifier_value, make_number (meta_modifier));
9153   Fput (Qsuper, Qmodifier_value, make_number (super_modifier));
9154   Fput (Qcontrol, Qmodifier_value, make_number (ctrl_modifier));
9156   DEFVAR_LISP ("ns-input-file", ns_input_file,
9157               "The file specified in the last NS event.");
9158   ns_input_file =Qnil;
9160   DEFVAR_LISP ("ns-working-text", ns_working_text,
9161               "String for visualizing working composition sequence.");
9162   ns_working_text =Qnil;
9164   DEFVAR_LISP ("ns-input-font", ns_input_font,
9165               "The font specified in the last NS event.");
9166   ns_input_font =Qnil;
9168   DEFVAR_LISP ("ns-input-fontsize", ns_input_fontsize,
9169               "The fontsize specified in the last NS event.");
9170   ns_input_fontsize =Qnil;
9172   DEFVAR_LISP ("ns-input-line", ns_input_line,
9173                "The line specified in the last NS event.");
9174   ns_input_line =Qnil;
9176   DEFVAR_LISP ("ns-input-spi-name", ns_input_spi_name,
9177                "The service name specified in the last NS event.");
9178   ns_input_spi_name =Qnil;
9180   DEFVAR_LISP ("ns-input-spi-arg", ns_input_spi_arg,
9181                "The service argument specified in the last NS event.");
9182   ns_input_spi_arg =Qnil;
9184   DEFVAR_LISP ("ns-alternate-modifier", ns_alternate_modifier,
9185                "This variable describes the behavior of the alternate or option key.\n\
9186 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9187 that key.\n\
9188 Set to none means that the alternate / option key is not interpreted by Emacs\n\
9189 at all, allowing it to be used at a lower level for accented character entry.");
9190   ns_alternate_modifier = Qmeta;
9192   DEFVAR_LISP ("ns-right-alternate-modifier", ns_right_alternate_modifier,
9193                "This variable describes the behavior of the right alternate or option key.\n\
9194 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9195 that key.\n\
9196 Set to left means be the same key as `ns-alternate-modifier'.\n\
9197 Set to none means that the alternate / option key is not interpreted by Emacs\n\
9198 at all, allowing it to be used at a lower level for accented character entry.");
9199   ns_right_alternate_modifier = Qleft;
9201   DEFVAR_LISP ("ns-command-modifier", ns_command_modifier,
9202                "This variable describes the behavior of the command key.\n\
9203 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9204 that key.");
9205   ns_command_modifier = Qsuper;
9207   DEFVAR_LISP ("ns-right-command-modifier", ns_right_command_modifier,
9208                "This variable describes the behavior of the right command key.\n\
9209 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9210 that key.\n\
9211 Set to left means be the same key as `ns-command-modifier'.\n\
9212 Set to none means that the command / option key is not interpreted by Emacs\n\
9213 at all, allowing it to be used at a lower level for accented character entry.");
9214   ns_right_command_modifier = Qleft;
9216   DEFVAR_LISP ("ns-control-modifier", ns_control_modifier,
9217                "This variable describes the behavior of the control key.\n\
9218 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9219 that key.");
9220   ns_control_modifier = Qcontrol;
9222   DEFVAR_LISP ("ns-right-control-modifier", ns_right_control_modifier,
9223                "This variable describes the behavior of the right control key.\n\
9224 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9225 that key.\n\
9226 Set to left means be the same key as `ns-control-modifier'.\n\
9227 Set to none means that the control / option key is not interpreted by Emacs\n\
9228 at all, allowing it to be used at a lower level for accented character entry.");
9229   ns_right_control_modifier = Qleft;
9231   DEFVAR_LISP ("ns-function-modifier", ns_function_modifier,
9232                "This variable describes the behavior of the function key (on laptops).\n\
9233 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9234 that key.\n\
9235 Set to none means that the function key is not interpreted by Emacs at all,\n\
9236 allowing it to be used at a lower level for accented character entry.");
9237   ns_function_modifier = Qnone;
9239   DEFVAR_LISP ("ns-antialias-text", ns_antialias_text,
9240                "Non-nil (the default) means to render text antialiased.");
9241   ns_antialias_text = Qt;
9243   DEFVAR_LISP ("ns-use-thin-smoothing", ns_use_thin_smoothing,
9244                "Non-nil turns on a font smoothing method that produces thinner strokes.");
9245   ns_use_thin_smoothing = Qnil;
9247   DEFVAR_LISP ("ns-confirm-quit", ns_confirm_quit,
9248                "Whether to confirm application quit using dialog.");
9249   ns_confirm_quit = Qnil;
9251   DEFVAR_LISP ("ns-auto-hide-menu-bar", ns_auto_hide_menu_bar,
9252                doc: /* Non-nil means that the menu bar is hidden, but appears when the mouse is near.
9253 Only works on Mac OS X 10.6 or later.  */);
9254   ns_auto_hide_menu_bar = Qnil;
9256   DEFVAR_BOOL ("ns-use-native-fullscreen", ns_use_native_fullscreen,
9257      doc: /*Non-nil means to use native fullscreen on Mac OS X 10.7 and later.
9258 Nil means use fullscreen the old (< 10.7) way.  The old way works better with
9259 multiple monitors, but lacks tool bar.  This variable is ignored on
9260 Mac OS X < 10.7.  Default is t.  */);
9261   ns_use_native_fullscreen = YES;
9262   ns_last_use_native_fullscreen = ns_use_native_fullscreen;
9264   DEFVAR_BOOL ("ns-use-fullscreen-animation", ns_use_fullscreen_animation,
9265      doc: /*Non-nil means use animation on non-native fullscreen.
9266 For native fullscreen, this does nothing.
9267 Default is nil.  */);
9268   ns_use_fullscreen_animation = NO;
9270   DEFVAR_BOOL ("ns-use-srgb-colorspace", ns_use_srgb_colorspace,
9271      doc: /*Non-nil means to use sRGB colorspace on Mac OS X 10.7 and later.
9272 Note that this does not apply to images.
9273 This variable is ignored on Mac OS X < 10.7 and GNUstep.  */);
9274   ns_use_srgb_colorspace = YES;
9276   DEFVAR_BOOL ("ns-use-mwheel-acceleration",
9277                ns_use_mwheel_acceleration,
9278      doc: /*Non-nil means use macOS's standard mouse wheel acceleration.
9279 This variable is ignored on macOS < 10.7 and GNUstep.  Default is t.  */);
9280   ns_use_mwheel_acceleration = YES;
9282   DEFVAR_LISP ("ns-mwheel-line-height", ns_mwheel_line_height,
9283                doc: /*The number of pixels touchpad scrolling considers one line.
9284 Nil or a non-number means use the default frame line height.
9285 This variable is ignored on macOS < 10.7 and GNUstep.  Default is nil.  */);
9286   ns_mwheel_line_height = Qnil;
9288   DEFVAR_BOOL ("ns-use-mwheel-momentum", ns_use_mwheel_momentum,
9289                doc: /*Non-nil means mouse wheel scrolling uses momentum.
9290 This variable is ignored on macOS < 10.7 and GNUstep.  Default is t.  */);
9291   ns_use_mwheel_momentum = YES;
9293   /* TODO: move to common code */
9294   DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars,
9295                doc: /* Which toolkit scroll bars Emacs uses, if any.
9296 A value of nil means Emacs doesn't use toolkit scroll bars.
9297 With the X Window system, the value is a symbol describing the
9298 X toolkit.  Possible values are: gtk, motif, xaw, or xaw3d.
9299 With MS Windows or Nextstep, the value is t.  */);
9300   Vx_toolkit_scroll_bars = Qt;
9302   DEFVAR_BOOL ("x-use-underline-position-properties",
9303                x_use_underline_position_properties,
9304      doc: /*Non-nil means make use of UNDERLINE_POSITION font properties.
9305 A value of nil means ignore them.  If you encounter fonts with bogus
9306 UNDERLINE_POSITION font properties, for example 7x13 on XFree prior
9307 to 4.1, set this to nil. */);
9308   x_use_underline_position_properties = 0;
9310   DEFVAR_BOOL ("x-underline-at-descent-line",
9311                x_underline_at_descent_line,
9312      doc: /* Non-nil means to draw the underline at the same place as the descent line.
9313 A value of nil means to draw the underline according to the value of the
9314 variable `x-use-underline-position-properties', which is usually at the
9315 baseline level.  The default value is nil.  */);
9316   x_underline_at_descent_line = 0;
9318   /* Tell Emacs about this window system.  */
9319   Fprovide (Qns, Qnil);
9321   DEFSYM (Qcocoa, "cocoa");
9322   DEFSYM (Qgnustep, "gnustep");
9324 #ifdef NS_IMPL_COCOA
9325   Fprovide (Qcocoa, Qnil);
9326   syms_of_macfont ();
9327 #else
9328   Fprovide (Qgnustep, Qnil);
9329   syms_of_nsfont ();
9330 #endif