Merge from Gnulib
[emacs.git] / src / nsterm.m
blob5c29f039e51fbfb27656b218450d55707a3cb188
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       int toolbar_height;
6808 #ifdef NS_IMPL_GNUSTEP
6809       // GNUstep does not always update the tool bar height.  Force it.
6810       if (toolbar && [toolbar isVisible])
6811           update_frame_tool_bar (emacsframe);
6812 #endif
6814       toolbar_height = FRAME_TOOLBAR_HEIGHT (emacsframe);
6815       if (toolbar_height < 0)
6816         toolbar_height = 35;
6818       extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
6819         + toolbar_height;
6820     }
6822   if (wait_for_tool_bar)
6823     {
6824       /* The toolbar height is always 0 in fullscreen and undecorated
6825          frames, so don't wait for it to become available. */
6826       if (FRAME_TOOLBAR_HEIGHT (emacsframe) == 0
6827           && FRAME_UNDECORATED (emacsframe) == false
6828           && ! [self isFullscreen])
6829         {
6830           NSTRACE_MSG ("Waiting for toolbar");
6831           return;
6832         }
6833       wait_for_tool_bar = NO;
6834     }
6836   neww = (int)wr.size.width - emacsframe->border_width;
6837   newh = (int)wr.size.height - extra;
6839   NSTRACE_SIZE ("New size", NSMakeSize (neww, newh));
6840   NSTRACE_MSG ("FRAME_TOOLBAR_HEIGHT: %d", FRAME_TOOLBAR_HEIGHT (emacsframe));
6841   NSTRACE_MSG ("FRAME_NS_TITLEBAR_HEIGHT: %d", FRAME_NS_TITLEBAR_HEIGHT (emacsframe));
6843   cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, neww);
6844   rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe, newh);
6846   if (cols < MINWIDTH)
6847     cols = MINWIDTH;
6849   if (rows < MINHEIGHT)
6850     rows = MINHEIGHT;
6852   NSTRACE_MSG ("New columns: %d", cols);
6853   NSTRACE_MSG ("New rows: %d", rows);
6855   if (oldr != rows || oldc != cols || neww != oldw || newh != oldh)
6856     {
6857       NSView *view = FRAME_NS_VIEW (emacsframe);
6859       change_frame_size (emacsframe,
6860                          FRAME_PIXEL_TO_TEXT_WIDTH (emacsframe, neww),
6861                          FRAME_PIXEL_TO_TEXT_HEIGHT (emacsframe, newh),
6862                          0, delay, 0, 1);
6863       SET_FRAME_GARBAGED (emacsframe);
6864       cancel_mouse_face (emacsframe);
6866       /* The next two lines set the frame to the same size as we've
6867          already set above.  We need to do this when we switch back
6868          from non-native fullscreen, in other circumstances it appears
6869          to be a noop.  (bug#28872) */
6870       wr = NSMakeRect (0, 0, neww, newh);
6871       [view setFrame: wr];
6873       // to do: consider using [NSNotificationCenter postNotificationName:].
6874       [self windowDidMove: // Update top/left.
6875               [NSNotification notificationWithName:NSWindowDidMoveNotification
6876                                             object:[view window]]];
6877     }
6878   else
6879     {
6880       NSTRACE_MSG ("No change");
6881     }
6884 - (NSSize)windowWillResize: (NSWindow *)sender toSize: (NSSize)frameSize
6885 /* normalize frame to gridded text size */
6887   int extra = 0;
6889   NSTRACE ("[EmacsView windowWillResize:toSize: " NSTRACE_FMT_SIZE "]",
6890            NSTRACE_ARG_SIZE (frameSize));
6891   NSTRACE_RECT   ("[sender frame]", [sender frame]);
6892   NSTRACE_FSTYPE ("fs_state", fs_state);
6894   if (!FRAME_LIVE_P (emacsframe))
6895     return frameSize;
6897   if (fs_state == FULLSCREEN_MAXIMIZED
6898       && (maximized_width != (int)frameSize.width
6899           || maximized_height != (int)frameSize.height))
6900     [self setFSValue: FULLSCREEN_NONE];
6901   else if (fs_state == FULLSCREEN_WIDTH
6902            && maximized_width != (int)frameSize.width)
6903     [self setFSValue: FULLSCREEN_NONE];
6904   else if (fs_state == FULLSCREEN_HEIGHT
6905            && maximized_height != (int)frameSize.height)
6906     [self setFSValue: FULLSCREEN_NONE];
6908   if (fs_state == FULLSCREEN_NONE)
6909     maximized_width = maximized_height = -1;
6911   if (! [self isFullscreen])
6912     {
6913       extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
6914         + FRAME_TOOLBAR_HEIGHT (emacsframe);
6915     }
6917   cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, frameSize.width);
6918   if (cols < MINWIDTH)
6919     cols = MINWIDTH;
6921   rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe,
6922                                            frameSize.height - extra);
6923   if (rows < MINHEIGHT)
6924     rows = MINHEIGHT;
6925 #ifdef NS_IMPL_COCOA
6926   {
6927     /* this sets window title to have size in it; the wm does this under GS */
6928     NSRect r = [[self window] frame];
6929     if (r.size.height == frameSize.height && r.size.width == frameSize.width)
6930       {
6931         if (old_title != 0)
6932           {
6933             xfree (old_title);
6934             old_title = 0;
6935           }
6936       }
6937     else if (fs_state == FULLSCREEN_NONE && ! maximizing_resize
6938              && [[self window] title] != NULL)
6939       {
6940         char *size_title;
6941         NSWindow *window = [self window];
6942         if (old_title == 0)
6943           {
6944             char *t = strdup ([[[self window] title] UTF8String]);
6945             char *pos = strstr (t, "  â€”  ");
6946             if (pos)
6947               *pos = '\0';
6948             old_title = t;
6949           }
6950         size_title = xmalloc (strlen (old_title) + 40);
6951         esprintf (size_title, "%s  â€”  (%d x %d)", old_title, cols, rows);
6952         [window setTitle: [NSString stringWithUTF8String: size_title]];
6953         [window display];
6954         xfree (size_title);
6955       }
6956   }
6957 #endif /* NS_IMPL_COCOA */
6959   NSTRACE_MSG ("cols: %d  rows: %d", cols, rows);
6961   /* Restrict the new size to the text gird.
6963      Don't restrict the width if the user only adjusted the height, and
6964      vice versa.  (Without this, the frame would shrink, and move
6965      slightly, if the window was resized by dragging one of its
6966      borders.) */
6967   if (!frame_resize_pixelwise)
6968     {
6969       NSRect r = [[self window] frame];
6971       if (r.size.width != frameSize.width)
6972         {
6973           frameSize.width =
6974             FRAME_TEXT_COLS_TO_PIXEL_WIDTH  (emacsframe, cols);
6975         }
6977       if (r.size.height != frameSize.height)
6978         {
6979           frameSize.height =
6980             FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (emacsframe, rows) + extra;
6981         }
6982     }
6984   NSTRACE_RETURN_SIZE (frameSize);
6986   return frameSize;
6990 - (void)windowDidResize: (NSNotification *)notification
6992   NSTRACE ("[EmacsView windowDidResize:]");
6993   if (!FRAME_LIVE_P (emacsframe))
6994     {
6995       NSTRACE_MSG ("Ignored (frame dead)");
6996       return;
6997     }
6998   if (emacsframe->output_data.ns->in_animation)
6999     {
7000       NSTRACE_MSG ("Ignored (in animation)");
7001       return;
7002     }
7004   if (! [self fsIsNative])
7005     {
7006       NSWindow *theWindow = [notification object];
7007       /* We can get notification on the non-FS window when in
7008          fullscreen mode.  */
7009       if ([self window] != theWindow) return;
7010     }
7012   NSTRACE_RECT ("frame", [[notification object] frame]);
7014 #ifdef NS_IMPL_GNUSTEP
7015   NSWindow *theWindow = [notification object];
7017    /* In GNUstep, at least currently, it's possible to get a didResize
7018       without getting a willResize.. therefore we need to act as if we got
7019       the willResize now */
7020   NSSize sz = [theWindow frame].size;
7021   sz = [self windowWillResize: theWindow toSize: sz];
7022 #endif /* NS_IMPL_GNUSTEP */
7024   if (cols > 0 && rows > 0)
7025     {
7026       [self updateFrameSize: YES];
7027     }
7029   ns_send_appdefined (-1);
7032 #ifdef NS_IMPL_COCOA
7033 - (void)viewDidEndLiveResize
7035   NSTRACE ("[EmacsView viewDidEndLiveResize]");
7037   [super viewDidEndLiveResize];
7038   if (old_title != 0)
7039     {
7040       [[self window] setTitle: [NSString stringWithUTF8String: old_title]];
7041       xfree (old_title);
7042       old_title = 0;
7043     }
7044   maximizing_resize = NO;
7046 #endif /* NS_IMPL_COCOA */
7049 - (void)windowDidBecomeKey: (NSNotification *)notification
7050 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
7052   [self windowDidBecomeKey];
7056 - (void)windowDidBecomeKey      /* for direct calls */
7058   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
7059   struct frame *old_focus = dpyinfo->x_focus_frame;
7061   NSTRACE ("[EmacsView windowDidBecomeKey]");
7063   if (emacsframe != old_focus)
7064     dpyinfo->x_focus_frame = emacsframe;
7066   ns_frame_rehighlight (emacsframe);
7068   if (emacs_event)
7069     {
7070       emacs_event->kind = FOCUS_IN_EVENT;
7071       EV_TRAILER ((id)nil);
7072     }
7076 - (void)windowDidResignKey: (NSNotification *)notification
7077 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
7079   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
7080   BOOL is_focus_frame = dpyinfo->x_focus_frame == emacsframe;
7081   NSTRACE ("[EmacsView windowDidResignKey:]");
7083   if (is_focus_frame)
7084     dpyinfo->x_focus_frame = 0;
7086   emacsframe->mouse_moved = 0;
7087   ns_frame_rehighlight (emacsframe);
7089   /* FIXME: for some reason needed on second and subsequent clicks away
7090             from sole-frame Emacs to get hollow box to show */
7091   if (!windowClosing && [[self window] isVisible] == YES)
7092     {
7093       x_update_cursor (emacsframe, 1);
7094       x_set_frame_alpha (emacsframe);
7095     }
7097   if (any_help_event_p)
7098     {
7099       Lisp_Object frame;
7100       XSETFRAME (frame, emacsframe);
7101       help_echo_string = Qnil;
7102       gen_help_event (Qnil, frame, Qnil, Qnil, 0);
7103     }
7105   if (emacs_event && is_focus_frame)
7106     {
7107       [self deleteWorkingText];
7108       emacs_event->kind = FOCUS_OUT_EVENT;
7109       EV_TRAILER ((id)nil);
7110     }
7114 - (void)windowWillMiniaturize: sender
7116   NSTRACE ("[EmacsView windowWillMiniaturize:]");
7120 - (void)setFrame:(NSRect)frameRect
7122   NSTRACE ("[EmacsView setFrame:" NSTRACE_FMT_RECT "]",
7123            NSTRACE_ARG_RECT (frameRect));
7125   [super setFrame:(NSRect)frameRect];
7129 - (BOOL)isFlipped
7131   return YES;
7135 - (BOOL)isOpaque
7137   return NO;
7141 - (void)createToolbar: (struct frame *)f
7143   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
7144   NSWindow *window = [view window];
7146   toolbar = [[EmacsToolbar alloc] initForView: self withIdentifier:
7147                    [NSString stringWithFormat: @"Emacs Frame %d",
7148                              ns_window_num]];
7149   [toolbar setVisible: NO];
7150   [window setToolbar: toolbar];
7152   /* Don't set frame garbaged until tool bar is up to date?
7153      This avoids an extra clear and redraw (flicker) at frame creation.  */
7154   if (FRAME_EXTERNAL_TOOL_BAR (f)) wait_for_tool_bar = YES;
7155   else wait_for_tool_bar = NO;
7158 #ifdef NS_IMPL_COCOA
7159   {
7160     NSButton *toggleButton;
7161     toggleButton = [window standardWindowButton: NSWindowToolbarButton];
7162     [toggleButton setTarget: self];
7163     [toggleButton setAction: @selector (toggleToolbar: )];
7164   }
7165 #endif
7169 - (instancetype) initFrameFromEmacs: (struct frame *)f
7171   NSRect r, wr;
7172   Lisp_Object tem;
7173   NSWindow *win;
7174   NSColor *col;
7175   NSString *name;
7177   NSTRACE ("[EmacsView initFrameFromEmacs:]");
7178   NSTRACE_MSG ("cols:%d lines:%d", f->text_cols, f->text_lines);
7180   windowClosing = NO;
7181   processingCompose = NO;
7182   scrollbarsNeedingUpdate = 0;
7183   fs_state = FULLSCREEN_NONE;
7184   fs_before_fs = next_maximized = -1;
7186   fs_is_native = NO;
7187 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7188 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7189   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7190 #endif
7191     fs_is_native = ns_use_native_fullscreen;
7192 #endif
7194   maximized_width = maximized_height = -1;
7195   nonfs_window = nil;
7197   ns_userRect = NSMakeRect (0, 0, 0, 0);
7198   r = NSMakeRect (0, 0, FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, f->text_cols),
7199                  FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, f->text_lines));
7200   [self initWithFrame: r];
7201   [self setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
7203   FRAME_NS_VIEW (f) = self;
7204   emacsframe = f;
7205 #ifdef NS_IMPL_COCOA
7206   old_title = 0;
7207   maximizing_resize = NO;
7208 #endif
7210   win = [[EmacsWindow alloc]
7211             initWithContentRect: r
7212                       styleMask: (FRAME_UNDECORATED (f)
7213                                   ? FRAME_UNDECORATED_FLAGS
7214                                   : FRAME_DECORATED_FLAGS)
7215                         backing: NSBackingStoreBuffered
7216                           defer: YES];
7218 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7219 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7220   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7221 #endif
7222     [win setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
7223 #endif
7225   wr = [win frame];
7226   bwidth = f->border_width = wr.size.width - r.size.width;
7228   [win setAcceptsMouseMovedEvents: YES];
7229   [win setDelegate: self];
7230 #if !defined (NS_IMPL_COCOA) || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7231 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7232   if ([win respondsToSelector: @selector(useOptimizedDrawing:)])
7233 #endif
7234     [win useOptimizedDrawing: YES];
7235 #endif
7237   [[win contentView] addSubview: self];
7239   if (ns_drag_types)
7240     [self registerForDraggedTypes: ns_drag_types];
7242   tem = f->name;
7243   name = [NSString stringWithUTF8String:
7244                    NILP (tem) ? "Emacs" : SSDATA (tem)];
7245   [win setTitle: name];
7247   /* toolbar support */
7248   if (! FRAME_UNDECORATED (f))
7249     [self createToolbar: f];
7251 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
7252 #ifndef NSAppKitVersionNumber10_10
7253 #define NSAppKitVersionNumber10_10 1343
7254 #endif
7256   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_10
7257       && FRAME_NS_APPEARANCE (f) != ns_appearance_aqua)
7258     win.appearance = [NSAppearance
7259                           appearanceNamed: NSAppearanceNameVibrantDark];
7260 #endif
7262 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
7263   if ([win respondsToSelector: @selector(titlebarAppearsTransparent)])
7264     win.titlebarAppearsTransparent = FRAME_NS_TRANSPARENT_TITLEBAR (f);
7265 #endif
7267   tem = f->icon_name;
7268   if (!NILP (tem))
7269     [win setMiniwindowTitle:
7270            [NSString stringWithUTF8String: SSDATA (tem)]];
7272   if (FRAME_PARENT_FRAME (f) != NULL)
7273     {
7274       NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
7275       [parent addChildWindow: win
7276                      ordered: NSWindowAbove];
7277     }
7279   if (FRAME_Z_GROUP (f) != z_group_none)
7280       win.level = NSNormalWindowLevel
7281         + (FRAME_Z_GROUP_BELOW (f) ? -1 : 1);
7283   {
7284     NSScreen *screen = [win screen];
7286     if (screen != 0)
7287       {
7288         NSPoint pt = NSMakePoint
7289           (IN_BOUND (-SCREENMAX, f->left_pos
7290                      + NS_PARENT_WINDOW_LEFT_POS (f), SCREENMAX),
7291            IN_BOUND (-SCREENMAX,
7292                      NS_PARENT_WINDOW_TOP_POS (f) - f->top_pos,
7293                      SCREENMAX));
7295         [win setFrameTopLeftPoint: pt];
7297         NSTRACE_RECT ("new frame", [win frame]);
7298       }
7299   }
7301   [win makeFirstResponder: self];
7303   col = ns_lookup_indexed_color (NS_FACE_BACKGROUND
7304                                  (FACE_FROM_ID (emacsframe, DEFAULT_FACE_ID)),
7305                                  emacsframe);
7306   [win setBackgroundColor: col];
7307   if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7308     [win setOpaque: NO];
7310 #if !defined (NS_IMPL_COCOA) \
7311   || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7312 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7313   if ([self respondsToSelector: @selector(allocateGState)])
7314 #endif
7315     [self allocateGState];
7316 #endif
7317   [NSApp registerServicesMenuSendTypes: ns_send_types
7318                            returnTypes: [NSArray array]];
7320   /* macOS Sierra automatically enables tabbed windows.  We can't
7321      allow this to be enabled until it's available on a Free system.
7322      Currently it only happens by accident and is buggy anyway. */
7323 #if defined (NS_IMPL_COCOA) \
7324   && MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
7325 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101200
7326   if ([win respondsToSelector: @selector(setTabbingMode:)])
7327 #endif
7328     [win setTabbingMode: NSWindowTabbingModeDisallowed];
7329 #endif
7331   ns_window_num++;
7332   return self;
7336 - (void)windowDidMove: sender
7338   NSWindow *win = [self window];
7339   NSRect r = [win frame];
7340   NSArray *screens = [NSScreen screens];
7341   NSScreen *screen = [screens objectAtIndex: 0];
7343   NSTRACE ("[EmacsView windowDidMove:]");
7345   if (!emacsframe->output_data.ns)
7346     return;
7347   if (screen != nil)
7348     {
7349       emacsframe->left_pos = r.origin.x - NS_PARENT_WINDOW_LEFT_POS (emacsframe);
7350       emacsframe->top_pos =
7351         NS_PARENT_WINDOW_TOP_POS (emacsframe) - (r.origin.y + r.size.height);
7353       if (emacs_event)
7354         {
7355           emacs_event->kind = MOVE_FRAME_EVENT;
7356           EV_TRAILER ((id)nil);
7357         }
7358     }
7362 /* Called AFTER method below, but before our windowWillResize call there leads
7363    to windowDidResize -> x_set_window_size.  Update emacs' notion of frame
7364    location so set_window_size moves the frame. */
7365 - (BOOL)windowShouldZoom: (NSWindow *)sender toFrame: (NSRect)newFrame
7367   NSTRACE (("[EmacsView windowShouldZoom:toFrame:" NSTRACE_FMT_RECT "]"
7368             NSTRACE_FMT_RETURN "YES"),
7369            NSTRACE_ARG_RECT (newFrame));
7371   emacsframe->output_data.ns->zooming = 1;
7372   return YES;
7376 /* Override to do something slightly nonstandard, but nice.  First click on
7377    zoom button will zoom vertically.  Second will zoom completely.  Third
7378    returns to original. */
7379 - (NSRect)windowWillUseStandardFrame:(NSWindow *)sender
7380                         defaultFrame:(NSRect)defaultFrame
7382   // TODO: Rename to "currentFrame" and assign "result" properly in
7383   // all paths.
7384   NSRect result = [sender frame];
7386   NSTRACE (("[EmacsView windowWillUseStandardFrame:defaultFrame:"
7387             NSTRACE_FMT_RECT "]"),
7388            NSTRACE_ARG_RECT (defaultFrame));
7389   NSTRACE_FSTYPE ("fs_state", fs_state);
7390   NSTRACE_FSTYPE ("fs_before_fs", fs_before_fs);
7391   NSTRACE_FSTYPE ("next_maximized", next_maximized);
7392   NSTRACE_RECT   ("ns_userRect", ns_userRect);
7393   NSTRACE_RECT   ("[sender frame]", [sender frame]);
7395   if (fs_before_fs != -1) /* Entering fullscreen */
7396     {
7397       NSTRACE_MSG ("Entering fullscreen");
7398       result = defaultFrame;
7399     }
7400   else
7401     {
7402       // Save the window size and position (frame) before the resize.
7403       if (fs_state != FULLSCREEN_MAXIMIZED
7404           && fs_state != FULLSCREEN_WIDTH)
7405         {
7406           ns_userRect.size.width = result.size.width;
7407           ns_userRect.origin.x   = result.origin.x;
7408         }
7410       if (fs_state != FULLSCREEN_MAXIMIZED
7411           && fs_state != FULLSCREEN_HEIGHT)
7412         {
7413           ns_userRect.size.height = result.size.height;
7414           ns_userRect.origin.y    = result.origin.y;
7415         }
7417       NSTRACE_RECT ("ns_userRect (2)", ns_userRect);
7419       if (next_maximized == FULLSCREEN_HEIGHT
7420           || (next_maximized == -1
7421               && abs ((int)(defaultFrame.size.height - result.size.height))
7422               > FRAME_LINE_HEIGHT (emacsframe)))
7423         {
7424           /* first click */
7425           NSTRACE_MSG ("FULLSCREEN_HEIGHT");
7426           maximized_height = result.size.height = defaultFrame.size.height;
7427           maximized_width = -1;
7428           result.origin.y = defaultFrame.origin.y;
7429           if (ns_userRect.size.height != 0)
7430             {
7431               result.origin.x = ns_userRect.origin.x;
7432               result.size.width = ns_userRect.size.width;
7433             }
7434           [self setFSValue: FULLSCREEN_HEIGHT];
7435 #ifdef NS_IMPL_COCOA
7436           maximizing_resize = YES;
7437 #endif
7438         }
7439       else if (next_maximized == FULLSCREEN_WIDTH)
7440         {
7441           NSTRACE_MSG ("FULLSCREEN_WIDTH");
7442           maximized_width = result.size.width = defaultFrame.size.width;
7443           maximized_height = -1;
7444           result.origin.x = defaultFrame.origin.x;
7445           if (ns_userRect.size.width != 0)
7446             {
7447               result.origin.y = ns_userRect.origin.y;
7448               result.size.height = ns_userRect.size.height;
7449             }
7450           [self setFSValue: FULLSCREEN_WIDTH];
7451         }
7452       else if (next_maximized == FULLSCREEN_MAXIMIZED
7453                || (next_maximized == -1
7454                    && abs ((int)(defaultFrame.size.width - result.size.width))
7455                    > FRAME_COLUMN_WIDTH (emacsframe)))
7456         {
7457           NSTRACE_MSG ("FULLSCREEN_MAXIMIZED");
7459           result = defaultFrame;  /* second click */
7460           maximized_width = result.size.width;
7461           maximized_height = result.size.height;
7462           [self setFSValue: FULLSCREEN_MAXIMIZED];
7463 #ifdef NS_IMPL_COCOA
7464           maximizing_resize = YES;
7465 #endif
7466         }
7467       else
7468         {
7469           /* restore */
7470           NSTRACE_MSG ("Restore");
7471           result = ns_userRect.size.height ? ns_userRect : result;
7472           NSTRACE_RECT ("restore (2)", result);
7473           ns_userRect = NSMakeRect (0, 0, 0, 0);
7474 #ifdef NS_IMPL_COCOA
7475           maximizing_resize = fs_state != FULLSCREEN_NONE;
7476 #endif
7477           [self setFSValue: FULLSCREEN_NONE];
7478           maximized_width = maximized_height = -1;
7479         }
7480     }
7482   if (fs_before_fs == -1) next_maximized = -1;
7484   NSTRACE_RECT   ("Final ns_userRect", ns_userRect);
7485   NSTRACE_MSG    ("Final maximized_width: %d", maximized_width);
7486   NSTRACE_MSG    ("Final maximized_height: %d", maximized_height);
7487   NSTRACE_FSTYPE ("Final next_maximized", next_maximized);
7489   [self windowWillResize: sender toSize: result.size];
7491   NSTRACE_RETURN_RECT (result);
7493   return result;
7497 - (void)windowDidDeminiaturize: sender
7499   NSTRACE ("[EmacsView windowDidDeminiaturize:]");
7500   if (!emacsframe->output_data.ns)
7501     return;
7503   SET_FRAME_ICONIFIED (emacsframe, 0);
7504   SET_FRAME_VISIBLE (emacsframe, 1);
7505   windows_or_buffers_changed = 63;
7507   if (emacs_event)
7508     {
7509       emacs_event->kind = DEICONIFY_EVENT;
7510       EV_TRAILER ((id)nil);
7511     }
7515 - (void)windowDidExpose: sender
7517   NSTRACE ("[EmacsView windowDidExpose:]");
7518   if (!emacsframe->output_data.ns)
7519     return;
7521   SET_FRAME_VISIBLE (emacsframe, 1);
7522   SET_FRAME_GARBAGED (emacsframe);
7524   if (send_appdefined)
7525     ns_send_appdefined (-1);
7529 - (void)windowDidMiniaturize: sender
7531   NSTRACE ("[EmacsView windowDidMiniaturize:]");
7532   if (!emacsframe->output_data.ns)
7533     return;
7535   SET_FRAME_ICONIFIED (emacsframe, 1);
7536   SET_FRAME_VISIBLE (emacsframe, 0);
7538   if (emacs_event)
7539     {
7540       emacs_event->kind = ICONIFY_EVENT;
7541       EV_TRAILER ((id)nil);
7542     }
7545 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7546 - (NSApplicationPresentationOptions)window:(NSWindow *)window
7547       willUseFullScreenPresentationOptions:
7548   (NSApplicationPresentationOptions)proposedOptions
7550   return proposedOptions|NSApplicationPresentationAutoHideToolbar;
7552 #endif
7554 - (void)windowWillEnterFullScreen:(NSNotification *)notification
7556   NSTRACE ("[EmacsView windowWillEnterFullScreen:]");
7557   [self windowWillEnterFullScreen];
7559 - (void)windowWillEnterFullScreen /* provided for direct calls */
7561   NSTRACE ("[EmacsView windowWillEnterFullScreen]");
7562   fs_before_fs = fs_state;
7565 - (void)windowDidEnterFullScreen:(NSNotification *)notification
7567   NSTRACE ("[EmacsView windowDidEnterFullScreen:]");
7568   [self windowDidEnterFullScreen];
7571 - (void)windowDidEnterFullScreen /* provided for direct calls */
7573   NSTRACE ("[EmacsView windowDidEnterFullScreen]");
7574   [self setFSValue: FULLSCREEN_BOTH];
7575   if (! [self fsIsNative])
7576     {
7577       [self windowDidBecomeKey];
7578       [nonfs_window orderOut:self];
7579     }
7580   else
7581     {
7582       BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (emacsframe) ? YES : NO;
7583 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 \
7584   && MAC_OS_X_VERSION_MIN_REQUIRED <= 1070
7585       unsigned val = (unsigned)[NSApp presentationOptions];
7587       // Mac OS X 10.7 bug fix, the menu won't appear without this.
7588       // val is non-zero on other macOS versions.
7589       if (val == 0)
7590         {
7591           NSApplicationPresentationOptions options
7592             = NSApplicationPresentationAutoHideDock
7593             | NSApplicationPresentationAutoHideMenuBar
7594             | NSApplicationPresentationFullScreen
7595             | NSApplicationPresentationAutoHideToolbar;
7597           [NSApp setPresentationOptions: options];
7598         }
7599 #endif
7600       [toolbar setVisible:tbar_visible];
7601     }
7604 - (void)windowWillExitFullScreen:(NSNotification *)notification
7606   NSTRACE ("[EmacsView windowWillExitFullScreen:]");
7607   [self windowWillExitFullScreen];
7610 - (void)windowWillExitFullScreen /* provided for direct calls */
7612   NSTRACE ("[EmacsView windowWillExitFullScreen]");
7613   if (!FRAME_LIVE_P (emacsframe))
7614     {
7615       NSTRACE_MSG ("Ignored (frame dead)");
7616       return;
7617     }
7618   if (next_maximized != -1)
7619     fs_before_fs = next_maximized;
7622 - (void)windowDidExitFullScreen:(NSNotification *)notification
7624   NSTRACE ("[EmacsView windowDidExitFullScreen:]");
7625   [self windowDidExitFullScreen];
7628 - (void)windowDidExitFullScreen /* provided for direct calls */
7630   NSTRACE ("[EmacsView windowDidExitFullScreen]");
7631   if (!FRAME_LIVE_P (emacsframe))
7632     {
7633       NSTRACE_MSG ("Ignored (frame dead)");
7634       return;
7635     }
7636   [self setFSValue: fs_before_fs];
7637   fs_before_fs = -1;
7638 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7639   [self updateCollectionBehavior];
7640 #endif
7641   if (FRAME_EXTERNAL_TOOL_BAR (emacsframe))
7642     {
7643       [toolbar setVisible:YES];
7644       update_frame_tool_bar (emacsframe);
7645       [self updateFrameSize:YES];
7646       [[self window] display];
7647     }
7648   else
7649     [toolbar setVisible:NO];
7651   if (next_maximized != -1)
7652     [[self window] performZoom:self];
7655 - (BOOL)fsIsNative
7657   return fs_is_native;
7660 - (BOOL)isFullscreen
7662   BOOL res;
7664   if (! fs_is_native)
7665     {
7666       res = (nonfs_window != nil);
7667     }
7668   else
7669     {
7670 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7671       res = (([[self window] styleMask] & NSWindowStyleMaskFullScreen) != 0);
7672 #else
7673       res = NO;
7674 #endif
7675     }
7677   NSTRACE ("[EmacsView isFullscreen] " NSTRACE_FMT_RETURN " %d",
7678            (int) res);
7680   return res;
7683 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7684 - (void)updateCollectionBehavior
7686   NSTRACE ("[EmacsView updateCollectionBehavior]");
7688   if (! [self isFullscreen])
7689     {
7690       NSWindow *win = [self window];
7691       NSWindowCollectionBehavior b = [win collectionBehavior];
7692       if (ns_use_native_fullscreen)
7693         b |= NSWindowCollectionBehaviorFullScreenPrimary;
7694       else
7695         b &= ~NSWindowCollectionBehaviorFullScreenPrimary;
7697       [win setCollectionBehavior: b];
7698 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7699       if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7700 #endif
7701         fs_is_native = ns_use_native_fullscreen;
7702     }
7704 #endif
7706 - (void)toggleFullScreen: (id)sender
7708   NSWindow *w, *fw;
7709   BOOL onFirstScreen;
7710   struct frame *f;
7711   NSRect r, wr;
7712   NSColor *col;
7714   NSTRACE ("[EmacsView toggleFullScreen:]");
7716   if (fs_is_native)
7717     {
7718 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7719 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7720       if ([[self window] respondsToSelector: @selector(toggleFullScreen:)])
7721 #endif
7722         [[self window] toggleFullScreen:sender];
7723 #endif
7724       return;
7725     }
7727   w = [self window];
7728   onFirstScreen = [[w screen] isEqual:[[NSScreen screens] objectAtIndex:0]];
7729   f = emacsframe;
7730   wr = [w frame];
7731   col = ns_lookup_indexed_color (NS_FACE_BACKGROUND
7732                                  (FACE_FROM_ID (f, DEFAULT_FACE_ID)),
7733                                  f);
7735   if (fs_state != FULLSCREEN_BOTH)
7736     {
7737       NSScreen *screen = [w screen];
7739 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
7740       /* Hide ghost menu bar on secondary monitor? */
7741       if (! onFirstScreen
7742 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
7743           && [NSScreen respondsToSelector: @selector(screensHaveSeparateSpaces)]
7744 #endif
7745           )
7746         onFirstScreen = [NSScreen screensHaveSeparateSpaces];
7747 #endif
7748       /* Hide dock and menubar if we are on the primary screen.  */
7749       if (onFirstScreen)
7750         {
7751 #ifdef NS_IMPL_COCOA
7752           NSApplicationPresentationOptions options
7753             = NSApplicationPresentationAutoHideDock
7754             | NSApplicationPresentationAutoHideMenuBar;
7756           [NSApp setPresentationOptions: options];
7757 #else
7758           [NSMenu setMenuBarVisible:NO];
7759 #endif
7760         }
7762       fw = [[EmacsFSWindow alloc]
7763                        initWithContentRect:[w contentRectForFrameRect:wr]
7764                                  styleMask:NSWindowStyleMaskBorderless
7765                                    backing:NSBackingStoreBuffered
7766                                      defer:YES
7767                                     screen:screen];
7769       [fw setContentView:[w contentView]];
7770       [fw setTitle:[w title]];
7771       [fw setDelegate:self];
7772       [fw setAcceptsMouseMovedEvents: YES];
7773 #if !defined (NS_IMPL_COCOA) \
7774   || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7775 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7776       if ([fw respondsToSelector: @selector(useOptimizedDrawing:)])
7777 #endif
7778         [fw useOptimizedDrawing: YES];
7779 #endif
7780       [fw setBackgroundColor: col];
7781       if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7782         [fw setOpaque: NO];
7784       f->border_width = 0;
7786       nonfs_window = w;
7788       [self windowWillEnterFullScreen];
7789       [fw makeKeyAndOrderFront:NSApp];
7790       [fw makeFirstResponder:self];
7791       [w orderOut:self];
7792       r = [fw frameRectForContentRect:[screen frame]];
7793       [fw setFrame: r display:YES animate:ns_use_fullscreen_animation];
7794       [self windowDidEnterFullScreen];
7795       [fw display];
7796     }
7797   else
7798     {
7799       fw = w;
7800       w = nonfs_window;
7801       nonfs_window = nil;
7803       if (onFirstScreen)
7804         {
7805 #ifdef NS_IMPL_COCOA
7806           [NSApp setPresentationOptions: NSApplicationPresentationDefault];
7807 #else
7808           [NSMenu setMenuBarVisible:YES];
7809 #endif
7810         }
7812       [w setContentView:[fw contentView]];
7813       [w setBackgroundColor: col];
7814       if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7815         [w setOpaque: NO];
7817       f->border_width = bwidth;
7819       // to do: consider using [NSNotificationCenter postNotificationName:] to send notifications.
7821       [self windowWillExitFullScreen];
7822       [fw setFrame: [w frame] display:YES animate:ns_use_fullscreen_animation];
7823       [fw close];
7824       [w makeKeyAndOrderFront:NSApp];
7825       [self windowDidExitFullScreen];
7826       [self updateFrameSize:YES];
7827     }
7830 - (void)handleFS
7832   NSTRACE ("[EmacsView handleFS]");
7834   if (fs_state != emacsframe->want_fullscreen)
7835     {
7836       if (fs_state == FULLSCREEN_BOTH)
7837         {
7838           NSTRACE_MSG ("fs_state == FULLSCREEN_BOTH");
7839           [self toggleFullScreen:self];
7840         }
7842       switch (emacsframe->want_fullscreen)
7843         {
7844         case FULLSCREEN_BOTH:
7845           NSTRACE_MSG ("FULLSCREEN_BOTH");
7846           [self toggleFullScreen:self];
7847           break;
7848         case FULLSCREEN_WIDTH:
7849           NSTRACE_MSG ("FULLSCREEN_WIDTH");
7850           next_maximized = FULLSCREEN_WIDTH;
7851           if (fs_state != FULLSCREEN_BOTH)
7852             [[self window] performZoom:self];
7853           break;
7854         case FULLSCREEN_HEIGHT:
7855           NSTRACE_MSG ("FULLSCREEN_HEIGHT");
7856           next_maximized = FULLSCREEN_HEIGHT;
7857           if (fs_state != FULLSCREEN_BOTH)
7858             [[self window] performZoom:self];
7859           break;
7860         case FULLSCREEN_MAXIMIZED:
7861           NSTRACE_MSG ("FULLSCREEN_MAXIMIZED");
7862           next_maximized = FULLSCREEN_MAXIMIZED;
7863           if (fs_state != FULLSCREEN_BOTH)
7864             [[self window] performZoom:self];
7865           break;
7866         case FULLSCREEN_NONE:
7867           NSTRACE_MSG ("FULLSCREEN_NONE");
7868           if (fs_state != FULLSCREEN_BOTH)
7869             {
7870               next_maximized = FULLSCREEN_NONE;
7871               [[self window] performZoom:self];
7872             }
7873           break;
7874         }
7876       emacsframe->want_fullscreen = FULLSCREEN_NONE;
7877     }
7881 - (void) setFSValue: (int)value
7883   NSTRACE ("[EmacsView setFSValue:" NSTRACE_FMT_FSTYPE "]",
7884            NSTRACE_ARG_FSTYPE(value));
7886   Lisp_Object lval = Qnil;
7887   switch (value)
7888     {
7889     case FULLSCREEN_BOTH:
7890       lval = Qfullboth;
7891       break;
7892     case FULLSCREEN_WIDTH:
7893       lval = Qfullwidth;
7894       break;
7895     case FULLSCREEN_HEIGHT:
7896       lval = Qfullheight;
7897       break;
7898     case FULLSCREEN_MAXIMIZED:
7899       lval = Qmaximized;
7900       break;
7901     }
7902   store_frame_param (emacsframe, Qfullscreen, lval);
7903   fs_state = value;
7906 - (void)mouseEntered: (NSEvent *)theEvent
7908   NSTRACE ("[EmacsView mouseEntered:]");
7909   if (emacsframe)
7910     FRAME_DISPLAY_INFO (emacsframe)->last_mouse_movement_time
7911       = EV_TIMESTAMP (theEvent);
7915 - (void)mouseExited: (NSEvent *)theEvent
7917   Mouse_HLInfo *hlinfo = emacsframe ? MOUSE_HL_INFO (emacsframe) : NULL;
7919   NSTRACE ("[EmacsView mouseExited:]");
7921   if (!hlinfo)
7922     return;
7924   FRAME_DISPLAY_INFO (emacsframe)->last_mouse_movement_time
7925     = EV_TIMESTAMP (theEvent);
7927   if (emacsframe == hlinfo->mouse_face_mouse_frame)
7928     {
7929       clear_mouse_face (hlinfo);
7930       hlinfo->mouse_face_mouse_frame = 0;
7931     }
7935 - (instancetype)menuDown: sender
7937   NSTRACE ("[EmacsView menuDown:]");
7938   if (context_menu_value == -1)
7939     context_menu_value = [sender tag];
7940   else
7941     {
7942       NSInteger tag = [sender tag];
7943       find_and_call_menu_selection (emacsframe, emacsframe->menu_bar_items_used,
7944                                     emacsframe->menu_bar_vector,
7945                                     (void *)tag);
7946     }
7948   ns_send_appdefined (-1);
7949   return self;
7953 - (EmacsToolbar *)toolbar
7955   return toolbar;
7959 /* this gets called on toolbar button click */
7960 - (instancetype)toolbarClicked: (id)item
7962   NSEvent *theEvent;
7963   int idx = [item tag] * TOOL_BAR_ITEM_NSLOTS;
7965   NSTRACE ("[EmacsView toolbarClicked:]");
7967   if (!emacs_event)
7968     return self;
7970   /* send first event (for some reason two needed) */
7971   theEvent = [[self window] currentEvent];
7972   emacs_event->kind = TOOL_BAR_EVENT;
7973   XSETFRAME (emacs_event->arg, emacsframe);
7974   EV_TRAILER (theEvent);
7976   emacs_event->kind = TOOL_BAR_EVENT;
7977 /*   XSETINT (emacs_event->code, 0); */
7978   emacs_event->arg = AREF (emacsframe->tool_bar_items,
7979                            idx + TOOL_BAR_ITEM_KEY);
7980   emacs_event->modifiers = EV_MODIFIERS (theEvent);
7981   EV_TRAILER (theEvent);
7982   return self;
7986 - (instancetype)toggleToolbar: (id)sender
7988   NSTRACE ("[EmacsView toggleToolbar:]");
7990   if (!emacs_event)
7991     return self;
7993   emacs_event->kind = NS_NONKEY_EVENT;
7994   emacs_event->code = KEY_NS_TOGGLE_TOOLBAR;
7995   EV_TRAILER ((id)nil);
7996   return self;
8000 - (void)drawRect: (NSRect)rect
8002   int x = NSMinX (rect), y = NSMinY (rect);
8003   int width = NSWidth (rect), height = NSHeight (rect);
8005   NSTRACE ("[EmacsView drawRect:" NSTRACE_FMT_RECT "]",
8006            NSTRACE_ARG_RECT(rect));
8008   if (!emacsframe || !emacsframe->output_data.ns)
8009     return;
8011   ns_clear_frame_area (emacsframe, x, y, width, height);
8012   block_input ();
8013   expose_frame (emacsframe, x, y, width, height);
8014   unblock_input ();
8016   /*
8017     drawRect: may be called (at least in Mac OS X 10.5) for invisible
8018     views as well for some reason.  Thus, do not infer visibility
8019     here.
8021     emacsframe->async_visible = 1;
8022     emacsframe->async_iconified = 0;
8023   */
8027 /* NSDraggingDestination protocol methods.  Actually this is not really a
8028    protocol, but a category of Object.  O well...  */
8030 -(NSDragOperation) draggingEntered: (id <NSDraggingInfo>) sender
8032   NSTRACE ("[EmacsView draggingEntered:]");
8033   return NSDragOperationGeneric;
8037 -(BOOL)prepareForDragOperation: (id <NSDraggingInfo>) sender
8039   return YES;
8043 -(BOOL)performDragOperation: (id <NSDraggingInfo>) sender
8045   id pb;
8046   int x, y;
8047   NSString *type;
8048   NSEvent *theEvent = [[self window] currentEvent];
8049   NSPoint position;
8050   NSDragOperation op = [sender draggingSourceOperationMask];
8051   int modifiers = 0;
8053   NSTRACE ("[EmacsView performDragOperation:]");
8055   if (!emacs_event)
8056     return NO;
8058   position = [self convertPoint: [sender draggingLocation] fromView: nil];
8059   x = lrint (position.x);  y = lrint (position.y);
8061   pb = [sender draggingPasteboard];
8062   type = [pb availableTypeFromArray: ns_drag_types];
8064   if (! (op & (NSDragOperationMove|NSDragOperationDelete)) &&
8065       // URL drags contain all operations (0xf), don't allow all to be set.
8066       (op & 0xf) != 0xf)
8067     {
8068       if (op & NSDragOperationLink)
8069         modifiers |= NSEventModifierFlagControl;
8070       if (op & NSDragOperationCopy)
8071         modifiers |= NSEventModifierFlagOption;
8072       if (op & NSDragOperationGeneric)
8073         modifiers |= NSEventModifierFlagCommand;
8074     }
8076   modifiers = EV_MODIFIERS2 (modifiers);
8077   if (type == 0)
8078     {
8079       return NO;
8080     }
8081   else if ([type isEqualToString: NSFilenamesPboardType])
8082     {
8083       NSArray *files;
8084       NSEnumerator *fenum;
8085       NSString *file;
8087       if (!(files = [pb propertyListForType: type]))
8088         return NO;
8090       fenum = [files objectEnumerator];
8091       while ( (file = [fenum nextObject]) )
8092         {
8093           emacs_event->kind = DRAG_N_DROP_EVENT;
8094           XSETINT (emacs_event->x, x);
8095           XSETINT (emacs_event->y, y);
8096           ns_input_file = append2 (ns_input_file,
8097                                    build_string ([file UTF8String]));
8098           emacs_event->modifiers = modifiers;
8099           emacs_event->arg =  list2 (Qfile, build_string ([file UTF8String]));
8100           EV_TRAILER (theEvent);
8101         }
8102       return YES;
8103     }
8104   else if ([type isEqualToString: NSURLPboardType])
8105     {
8106       NSURL *url = [NSURL URLFromPasteboard: pb];
8107       if (url == nil) return NO;
8109       emacs_event->kind = DRAG_N_DROP_EVENT;
8110       XSETINT (emacs_event->x, x);
8111       XSETINT (emacs_event->y, y);
8112       emacs_event->modifiers = modifiers;
8113       emacs_event->arg =  list2 (Qurl,
8114                                  build_string ([[url absoluteString]
8115                                                  UTF8String]));
8116       EV_TRAILER (theEvent);
8118       if ([url isFileURL] != NO)
8119         {
8120           NSString *file = [url path];
8121           ns_input_file = append2 (ns_input_file,
8122                                    build_string ([file UTF8String]));
8123         }
8124       return YES;
8125     }
8126   else if ([type isEqualToString: NSStringPboardType]
8127            || [type isEqualToString: NSTabularTextPboardType])
8128     {
8129       NSString *data;
8131       if (! (data = [pb stringForType: type]))
8132         return NO;
8134       emacs_event->kind = DRAG_N_DROP_EVENT;
8135       XSETINT (emacs_event->x, x);
8136       XSETINT (emacs_event->y, y);
8137       emacs_event->modifiers = modifiers;
8138       emacs_event->arg =  list2 (Qnil, build_string ([data UTF8String]));
8139       EV_TRAILER (theEvent);
8140       return YES;
8141     }
8142   else
8143     {
8144       fprintf (stderr, "Invalid data type in dragging pasteboard");
8145       return NO;
8146     }
8150 - (id) validRequestorForSendType: (NSString *)typeSent
8151                       returnType: (NSString *)typeReturned
8153   NSTRACE ("[EmacsView validRequestorForSendType:returnType:]");
8154   if (typeSent != nil && [ns_send_types indexOfObject: typeSent] != NSNotFound
8155       && typeReturned == nil)
8156     {
8157       if (! NILP (ns_get_local_selection (QPRIMARY, QUTF8_STRING)))
8158         return self;
8159     }
8161   return [super validRequestorForSendType: typeSent
8162                                returnType: typeReturned];
8166 /* The next two methods are part of NSServicesRequests informal protocol,
8167    supposedly called when a services menu item is chosen from this app.
8168    But this should not happen because we override the services menu with our
8169    own entries which call ns-perform-service.
8170    Nonetheless, it appeared to happen (under strange circumstances): bug#1435.
8171    So let's at least stub them out until further investigation can be done. */
8173 - (BOOL) readSelectionFromPasteboard: (NSPasteboard *)pb
8175   /* we could call ns_string_from_pasteboard(pboard) here but then it should
8176      be written into the buffer in place of the existing selection..
8177      ordinary service calls go through functions defined in ns-win.el */
8178   return NO;
8181 - (BOOL) writeSelectionToPasteboard: (NSPasteboard *)pb types: (NSArray *)types
8183   NSArray *typesDeclared;
8184   Lisp_Object val;
8186   NSTRACE ("[EmacsView writeSelectionToPasteboard:types:]");
8188   /* We only support NSStringPboardType */
8189   if ([types containsObject:NSStringPboardType] == NO) {
8190     return NO;
8191   }
8193   val = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8194   if (CONSP (val) && SYMBOLP (XCAR (val)))
8195     {
8196       val = XCDR (val);
8197       if (CONSP (val) && NILP (XCDR (val)))
8198         val = XCAR (val);
8199     }
8200   if (! STRINGP (val))
8201     return NO;
8203   typesDeclared = [NSArray arrayWithObject:NSStringPboardType];
8204   [pb declareTypes:typesDeclared owner:nil];
8205   ns_string_to_pasteboard (pb, val);
8206   return YES;
8210 /* setMini =YES means set from internal (gives a finder icon), NO means set nil
8211    (gives a miniaturized version of the window); currently we use the latter for
8212    frames whose active buffer doesn't correspond to any file
8213    (e.g., '*scratch*') */
8214 - (instancetype)setMiniwindowImage: (BOOL) setMini
8216   id image = [[self window] miniwindowImage];
8217   NSTRACE ("[EmacsView setMiniwindowImage:%d]", setMini);
8219   /* NOTE: under Cocoa miniwindowImage always returns nil, documentation
8220      about "AppleDockIconEnabled" notwithstanding, however the set message
8221      below has its effect nonetheless. */
8222   if (image != emacsframe->output_data.ns->miniimage)
8223     {
8224       if (image && [image isKindOfClass: [EmacsImage class]])
8225         [image release];
8226       [[self window] setMiniwindowImage:
8227                        setMini ? emacsframe->output_data.ns->miniimage : nil];
8228     }
8230   return self;
8234 - (void) setRows: (int) r andColumns: (int) c
8236   NSTRACE ("[EmacsView setRows:%d andColumns:%d]", r, c);
8237   rows = r;
8238   cols = c;
8241 - (int) fullscreenState
8243   return fs_state;
8246 @end  /* EmacsView */
8250 /* ==========================================================================
8252     EmacsWindow implementation
8254    ========================================================================== */
8256 @implementation EmacsWindow
8258 #ifdef NS_IMPL_COCOA
8259 - (id)accessibilityAttributeValue:(NSString *)attribute
8261   Lisp_Object str = Qnil;
8262   struct frame *f = SELECTED_FRAME ();
8263   struct buffer *curbuf = XBUFFER (XWINDOW (f->selected_window)->contents);
8265   NSTRACE ("[EmacsWindow accessibilityAttributeValue:]");
8267   if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
8268     return NSAccessibilityTextFieldRole;
8270   if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]
8271       && curbuf && ! NILP (BVAR (curbuf, mark_active)))
8272     {
8273       str = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8274     }
8275   else if (curbuf && [attribute isEqualToString:NSAccessibilityValueAttribute])
8276     {
8277       if (! NILP (BVAR (curbuf, mark_active)))
8278           str = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8280       if (NILP (str))
8281         {
8282           ptrdiff_t start_byte = BUF_BEGV_BYTE (curbuf);
8283           ptrdiff_t byte_range = BUF_ZV_BYTE (curbuf) - start_byte;
8284           ptrdiff_t range = BUF_ZV (curbuf) - BUF_BEGV (curbuf);
8286           if (! NILP (BVAR (curbuf, enable_multibyte_characters)))
8287             str = make_uninit_multibyte_string (range, byte_range);
8288           else
8289             str = make_uninit_string (range);
8290           /* To check: This returns emacs-utf-8, which is a superset of utf-8.
8291              Is this a problem?  */
8292           memcpy (SDATA (str), BYTE_POS_ADDR (start_byte), byte_range);
8293         }
8294     }
8297   if (! NILP (str))
8298     {
8299       if (CONSP (str) && SYMBOLP (XCAR (str)))
8300         {
8301           str = XCDR (str);
8302           if (CONSP (str) && NILP (XCDR (str)))
8303             str = XCAR (str);
8304         }
8305       if (STRINGP (str))
8306         {
8307           const char *utfStr = SSDATA (str);
8308           NSString *nsStr = [NSString stringWithUTF8String: utfStr];
8309           return nsStr;
8310         }
8311     }
8313   return [super accessibilityAttributeValue:attribute];
8315 #endif /* NS_IMPL_COCOA */
8317 /* Constrain size and placement of a frame.
8319    By returning the original "frameRect", the frame is not
8320    constrained. This can lead to unwanted situations where, for
8321    example, the menu bar covers the frame.
8323    The default implementation (accessed using "super") constrains the
8324    frame to the visible area of SCREEN, minus the menu bar (if
8325    present) and the Dock.  Note that default implementation also calls
8326    windowWillResize, with the frame it thinks should have.  (This can
8327    make the frame exit maximized mode.)
8329    Note that this should work in situations where multiple monitors
8330    are present.  Common configurations are side-by-side monitors and a
8331    monitor on top of another (e.g. when a laptop is placed under a
8332    large screen). */
8333 - (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
8335   NSTRACE ("[EmacsWindow constrainFrameRect:" NSTRACE_FMT_RECT " toScreen:]",
8336              NSTRACE_ARG_RECT (frameRect));
8338 #ifdef NS_IMPL_COCOA
8339 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
8340   // If separate spaces is on, it is like each screen is independent.  There is
8341   // no spanning of frames across screens.
8342   if (
8343 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
8344       [NSScreen respondsToSelector: @selector(screensHaveSeparateSpaces)] &&
8345 #endif
8346       [NSScreen screensHaveSeparateSpaces])
8347     {
8348       NSTRACE_MSG ("Screens have separate spaces");
8349       frameRect = [super constrainFrameRect:frameRect toScreen:screen];
8350       NSTRACE_RETURN_RECT (frameRect);
8351       return frameRect;
8352     }
8353   else
8354 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 */
8356     // Check that the proposed frameRect is visible in at least one
8357     // screen.  If it is not, ask the system to reposition it (only
8358     // for non-child windows).
8360     if (!FRAME_PARENT_FRAME (((EmacsView *)[self delegate])->emacsframe))
8361     {
8362       NSArray *screens = [NSScreen screens];
8363       NSUInteger nr_screens = [screens count];
8365       int i;
8366       BOOL frame_on_screen = NO;
8368       for (i = 0; i < nr_screens; ++i)
8369         {
8370           NSScreen *s = [screens objectAtIndex: i];
8371           NSRect scrRect = [s frame];
8373           if (NSIntersectsRect(frameRect, scrRect))
8374             {
8375               frame_on_screen = YES;
8376               break;
8377             }
8378         }
8380       if (!frame_on_screen)
8381         {
8382           NSTRACE_MSG ("Frame outside screens; constraining");
8383           frameRect = [super constrainFrameRect:frameRect toScreen:screen];
8384           NSTRACE_RETURN_RECT (frameRect);
8385           return frameRect;
8386         }
8387     }
8388 #endif
8390   return constrain_frame_rect(frameRect,
8391                               [(EmacsView *)[self delegate] isFullscreen]);
8395 - (void)performZoom:(id)sender
8397   NSTRACE ("[EmacsWindow performZoom:]");
8399   return [super performZoom:sender];
8402 - (void)zoom:(id)sender
8404   NSTRACE ("[EmacsWindow zoom:]");
8406   ns_update_auto_hide_menu_bar();
8408   // Below are three zoom implementations.  In the final commit, the
8409   // idea is that the last should be included.
8411 #if 0
8412   // Native zoom done using the standard zoom animation.  Size of the
8413   // resulting frame reduced to accommodate the Dock and, if present,
8414   // the menu-bar.
8415   [super zoom:sender];
8417 #elif 0
8418   // Native zoom done using the standard zoom animation, plus an
8419   // explicit resize to cover the full screen, except the menu-bar and
8420   // dock, if present.
8421   [super zoom:sender];
8423   // After the native zoom, resize the resulting frame to fill the
8424   // entire screen, except the menu-bar.
8425   //
8426   // This works for all practical purposes.  (The only minor oddity is
8427   // when transiting from full-height frame to a maximized, the
8428   // animation reduces the height of the frame slightly (to the 4
8429   // pixels needed to accommodate the Doc) before it snaps back into
8430   // full height.  The user would need a very trained eye to spot
8431   // this.)
8432   NSScreen * screen = [self screen];
8433   if (screen != nil)
8434     {
8435       int fs_state = [(EmacsView *)[self delegate] fullscreenState];
8437       NSTRACE_FSTYPE ("fullscreenState", fs_state);
8439       NSRect sr = [screen frame];
8440       struct EmacsMargins margins
8441         = ns_screen_margins_ignoring_hidden_dock(screen);
8443       NSRect wr = [self frame];
8444       NSTRACE_RECT ("Rect after zoom", wr);
8446       NSRect newWr = wr;
8448       if (fs_state == FULLSCREEN_MAXIMIZED
8449           || fs_state == FULLSCREEN_HEIGHT)
8450         {
8451           newWr.origin.y = sr.origin.y + margins.bottom;
8452           newWr.size.height = sr.size.height - margins.top - margins.bottom;
8453         }
8455       if (fs_state == FULLSCREEN_MAXIMIZED
8456           || fs_state == FULLSCREEN_WIDTH)
8457         {
8458           newWr.origin.x = sr.origin.x + margins.left;
8459           newWr.size.width = sr.size.width - margins.right - margins.left;
8460         }
8462       if (newWr.size.width     != wr.size.width
8463           || newWr.size.height != wr.size.height
8464           || newWr.origin.x    != wr.origin.x
8465           || newWr.origin.y    != wr.origin.y)
8466         {
8467           NSTRACE_MSG ("New frame different");
8468           [self setFrame: newWr display: NO];
8469         }
8470     }
8471 #else
8472   // Non-native zoom which is done instantaneously.  The resulting
8473   // frame covers the entire screen, except the menu-bar and dock, if
8474   // present.
8475   NSScreen * screen = [self screen];
8476   if (screen != nil)
8477     {
8478       NSRect sr = [screen frame];
8479       struct EmacsMargins margins
8480         = ns_screen_margins_ignoring_hidden_dock(screen);
8482       sr.size.height -= (margins.top + margins.bottom);
8483       sr.size.width  -= (margins.left + margins.right);
8484       sr.origin.x += margins.left;
8485       sr.origin.y += margins.bottom;
8487       sr = [[self delegate] windowWillUseStandardFrame:self
8488                                           defaultFrame:sr];
8489       [self setFrame: sr display: NO];
8490     }
8491 #endif
8494 - (void)setFrame:(NSRect)windowFrame
8495          display:(BOOL)displayViews
8497   NSTRACE ("[EmacsWindow setFrame:" NSTRACE_FMT_RECT " display:%d]",
8498            NSTRACE_ARG_RECT (windowFrame), displayViews);
8500   [super setFrame:windowFrame display:displayViews];
8503 - (void)setFrame:(NSRect)windowFrame
8504          display:(BOOL)displayViews
8505          animate:(BOOL)performAnimation
8507   NSTRACE ("[EmacsWindow setFrame:" NSTRACE_FMT_RECT
8508            " display:%d performAnimation:%d]",
8509            NSTRACE_ARG_RECT (windowFrame), displayViews, performAnimation);
8511   [super setFrame:windowFrame display:displayViews animate:performAnimation];
8514 - (void)setFrameTopLeftPoint:(NSPoint)point
8516   NSTRACE ("[EmacsWindow setFrameTopLeftPoint:" NSTRACE_FMT_POINT "]",
8517            NSTRACE_ARG_POINT (point));
8519   [super setFrameTopLeftPoint:point];
8522 - (BOOL)canBecomeKeyWindow
8524   return !FRAME_NO_ACCEPT_FOCUS (((EmacsView *)[self delegate])->emacsframe);
8526 @end /* EmacsWindow */
8529 @implementation EmacsFSWindow
8531 - (BOOL)canBecomeKeyWindow
8533   return YES;
8536 - (BOOL)canBecomeMainWindow
8538   return YES;
8541 @end
8543 /* ==========================================================================
8545     EmacsScroller implementation
8547    ========================================================================== */
8550 @implementation EmacsScroller
8552 /* for repeat button push */
8553 #define SCROLL_BAR_FIRST_DELAY 0.5
8554 #define SCROLL_BAR_CONTINUOUS_DELAY (1.0 / 15)
8556 + (CGFloat) scrollerWidth
8558   /* TODO: if we want to allow variable widths, this is the place to do it,
8559            however neither GNUstep nor Cocoa support it very well */
8560   CGFloat r;
8561 #if defined (NS_IMPL_COCOA) \
8562   && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
8563 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
8564   if ([NSScroller respondsToSelector:
8565                     @selector(scrollerWidthForControlSize:scrollerStyle:)])
8566 #endif
8567     r = [NSScroller scrollerWidthForControlSize: NSControlSizeRegular
8568                                   scrollerStyle: NSScrollerStyleLegacy];
8569 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
8570   else
8571 #endif
8572 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
8573 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 \
8574   || defined (NS_IMPL_GNUSTEP)
8575     r = [NSScroller scrollerWidth];
8576 #endif
8577   return r;
8580 - (instancetype)initFrame: (NSRect )r window: (Lisp_Object)nwin
8582   NSTRACE ("[EmacsScroller initFrame: window:]");
8584   if (r.size.width > r.size.height)
8585       horizontal = YES;
8586   else
8587       horizontal = NO;
8589   [super initWithFrame: r/*NSMakeRect (0, 0, 0, 0)*/];
8590   [self setContinuous: YES];
8591   [self setEnabled: YES];
8593   /* Ensure auto resizing of scrollbars occurs within the emacs frame's view
8594      locked against the top and bottom edges, and right edge on macOS, where
8595      scrollers are on right. */
8596 #ifdef NS_IMPL_GNUSTEP
8597   [self setAutoresizingMask: NSViewMaxXMargin | NSViewHeightSizable];
8598 #else
8599   [self setAutoresizingMask: NSViewMinXMargin | NSViewHeightSizable];
8600 #endif
8602   window = XWINDOW (nwin);
8603   condemned = NO;
8604   if (horizontal)
8605     pixel_length = NSWidth (r);
8606   else
8607     pixel_length = NSHeight (r);
8608   if (pixel_length == 0) pixel_length = 1;
8609   min_portion = 20 / pixel_length;
8611   frame = XFRAME (window->frame);
8612   if (FRAME_LIVE_P (frame))
8613     {
8614       int i;
8615       EmacsView *view = FRAME_NS_VIEW (frame);
8616       NSView *sview = [[view window] contentView];
8617       NSArray *subs = [sview subviews];
8619       /* disable optimization stopping redraw of other scrollbars */
8620       view->scrollbarsNeedingUpdate = 0;
8621       for (i =[subs count]-1; i >= 0; i--)
8622         if ([[subs objectAtIndex: i] isKindOfClass: [EmacsScroller class]])
8623           view->scrollbarsNeedingUpdate++;
8624       [sview addSubview: self];
8625     }
8627 /*  [self setFrame: r]; */
8629   return self;
8633 - (void)setFrame: (NSRect)newRect
8635   NSTRACE ("[EmacsScroller setFrame:]");
8637 /*  block_input (); */
8638   if (horizontal)
8639     pixel_length = NSWidth (newRect);
8640   else
8641     pixel_length = NSHeight (newRect);
8642   if (pixel_length == 0) pixel_length = 1;
8643   min_portion = 20 / pixel_length;
8644   [super setFrame: newRect];
8645 /*  unblock_input (); */
8649 - (void)dealloc
8651   NSTRACE ("[EmacsScroller dealloc]");
8652   if (window)
8653     {
8654       if (horizontal)
8655         wset_horizontal_scroll_bar (window, Qnil);
8656       else
8657         wset_vertical_scroll_bar (window, Qnil);
8658     }
8659   window = 0;
8660   [super dealloc];
8664 - (instancetype)condemn
8666   NSTRACE ("[EmacsScroller condemn]");
8667   condemned =YES;
8668   return self;
8672 - (instancetype)reprieve
8674   NSTRACE ("[EmacsScroller reprieve]");
8675   condemned =NO;
8676   return self;
8680 -(bool)judge
8682   NSTRACE ("[EmacsScroller judge]");
8683   bool ret = condemned;
8684   if (condemned)
8685     {
8686       EmacsView *view;
8687       block_input ();
8688       /* ensure other scrollbar updates after deletion */
8689       view = (EmacsView *)FRAME_NS_VIEW (frame);
8690       if (view != nil)
8691         view->scrollbarsNeedingUpdate++;
8692       if (window)
8693         {
8694           if (horizontal)
8695             wset_horizontal_scroll_bar (window, Qnil);
8696           else
8697             wset_vertical_scroll_bar (window, Qnil);
8698         }
8699       window = 0;
8700       [self removeFromSuperview];
8701       [self release];
8702       unblock_input ();
8703     }
8704   return ret;
8708 - (void)resetCursorRects
8710   NSRect visible = [self visibleRect];
8711   NSTRACE ("[EmacsScroller resetCursorRects]");
8713   if (!NSIsEmptyRect (visible))
8714     [self addCursorRect: visible cursor: [NSCursor arrowCursor]];
8715   [[NSCursor arrowCursor] setOnMouseEntered: YES];
8719 - (int) checkSamePosition: (int) position portion: (int) portion
8720                     whole: (int) whole
8722   return em_position ==position && em_portion ==portion && em_whole ==whole
8723     && portion != whole; /* needed for resize empty buf */
8727 - (instancetype)setPosition: (int)position portion: (int)portion whole: (int)whole
8729   NSTRACE ("[EmacsScroller setPosition:portion:whole:]");
8731   em_position = position;
8732   em_portion = portion;
8733   em_whole = whole;
8735   if (portion >= whole)
8736     {
8737 #ifdef NS_IMPL_COCOA
8738       [self setKnobProportion: 1.0];
8739       [self setDoubleValue: 1.0];
8740 #else
8741       [self setFloatValue: 0.0 knobProportion: 1.0];
8742 #endif
8743     }
8744   else
8745     {
8746       float pos;
8747       CGFloat por;
8748       portion = max ((float)whole*min_portion/pixel_length, portion);
8749       pos = (float)position / (whole - portion);
8750       por = (CGFloat)portion/whole;
8751 #ifdef NS_IMPL_COCOA
8752       [self setKnobProportion: por];
8753       [self setDoubleValue: pos];
8754 #else
8755       [self setFloatValue: pos knobProportion: por];
8756 #endif
8757     }
8759   return self;
8762 /* set up emacs_event */
8763 - (void) sendScrollEventAtLoc: (float)loc fromEvent: (NSEvent *)e
8765   Lisp_Object win;
8767   NSTRACE ("[EmacsScroller sendScrollEventAtLoc:fromEvent:]");
8769   if (!emacs_event)
8770     return;
8772   emacs_event->part = last_hit_part;
8773   emacs_event->code = 0;
8774   emacs_event->modifiers = EV_MODIFIERS (e) | down_modifier;
8775   XSETWINDOW (win, window);
8776   emacs_event->frame_or_window = win;
8777   emacs_event->timestamp = EV_TIMESTAMP (e);
8778   emacs_event->arg = Qnil;
8780   if (horizontal)
8781     {
8782       emacs_event->kind = HORIZONTAL_SCROLL_BAR_CLICK_EVENT;
8783       XSETINT (emacs_event->x, em_whole * loc / pixel_length);
8784       XSETINT (emacs_event->y, em_whole);
8785     }
8786   else
8787     {
8788       emacs_event->kind = SCROLL_BAR_CLICK_EVENT;
8789       XSETINT (emacs_event->x, loc);
8790       XSETINT (emacs_event->y, pixel_length-20);
8791     }
8793   if (q_event_ptr)
8794     {
8795       n_emacs_events_pending++;
8796       kbd_buffer_store_event_hold (emacs_event, q_event_ptr);
8797     }
8798   else
8799     hold_event (emacs_event);
8800   EVENT_INIT (*emacs_event);
8801   ns_send_appdefined (-1);
8805 /* called manually thru timer to implement repeated button action w/hold-down */
8806 - (instancetype)repeatScroll: (NSTimer *)scrollEntry
8808   NSEvent *e = [[self window] currentEvent];
8809   NSPoint p =  [[self window] mouseLocationOutsideOfEventStream];
8810   BOOL inKnob = [self testPart: p] == NSScrollerKnob;
8812   NSTRACE ("[EmacsScroller repeatScroll:]");
8814   /* clear timer if need be */
8815   if (inKnob || [scroll_repeat_entry timeInterval] == SCROLL_BAR_FIRST_DELAY)
8816     {
8817         [scroll_repeat_entry invalidate];
8818         [scroll_repeat_entry release];
8819         scroll_repeat_entry = nil;
8821         if (inKnob)
8822           return self;
8824         scroll_repeat_entry
8825           = [[NSTimer scheduledTimerWithTimeInterval:
8826                         SCROLL_BAR_CONTINUOUS_DELAY
8827                                             target: self
8828                                           selector: @selector (repeatScroll:)
8829                                           userInfo: 0
8830                                            repeats: YES]
8831               retain];
8832     }
8834   [self sendScrollEventAtLoc: 0 fromEvent: e];
8835   return self;
8839 /* Asynchronous mouse tracking for scroller.  This allows us to dispatch
8840    mouseDragged events without going into a modal loop. */
8841 - (void)mouseDown: (NSEvent *)e
8843   NSRect sr, kr;
8844   /* hitPart is only updated AFTER event is passed on */
8845   NSScrollerPart part = [self testPart: [e locationInWindow]];
8846   CGFloat loc, kloc, pos UNINIT;
8847   int edge = 0;
8849   NSTRACE ("[EmacsScroller mouseDown:]");
8851   switch (part)
8852     {
8853     case NSScrollerDecrementPage:
8854       last_hit_part = horizontal ? scroll_bar_before_handle : scroll_bar_above_handle; break;
8855     case NSScrollerIncrementPage:
8856       last_hit_part = horizontal ? scroll_bar_after_handle : scroll_bar_below_handle; break;
8857     case NSScrollerDecrementLine:
8858       last_hit_part = horizontal ? scroll_bar_left_arrow : scroll_bar_up_arrow; break;
8859     case NSScrollerIncrementLine:
8860       last_hit_part = horizontal ? scroll_bar_right_arrow : scroll_bar_down_arrow; break;
8861     case NSScrollerKnob:
8862       last_hit_part = horizontal ? scroll_bar_horizontal_handle : scroll_bar_handle; break;
8863     case NSScrollerKnobSlot:  /* GNUstep-only */
8864       last_hit_part = scroll_bar_move_ratio; break;
8865     default:  /* NSScrollerNoPart? */
8866       fprintf (stderr, "EmacsScroller-mouseDown: unexpected part %ld\n",
8867                (long) part);
8868       return;
8869     }
8871   if (part == NSScrollerKnob || part == NSScrollerKnobSlot)
8872     {
8873       /* handle, or on GNUstep possibly slot */
8874       NSEvent *fake_event;
8875       int length;
8877       /* compute float loc in slot and mouse offset on knob */
8878       sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot]
8879                       toView: nil];
8880       if (horizontal)
8881         {
8882           length = NSWidth (sr);
8883           loc = ([e locationInWindow].x - NSMinX (sr));
8884         }
8885       else
8886         {
8887           length = NSHeight (sr);
8888           loc = length - ([e locationInWindow].y - NSMinY (sr));
8889         }
8891       if (loc <= 0.0)
8892         {
8893           loc = 0.0;
8894           edge = -1;
8895         }
8896       else if (loc >= length)
8897         {
8898           loc = length;
8899           edge = 1;
8900         }
8902       if (edge)
8903         kloc = 0.5 * edge;
8904       else
8905         {
8906           kr = [self convertRect: [self rectForPart: NSScrollerKnob]
8907                           toView: nil];
8908           if (horizontal)
8909             kloc = ([e locationInWindow].x - NSMinX (kr));
8910           else
8911             kloc = NSHeight (kr) - ([e locationInWindow].y - NSMinY (kr));
8912         }
8913       last_mouse_offset = kloc;
8915       /* if knob, tell emacs a location offset by knob pos
8916          (to indicate top of handle) */
8917       if (part == NSScrollerKnob)
8918         pos = (loc - last_mouse_offset);
8919       else
8920         /* else this is a slot click on GNUstep: go straight there */
8921         pos = loc;
8923       /* If there are buttons in the scroller area, we need to
8924          recalculate pos as emacs expects the scroller slot to take up
8925          the entire available length.  */
8926       if (length != pixel_length)
8927         pos = pos * pixel_length / length;
8929       /* send a fake mouse-up to super to preempt modal -trackKnob: mode */
8930       fake_event = [NSEvent mouseEventWithType: NSEventTypeLeftMouseUp
8931                                       location: [e locationInWindow]
8932                                  modifierFlags: [e modifierFlags]
8933                                      timestamp: [e timestamp]
8934                                   windowNumber: [e windowNumber]
8935                                        context: nil
8936                                    eventNumber: [e eventNumber]
8937                                     clickCount: [e clickCount]
8938                                       pressure: [e pressure]];
8939       [super mouseUp: fake_event];
8940     }
8941   else
8942     {
8943       pos = 0;      /* ignored */
8945       /* set a timer to repeat, as we can't let superclass do this modally */
8946       scroll_repeat_entry
8947         = [[NSTimer scheduledTimerWithTimeInterval: SCROLL_BAR_FIRST_DELAY
8948                                             target: self
8949                                           selector: @selector (repeatScroll:)
8950                                           userInfo: 0
8951                                            repeats: YES]
8952             retain];
8953     }
8955   if (part != NSScrollerKnob)
8956     [self sendScrollEventAtLoc: pos fromEvent: e];
8960 /* Called as we manually track scroller drags, rather than superclass. */
8961 - (void)mouseDragged: (NSEvent *)e
8963     NSRect sr;
8964     double loc, pos;
8965     int length;
8967     NSTRACE ("[EmacsScroller mouseDragged:]");
8969       sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot]
8970                       toView: nil];
8972       if (horizontal)
8973         {
8974           length = NSWidth (sr);
8975           loc = ([e locationInWindow].x - NSMinX (sr));
8976         }
8977       else
8978         {
8979           length = NSHeight (sr);
8980           loc = length - ([e locationInWindow].y - NSMinY (sr));
8981         }
8983       if (loc <= 0.0)
8984         {
8985           loc = 0.0;
8986         }
8987       else if (loc >= length + last_mouse_offset)
8988         {
8989           loc = length + last_mouse_offset;
8990         }
8992       pos = (loc - last_mouse_offset);
8994       /* If there are buttons in the scroller area, we need to
8995          recalculate pos as emacs expects the scroller slot to take up
8996          the entire available length.  */
8997       if (length != pixel_length)
8998         pos = pos * pixel_length / length;
9000       [self sendScrollEventAtLoc: pos fromEvent: e];
9004 - (void)mouseUp: (NSEvent *)e
9006   NSTRACE ("[EmacsScroller mouseUp:]");
9008   if (scroll_repeat_entry)
9009     {
9010       [scroll_repeat_entry invalidate];
9011       [scroll_repeat_entry release];
9012       scroll_repeat_entry = nil;
9013     }
9014   last_hit_part = scroll_bar_above_handle;
9018 /* treat scrollwheel events in the bar as though they were in the main window */
9019 - (void) scrollWheel: (NSEvent *)theEvent
9021   NSTRACE ("[EmacsScroller scrollWheel:]");
9023   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (frame);
9024   [view mouseDown: theEvent];
9027 @end  /* EmacsScroller */
9030 #ifdef NS_IMPL_GNUSTEP
9031 /* Dummy class to get rid of startup warnings.  */
9032 @implementation EmacsDocument
9034 @end
9035 #endif
9038 /* ==========================================================================
9040    Font-related functions; these used to be in nsfaces.m
9042    ========================================================================== */
9045 Lisp_Object
9046 x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
9048   struct font *font = XFONT_OBJECT (font_object);
9049   EmacsView *view = FRAME_NS_VIEW (f);
9050   int font_ascent, font_descent;
9052   if (fontset < 0)
9053     fontset = fontset_from_font (font_object);
9054   FRAME_FONTSET (f) = fontset;
9056   if (FRAME_FONT (f) == font)
9057     /* This font is already set in frame F.  There's nothing more to
9058        do.  */
9059     return font_object;
9061   FRAME_FONT (f) = font;
9063   FRAME_BASELINE_OFFSET (f) = font->baseline_offset;
9064   FRAME_COLUMN_WIDTH (f) = font->average_width;
9065   get_font_ascent_descent (font, &font_ascent, &font_descent);
9066   FRAME_LINE_HEIGHT (f) = font_ascent + font_descent;
9068   /* Compute the scroll bar width in character columns.  */
9069   if (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) > 0)
9070     {
9071       int wid = FRAME_COLUMN_WIDTH (f);
9072       FRAME_CONFIG_SCROLL_BAR_COLS (f)
9073         = (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) + wid - 1) / wid;
9074     }
9075   else
9076     {
9077       int wid = FRAME_COLUMN_WIDTH (f);
9078       FRAME_CONFIG_SCROLL_BAR_COLS (f) = (14 + wid - 1) / wid;
9079     }
9081   /* Compute the scroll bar height in character lines.  */
9082   if (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) > 0)
9083     {
9084       int height = FRAME_LINE_HEIGHT (f);
9085       FRAME_CONFIG_SCROLL_BAR_LINES (f)
9086         = (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) + height - 1) / height;
9087     }
9088   else
9089     {
9090       int height = FRAME_LINE_HEIGHT (f);
9091       FRAME_CONFIG_SCROLL_BAR_LINES (f) = (14 + height - 1) / height;
9092     }
9094   /* Now make the frame display the given font.  */
9095   if (FRAME_NS_WINDOW (f) != 0 && ! [view isFullscreen])
9096     adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
9097                        FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3,
9098                        false, Qfont);
9100   return font_object;
9104 /* XLFD: -foundry-family-weight-slant-swidth-adstyle-pxlsz-ptSz-resx-resy-spc-avgWidth-rgstry-encoding */
9105 /* Note: ns_font_to_xlfd and ns_fontname_to_xlfd no longer needed, removed
9106          in 1.43. */
9108 const char *
9109 ns_xlfd_to_fontname (const char *xlfd)
9110 /* --------------------------------------------------------------------------
9111     Convert an X font name (XLFD) to an NS font name.
9112     Only family is used.
9113     The string returned is temporarily allocated.
9114    -------------------------------------------------------------------------- */
9116   char *name = xmalloc (180);
9117   int i, len;
9118   const char *ret;
9120   if (!strncmp (xlfd, "--", 2))
9121     sscanf (xlfd, "--%*[^-]-%[^-]179-", name);
9122   else
9123     sscanf (xlfd, "-%*[^-]-%[^-]179-", name);
9125   /* stopgap for malformed XLFD input */
9126   if (strlen (name) == 0)
9127     strcpy (name, "Monaco");
9129   /* undo hack in ns_fontname_to_xlfd, converting '$' to '-', '_' to ' '
9130      also uppercase after '-' or ' ' */
9131   name[0] = c_toupper (name[0]);
9132   for (len =strlen (name), i =0; i<len; i++)
9133     {
9134       if (name[i] == '$')
9135         {
9136           name[i] = '-';
9137           if (i+1<len)
9138             name[i+1] = c_toupper (name[i+1]);
9139         }
9140       else if (name[i] == '_')
9141         {
9142           name[i] = ' ';
9143           if (i+1<len)
9144             name[i+1] = c_toupper (name[i+1]);
9145         }
9146     }
9147 /*fprintf (stderr, "converted '%s' to '%s'\n",xlfd,name);  */
9148   ret = [[NSString stringWithUTF8String: name] UTF8String];
9149   xfree (name);
9150   return ret;
9154 void
9155 syms_of_nsterm (void)
9157   NSTRACE ("syms_of_nsterm");
9159   ns_antialias_threshold = 10.0;
9161   /* from 23+ we need to tell emacs what modifiers there are.. */
9162   DEFSYM (Qmodifier_value, "modifier-value");
9163   DEFSYM (Qalt, "alt");
9164   DEFSYM (Qhyper, "hyper");
9165   DEFSYM (Qmeta, "meta");
9166   DEFSYM (Qsuper, "super");
9167   DEFSYM (Qcontrol, "control");
9168   DEFSYM (QUTF8_STRING, "UTF8_STRING");
9170   DEFSYM (Qfile, "file");
9171   DEFSYM (Qurl, "url");
9173   Fput (Qalt, Qmodifier_value, make_number (alt_modifier));
9174   Fput (Qhyper, Qmodifier_value, make_number (hyper_modifier));
9175   Fput (Qmeta, Qmodifier_value, make_number (meta_modifier));
9176   Fput (Qsuper, Qmodifier_value, make_number (super_modifier));
9177   Fput (Qcontrol, Qmodifier_value, make_number (ctrl_modifier));
9179   DEFVAR_LISP ("ns-input-file", ns_input_file,
9180               "The file specified in the last NS event.");
9181   ns_input_file =Qnil;
9183   DEFVAR_LISP ("ns-working-text", ns_working_text,
9184               "String for visualizing working composition sequence.");
9185   ns_working_text =Qnil;
9187   DEFVAR_LISP ("ns-input-font", ns_input_font,
9188               "The font specified in the last NS event.");
9189   ns_input_font =Qnil;
9191   DEFVAR_LISP ("ns-input-fontsize", ns_input_fontsize,
9192               "The fontsize specified in the last NS event.");
9193   ns_input_fontsize =Qnil;
9195   DEFVAR_LISP ("ns-input-line", ns_input_line,
9196                "The line specified in the last NS event.");
9197   ns_input_line =Qnil;
9199   DEFVAR_LISP ("ns-input-spi-name", ns_input_spi_name,
9200                "The service name specified in the last NS event.");
9201   ns_input_spi_name =Qnil;
9203   DEFVAR_LISP ("ns-input-spi-arg", ns_input_spi_arg,
9204                "The service argument specified in the last NS event.");
9205   ns_input_spi_arg =Qnil;
9207   DEFVAR_LISP ("ns-alternate-modifier", ns_alternate_modifier,
9208                "This variable describes the behavior of the alternate or option 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 none means that the alternate / option key is not interpreted by Emacs\n\
9212 at all, allowing it to be used at a lower level for accented character entry.");
9213   ns_alternate_modifier = Qmeta;
9215   DEFVAR_LISP ("ns-right-alternate-modifier", ns_right_alternate_modifier,
9216                "This variable describes the behavior of the right alternate or option key.\n\
9217 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9218 that key.\n\
9219 Set to left means be the same key as `ns-alternate-modifier'.\n\
9220 Set to none means that the alternate / option key is not interpreted by Emacs\n\
9221 at all, allowing it to be used at a lower level for accented character entry.");
9222   ns_right_alternate_modifier = Qleft;
9224   DEFVAR_LISP ("ns-command-modifier", ns_command_modifier,
9225                "This variable describes the behavior of the command key.\n\
9226 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9227 that key.");
9228   ns_command_modifier = Qsuper;
9230   DEFVAR_LISP ("ns-right-command-modifier", ns_right_command_modifier,
9231                "This variable describes the behavior of the right command key.\n\
9232 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9233 that key.\n\
9234 Set to left means be the same key as `ns-command-modifier'.\n\
9235 Set to none means that the command / option key is not interpreted by Emacs\n\
9236 at all, allowing it to be used at a lower level for accented character entry.");
9237   ns_right_command_modifier = Qleft;
9239   DEFVAR_LISP ("ns-control-modifier", ns_control_modifier,
9240                "This variable describes the behavior of the control key.\n\
9241 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9242 that key.");
9243   ns_control_modifier = Qcontrol;
9245   DEFVAR_LISP ("ns-right-control-modifier", ns_right_control_modifier,
9246                "This variable describes the behavior of the right control key.\n\
9247 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9248 that key.\n\
9249 Set to left means be the same key as `ns-control-modifier'.\n\
9250 Set to none means that the control / option key is not interpreted by Emacs\n\
9251 at all, allowing it to be used at a lower level for accented character entry.");
9252   ns_right_control_modifier = Qleft;
9254   DEFVAR_LISP ("ns-function-modifier", ns_function_modifier,
9255                "This variable describes the behavior of the function key (on laptops).\n\
9256 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9257 that key.\n\
9258 Set to none means that the function key is not interpreted by Emacs at all,\n\
9259 allowing it to be used at a lower level for accented character entry.");
9260   ns_function_modifier = Qnone;
9262   DEFVAR_LISP ("ns-antialias-text", ns_antialias_text,
9263                "Non-nil (the default) means to render text antialiased.");
9264   ns_antialias_text = Qt;
9266   DEFVAR_LISP ("ns-use-thin-smoothing", ns_use_thin_smoothing,
9267                "Non-nil turns on a font smoothing method that produces thinner strokes.");
9268   ns_use_thin_smoothing = Qnil;
9270   DEFVAR_LISP ("ns-confirm-quit", ns_confirm_quit,
9271                "Whether to confirm application quit using dialog.");
9272   ns_confirm_quit = Qnil;
9274   DEFVAR_LISP ("ns-auto-hide-menu-bar", ns_auto_hide_menu_bar,
9275                doc: /* Non-nil means that the menu bar is hidden, but appears when the mouse is near.
9276 Only works on Mac OS X 10.6 or later.  */);
9277   ns_auto_hide_menu_bar = Qnil;
9279   DEFVAR_BOOL ("ns-use-native-fullscreen", ns_use_native_fullscreen,
9280      doc: /*Non-nil means to use native fullscreen on Mac OS X 10.7 and later.
9281 Nil means use fullscreen the old (< 10.7) way.  The old way works better with
9282 multiple monitors, but lacks tool bar.  This variable is ignored on
9283 Mac OS X < 10.7.  Default is t.  */);
9284   ns_use_native_fullscreen = YES;
9285   ns_last_use_native_fullscreen = ns_use_native_fullscreen;
9287   DEFVAR_BOOL ("ns-use-fullscreen-animation", ns_use_fullscreen_animation,
9288      doc: /*Non-nil means use animation on non-native fullscreen.
9289 For native fullscreen, this does nothing.
9290 Default is nil.  */);
9291   ns_use_fullscreen_animation = NO;
9293   DEFVAR_BOOL ("ns-use-srgb-colorspace", ns_use_srgb_colorspace,
9294      doc: /*Non-nil means to use sRGB colorspace on Mac OS X 10.7 and later.
9295 Note that this does not apply to images.
9296 This variable is ignored on Mac OS X < 10.7 and GNUstep.  */);
9297   ns_use_srgb_colorspace = YES;
9299   DEFVAR_BOOL ("ns-use-mwheel-acceleration",
9300                ns_use_mwheel_acceleration,
9301      doc: /*Non-nil means use macOS's standard mouse wheel acceleration.
9302 This variable is ignored on macOS < 10.7 and GNUstep.  Default is t.  */);
9303   ns_use_mwheel_acceleration = YES;
9305   DEFVAR_LISP ("ns-mwheel-line-height", ns_mwheel_line_height,
9306                doc: /*The number of pixels touchpad scrolling considers one line.
9307 Nil or a non-number means use the default frame line height.
9308 This variable is ignored on macOS < 10.7 and GNUstep.  Default is nil.  */);
9309   ns_mwheel_line_height = Qnil;
9311   DEFVAR_BOOL ("ns-use-mwheel-momentum", ns_use_mwheel_momentum,
9312                doc: /*Non-nil means mouse wheel scrolling uses momentum.
9313 This variable is ignored on macOS < 10.7 and GNUstep.  Default is t.  */);
9314   ns_use_mwheel_momentum = YES;
9316   /* TODO: move to common code */
9317   DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars,
9318                doc: /* Which toolkit scroll bars Emacs uses, if any.
9319 A value of nil means Emacs doesn't use toolkit scroll bars.
9320 With the X Window system, the value is a symbol describing the
9321 X toolkit.  Possible values are: gtk, motif, xaw, or xaw3d.
9322 With MS Windows or Nextstep, the value is t.  */);
9323   Vx_toolkit_scroll_bars = Qt;
9325   DEFVAR_BOOL ("x-use-underline-position-properties",
9326                x_use_underline_position_properties,
9327      doc: /*Non-nil means make use of UNDERLINE_POSITION font properties.
9328 A value of nil means ignore them.  If you encounter fonts with bogus
9329 UNDERLINE_POSITION font properties, for example 7x13 on XFree prior
9330 to 4.1, set this to nil. */);
9331   x_use_underline_position_properties = 0;
9333   DEFVAR_BOOL ("x-underline-at-descent-line",
9334                x_underline_at_descent_line,
9335      doc: /* Non-nil means to draw the underline at the same place as the descent line.
9336 A value of nil means to draw the underline according to the value of the
9337 variable `x-use-underline-position-properties', which is usually at the
9338 baseline level.  The default value is nil.  */);
9339   x_underline_at_descent_line = 0;
9341   /* Tell Emacs about this window system.  */
9342   Fprovide (Qns, Qnil);
9344   DEFSYM (Qcocoa, "cocoa");
9345   DEFSYM (Qgnustep, "gnustep");
9347 #ifdef NS_IMPL_COCOA
9348   Fprovide (Qcocoa, Qnil);
9349   syms_of_macfont ();
9350 #else
9351   Fprovide (Qgnustep, Qnil);
9352   syms_of_nsfont ();
9353 #endif