Improve responsiveness while in 'replace-buffer-contents'
[emacs.git] / src / nsterm.m
blob5ed71c9f8f1687f0b62988b0eec681490a6a006a
1 /* NeXT/Open/GNUstep / macOS communication module.      -*- coding: utf-8 -*-
3 Copyright (C) 1989, 1993-1994, 2005-2006, 2008-2018 Free Software
4 Foundation, Inc.
6 This file is part of GNU Emacs.
8 GNU Emacs is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or (at
11 your option) any later version.
13 GNU Emacs is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
22 Originally by Carl Edman
23 Updated by Christian Limpach (chris@nice.ch)
24 OpenStep/Rhapsody port by Scott Bender (sbender@harmony-ds.com)
25 macOS/Aqua port by Christophe de Dinechin (descubes@earthlink.net)
26 GNUstep port and post-20 update by Adrian Robert (arobert@cogsci.ucsd.edu)
29 /* This should be the first include, as it may set up #defines affecting
30    interpretation of even the system includes. */
31 #include <config.h>
33 #include <fcntl.h>
34 #include <math.h>
35 #include <pthread.h>
36 #include <sys/types.h>
37 #include <time.h>
38 #include <signal.h>
39 #include <unistd.h>
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);
1695   /* Ensure that sendEvent does not attempt to dereference a freed
1696      frame. (bug#30800) */
1697   if (represented_frame == f)
1698     represented_frame = NULL;
1700   if (f->output_data.ns->miniimage != nil)
1701     [f->output_data.ns->miniimage release];
1703   [[view window] close];
1704   [view release];
1706   xfree (f->output_data.ns);
1708   unblock_input ();
1711 void
1712 x_destroy_window (struct frame *f)
1713 /* --------------------------------------------------------------------------
1714      External: Delete the window
1715    -------------------------------------------------------------------------- */
1717   NSTRACE ("x_destroy_window");
1719   /* If this frame has a parent window, detach it as not doing so can
1720      cause a crash in GNUStep. */
1721   if (FRAME_PARENT_FRAME (f) != NULL)
1722     {
1723       NSWindow *child = [FRAME_NS_VIEW (f) window];
1724       NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
1726       [parent removeChildWindow: child];
1727     }
1729   check_window_system (f);
1730   x_free_frame_resources (f);
1731   ns_window_num--;
1735 void
1736 x_set_offset (struct frame *f, int xoff, int yoff, int change_grav)
1737 /* --------------------------------------------------------------------------
1738      External: Position the window
1739    -------------------------------------------------------------------------- */
1741   NSView *view = FRAME_NS_VIEW (f);
1742   NSArray *screens = [NSScreen screens];
1743   NSScreen *screen = [[view window] screen];
1745   NSTRACE ("x_set_offset");
1747   block_input ();
1749   f->left_pos = xoff;
1750   f->top_pos = yoff;
1752   if (view != nil)
1753     {
1754       if (FRAME_PARENT_FRAME (f) == NULL && screen)
1755         {
1756           f->left_pos = f->size_hint_flags & XNegative
1757             ? [screen visibleFrame].size.width + f->left_pos - FRAME_PIXEL_WIDTH (f)
1758             : f->left_pos;
1759           /* We use visibleFrame here to take menu bar into account.
1760              Ideally we should also adjust left/top with visibleFrame.origin.  */
1762           f->top_pos = f->size_hint_flags & YNegative
1763             ? ([screen visibleFrame].size.height + f->top_pos
1764                - FRAME_PIXEL_HEIGHT (f) - FRAME_NS_TITLEBAR_HEIGHT (f)
1765                - FRAME_TOOLBAR_HEIGHT (f))
1766             : f->top_pos;
1767 #ifdef NS_IMPL_GNUSTEP
1768           if (f->left_pos < 100)
1769             f->left_pos = 100;  /* don't overlap menu */
1770 #endif
1771         }
1772       else if (FRAME_PARENT_FRAME (f) != NULL)
1773         {
1774           struct frame *parent = FRAME_PARENT_FRAME (f);
1776           /* On X negative values for child frames always result in
1777              positioning relative to the bottom right corner of the
1778              parent frame.  */
1779           if (f->left_pos < 0)
1780             f->left_pos = FRAME_PIXEL_WIDTH (parent) - FRAME_PIXEL_WIDTH (f) + f->left_pos;
1782           if (f->top_pos < 0)
1783             f->top_pos = FRAME_PIXEL_HEIGHT (parent) + FRAME_TOOLBAR_HEIGHT (parent)
1784               - FRAME_PIXEL_HEIGHT (f) + f->top_pos;
1785         }
1787       /* Constrain the setFrameTopLeftPoint so we don't move behind the
1788          menu bar.  */
1789       NSPoint pt = NSMakePoint (SCREENMAXBOUND (f->left_pos
1790                                                 + NS_PARENT_WINDOW_LEFT_POS (f)),
1791                                 SCREENMAXBOUND (NS_PARENT_WINDOW_TOP_POS (f)
1792                                                 - f->top_pos));
1793       NSTRACE_POINT ("setFrameTopLeftPoint", pt);
1794       [[view window] setFrameTopLeftPoint: pt];
1795       f->size_hint_flags &= ~(XNegative|YNegative);
1796     }
1798   unblock_input ();
1802 void
1803 x_set_window_size (struct frame *f,
1804                    bool change_gravity,
1805                    int width,
1806                    int height,
1807                    bool pixelwise)
1808 /* --------------------------------------------------------------------------
1809      Adjust window pixel size based on given character grid size
1810      Impl is a bit more complex than other terms, need to do some
1811      internal clipping.
1812    -------------------------------------------------------------------------- */
1814   EmacsView *view = FRAME_NS_VIEW (f);
1815   NSWindow *window = [view window];
1816   NSRect wr = [window frame];
1817   int pixelwidth, pixelheight;
1818   int orig_height = wr.size.height;
1820   NSTRACE ("x_set_window_size");
1822   if (view == nil)
1823     return;
1825   NSTRACE_RECT ("current", wr);
1826   NSTRACE_MSG ("Width:%d Height:%d Pixelwise:%d", width, height, pixelwise);
1827   NSTRACE_MSG ("Font %d x %d", FRAME_COLUMN_WIDTH (f), FRAME_LINE_HEIGHT (f));
1829   block_input ();
1831   if (pixelwise)
1832     {
1833       pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
1834       pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
1835     }
1836   else
1837     {
1838       pixelwidth =  FRAME_TEXT_COLS_TO_PIXEL_WIDTH   (f, width);
1839       pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, height);
1840     }
1842   wr.size.width = pixelwidth + f->border_width;
1843   wr.size.height = pixelheight;
1844   if (! [view isFullscreen])
1845     wr.size.height += FRAME_NS_TITLEBAR_HEIGHT (f)
1846       + FRAME_TOOLBAR_HEIGHT (f);
1848   /* Do not try to constrain to this screen.  We may have multiple
1849      screens, and want Emacs to span those.  Constraining to screen
1850      prevents that, and that is not nice to the user.  */
1851  if (f->output_data.ns->zooming)
1852    f->output_data.ns->zooming = 0;
1853  else
1854    wr.origin.y += orig_height - wr.size.height;
1856  frame_size_history_add
1857    (f, Qx_set_window_size_1, width, height,
1858     list5 (Fcons (make_number (pixelwidth), make_number (pixelheight)),
1859            Fcons (make_number (wr.size.width), make_number (wr.size.height)),
1860            make_number (f->border_width),
1861            make_number (FRAME_NS_TITLEBAR_HEIGHT (f)),
1862            make_number (FRAME_TOOLBAR_HEIGHT (f))));
1864   [window setFrame: wr display: YES];
1866   [view updateFrameSize: NO];
1867   unblock_input ();
1870 #ifdef NS_IMPL_COCOA
1871 void
1872 x_set_undecorated (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1873 /* --------------------------------------------------------------------------
1874      Set frame F's `undecorated' parameter.  If non-nil, F's window-system
1875      window is drawn without decorations, title, minimize/maximize boxes
1876      and external borders.  This usually means that the window cannot be
1877      dragged, resized, iconified, maximized or deleted with the mouse.  If
1878      nil, draw the frame with all the elements listed above unless these
1879      have been suspended via window manager settings.
1881      GNUStep cannot change an existing window's style.
1882    -------------------------------------------------------------------------- */
1884   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
1885   NSWindow *window = [view window];
1887   NSTRACE ("x_set_undecorated");
1889   if (!EQ (new_value, old_value))
1890     {
1891       block_input ();
1893       if (NILP (new_value))
1894         {
1895           FRAME_UNDECORATED (f) = false;
1896           [window setStyleMask: ((window.styleMask | FRAME_DECORATED_FLAGS)
1897                                   ^ FRAME_UNDECORATED_FLAGS)];
1899           [view createToolbar: f];
1900         }
1901       else
1902         {
1903           [window setToolbar: nil];
1904           /* Do I need to release the toolbar here? */
1906           FRAME_UNDECORATED (f) = true;
1907           [window setStyleMask: ((window.styleMask | FRAME_UNDECORATED_FLAGS)
1908                                  ^ FRAME_DECORATED_FLAGS)];
1909         }
1911       /* At this point it seems we don't have an active NSResponder,
1912          so some key presses (TAB) are swallowed by the system. */
1913       [window makeFirstResponder: view];
1915       [view updateFrameSize: NO];
1916       unblock_input ();
1917     }
1919 #endif /* NS_IMPL_COCOA */
1921 void
1922 x_set_parent_frame (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1923 /* --------------------------------------------------------------------------
1924      Set frame F's `parent-frame' parameter.  If non-nil, make F a child
1925      frame of the frame specified by that parameter.  Technically, this
1926      makes F's window-system window a child window of the parent frame's
1927      window-system window.  If nil, make F's window-system window a
1928      top-level window--a child of its display's root window.
1930      A child frame's `left' and `top' parameters specify positions
1931      relative to the top-left corner of its parent frame's native
1932      rectangle.  On macOS moving a parent frame moves all its child
1933      frames too, keeping their position relative to the parent
1934      unaltered.  When a parent frame is iconified or made invisible, its
1935      child frames are made invisible.  When a parent frame is deleted,
1936      its child frames are deleted too.
1938      Whether a child frame has a tool bar may be window-system or window
1939      manager dependent.  It's advisable to disable it via the frame
1940      parameter settings.
1942      Some window managers may not honor this parameter.
1943    -------------------------------------------------------------------------- */
1945   struct frame *p = NULL;
1946   NSWindow *parent, *child;
1948   NSTRACE ("x_set_parent_frame");
1950   if (!NILP (new_value)
1951       && (!FRAMEP (new_value)
1952           || !FRAME_LIVE_P (p = XFRAME (new_value))
1953           || !FRAME_NS_P (p)))
1954     {
1955       store_frame_param (f, Qparent_frame, old_value);
1956       error ("Invalid specification of `parent-frame'");
1957     }
1959   if (p != FRAME_PARENT_FRAME (f))
1960     {
1961       block_input ();
1962       child = [FRAME_NS_VIEW (f) window];
1964       if ([child parentWindow] != nil)
1965         {
1966           [[child parentWindow] removeChildWindow:child];
1967 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
1968 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101000
1969           if ([child respondsToSelector:@selector(setAccessibilitySubrole:)]
1970 #endif
1971               [child setAccessibilitySubrole:NSAccessibilityStandardWindowSubrole];
1972 #endif
1973         }
1975       if (!NILP (new_value))
1976         {
1977           parent = [FRAME_NS_VIEW (p) window];
1979           [parent addChildWindow: child
1980                          ordered: NSWindowAbove];
1981 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
1982 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101000
1983           if ([child respondsToSelector:@selector(setAccessibilitySubrole:)]
1984 #endif
1985               [child setAccessibilitySubrole:NSAccessibilityFloatingWindowSubrole];
1986 #endif
1987         }
1989       unblock_input ();
1991       fset_parent_frame (f, new_value);
1992     }
1995 void
1996 x_set_no_focus_on_map (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1997 /* Set frame F's `no-focus-on-map' parameter which, if non-nil, means
1998  * that F's window-system window does not want to receive input focus
1999  * when it is mapped.  (A frame's window is mapped when the frame is
2000  * displayed for the first time and when the frame changes its state
2001  * from `iconified' or `invisible' to `visible'.)
2003  * Some window managers may not honor this parameter. */
2005   NSTRACE ("x_set_no_focus_on_map");
2007   if (!EQ (new_value, old_value))
2008     {
2009       FRAME_NO_FOCUS_ON_MAP (f) = !NILP (new_value);
2010     }
2013 void
2014 x_set_no_accept_focus (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
2015 /*  Set frame F's `no-accept-focus' parameter which, if non-nil, hints
2016  * that F's window-system window does not want to receive input focus
2017  * via mouse clicks or by moving the mouse into it.
2019  * If non-nil, this may have the unwanted side-effect that a user cannot
2020  * scroll a non-selected frame with the mouse.
2022  * Some window managers may not honor this parameter. */
2024   NSTRACE ("x_set_no_accept_focus");
2026   if (!EQ (new_value, old_value))
2027     FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value);
2030 void
2031 x_set_z_group (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
2032 /* Set frame F's `z-group' parameter.  If `above', F's window-system
2033    window is displayed above all windows that do not have the `above'
2034    property set.  If nil, F's window is shown below all windows that
2035    have the `above' property set and above all windows that have the
2036    `below' property set.  If `below', F's window is displayed below
2037    all windows that do.
2039    Some window managers may not honor this parameter. */
2041   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2042   NSWindow *window = [view window];
2044   NSTRACE ("x_set_z_group");
2046   if (NILP (new_value))
2047     {
2048       window.level = NSNormalWindowLevel;
2049       FRAME_Z_GROUP (f) = z_group_none;
2050     }
2051   else if (EQ (new_value, Qabove))
2052     {
2053       window.level = NSNormalWindowLevel + 1;
2054       FRAME_Z_GROUP (f) = z_group_above;
2055     }
2056   else if (EQ (new_value, Qabove_suspended))
2057     {
2058       /* Not sure what level this should be. */
2059       window.level = NSNormalWindowLevel + 1;
2060       FRAME_Z_GROUP (f) = z_group_above_suspended;
2061     }
2062   else if (EQ (new_value, Qbelow))
2063     {
2064       window.level = NSNormalWindowLevel - 1;
2065       FRAME_Z_GROUP (f) = z_group_below;
2066     }
2067   else
2068     error ("Invalid z-group specification");
2071 #ifdef NS_IMPL_COCOA
2072 void
2073 ns_set_appearance (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
2075 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
2076   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2077   NSWindow *window = [view window];
2079   NSTRACE ("ns_set_appearance");
2081 #ifndef NSAppKitVersionNumber10_10
2082 #define NSAppKitVersionNumber10_10 1343
2083 #endif
2085   if (NSAppKitVersionNumber < NSAppKitVersionNumber10_10)
2086     return;
2088   if (EQ (new_value, Qdark))
2089     {
2090       window.appearance = [NSAppearance
2091                             appearanceNamed: NSAppearanceNameVibrantDark];
2092       FRAME_NS_APPEARANCE (f) = ns_appearance_vibrant_dark;
2093     }
2094   else
2095     {
2096       window.appearance = [NSAppearance
2097                             appearanceNamed: NSAppearanceNameAqua];
2098       FRAME_NS_APPEARANCE (f) = ns_appearance_aqua;
2099     }
2100 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 */
2103 void
2104 ns_set_transparent_titlebar (struct frame *f, Lisp_Object new_value,
2105                              Lisp_Object old_value)
2107 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
2108   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2109   NSWindow *window = [view window];
2111   NSTRACE ("ns_set_transparent_titlebar");
2113   if ([window respondsToSelector: @selector(titlebarAppearsTransparent)]
2114       && !EQ (new_value, old_value))
2115     {
2116       window.titlebarAppearsTransparent = !NILP (new_value);
2117       FRAME_NS_TRANSPARENT_TITLEBAR (f) = !NILP (new_value);
2118     }
2119 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 */
2121 #endif /* NS_IMPL_COCOA */
2123 static void
2124 ns_fullscreen_hook (struct frame *f)
2126   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2128   NSTRACE ("ns_fullscreen_hook");
2130   if (!FRAME_VISIBLE_P (f))
2131     return;
2133    if (! [view fsIsNative] && f->want_fullscreen == FULLSCREEN_BOTH)
2134     {
2135       /* Old style fs don't initiate correctly if created from
2136          init/default-frame alist, so use a timer (not nice...).
2137       */
2138       [NSTimer scheduledTimerWithTimeInterval: 0.5 target: view
2139                                      selector: @selector (handleFS)
2140                                      userInfo: nil repeats: NO];
2141       return;
2142     }
2144   block_input ();
2145   [view handleFS];
2146   unblock_input ();
2149 /* ==========================================================================
2151     Color management
2153    ========================================================================== */
2156 NSColor *
2157 ns_lookup_indexed_color (unsigned long idx, struct frame *f)
2159   struct ns_color_table *color_table = FRAME_DISPLAY_INFO (f)->color_table;
2160   if (idx < 1 || idx >= color_table->avail)
2161     return nil;
2162   return color_table->colors[idx];
2166 unsigned long
2167 ns_index_color (NSColor *color, struct frame *f)
2169   struct ns_color_table *color_table = FRAME_DISPLAY_INFO (f)->color_table;
2170   ptrdiff_t idx;
2171   ptrdiff_t i;
2173   if (!color_table->colors)
2174     {
2175       color_table->size = NS_COLOR_CAPACITY;
2176       color_table->avail = 1; /* skip idx=0 as marker */
2177       color_table->colors = xmalloc (color_table->size * sizeof (NSColor *));
2178       color_table->colors[0] = nil;
2179       color_table->empty_indices = [[NSMutableSet alloc] init];
2180     }
2182   /* Do we already have this color?  */
2183   for (i = 1; i < color_table->avail; i++)
2184     if (color_table->colors[i] && [color_table->colors[i] isEqual: color])
2185       return i;
2187   if ([color_table->empty_indices count] > 0)
2188     {
2189       NSNumber *index = [color_table->empty_indices anyObject];
2190       [color_table->empty_indices removeObject: index];
2191       idx = [index unsignedLongValue];
2192     }
2193   else
2194     {
2195       if (color_table->avail == color_table->size)
2196         color_table->colors =
2197           xpalloc (color_table->colors, &color_table->size, 1,
2198                    min (ULONG_MAX, PTRDIFF_MAX), sizeof *color_table->colors);
2199       idx = color_table->avail++;
2200     }
2202   color_table->colors[idx] = color;
2203   [color retain];
2204 /*fprintf(stderr, "color_table: allocated %d\n",idx);*/
2205   return idx;
2209 static int
2210 ns_get_color (const char *name, NSColor **col)
2211 /* --------------------------------------------------------------------------
2212      Parse a color name
2213    -------------------------------------------------------------------------- */
2214 /* On *Step, we attempt to mimic the X11 platform here, down to installing an
2215    X11 rgb.txt-compatible color list in Emacs.clr (see ns_term_init()).
2216    See: http://thread.gmane.org/gmane.emacs.devel/113050/focus=113272). */
2218   NSColor *new = nil;
2219   static char hex[20];
2220   int scaling = 0;
2221   float r = -1.0, g, b;
2222   NSString *nsname = [NSString stringWithUTF8String: name];
2224   NSTRACE ("ns_get_color(%s, **)", name);
2226   block_input ();
2228   if ([nsname isEqualToString: @"ns_selection_bg_color"])
2229     {
2230 #ifdef NS_IMPL_COCOA
2231       NSString *defname = [[NSUserDefaults standardUserDefaults]
2232                             stringForKey: @"AppleHighlightColor"];
2233       if (defname != nil)
2234         nsname = defname;
2235       else
2236 #endif
2237       if ((new = [NSColor selectedTextBackgroundColor]) != nil)
2238         {
2239           *col = [new colorUsingDefaultColorSpace];
2240           unblock_input ();
2241           return 0;
2242         }
2243       else
2244         nsname = NS_SELECTION_BG_COLOR_DEFAULT;
2246       name = [nsname UTF8String];
2247     }
2248   else if ([nsname isEqualToString: @"ns_selection_fg_color"])
2249     {
2250       /* NOTE: macOS applications normally don't set foreground
2251          selection, but text may be unreadable if we don't.
2252       */
2253       if ((new = [NSColor selectedTextColor]) != nil)
2254         {
2255           *col = [new colorUsingDefaultColorSpace];
2256           unblock_input ();
2257           return 0;
2258         }
2260       nsname = NS_SELECTION_FG_COLOR_DEFAULT;
2261       name = [nsname UTF8String];
2262     }
2264   /* First, check for some sort of numeric specification. */
2265   hex[0] = '\0';
2267   if (name[0] == '0' || name[0] == '1' || name[0] == '.')  /* RGB decimal */
2268     {
2269       NSScanner *scanner = [NSScanner scannerWithString: nsname];
2270       [scanner scanFloat: &r];
2271       [scanner scanFloat: &g];
2272       [scanner scanFloat: &b];
2273     }
2274   else if (!strncmp(name, "rgb:", 4))  /* A newer X11 format -- rgb:r/g/b */
2275     scaling = (snprintf (hex, sizeof hex, "%s", name + 4) - 2) / 3;
2276   else if (name[0] == '#')        /* An old X11 format; convert to newer */
2277     {
2278       int len = (strlen(name) - 1);
2279       int start = (len % 3 == 0) ? 1 : len / 4 + 1;
2280       int i;
2281       scaling = strlen(name+start) / 3;
2282       for (i = 0; i < 3; i++)
2283         sprintf (hex + i * (scaling + 1), "%.*s/", scaling,
2284                  name + start + i * scaling);
2285       hex[3 * (scaling + 1) - 1] = '\0';
2286     }
2288   if (hex[0])
2289     {
2290       unsigned int rr, gg, bb;
2291       float fscale = scaling == 4 ? 65535.0 : (scaling == 2 ? 255.0 : 15.0);
2292       if (sscanf (hex, "%x/%x/%x", &rr, &gg, &bb))
2293         {
2294           r = rr / fscale;
2295           g = gg / fscale;
2296           b = bb / fscale;
2297         }
2298     }
2300   if (r >= 0.0F)
2301     {
2302       *col = [NSColor colorForEmacsRed: r green: g blue: b alpha: 1.0];
2303       unblock_input ();
2304       return 0;
2305     }
2307   /* Otherwise, color is expected to be from a list */
2308   {
2309     NSEnumerator *lenum, *cenum;
2310     NSString *name;
2311     NSColorList *clist;
2313 #ifdef NS_IMPL_GNUSTEP
2314     /* XXX: who is wrong, the requestor or the implementation? */
2315     if ([nsname compare: @"Highlight" options: NSCaseInsensitiveSearch]
2316         == NSOrderedSame)
2317       nsname = @"highlightColor";
2318 #endif
2320     lenum = [[NSColorList availableColorLists] objectEnumerator];
2321     while ( (clist = [lenum nextObject]) && new == nil)
2322       {
2323         cenum = [[clist allKeys] objectEnumerator];
2324         while ( (name = [cenum nextObject]) && new == nil )
2325           {
2326             if ([name compare: nsname
2327                       options: NSCaseInsensitiveSearch] == NSOrderedSame )
2328               new = [clist colorWithKey: name];
2329           }
2330       }
2331   }
2333   if (new)
2334     *col = [new colorUsingDefaultColorSpace];
2335   unblock_input ();
2336   return new ? 0 : 1;
2341 ns_lisp_to_color (Lisp_Object color, NSColor **col)
2342 /* --------------------------------------------------------------------------
2343      Convert a Lisp string object to a NS color
2344    -------------------------------------------------------------------------- */
2346   NSTRACE ("ns_lisp_to_color");
2347   if (STRINGP (color))
2348     return ns_get_color (SSDATA (color), col);
2349   else if (SYMBOLP (color))
2350     return ns_get_color (SSDATA (SYMBOL_NAME (color)), col);
2351   return 1;
2355 void
2356 ns_query_color(void *col, XColor *color_def, int setPixel)
2357 /* --------------------------------------------------------------------------
2358          Get ARGB values out of NSColor col and put them into color_def.
2359          If setPixel, set the pixel to a concatenated version.
2360          and set color_def pixel to the resulting index.
2361    -------------------------------------------------------------------------- */
2363   EmacsCGFloat r, g, b, a;
2365   [((NSColor *)col) getRed: &r green: &g blue: &b alpha: &a];
2366   color_def->red   = r * 65535;
2367   color_def->green = g * 65535;
2368   color_def->blue  = b * 65535;
2370   if (setPixel == YES)
2371     color_def->pixel
2372       = ARGB_TO_ULONG((int)(a*255),
2373                       (int)(r*255), (int)(g*255), (int)(b*255));
2377 bool
2378 ns_defined_color (struct frame *f,
2379                   const char *name,
2380                   XColor *color_def,
2381                   bool alloc,
2382                   bool makeIndex)
2383 /* --------------------------------------------------------------------------
2384          Return true if named color found, and set color_def rgb accordingly.
2385          If makeIndex and alloc are nonzero put the color in the color_table,
2386          and set color_def pixel to the resulting index.
2387          If makeIndex is zero, set color_def pixel to ARGB.
2388          Return false if not found
2389    -------------------------------------------------------------------------- */
2391   NSColor *col;
2392   NSTRACE_WHEN (NSTRACE_GROUP_COLOR, "ns_defined_color");
2394   block_input ();
2395   if (ns_get_color (name, &col) != 0) /* Color not found  */
2396     {
2397       unblock_input ();
2398       return 0;
2399     }
2400   if (makeIndex && alloc)
2401     color_def->pixel = ns_index_color (col, f);
2402   ns_query_color (col, color_def, !makeIndex);
2403   unblock_input ();
2404   return 1;
2408 void
2409 x_set_frame_alpha (struct frame *f)
2410 /* --------------------------------------------------------------------------
2411      change the entire-frame transparency
2412    -------------------------------------------------------------------------- */
2414   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
2415   double alpha = 1.0;
2416   double alpha_min = 1.0;
2418   NSTRACE ("x_set_frame_alpha");
2420   if (dpyinfo->x_highlight_frame == f)
2421     alpha = f->alpha[0];
2422   else
2423     alpha = f->alpha[1];
2425   if (FLOATP (Vframe_alpha_lower_limit))
2426     alpha_min = XFLOAT_DATA (Vframe_alpha_lower_limit);
2427   else if (INTEGERP (Vframe_alpha_lower_limit))
2428     alpha_min = (XINT (Vframe_alpha_lower_limit)) / 100.0;
2430   if (alpha < 0.0)
2431     return;
2432   else if (1.0 < alpha)
2433     alpha = 1.0;
2434   else if (0.0 <= alpha && alpha < alpha_min && alpha_min <= 1.0)
2435     alpha = alpha_min;
2437 #ifdef NS_IMPL_COCOA
2438   {
2439     EmacsView *view = FRAME_NS_VIEW (f);
2440   [[view window] setAlphaValue: alpha];
2441   }
2442 #endif
2446 /* ==========================================================================
2448     Mouse handling
2450    ========================================================================== */
2453 void
2454 frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y)
2455 /* --------------------------------------------------------------------------
2456      Programmatically reposition mouse pointer in pixel coordinates
2457    -------------------------------------------------------------------------- */
2459   NSTRACE ("frame_set_mouse_pixel_position");
2461   /* FIXME: what about GNUstep? */
2462 #ifdef NS_IMPL_COCOA
2463   CGPoint mouse_pos =
2464     CGPointMake(f->left_pos + pix_x,
2465                 f->top_pos + pix_y +
2466                 FRAME_NS_TITLEBAR_HEIGHT(f) + FRAME_TOOLBAR_HEIGHT(f));
2467   CGWarpMouseCursorPosition (mouse_pos);
2468 #endif
2471 static int
2472 note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y)
2473 /*   ------------------------------------------------------------------------
2474      Called by EmacsView on mouseMovement events.  Passes on
2475      to emacs mainstream code if we moved off of a rect of interest
2476      known as last_mouse_glyph.
2477      ------------------------------------------------------------------------ */
2479   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
2480   NSRect *r;
2482 //  NSTRACE ("note_mouse_movement");
2484   dpyinfo->last_mouse_motion_frame = frame;
2485   r = &dpyinfo->last_mouse_glyph;
2487   /* Note, this doesn't get called for enter/leave, since we don't have a
2488      position.  Those are taken care of in the corresponding NSView methods. */
2490   /* has movement gone beyond last rect we were tracking? */
2491   if (x < r->origin.x || x >= r->origin.x + r->size.width
2492       || y < r->origin.y || y >= r->origin.y + r->size.height)
2493     {
2494       ns_update_begin (frame);
2495       frame->mouse_moved = 1;
2496       note_mouse_highlight (frame, x, y);
2497       remember_mouse_glyph (frame, x, y, r);
2498       ns_update_end (frame);
2499       return 1;
2500     }
2502   return 0;
2506 static void
2507 ns_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window,
2508                    enum scroll_bar_part *part, Lisp_Object *x, Lisp_Object *y,
2509                    Time *time)
2510 /* --------------------------------------------------------------------------
2511     External (hook): inform emacs about mouse position and hit parts.
2512     If a scrollbar is being dragged, set bar_window, part, x, y, time.
2513     x & y should be position in the scrollbar (the whole bar, not the handle)
2514     and length of scrollbar respectively
2515    -------------------------------------------------------------------------- */
2517   id view;
2518   NSPoint position;
2519   Lisp_Object frame, tail;
2520   struct frame *f;
2521   struct ns_display_info *dpyinfo;
2523   NSTRACE ("ns_mouse_position");
2525   if (*fp == NULL)
2526     {
2527       fprintf (stderr, "Warning: ns_mouse_position () called with null *fp.\n");
2528       return;
2529     }
2531   dpyinfo = FRAME_DISPLAY_INFO (*fp);
2533   block_input ();
2535   /* Clear the mouse-moved flag for every frame on this display.  */
2536   FOR_EACH_FRAME (tail, frame)
2537     if (FRAME_NS_P (XFRAME (frame))
2538         && FRAME_NS_DISPLAY (XFRAME (frame)) == FRAME_NS_DISPLAY (*fp))
2539       XFRAME (frame)->mouse_moved = 0;
2541   dpyinfo->last_mouse_scroll_bar = nil;
2542   if (dpyinfo->last_mouse_frame
2543       && FRAME_LIVE_P (dpyinfo->last_mouse_frame))
2544     f = dpyinfo->last_mouse_frame;
2545   else
2546     f = dpyinfo->x_focus_frame ? dpyinfo->x_focus_frame : SELECTED_FRAME ();
2548   if (f && FRAME_NS_P (f))
2549     {
2550       view = FRAME_NS_VIEW (f);
2552       position = [[view window] mouseLocationOutsideOfEventStream];
2553       position = [view convertPoint: position fromView: nil];
2554       remember_mouse_glyph (f, position.x, position.y,
2555                             &dpyinfo->last_mouse_glyph);
2556       NSTRACE_POINT ("position", position);
2558       if (bar_window) *bar_window = Qnil;
2559       if (part) *part = scroll_bar_above_handle;
2561       if (x) XSETINT (*x, lrint (position.x));
2562       if (y) XSETINT (*y, lrint (position.y));
2563       if (time)
2564         *time = dpyinfo->last_mouse_movement_time;
2565       *fp = f;
2566     }
2568   unblock_input ();
2572 static void
2573 ns_frame_up_to_date (struct frame *f)
2574 /* --------------------------------------------------------------------------
2575     External (hook): Fix up mouse highlighting right after a full update.
2576     Can't use FRAME_MOUSE_UPDATE due to ns_frame_begin and ns_frame_end calls.
2577    -------------------------------------------------------------------------- */
2579   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_frame_up_to_date");
2581   if (FRAME_NS_P (f))
2582     {
2583       Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
2584       if (f == hlinfo->mouse_face_mouse_frame)
2585         {
2586           block_input ();
2587           ns_update_begin(f);
2588           note_mouse_highlight (hlinfo->mouse_face_mouse_frame,
2589                                 hlinfo->mouse_face_mouse_x,
2590                                 hlinfo->mouse_face_mouse_y);
2591           ns_update_end(f);
2592           unblock_input ();
2593         }
2594     }
2598 static void
2599 ns_define_frame_cursor (struct frame *f, Cursor cursor)
2600 /* --------------------------------------------------------------------------
2601     External (RIF): set frame mouse pointer type.
2602    -------------------------------------------------------------------------- */
2604   NSTRACE ("ns_define_frame_cursor");
2605   if (FRAME_POINTER_TYPE (f) != cursor)
2606     {
2607       EmacsView *view = FRAME_NS_VIEW (f);
2608       FRAME_POINTER_TYPE (f) = cursor;
2609       [[view window] invalidateCursorRectsForView: view];
2610       /* Redisplay assumes this function also draws the changed frame
2611          cursor, but this function doesn't, so do it explicitly.  */
2612       x_update_cursor (f, 1);
2613     }
2618 /* ==========================================================================
2620     Keyboard handling
2622    ========================================================================== */
2625 static unsigned
2626 ns_convert_key (unsigned code)
2627 /* --------------------------------------------------------------------------
2628     Internal call used by NSView-keyDown.
2629    -------------------------------------------------------------------------- */
2631   const unsigned last_keysym = ARRAYELTS (convert_ns_to_X_keysym);
2632   unsigned keysym;
2633   /* An array would be faster, but less easy to read. */
2634   for (keysym = 0; keysym < last_keysym; keysym += 2)
2635     if (code == convert_ns_to_X_keysym[keysym])
2636       return 0xFF00 | convert_ns_to_X_keysym[keysym+1];
2637   return 0;
2638 /* if decide to use keyCode and Carbon table, use this line:
2639      return code > 0xff ? 0 : 0xFF00 | ns_keycode_to_xkeysym_table[code]; */
2643 char *
2644 x_get_keysym_name (int keysym)
2645 /* --------------------------------------------------------------------------
2646     Called by keyboard.c.  Not sure if the return val is important, except
2647     that it be unique.
2648    -------------------------------------------------------------------------- */
2650   static char value[16];
2651   NSTRACE ("x_get_keysym_name");
2652   sprintf (value, "%d", keysym);
2653   return value;
2658 /* ==========================================================================
2660     Block drawing operations
2662    ========================================================================== */
2665 static void
2666 ns_redraw_scroll_bars (struct frame *f)
2668   int i;
2669   id view;
2670   NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews];
2671   NSTRACE ("ns_redraw_scroll_bars");
2672   for (i =[subviews count]-1; i >= 0; i--)
2673     {
2674       view = [subviews objectAtIndex: i];
2675       if (![view isKindOfClass: [EmacsScroller class]]) continue;
2676       [view display];
2677     }
2681 void
2682 ns_clear_frame (struct frame *f)
2683 /* --------------------------------------------------------------------------
2684       External (hook): Erase the entire frame
2685    -------------------------------------------------------------------------- */
2687   NSView *view = FRAME_NS_VIEW (f);
2688   NSRect r;
2690   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_clear_frame");
2692  /* comes on initial frame because we have
2693     after-make-frame-functions = select-frame */
2694  if (!FRAME_DEFAULT_FACE (f))
2695    return;
2697   mark_window_cursors_off (XWINDOW (FRAME_ROOT_WINDOW (f)));
2699   r = [view bounds];
2701   block_input ();
2702   ns_focus (f, &r, 1);
2703   [ns_lookup_indexed_color (NS_FACE_BACKGROUND
2704                             (FACE_FROM_ID (f, DEFAULT_FACE_ID)), f) set];
2705   NSRectFill (r);
2706   ns_unfocus (f);
2708   /* as of 2006/11 or so this is now needed */
2709   ns_redraw_scroll_bars (f);
2710   unblock_input ();
2714 static void
2715 ns_clear_frame_area (struct frame *f, int x, int y, int width, int height)
2716 /* --------------------------------------------------------------------------
2717     External (RIF):  Clear section of frame
2718    -------------------------------------------------------------------------- */
2720   NSRect r = NSMakeRect (x, y, width, height);
2721   NSView *view = FRAME_NS_VIEW (f);
2722   struct face *face = FRAME_DEFAULT_FACE (f);
2724   if (!view || !face)
2725     return;
2727   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_clear_frame_area");
2729   r = NSIntersectionRect (r, [view frame]);
2730   ns_focus (f, &r, 1);
2731   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), f) set];
2733   NSRectFill (r);
2735   ns_unfocus (f);
2736   return;
2739 static void
2740 ns_copy_bits (struct frame *f, NSRect src, NSRect dest)
2742   NSTRACE ("ns_copy_bits");
2744   if (FRAME_NS_VIEW (f))
2745     {
2746       hide_bell();              // Ensure the bell image isn't scrolled.
2748       ns_focus (f, &dest, 1);
2749       [FRAME_NS_VIEW (f) scrollRect: src
2750                                  by: NSMakeSize (dest.origin.x - src.origin.x,
2751                                                  dest.origin.y - src.origin.y)];
2752       ns_unfocus (f);
2753     }
2756 static void
2757 ns_scroll_run (struct window *w, struct run *run)
2758 /* --------------------------------------------------------------------------
2759     External (RIF):  Insert or delete n lines at line vpos
2760    -------------------------------------------------------------------------- */
2762   struct frame *f = XFRAME (w->frame);
2763   int x, y, width, height, from_y, to_y, bottom_y;
2765   NSTRACE ("ns_scroll_run");
2767   /* begin copy from other terms */
2768   /* Get frame-relative bounding box of the text display area of W,
2769      without mode lines.  Include in this box the left and right
2770      fringe of W.  */
2771   window_box (w, ANY_AREA, &x, &y, &width, &height);
2773   from_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->current_y);
2774   to_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->desired_y);
2775   bottom_y = y + height;
2777   if (to_y < from_y)
2778     {
2779       /* Scrolling up.  Make sure we don't copy part of the mode
2780          line at the bottom.  */
2781       if (from_y + run->height > bottom_y)
2782         height = bottom_y - from_y;
2783       else
2784         height = run->height;
2785     }
2786   else
2787     {
2788       /* Scrolling down.  Make sure we don't copy over the mode line.
2789          at the bottom.  */
2790       if (to_y + run->height > bottom_y)
2791         height = bottom_y - to_y;
2792       else
2793         height = run->height;
2794     }
2795   /* end copy from other terms */
2797   if (height == 0)
2798       return;
2800   block_input ();
2802   x_clear_cursor (w);
2804   {
2805     NSRect srcRect = NSMakeRect (x, from_y, width, height);
2806     NSRect dstRect = NSMakeRect (x, to_y, width, height);
2808     ns_copy_bits (f, srcRect , dstRect);
2809   }
2811   unblock_input ();
2815 static void
2816 ns_after_update_window_line (struct window *w, struct glyph_row *desired_row)
2817 /* --------------------------------------------------------------------------
2818     External (RIF): preparatory to fringe update after text was updated
2819    -------------------------------------------------------------------------- */
2821   struct frame *f;
2822   int width, height;
2824   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_after_update_window_line");
2826   /* begin copy from other terms */
2827   eassert (w);
2829   if (!desired_row->mode_line_p && !w->pseudo_window_p)
2830     desired_row->redraw_fringe_bitmaps_p = 1;
2832   /* When a window has disappeared, make sure that no rest of
2833      full-width rows stays visible in the internal border.  */
2834   if (windows_or_buffers_changed
2835       && desired_row->full_width_p
2836       && (f = XFRAME (w->frame),
2837           width = FRAME_INTERNAL_BORDER_WIDTH (f),
2838           width != 0)
2839       && (height = desired_row->visible_height,
2840           height > 0))
2841     {
2842       int y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, desired_row->y));
2844       block_input ();
2845       ns_clear_frame_area (f, 0, y, width, height);
2846       ns_clear_frame_area (f,
2847                            FRAME_PIXEL_WIDTH (f) - width,
2848                            y, width, height);
2849       unblock_input ();
2850     }
2854 static void
2855 ns_shift_glyphs_for_insert (struct frame *f,
2856                            int x, int y, int width, int height,
2857                            int shift_by)
2858 /* --------------------------------------------------------------------------
2859     External (RIF): copy an area horizontally, don't worry about clearing src
2860    -------------------------------------------------------------------------- */
2862   NSRect srcRect = NSMakeRect (x, y, width, height);
2863   NSRect dstRect = NSMakeRect (x+shift_by, y, width, height);
2865   NSTRACE ("ns_shift_glyphs_for_insert");
2867   ns_copy_bits (f, srcRect, dstRect);
2872 /* ==========================================================================
2874     Character encoding and metrics
2876    ========================================================================== */
2879 static void
2880 ns_compute_glyph_string_overhangs (struct glyph_string *s)
2881 /* --------------------------------------------------------------------------
2882      External (RIF); compute left/right overhang of whole string and set in s
2883    -------------------------------------------------------------------------- */
2885   struct font *font = s->font;
2887   if (s->char2b)
2888     {
2889       struct font_metrics metrics;
2890       unsigned int codes[2];
2891       codes[0] = *(s->char2b);
2892       codes[1] = *(s->char2b + s->nchars - 1);
2894       font->driver->text_extents (font, codes, 2, &metrics);
2895       s->left_overhang = -metrics.lbearing;
2896       s->right_overhang
2897         = metrics.rbearing > metrics.width
2898         ? metrics.rbearing - metrics.width : 0;
2899     }
2900   else
2901     {
2902       s->left_overhang = 0;
2903       if (EQ (font->driver->type, Qns))
2904         s->right_overhang = ((struct nsfont_info *)font)->ital ?
2905           FONT_HEIGHT (font) * 0.2 : 0;
2906       else
2907         s->right_overhang = 0;
2908     }
2913 /* ==========================================================================
2915     Fringe and cursor drawing
2917    ========================================================================== */
2920 extern int max_used_fringe_bitmap;
2921 static void
2922 ns_draw_fringe_bitmap (struct window *w, struct glyph_row *row,
2923                       struct draw_fringe_bitmap_params *p)
2924 /* --------------------------------------------------------------------------
2925     External (RIF); fringe-related
2926    -------------------------------------------------------------------------- */
2928   /* Fringe bitmaps comes in two variants, normal and periodic.  A
2929      periodic bitmap is used to create a continuous pattern.  Since a
2930      bitmap is rendered one text line at a time, the start offset (dh)
2931      of the bitmap varies.  Concretely, this is used for the empty
2932      line indicator.
2934      For a bitmap, "h + dh" is the full height and is always
2935      invariant.  For a normal bitmap "dh" is zero.
2937      For example, when the period is three and the full height is 72
2938      the following combinations exists:
2940        h=72 dh=0
2941        h=71 dh=1
2942        h=70 dh=2 */
2944   struct frame *f = XFRAME (WINDOW_FRAME (w));
2945   struct face *face = p->face;
2946   static EmacsImage **bimgs = NULL;
2947   static int nBimgs = 0;
2949   NSTRACE_WHEN (NSTRACE_GROUP_FRINGE, "ns_draw_fringe_bitmap");
2950   NSTRACE_MSG ("which:%d cursor:%d overlay:%d width:%d height:%d period:%d",
2951                p->which, p->cursor_p, p->overlay_p, p->wd, p->h, p->dh);
2953   /* grow bimgs if needed */
2954   if (nBimgs < max_used_fringe_bitmap)
2955     {
2956       bimgs = xrealloc (bimgs, max_used_fringe_bitmap * sizeof *bimgs);
2957       memset (bimgs + nBimgs, 0,
2958               (max_used_fringe_bitmap - nBimgs) * sizeof *bimgs);
2959       nBimgs = max_used_fringe_bitmap;
2960     }
2962   /* Must clip because of partially visible lines.  */
2963   ns_clip_to_row (w, row, ANY_AREA, YES);
2965   if (!p->overlay_p)
2966     {
2967       int bx = p->bx, by = p->by, nx = p->nx, ny = p->ny;
2969       if (bx >= 0 && nx > 0)
2970         {
2971           NSRect r = NSMakeRect (bx, by, nx, ny);
2972           NSRectClip (r);
2973           [ns_lookup_indexed_color (face->background, f) set];
2974           NSRectFill (r);
2975         }
2976     }
2978   if (p->which)
2979     {
2980       NSRect r = NSMakeRect (p->x, p->y, p->wd, p->h);
2981       EmacsImage *img = bimgs[p->which - 1];
2983       if (!img)
2984         {
2985           // Note: For "periodic" images, allocate one EmacsImage for
2986           // the base image, and use it for all dh:s.
2987           unsigned short *bits = p->bits;
2988           int full_height = p->h + p->dh;
2989           int i;
2990           unsigned char *cbits = xmalloc (full_height);
2992           for (i = 0; i < full_height; i++)
2993             cbits[i] = bits[i];
2994           img = [[EmacsImage alloc] initFromXBM: cbits width: 8
2995                                          height: full_height
2996                                              fg: 0 bg: 0];
2997           bimgs[p->which - 1] = img;
2998           xfree (cbits);
2999         }
3001       NSTRACE_RECT ("r", r);
3003       NSRectClip (r);
3004       /* Since we composite the bitmap instead of just blitting it, we need
3005          to erase the whole background. */
3006       [ns_lookup_indexed_color(face->background, f) set];
3007       NSRectFill (r);
3009       {
3010         NSColor *bm_color;
3011         if (!p->cursor_p)
3012           bm_color = ns_lookup_indexed_color(face->foreground, f);
3013         else if (p->overlay_p)
3014           bm_color = ns_lookup_indexed_color(face->background, f);
3015         else
3016           bm_color = f->output_data.ns->cursor_color;
3017         [img setXBMColor: bm_color];
3018       }
3020 #ifdef NS_IMPL_COCOA
3021       // Note: For periodic images, the full image height is "h + hd".
3022       // By using the height h, a suitable part of the image is used.
3023       NSRect fromRect = NSMakeRect(0, 0, p->wd, p->h);
3025       NSTRACE_RECT ("fromRect", fromRect);
3027       [img drawInRect: r
3028               fromRect: fromRect
3029              operation: NSCompositingOperationSourceOver
3030               fraction: 1.0
3031            respectFlipped: YES
3032                 hints: nil];
3033 #else
3034       {
3035         NSPoint pt = r.origin;
3036         pt.y += p->h;
3037         [img compositeToPoint: pt operation: NSCompositingOperationSourceOver];
3038       }
3039 #endif
3040     }
3041   ns_unfocus (f);
3045 static void
3046 ns_draw_window_cursor (struct window *w, struct glyph_row *glyph_row,
3047                        int x, int y, enum text_cursor_kinds cursor_type,
3048                        int cursor_width, bool on_p, bool active_p)
3049 /* --------------------------------------------------------------------------
3050      External call (RIF): draw cursor.
3051      Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
3052    -------------------------------------------------------------------------- */
3054   NSRect r, s;
3055   int fx, fy, h, cursor_height;
3056   struct frame *f = WINDOW_XFRAME (w);
3057   struct glyph *phys_cursor_glyph;
3058   struct glyph *cursor_glyph;
3059   struct face *face;
3060   NSColor *hollow_color = FRAME_BACKGROUND_COLOR (f);
3062   /* If cursor is out of bounds, don't draw garbage.  This can happen
3063      in mini-buffer windows when switching between echo area glyphs
3064      and mini-buffer.  */
3066   NSTRACE ("ns_draw_window_cursor");
3068   if (!on_p)
3069     return;
3071   w->phys_cursor_type = cursor_type;
3072   w->phys_cursor_on_p = on_p;
3074   if (cursor_type == NO_CURSOR)
3075     {
3076       w->phys_cursor_width = 0;
3077       return;
3078     }
3080   if ((phys_cursor_glyph = get_phys_cursor_glyph (w)) == NULL)
3081     {
3082       if (glyph_row->exact_window_width_line_p
3083           && w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])
3084         {
3085           glyph_row->cursor_in_fringe_p = 1;
3086           draw_fringe_bitmap (w, glyph_row, 0);
3087         }
3088       return;
3089     }
3091   /* We draw the cursor (with NSRectFill), then draw the glyph on top
3092      (other terminals do it the other way round).  We must set
3093      w->phys_cursor_width to the cursor width.  For bar cursors, that
3094      is CURSOR_WIDTH; for box cursors, it is the glyph width.  */
3095   get_phys_cursor_geometry (w, glyph_row, phys_cursor_glyph, &fx, &fy, &h);
3097   /* The above get_phys_cursor_geometry call set w->phys_cursor_width
3098      to the glyph width; replace with CURSOR_WIDTH for (V)BAR cursors. */
3099   if (cursor_type == BAR_CURSOR)
3100     {
3101       if (cursor_width < 1)
3102         cursor_width = max (FRAME_CURSOR_WIDTH (f), 1);
3104       /* The bar cursor should never be wider than the glyph. */
3105       if (cursor_width < w->phys_cursor_width)
3106         w->phys_cursor_width = cursor_width;
3107     }
3108   /* If we have an HBAR, "cursor_width" MAY specify height. */
3109   else if (cursor_type == HBAR_CURSOR)
3110     {
3111       cursor_height = (cursor_width < 1) ? lrint (0.25 * h) : cursor_width;
3112       if (cursor_height > glyph_row->height)
3113         cursor_height = glyph_row->height;
3114       if (h > cursor_height) // Cursor smaller than line height, move down
3115         fy += h - cursor_height;
3116       h = cursor_height;
3117     }
3119   r.origin.x = fx, r.origin.y = fy;
3120   r.size.height = h;
3121   r.size.width = w->phys_cursor_width;
3123   /* Prevent the cursor from being drawn outside the text area. */
3124   ns_clip_to_row (w, glyph_row, TEXT_AREA, NO); /* do ns_focus(f, &r, 1); if remove */
3127   face = FACE_FROM_ID_OR_NULL (f, phys_cursor_glyph->face_id);
3128   if (face && NS_FACE_BACKGROUND (face)
3129       == ns_index_color (FRAME_CURSOR_COLOR (f), f))
3130     {
3131       [ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), f) set];
3132       hollow_color = FRAME_CURSOR_COLOR (f);
3133     }
3134   else
3135     [FRAME_CURSOR_COLOR (f) set];
3137 #ifdef NS_IMPL_COCOA
3138   /* TODO: This makes drawing of cursor plus that of phys_cursor_glyph
3139            atomic.  Cleaner ways of doing this should be investigated.
3140            One way would be to set a global variable DRAWING_CURSOR
3141            when making the call to draw_phys..(), don't focus in that
3142            case, then move the ns_unfocus() here after that call. */
3143   NSDisableScreenUpdates ();
3144 #endif
3146   switch (cursor_type)
3147     {
3148     case DEFAULT_CURSOR:
3149     case NO_CURSOR:
3150       break;
3151     case FILLED_BOX_CURSOR:
3152       NSRectFill (r);
3153       break;
3154     case HOLLOW_BOX_CURSOR:
3155       NSRectFill (r);
3156       [hollow_color set];
3157       NSRectFill (NSInsetRect (r, 1, 1));
3158       [FRAME_CURSOR_COLOR (f) set];
3159       break;
3160     case HBAR_CURSOR:
3161       NSRectFill (r);
3162       break;
3163     case BAR_CURSOR:
3164       s = r;
3165       /* If the character under cursor is R2L, draw the bar cursor
3166          on the right of its glyph, rather than on the left.  */
3167       cursor_glyph = get_phys_cursor_glyph (w);
3168       if ((cursor_glyph->resolved_level & 1) != 0)
3169         s.origin.x += cursor_glyph->pixel_width - s.size.width;
3171       NSRectFill (s);
3172       break;
3173     }
3174   ns_unfocus (f);
3176   /* draw the character under the cursor */
3177   if (cursor_type != NO_CURSOR)
3178     draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
3180 #ifdef NS_IMPL_COCOA
3181   NSEnableScreenUpdates ();
3182 #endif
3187 static void
3188 ns_draw_vertical_window_border (struct window *w, int x, int y0, int y1)
3189 /* --------------------------------------------------------------------------
3190      External (RIF): Draw a vertical line.
3191    -------------------------------------------------------------------------- */
3193   struct frame *f = XFRAME (WINDOW_FRAME (w));
3194   struct face *face;
3195   NSRect r = NSMakeRect (x, y0, 1, y1-y0);
3197   NSTRACE ("ns_draw_vertical_window_border");
3199   face = FACE_FROM_ID_OR_NULL (f, VERTICAL_BORDER_FACE_ID);
3201   ns_focus (f, &r, 1);
3202   if (face)
3203     [ns_lookup_indexed_color(face->foreground, f) set];
3205   NSRectFill(r);
3206   ns_unfocus (f);
3210 static void
3211 ns_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1)
3212 /* --------------------------------------------------------------------------
3213      External (RIF): Draw a window divider.
3214    -------------------------------------------------------------------------- */
3216   struct frame *f = XFRAME (WINDOW_FRAME (w));
3217   struct face *face = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FACE_ID);
3218   struct face *face_first
3219     = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FIRST_PIXEL_FACE_ID);
3220   struct face *face_last
3221     = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_LAST_PIXEL_FACE_ID);
3222   unsigned long color = face ? face->foreground : FRAME_FOREGROUND_PIXEL (f);
3223   unsigned long color_first = (face_first
3224                                ? face_first->foreground
3225                                : FRAME_FOREGROUND_PIXEL (f));
3226   unsigned long color_last = (face_last
3227                               ? face_last->foreground
3228                               : FRAME_FOREGROUND_PIXEL (f));
3229   NSRect divider = NSMakeRect (x0, y0, x1-x0, y1-y0);
3231   NSTRACE ("ns_draw_window_divider");
3233   ns_focus (f, &divider, 1);
3235   if ((y1 - y0 > x1 - x0) && (x1 - x0 >= 3))
3236     /* A vertical divider, at least three pixels wide: Draw first and
3237        last pixels differently.  */
3238     {
3239       [ns_lookup_indexed_color(color_first, f) set];
3240       NSRectFill(NSMakeRect (x0, y0, 1, y1 - y0));
3241       [ns_lookup_indexed_color(color, f) set];
3242       NSRectFill(NSMakeRect (x0 + 1, y0, x1 - x0 - 2, y1 - y0));
3243       [ns_lookup_indexed_color(color_last, f) set];
3244       NSRectFill(NSMakeRect (x1 - 1, y0, 1, y1 - y0));
3245     }
3246   else if ((x1 - x0 > y1 - y0) && (y1 - y0 >= 3))
3247     /* A horizontal divider, at least three pixels high: Draw first and
3248        last pixels differently.  */
3249     {
3250       [ns_lookup_indexed_color(color_first, f) set];
3251       NSRectFill(NSMakeRect (x0, y0, x1 - x0, 1));
3252       [ns_lookup_indexed_color(color, f) set];
3253       NSRectFill(NSMakeRect (x0, y0 + 1, x1 - x0, y1 - y0 - 2));
3254       [ns_lookup_indexed_color(color_last, f) set];
3255       NSRectFill(NSMakeRect (x0, y1 - 1, x1 - x0, 1));
3256     }
3257   else
3258     {
3259       /* In any other case do not draw the first and last pixels
3260          differently.  */
3261       [ns_lookup_indexed_color(color, f) set];
3262       NSRectFill(divider);
3263     }
3265   ns_unfocus (f);
3268 static void
3269 ns_show_hourglass (struct frame *f)
3271   /* TODO: add NSProgressIndicator to all frames.  */
3274 static void
3275 ns_hide_hourglass (struct frame *f)
3277   /* TODO: remove NSProgressIndicator from all frames.  */
3280 /* ==========================================================================
3282     Glyph drawing operations
3284    ========================================================================== */
3286 static int
3287 ns_get_glyph_string_clip_rect (struct glyph_string *s, NativeRectangle *nr)
3288 /* --------------------------------------------------------------------------
3289     Wrapper utility to account for internal border width on full-width lines,
3290     and allow top full-width rows to hit the frame top.  nr should be pointer
3291     to two successive NSRects.  Number of rects actually used is returned.
3292    -------------------------------------------------------------------------- */
3294   int n = get_glyph_string_clip_rects (s, nr, 2);
3295   return n;
3298 /* --------------------------------------------------------------------
3299    Draw a wavy line under glyph string s. The wave fills wave_height
3300    pixels from y.
3302                     x          wave_length = 2
3303                                  --
3304                 y    *   *   *   *   *
3305                      |* * * * * * * * *
3306     wave_height = 3  | *   *   *   *
3307   --------------------------------------------------------------------- */
3309 static void
3310 ns_draw_underwave (struct glyph_string *s, EmacsCGFloat width, EmacsCGFloat x)
3312   int wave_height = 3, wave_length = 2;
3313   int y, dx, dy, odd, xmax;
3314   NSPoint a, b;
3315   NSRect waveClip;
3317   dx = wave_length;
3318   dy = wave_height - 1;
3319   y =  s->ybase - wave_height + 3;
3320   xmax = x + width;
3322   /* Find and set clipping rectangle */
3323   waveClip = NSMakeRect (x, y, width, wave_height);
3324   [[NSGraphicsContext currentContext] saveGraphicsState];
3325   NSRectClip (waveClip);
3327   /* Draw the waves */
3328   a.x = x - ((int)(x) % dx) + (EmacsCGFloat) 0.5;
3329   b.x = a.x + dx;
3330   odd = (int)(a.x/dx) % 2;
3331   a.y = b.y = y + 0.5;
3333   if (odd)
3334     a.y += dy;
3335   else
3336     b.y += dy;
3338   while (a.x <= xmax)
3339     {
3340       [NSBezierPath strokeLineFromPoint:a toPoint:b];
3341       a.x = b.x, a.y = b.y;
3342       b.x += dx, b.y = y + 0.5 + odd*dy;
3343       odd = !odd;
3344     }
3346   /* Restore previous clipping rectangle(s) */
3347   [[NSGraphicsContext currentContext] restoreGraphicsState];
3352 static void
3353 ns_draw_text_decoration (struct glyph_string *s, struct face *face,
3354                          NSColor *defaultCol, CGFloat width, CGFloat x)
3355 /* --------------------------------------------------------------------------
3356    Draw underline, overline, and strike-through on glyph string s.
3357    -------------------------------------------------------------------------- */
3359   if (s->for_overlaps)
3360     return;
3362   /* Do underline. */
3363   if (face->underline_p)
3364     {
3365       if (s->face->underline_type == FACE_UNDER_WAVE)
3366         {
3367           if (face->underline_defaulted_p)
3368             [defaultCol set];
3369           else
3370             [ns_lookup_indexed_color (face->underline_color, s->f) set];
3372           ns_draw_underwave (s, width, x);
3373         }
3374       else if (s->face->underline_type == FACE_UNDER_LINE)
3375         {
3377           NSRect r;
3378           unsigned long thickness, position;
3380           /* If the prev was underlined, match its appearance. */
3381           if (s->prev && s->prev->face->underline_p
3382               && s->prev->face->underline_type == FACE_UNDER_LINE
3383               && s->prev->underline_thickness > 0)
3384             {
3385               thickness = s->prev->underline_thickness;
3386               position = s->prev->underline_position;
3387             }
3388           else
3389             {
3390               struct font *font = font_for_underline_metrics (s);
3391               unsigned long descent = s->y + s->height - s->ybase;
3393               /* Use underline thickness of font, defaulting to 1. */
3394               thickness = (font && font->underline_thickness > 0)
3395                 ? font->underline_thickness : 1;
3397               /* Determine the offset of underlining from the baseline. */
3398               if (x_underline_at_descent_line)
3399                 position = descent - thickness;
3400               else if (x_use_underline_position_properties
3401                        && font && font->underline_position >= 0)
3402                 position = font->underline_position;
3403               else if (font)
3404                 position = lround (font->descent / 2);
3405               else
3406                 position = underline_minimum_offset;
3408               position = max (position, underline_minimum_offset);
3410               /* Ensure underlining is not cropped. */
3411               if (descent <= position)
3412                 {
3413                   position = descent - 1;
3414                   thickness = 1;
3415                 }
3416               else if (descent < position + thickness)
3417                 thickness = 1;
3418             }
3420           s->underline_thickness = thickness;
3421           s->underline_position = position;
3423           r = NSMakeRect (x, s->ybase + position, width, thickness);
3425           if (face->underline_defaulted_p)
3426             [defaultCol set];
3427           else
3428             [ns_lookup_indexed_color (face->underline_color, s->f) set];
3429           NSRectFill (r);
3430         }
3431     }
3432   /* Do overline. We follow other terms in using a thickness of 1
3433      and ignoring overline_margin. */
3434   if (face->overline_p)
3435     {
3436       NSRect r;
3437       r = NSMakeRect (x, s->y, width, 1);
3439       if (face->overline_color_defaulted_p)
3440         [defaultCol set];
3441       else
3442         [ns_lookup_indexed_color (face->overline_color, s->f) set];
3443       NSRectFill (r);
3444     }
3446   /* Do strike-through.  We follow other terms for thickness and
3447      vertical position.*/
3448   if (face->strike_through_p)
3449     {
3450       NSRect r;
3451       /* Y-coordinate and height of the glyph string's first glyph.
3452          We cannot use s->y and s->height because those could be
3453          larger if there are taller display elements (e.g., characters
3454          displayed with a larger font) in the same glyph row.  */
3455       int glyph_y = s->ybase - s->first_glyph->ascent;
3456       int glyph_height = s->first_glyph->ascent + s->first_glyph->descent;
3457       /* Strike-through width and offset from the glyph string's
3458          top edge.  */
3459       unsigned long h = 1;
3460       unsigned long dy;
3462       dy = lrint ((glyph_height - h) / 2);
3463       r = NSMakeRect (x, glyph_y + dy, width, 1);
3465       if (face->strike_through_color_defaulted_p)
3466         [defaultCol set];
3467       else
3468         [ns_lookup_indexed_color (face->strike_through_color, s->f) set];
3469       NSRectFill (r);
3470     }
3473 static void
3474 ns_draw_box (NSRect r, CGFloat thickness, NSColor *col,
3475              char left_p, char right_p)
3476 /* --------------------------------------------------------------------------
3477     Draw an unfilled rect inside r, optionally leaving left and/or right open.
3478     Note we can't just use an NSDrawRect command, because of the possibility
3479     of some sides not being drawn, and because the rect will be filled.
3480    -------------------------------------------------------------------------- */
3482   NSRect s = r;
3483   [col set];
3485   /* top, bottom */
3486   s.size.height = thickness;
3487   NSRectFill (s);
3488   s.origin.y += r.size.height - thickness;
3489   NSRectFill (s);
3491   s.size.height = r.size.height;
3492   s.origin.y = r.origin.y;
3494   /* left, right (optional) */
3495   s.size.width = thickness;
3496   if (left_p)
3497     NSRectFill (s);
3498   if (right_p)
3499     {
3500       s.origin.x += r.size.width - thickness;
3501       NSRectFill (s);
3502     }
3506 static void
3507 ns_draw_relief (NSRect r, int thickness, char raised_p,
3508                char top_p, char bottom_p, char left_p, char right_p,
3509                struct glyph_string *s)
3510 /* --------------------------------------------------------------------------
3511     Draw a relief rect inside r, optionally leaving some sides open.
3512     Note we can't just use an NSDrawBezel command, because of the possibility
3513     of some sides not being drawn, and because the rect will be filled.
3514    -------------------------------------------------------------------------- */
3516   static NSColor *baseCol = nil, *lightCol = nil, *darkCol = nil;
3517   NSColor *newBaseCol = nil;
3518   NSRect sr = r;
3520   NSTRACE ("ns_draw_relief");
3522   /* set up colors */
3524   if (s->face->use_box_color_for_shadows_p)
3525     {
3526       newBaseCol = ns_lookup_indexed_color (s->face->box_color, s->f);
3527     }
3528 /*     else if (s->first_glyph->type == IMAGE_GLYPH
3529            && s->img->pixmap
3530            && !IMAGE_BACKGROUND_TRANSPARENT (s->img, s->f, 0))
3531        {
3532          newBaseCol = IMAGE_BACKGROUND  (s->img, s->f, 0);
3533        } */
3534   else
3535     {
3536       newBaseCol = ns_lookup_indexed_color (s->face->background, s->f);
3537     }
3539   if (newBaseCol == nil)
3540     newBaseCol = [NSColor grayColor];
3542   if (newBaseCol != baseCol)  /* TODO: better check */
3543     {
3544       [baseCol release];
3545       baseCol = [newBaseCol retain];
3546       [lightCol release];
3547       lightCol = [[baseCol highlightWithLevel: 0.2] retain];
3548       [darkCol release];
3549       darkCol = [[baseCol shadowWithLevel: 0.3] retain];
3550     }
3552   [(raised_p ? lightCol : darkCol) set];
3554   /* TODO: mitering. Using NSBezierPath doesn't work because of color switch. */
3556   /* top */
3557   sr.size.height = thickness;
3558   if (top_p) NSRectFill (sr);
3560   /* left */
3561   sr.size.height = r.size.height;
3562   sr.size.width = thickness;
3563   if (left_p) NSRectFill (sr);
3565   [(raised_p ? darkCol : lightCol) set];
3567   /* bottom */
3568   sr.size.width = r.size.width;
3569   sr.size.height = thickness;
3570   sr.origin.y += r.size.height - thickness;
3571   if (bottom_p) NSRectFill (sr);
3573   /* right */
3574   sr.size.height = r.size.height;
3575   sr.origin.y = r.origin.y;
3576   sr.size.width = thickness;
3577   sr.origin.x += r.size.width - thickness;
3578   if (right_p) NSRectFill (sr);
3582 static void
3583 ns_dumpglyphs_box_or_relief (struct glyph_string *s)
3584 /* --------------------------------------------------------------------------
3585       Function modeled after x_draw_glyph_string_box ().
3586       Sets up parameters for drawing.
3587    -------------------------------------------------------------------------- */
3589   int right_x, last_x;
3590   char left_p, right_p;
3591   struct glyph *last_glyph;
3592   NSRect r;
3593   int thickness;
3594   struct face *face;
3596   if (s->hl == DRAW_MOUSE_FACE)
3597     {
3598       face = FACE_FROM_ID_OR_NULL (s->f,
3599                                    MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3600       if (!face)
3601         face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3602     }
3603   else
3604     face = s->face;
3606   thickness = face->box_line_width;
3608   NSTRACE ("ns_dumpglyphs_box_or_relief");
3610   last_x = ((s->row->full_width_p && !s->w->pseudo_window_p)
3611             ? WINDOW_RIGHT_EDGE_X (s->w)
3612             : window_box_right (s->w, s->area));
3613   last_glyph = (s->cmp || s->img
3614                 ? s->first_glyph : s->first_glyph + s->nchars-1);
3616   right_x = ((s->row->full_width_p && s->extends_to_end_of_line_p
3617               ? last_x - 1 : min (last_x, s->x + s->background_width) - 1));
3619   left_p = (s->first_glyph->left_box_line_p
3620             || (s->hl == DRAW_MOUSE_FACE
3621                 && (s->prev == NULL || s->prev->hl != s->hl)));
3622   right_p = (last_glyph->right_box_line_p
3623              || (s->hl == DRAW_MOUSE_FACE
3624                  && (s->next == NULL || s->next->hl != s->hl)));
3626   r = NSMakeRect (s->x, s->y, right_x - s->x + 1, s->height);
3628   /* TODO: Sometimes box_color is 0 and this seems wrong; should investigate. */
3629   if (s->face->box == FACE_SIMPLE_BOX && s->face->box_color)
3630     {
3631       ns_draw_box (r, abs (thickness),
3632                    ns_lookup_indexed_color (face->box_color, s->f),
3633                   left_p, right_p);
3634     }
3635   else
3636     {
3637       ns_draw_relief (r, abs (thickness), s->face->box == FACE_RAISED_BOX,
3638                      1, 1, left_p, right_p, s);
3639     }
3643 static void
3644 ns_maybe_dumpglyphs_background (struct glyph_string *s, char force_p)
3645 /* --------------------------------------------------------------------------
3646       Modeled after x_draw_glyph_string_background, which draws BG in
3647       certain cases.  Others are left to the text rendering routine.
3648    -------------------------------------------------------------------------- */
3650   NSTRACE ("ns_maybe_dumpglyphs_background");
3652   if (!s->background_filled_p/* || s->hl == DRAW_MOUSE_FACE*/)
3653     {
3654       int box_line_width = max (s->face->box_line_width, 0);
3655       if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width
3656           /* When xdisp.c ignores FONT_HEIGHT, we cannot trust font
3657              dimensions, since the actual glyphs might be much
3658              smaller.  So in that case we always clear the rectangle
3659              with background color.  */
3660           || FONT_TOO_HIGH (s->font)
3661           || s->font_not_found_p || s->extends_to_end_of_line_p || force_p)
3662         {
3663           struct face *face;
3664           if (s->hl == DRAW_MOUSE_FACE)
3665             {
3666               face
3667                 = FACE_FROM_ID_OR_NULL (s->f,
3668                                         MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3669               if (!face)
3670                 face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3671             }
3672           else
3673             face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3674           if (!face->stipple)
3675             [(NS_FACE_BACKGROUND (face) != 0
3676               ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
3677               : FRAME_BACKGROUND_COLOR (s->f)) set];
3678           else
3679             {
3680               struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
3681               [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set];
3682             }
3684           if (s->hl != DRAW_CURSOR)
3685             {
3686               NSRect r = NSMakeRect (s->x, s->y + box_line_width,
3687                                     s->background_width,
3688                                     s->height-2*box_line_width);
3689               NSRectFill (r);
3690             }
3692           s->background_filled_p = 1;
3693         }
3694     }
3698 static void
3699 ns_dumpglyphs_image (struct glyph_string *s, NSRect r)
3700 /* --------------------------------------------------------------------------
3701       Renders an image and associated borders.
3702    -------------------------------------------------------------------------- */
3704   EmacsImage *img = s->img->pixmap;
3705   int box_line_vwidth = max (s->face->box_line_width, 0);
3706   int x = s->x, y = s->ybase - image_ascent (s->img, s->face, &s->slice);
3707   int bg_x, bg_y, bg_height;
3708   int th;
3709   char raised_p;
3710   NSRect br;
3711   struct face *face;
3712   NSColor *tdCol;
3714   NSTRACE ("ns_dumpglyphs_image");
3716   if (s->face->box != FACE_NO_BOX
3717       && s->first_glyph->left_box_line_p && s->slice.x == 0)
3718     x += abs (s->face->box_line_width);
3720   bg_x = x;
3721   bg_y =  s->slice.y == 0 ? s->y : s->y + box_line_vwidth;
3722   bg_height = s->height;
3723   /* other terms have this, but was causing problems w/tabbar mode */
3724   /* - 2 * box_line_vwidth; */
3726   if (s->slice.x == 0) x += s->img->hmargin;
3727   if (s->slice.y == 0) y += s->img->vmargin;
3729   /* Draw BG: if we need larger area than image itself cleared, do that,
3730      otherwise, since we composite the image under NS (instead of mucking
3731      with its background color), we must clear just the image area. */
3732   if (s->hl == DRAW_MOUSE_FACE)
3733     {
3734       face = FACE_FROM_ID_OR_NULL (s->f,
3735                                    MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3736       if (!face)
3737        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3738     }
3739   else
3740     face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3742   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f) set];
3744   if (bg_height > s->slice.height || s->img->hmargin || s->img->vmargin
3745       || s->img->mask || s->img->pixmap == 0 || s->width != s->background_width)
3746     {
3747       br = NSMakeRect (bg_x, bg_y, s->background_width, bg_height);
3748       s->background_filled_p = 1;
3749     }
3750   else
3751     {
3752       br = NSMakeRect (x, y, s->slice.width, s->slice.height);
3753     }
3755   NSRectFill (br);
3757   /* Draw the image.. do we need to draw placeholder if img ==nil? */
3758   if (img != nil)
3759     {
3760 #ifdef NS_IMPL_COCOA
3761       NSRect dr = NSMakeRect (x, y, s->slice.width, s->slice.height);
3762       NSRect ir = NSMakeRect (s->slice.x,
3763                               s->img->height - s->slice.y - s->slice.height,
3764                               s->slice.width, s->slice.height);
3765       [img drawInRect: dr
3766              fromRect: ir
3767              operation: NSCompositingOperationSourceOver
3768               fraction: 1.0
3769            respectFlipped: YES
3770                 hints: nil];
3771 #else
3772       [img compositeToPoint: NSMakePoint (x, y + s->slice.height)
3773                   operation: NSCompositingOperationSourceOver];
3774 #endif
3775     }
3777   if (s->hl == DRAW_CURSOR)
3778     {
3779     [FRAME_CURSOR_COLOR (s->f) set];
3780     if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
3781       tdCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
3782     else
3783       /* Currently on NS img->mask is always 0. Since
3784          get_window_cursor_type specifies a hollow box cursor when on
3785          a non-masked image we never reach this clause. But we put it
3786          in, in anticipation of better support for image masks on
3787          NS. */
3788       tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3789     }
3790   else
3791     {
3792       tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3793     }
3795   /* Draw underline, overline, strike-through. */
3796   ns_draw_text_decoration (s, face, tdCol, br.size.width, br.origin.x);
3798   /* Draw relief, if requested */
3799   if (s->img->relief || s->hl ==DRAW_IMAGE_RAISED || s->hl ==DRAW_IMAGE_SUNKEN)
3800     {
3801       if (s->hl == DRAW_IMAGE_SUNKEN || s->hl == DRAW_IMAGE_RAISED)
3802         {
3803           th = tool_bar_button_relief >= 0 ?
3804             tool_bar_button_relief : DEFAULT_TOOL_BAR_BUTTON_RELIEF;
3805           raised_p = (s->hl == DRAW_IMAGE_RAISED);
3806         }
3807       else
3808         {
3809           th = abs (s->img->relief);
3810           raised_p = (s->img->relief > 0);
3811         }
3813       r.origin.x = x - th;
3814       r.origin.y = y - th;
3815       r.size.width = s->slice.width + 2*th-1;
3816       r.size.height = s->slice.height + 2*th-1;
3817       ns_draw_relief (r, th, raised_p,
3818                       s->slice.y == 0,
3819                       s->slice.y + s->slice.height == s->img->height,
3820                       s->slice.x == 0,
3821                       s->slice.x + s->slice.width == s->img->width, s);
3822     }
3824   /* If there is no mask, the background won't be seen,
3825      so draw a rectangle on the image for the cursor.
3826      Do this for all images, getting transparency right is not reliable.  */
3827   if (s->hl == DRAW_CURSOR)
3828     {
3829       int thickness = abs (s->img->relief);
3830       if (thickness == 0) thickness = 1;
3831       ns_draw_box (br, thickness, FRAME_CURSOR_COLOR (s->f), 1, 1);
3832     }
3836 static void
3837 ns_dumpglyphs_stretch (struct glyph_string *s)
3839   NSRect r[2];
3840   int n, i;
3841   struct face *face;
3842   NSColor *fgCol, *bgCol;
3844   if (!s->background_filled_p)
3845     {
3846       n = ns_get_glyph_string_clip_rect (s, r);
3847       *r = NSMakeRect (s->x, s->y, s->background_width, s->height);
3849       ns_focus (s->f, r, n);
3851       if (s->hl == DRAW_MOUSE_FACE)
3852        {
3853          face = FACE_FROM_ID_OR_NULL (s->f,
3854                                       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3855          if (!face)
3856            face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3857        }
3858       else
3859        face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3861       bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
3862       fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3864       for (i = 0; i < n; ++i)
3865         {
3866           if (!s->row->full_width_p)
3867             {
3868               int overrun, leftoverrun;
3870               /* truncate to avoid overwriting fringe and/or scrollbar */
3871               overrun = max (0, (s->x + s->background_width)
3872                              - (WINDOW_BOX_RIGHT_EDGE_X (s->w)
3873                                 - WINDOW_RIGHT_FRINGE_WIDTH (s->w)));
3874               r[i].size.width -= overrun;
3876               /* truncate to avoid overwriting to left of the window box */
3877               leftoverrun = (WINDOW_BOX_LEFT_EDGE_X (s->w)
3878                              + WINDOW_LEFT_FRINGE_WIDTH (s->w)) - s->x;
3880               if (leftoverrun > 0)
3881                 {
3882                   r[i].origin.x += leftoverrun;
3883                   r[i].size.width -= leftoverrun;
3884                 }
3886               /* XXX: Try to work between problem where a stretch glyph on
3887                  a partially-visible bottom row will clear part of the
3888                  modeline, and another where list-buffers headers and similar
3889                  rows erroneously have visible_height set to 0.  Not sure
3890                  where this is coming from as other terms seem not to show. */
3891               r[i].size.height = min (s->height, s->row->visible_height);
3892             }
3894           [bgCol set];
3896           /* NOTE: under NS this is NOT used to draw cursors, but we must avoid
3897              overwriting cursor (usually when cursor on a tab) */
3898           if (s->hl == DRAW_CURSOR)
3899             {
3900               CGFloat x, width;
3902               x = r[i].origin.x;
3903               width = s->w->phys_cursor_width;
3904               r[i].size.width -= width;
3905               r[i].origin.x += width;
3907               NSRectFill (r[i]);
3909               /* Draw overlining, etc. on the cursor. */
3910               if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
3911                 ns_draw_text_decoration (s, face, bgCol, width, x);
3912               else
3913                 ns_draw_text_decoration (s, face, fgCol, width, x);
3914             }
3915           else
3916             {
3917               NSRectFill (r[i]);
3918             }
3920           /* Draw overlining, etc. on the stretch glyph (or the part
3921              of the stretch glyph after the cursor). */
3922           ns_draw_text_decoration (s, face, fgCol, r[i].size.width,
3923                                    r[i].origin.x);
3924         }
3925       ns_unfocus (s->f);
3926       s->background_filled_p = 1;
3927     }
3931 static void
3932 ns_draw_glyph_string_foreground (struct glyph_string *s)
3934   int x, flags;
3935   struct font *font = s->font;
3937   /* If first glyph of S has a left box line, start drawing the text
3938      of S to the right of that box line.  */
3939   if (s->face && s->face->box != FACE_NO_BOX
3940       && s->first_glyph->left_box_line_p)
3941     x = s->x + eabs (s->face->box_line_width);
3942   else
3943     x = s->x;
3945   flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
3946     (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
3947      (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
3948       NS_DUMPGLYPH_NORMAL));
3950   font->driver->draw
3951     (s, s->cmp_from, s->nchars, x, s->ybase,
3952      (flags == NS_DUMPGLYPH_NORMAL && !s->background_filled_p)
3953      || flags == NS_DUMPGLYPH_MOUSEFACE);
3957 static void
3958 ns_draw_composite_glyph_string_foreground (struct glyph_string *s)
3960   int i, j, x;
3961   struct font *font = s->font;
3963   /* If first glyph of S has a left box line, start drawing the text
3964      of S to the right of that box line.  */
3965   if (s->face && s->face->box != FACE_NO_BOX
3966       && s->first_glyph->left_box_line_p)
3967     x = s->x + eabs (s->face->box_line_width);
3968   else
3969     x = s->x;
3971   /* S is a glyph string for a composition.  S->cmp_from is the index
3972      of the first character drawn for glyphs of this composition.
3973      S->cmp_from == 0 means we are drawing the very first character of
3974      this composition.  */
3976   /* Draw a rectangle for the composition if the font for the very
3977      first character of the composition could not be loaded.  */
3978   if (s->font_not_found_p)
3979     {
3980       if (s->cmp_from == 0)
3981         {
3982           NSRect r = NSMakeRect (s->x, s->y, s->width-1, s->height -1);
3983           ns_draw_box (r, 1, FRAME_CURSOR_COLOR (s->f), 1, 1);
3984         }
3985     }
3986   else if (! s->first_glyph->u.cmp.automatic)
3987     {
3988       int y = s->ybase;
3990       for (i = 0, j = s->cmp_from; i < s->nchars; i++, j++)
3991         /* TAB in a composition means display glyphs with padding
3992            space on the left or right.  */
3993         if (COMPOSITION_GLYPH (s->cmp, j) != '\t')
3994           {
3995             int xx = x + s->cmp->offsets[j * 2];
3996             int yy = y - s->cmp->offsets[j * 2 + 1];
3998             font->driver->draw (s, j, j + 1, xx, yy, false);
3999             if (s->face->overstrike)
4000               font->driver->draw (s, j, j + 1, xx + 1, yy, false);
4001           }
4002     }
4003   else
4004     {
4005       Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
4006       Lisp_Object glyph;
4007       int y = s->ybase;
4008       int width = 0;
4010       for (i = j = s->cmp_from; i < s->cmp_to; i++)
4011         {
4012           glyph = LGSTRING_GLYPH (gstring, i);
4013           if (NILP (LGLYPH_ADJUSTMENT (glyph)))
4014             width += LGLYPH_WIDTH (glyph);
4015           else
4016             {
4017               int xoff, yoff, wadjust;
4019               if (j < i)
4020                 {
4021                   font->driver->draw (s, j, i, x, y, false);
4022                   if (s->face->overstrike)
4023                     font->driver->draw (s, j, i, x + 1, y, false);
4024                   x += width;
4025                 }
4026               xoff = LGLYPH_XOFF (glyph);
4027               yoff = LGLYPH_YOFF (glyph);
4028               wadjust = LGLYPH_WADJUST (glyph);
4029               font->driver->draw (s, i, i + 1, x + xoff, y + yoff, false);
4030               if (s->face->overstrike)
4031                 font->driver->draw (s, i, i + 1, x + xoff + 1, y + yoff,
4032                                     false);
4033               x += wadjust;
4034               j = i + 1;
4035               width = 0;
4036             }
4037         }
4038       if (j < i)
4039         {
4040           font->driver->draw (s, j, i, x, y, false);
4041           if (s->face->overstrike)
4042             font->driver->draw (s, j, i, x + 1, y, false);
4043         }
4044     }
4047 static void
4048 ns_draw_glyph_string (struct glyph_string *s)
4049 /* --------------------------------------------------------------------------
4050       External (RIF): Main draw-text call.
4051    -------------------------------------------------------------------------- */
4053   /* TODO (optimize): focus for box and contents draw */
4054   NSRect r[2];
4055   int n;
4056   char box_drawn_p = 0;
4057   struct font *font = s->face->font;
4058   if (! font) font = FRAME_FONT (s->f);
4060   NSTRACE_WHEN (NSTRACE_GROUP_GLYPHS, "ns_draw_glyph_string");
4062   if (s->next && s->right_overhang && !s->for_overlaps/*&&s->hl!=DRAW_CURSOR*/)
4063     {
4064       int width;
4065       struct glyph_string *next;
4067       for (width = 0, next = s->next;
4068            next && width < s->right_overhang;
4069            width += next->width, next = next->next)
4070         if (next->first_glyph->type != IMAGE_GLYPH)
4071           {
4072             if (next->first_glyph->type != STRETCH_GLYPH)
4073               {
4074                 n = ns_get_glyph_string_clip_rect (s->next, r);
4075                 ns_focus (s->f, r, n);
4076                 ns_maybe_dumpglyphs_background (s->next, 1);
4077                 ns_unfocus (s->f);
4078               }
4079             else
4080               {
4081                 ns_dumpglyphs_stretch (s->next);
4082               }
4083             next->num_clips = 0;
4084           }
4085     }
4087   if (!s->for_overlaps && s->face->box != FACE_NO_BOX
4088         && (s->first_glyph->type == CHAR_GLYPH
4089             || s->first_glyph->type == COMPOSITE_GLYPH))
4090     {
4091       n = ns_get_glyph_string_clip_rect (s, r);
4092       ns_focus (s->f, r, n);
4093       ns_maybe_dumpglyphs_background (s, 1);
4094       ns_dumpglyphs_box_or_relief (s);
4095       ns_unfocus (s->f);
4096       box_drawn_p = 1;
4097     }
4099   switch (s->first_glyph->type)
4100     {
4102     case IMAGE_GLYPH:
4103       n = ns_get_glyph_string_clip_rect (s, r);
4104       ns_focus (s->f, r, n);
4105       ns_dumpglyphs_image (s, r[0]);
4106       ns_unfocus (s->f);
4107       break;
4109     case STRETCH_GLYPH:
4110       ns_dumpglyphs_stretch (s);
4111       break;
4113     case CHAR_GLYPH:
4114     case COMPOSITE_GLYPH:
4115       n = ns_get_glyph_string_clip_rect (s, r);
4116       ns_focus (s->f, r, n);
4118       if (s->for_overlaps || (s->cmp_from > 0
4119                               && ! s->first_glyph->u.cmp.automatic))
4120         s->background_filled_p = 1;
4121       else
4122         ns_maybe_dumpglyphs_background
4123           (s, s->first_glyph->type == COMPOSITE_GLYPH);
4125       if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
4126         {
4127           unsigned long tmp = NS_FACE_BACKGROUND (s->face);
4128           NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
4129           NS_FACE_FOREGROUND (s->face) = tmp;
4130         }
4132       {
4133         BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
4135         if (isComposite)
4136           ns_draw_composite_glyph_string_foreground (s);
4137         else
4138           ns_draw_glyph_string_foreground (s);
4139       }
4141       {
4142         NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
4143                         ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face),
4144                                                    s->f)
4145                         : FRAME_FOREGROUND_COLOR (s->f));
4146         [col set];
4148         /* Draw underline, overline, strike-through. */
4149         ns_draw_text_decoration (s, s->face, col, s->width, s->x);
4150       }
4152       if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
4153         {
4154           unsigned long tmp = NS_FACE_BACKGROUND (s->face);
4155           NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
4156           NS_FACE_FOREGROUND (s->face) = tmp;
4157         }
4159       ns_unfocus (s->f);
4160       break;
4162     case GLYPHLESS_GLYPH:
4163       n = ns_get_glyph_string_clip_rect (s, r);
4164       ns_focus (s->f, r, n);
4166       if (s->for_overlaps || (s->cmp_from > 0
4167                               && ! s->first_glyph->u.cmp.automatic))
4168         s->background_filled_p = 1;
4169       else
4170         ns_maybe_dumpglyphs_background
4171           (s, s->first_glyph->type == COMPOSITE_GLYPH);
4172       /* ... */
4173       /* Not yet implemented.  */
4174       /* ... */
4175       ns_unfocus (s->f);
4176       break;
4178     default:
4179       emacs_abort ();
4180     }
4182   /* Draw box if not done already. */
4183   if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX)
4184     {
4185       n = ns_get_glyph_string_clip_rect (s, r);
4186       ns_focus (s->f, r, n);
4187       ns_dumpglyphs_box_or_relief (s);
4188       ns_unfocus (s->f);
4189     }
4191   s->num_clips = 0;
4196 /* ==========================================================================
4198     Event loop
4200    ========================================================================== */
4203 static void
4204 ns_send_appdefined (int value)
4205 /* --------------------------------------------------------------------------
4206     Internal: post an appdefined event which EmacsApp-sendEvent will
4207               recognize and take as a command to halt the event loop.
4208    -------------------------------------------------------------------------- */
4210   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_send_appdefined(%d)", value);
4212   // GNUstep needs postEvent to happen on the main thread.
4213   // Cocoa needs nextEventMatchingMask to happen on the main thread too.
4214   if (! [[NSThread currentThread] isMainThread])
4215     {
4216       EmacsApp *app = (EmacsApp *)NSApp;
4217       app->nextappdefined = value;
4218       [app performSelectorOnMainThread:@selector (sendFromMainThread:)
4219                             withObject:nil
4220                          waitUntilDone:NO];
4221       return;
4222     }
4224   /* Only post this event if we haven't already posted one.  This will end
4225        the [NXApp run] main loop after having processed all events queued at
4226        this moment.  */
4228 #ifdef NS_IMPL_COCOA
4229   if (! send_appdefined)
4230     {
4231       /* OS X 10.10.1 swallows the AppDefined event we are sending ourselves
4232          in certain situations (rapid incoming events).
4233          So check if we have one, if not add one.  */
4234       NSEvent *appev = [NSApp nextEventMatchingMask:NSEventMaskApplicationDefined
4235                                           untilDate:[NSDate distantPast]
4236                                              inMode:NSDefaultRunLoopMode
4237                                             dequeue:NO];
4238       if (! appev) send_appdefined = YES;
4239     }
4240 #endif
4242   if (send_appdefined)
4243     {
4244       NSEvent *nxev;
4246       /* We only need one NX_APPDEFINED event to stop NXApp from running.  */
4247       send_appdefined = NO;
4249       /* Don't need wakeup timer any more */
4250       if (timed_entry)
4251         {
4252           [timed_entry invalidate];
4253           [timed_entry release];
4254           timed_entry = nil;
4255         }
4257       nxev = [NSEvent otherEventWithType: NSEventTypeApplicationDefined
4258                                 location: NSMakePoint (0, 0)
4259                            modifierFlags: 0
4260                                timestamp: 0
4261                             windowNumber: [[NSApp mainWindow] windowNumber]
4262                                  context: [NSApp context]
4263                                  subtype: 0
4264                                    data1: value
4265                                    data2: 0];
4267       /* Post an application defined event on the event queue.  When this is
4268          received the [NXApp run] will return, thus having processed all
4269          events which are currently queued.  */
4270       [NSApp postEvent: nxev atStart: NO];
4271     }
4274 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4275 static void
4276 check_native_fs ()
4278   Lisp_Object frame, tail;
4280   if (ns_last_use_native_fullscreen == ns_use_native_fullscreen)
4281     return;
4283   ns_last_use_native_fullscreen = ns_use_native_fullscreen;
4285   FOR_EACH_FRAME (tail, frame)
4286     {
4287       struct frame *f = XFRAME (frame);
4288       if (FRAME_NS_P (f))
4289         {
4290           EmacsView *view = FRAME_NS_VIEW (f);
4291           [view updateCollectionBehavior];
4292         }
4293     }
4295 #endif
4297 /* GNUstep does not have cancelTracking.  */
4298 #ifdef NS_IMPL_COCOA
4299 /* Check if menu open should be canceled or continued as normal.  */
4300 void
4301 ns_check_menu_open (NSMenu *menu)
4303   /* Click in menu bar? */
4304   NSArray *a = [[NSApp mainMenu] itemArray];
4305   int i;
4306   BOOL found = NO;
4308   if (menu == nil) // Menu tracking ended.
4309     {
4310       if (menu_will_open_state == MENU_OPENING)
4311         menu_will_open_state = MENU_NONE;
4312       return;
4313     }
4315   for (i = 0; ! found && i < [a count]; i++)
4316     found = menu == [[a objectAtIndex:i] submenu];
4317   if (found)
4318     {
4319       if (menu_will_open_state == MENU_NONE && emacs_event)
4320         {
4321           NSEvent *theEvent = [NSApp currentEvent];
4322           struct frame *emacsframe = SELECTED_FRAME ();
4324           [menu cancelTracking];
4325           menu_will_open_state = MENU_PENDING;
4326           emacs_event->kind = MENU_BAR_ACTIVATE_EVENT;
4327           EV_TRAILER (theEvent);
4329           CGEventRef ourEvent = CGEventCreate (NULL);
4330           menu_mouse_point = CGEventGetLocation (ourEvent);
4331           CFRelease (ourEvent);
4332         }
4333       else if (menu_will_open_state == MENU_OPENING)
4334         {
4335           menu_will_open_state = MENU_NONE;
4336         }
4337     }
4340 /* Redo saved menu click if state is MENU_PENDING.  */
4341 void
4342 ns_check_pending_open_menu ()
4344   if (menu_will_open_state == MENU_PENDING)
4345     {
4346       CGEventSourceRef source
4347         = CGEventSourceCreate (kCGEventSourceStateHIDSystemState);
4349       CGEventRef event = CGEventCreateMouseEvent (source,
4350                                                   kCGEventLeftMouseDown,
4351                                                   menu_mouse_point,
4352                                                   kCGMouseButtonLeft);
4353       CGEventSetType (event, kCGEventLeftMouseDown);
4354       CGEventPost (kCGHIDEventTap, event);
4355       CFRelease (event);
4356       CFRelease (source);
4358       menu_will_open_state = MENU_OPENING;
4359     }
4361 #endif /* NS_IMPL_COCOA */
4363 static int
4364 ns_read_socket (struct terminal *terminal, struct input_event *hold_quit)
4365 /* --------------------------------------------------------------------------
4366      External (hook): Post an event to ourself and keep reading events until
4367      we read it back again.  In effect process all events which were waiting.
4368      From 21+ we have to manage the event buffer ourselves.
4369    -------------------------------------------------------------------------- */
4371   struct input_event ev;
4372   int nevents;
4374   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_read_socket");
4376 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4377   check_native_fs ();
4378 #endif
4380   if ([NSApp modalWindow] != nil)
4381     return -1;
4383   if (hold_event_q.nr > 0)
4384     {
4385       int i;
4386       for (i = 0; i < hold_event_q.nr; ++i)
4387         kbd_buffer_store_event_hold (&hold_event_q.q[i], hold_quit);
4388       hold_event_q.nr = 0;
4389       return i;
4390     }
4392   if ([NSThread isMainThread])
4393     {
4394       block_input ();
4395       n_emacs_events_pending = 0;
4396       ns_init_events (&ev);
4397       q_event_ptr = hold_quit;
4399       /* we manage autorelease pools by allocate/reallocate each time around
4400          the loop; strict nesting is occasionally violated but seems not to
4401          matter.. earlier methods using full nesting caused major memory leaks */
4402       [outerpool release];
4403       outerpool = [[NSAutoreleasePool alloc] init];
4405       /* If have pending open-file requests, attend to the next one of those. */
4406       if (ns_pending_files && [ns_pending_files count] != 0
4407           && [(EmacsApp *)NSApp openFile: [ns_pending_files objectAtIndex: 0]])
4408         {
4409           [ns_pending_files removeObjectAtIndex: 0];
4410         }
4411       /* Deal with pending service requests. */
4412       else if (ns_pending_service_names && [ns_pending_service_names count] != 0
4413                && [(EmacsApp *)
4414                     NSApp fulfillService: [ns_pending_service_names objectAtIndex: 0]
4415                                  withArg: [ns_pending_service_args objectAtIndex: 0]])
4416         {
4417           [ns_pending_service_names removeObjectAtIndex: 0];
4418           [ns_pending_service_args removeObjectAtIndex: 0];
4419         }
4420       else
4421         {
4422           /* Run and wait for events.  We must always send one NX_APPDEFINED event
4423              to ourself, otherwise [NXApp run] will never exit.  */
4424           send_appdefined = YES;
4425           ns_send_appdefined (-1);
4427           [NSApp run];
4428         }
4430       nevents = n_emacs_events_pending;
4431       n_emacs_events_pending = 0;
4432       ns_finish_events ();
4433       q_event_ptr = NULL;
4434       unblock_input ();
4435     }
4436   else
4437     return -1;
4439   return nevents;
4444 ns_select (int nfds, fd_set *readfds, fd_set *writefds,
4445            fd_set *exceptfds, struct timespec *timeout,
4446            sigset_t *sigmask)
4447 /* --------------------------------------------------------------------------
4448      Replacement for select, checking for events
4449    -------------------------------------------------------------------------- */
4451   int result;
4452   int t, k, nr = 0;
4453   struct input_event event;
4454   char c;
4456   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_select");
4458 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4459   check_native_fs ();
4460 #endif
4462   if (hold_event_q.nr > 0)
4463     {
4464       /* We already have events pending. */
4465       raise (SIGIO);
4466       errno = EINTR;
4467       return -1;
4468     }
4470   for (k = 0; k < nfds+1; k++)
4471     {
4472       if (readfds && FD_ISSET(k, readfds)) ++nr;
4473       if (writefds && FD_ISSET(k, writefds)) ++nr;
4474     }
4476   if (NSApp == nil
4477       || ![NSThread isMainThread]
4478       || (timeout && timeout->tv_sec == 0 && timeout->tv_nsec == 0))
4479     return thread_select(pselect, nfds, readfds, writefds,
4480                          exceptfds, timeout, sigmask);
4481   else
4482     {
4483       struct timespec t = {0, 0};
4484       thread_select(pselect, 0, NULL, NULL, NULL, &t, sigmask);
4485     }
4487   [outerpool release];
4488   outerpool = [[NSAutoreleasePool alloc] init];
4491   send_appdefined = YES;
4492   if (nr > 0)
4493     {
4494       pthread_mutex_lock (&select_mutex);
4495       select_nfds = nfds;
4496       select_valid = 0;
4497       if (readfds)
4498         {
4499           select_readfds = *readfds;
4500           select_valid += SELECT_HAVE_READ;
4501         }
4502       if (writefds)
4503         {
4504           select_writefds = *writefds;
4505           select_valid += SELECT_HAVE_WRITE;
4506         }
4508       if (timeout)
4509         {
4510           select_timeout = *timeout;
4511           select_valid += SELECT_HAVE_TMO;
4512         }
4514       pthread_mutex_unlock (&select_mutex);
4516       /* Inform fd_handler that select should be called */
4517       c = 'g';
4518       emacs_write_sig (selfds[1], &c, 1);
4519     }
4520   else if (nr == 0 && timeout)
4521     {
4522       /* No file descriptor, just a timeout, no need to wake fd_handler  */
4523       double time = timespectod (*timeout);
4524       timed_entry = [[NSTimer scheduledTimerWithTimeInterval: time
4525                                                       target: NSApp
4526                                                     selector:
4527                                   @selector (timeout_handler:)
4528                                                     userInfo: 0
4529                                                      repeats: NO]
4530                       retain];
4531     }
4532   else /* No timeout and no file descriptors, can this happen?  */
4533     {
4534       /* Send appdefined so we exit from the loop */
4535       ns_send_appdefined (-1);
4536     }
4538   block_input ();
4539   ns_init_events (&event);
4541   [NSApp run];
4543   ns_finish_events ();
4544   if (nr > 0 && readfds)
4545     {
4546       c = 's';
4547       emacs_write_sig (selfds[1], &c, 1);
4548     }
4549   unblock_input ();
4551   t = last_appdefined_event_data;
4553   if (t != NO_APPDEFINED_DATA)
4554     {
4555       last_appdefined_event_data = NO_APPDEFINED_DATA;
4557       if (t == -2)
4558         {
4559           /* The NX_APPDEFINED event we received was a timeout. */
4560           result = 0;
4561         }
4562       else if (t == -1)
4563         {
4564           /* The NX_APPDEFINED event we received was the result of
4565              at least one real input event arriving.  */
4566           errno = EINTR;
4567           result = -1;
4568         }
4569       else
4570         {
4571           /* Received back from select () in fd_handler; copy the results */
4572           pthread_mutex_lock (&select_mutex);
4573           if (readfds) *readfds = select_readfds;
4574           if (writefds) *writefds = select_writefds;
4575           pthread_mutex_unlock (&select_mutex);
4576           result = t;
4577         }
4578     }
4579   else
4580     {
4581       errno = EINTR;
4582       result = -1;
4583     }
4585   return result;
4588 #ifdef HAVE_PTHREAD
4589 void
4590 ns_run_loop_break ()
4591 /* Break out of the NS run loop in ns_select or ns_read_socket. */
4593   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_run_loop_break");
4595   /* If we don't have a GUI, don't send the event. */
4596   if (NSApp != NULL)
4597     ns_send_appdefined(-1);
4599 #endif
4602 /* ==========================================================================
4604     Scrollbar handling
4606    ========================================================================== */
4609 static void
4610 ns_set_vertical_scroll_bar (struct window *window,
4611                            int portion, int whole, int position)
4612 /* --------------------------------------------------------------------------
4613       External (hook): Update or add scrollbar
4614    -------------------------------------------------------------------------- */
4616   Lisp_Object win;
4617   NSRect r, v;
4618   struct frame *f = XFRAME (WINDOW_FRAME (window));
4619   EmacsView *view = FRAME_NS_VIEW (f);
4620   EmacsScroller *bar;
4621   int window_y, window_height;
4622   int top, left, height, width;
4623   BOOL update_p = YES;
4625   /* optimization; display engine sends WAY too many of these.. */
4626   if (!NILP (window->vertical_scroll_bar))
4627     {
4628       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4629       if ([bar checkSamePosition: position portion: portion whole: whole])
4630         {
4631           if (view->scrollbarsNeedingUpdate == 0)
4632             {
4633               if (!windows_or_buffers_changed)
4634                   return;
4635             }
4636           else
4637             view->scrollbarsNeedingUpdate--;
4638           update_p = NO;
4639         }
4640     }
4642   NSTRACE ("ns_set_vertical_scroll_bar");
4644   /* Get dimensions.  */
4645   window_box (window, ANY_AREA, 0, &window_y, 0, &window_height);
4646   top = window_y;
4647   height = window_height;
4648   width = NS_SCROLL_BAR_WIDTH (f);
4649   left = WINDOW_SCROLL_BAR_AREA_X (window);
4651   r = NSMakeRect (left, top, width, height);
4652   /* the parent view is flipped, so we need to flip y value */
4653   v = [view frame];
4654   r.origin.y = (v.size.height - r.size.height - r.origin.y);
4656   XSETWINDOW (win, window);
4657   block_input ();
4659   /* we want at least 5 lines to display a scrollbar */
4660   if (WINDOW_TOTAL_LINES (window) < 5)
4661     {
4662       if (!NILP (window->vertical_scroll_bar))
4663         {
4664           bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4665           [bar removeFromSuperview];
4666           wset_vertical_scroll_bar (window, Qnil);
4667           [bar release];
4668         }
4669       ns_clear_frame_area (f, left, top, width, height);
4670       unblock_input ();
4671       return;
4672     }
4674   if (NILP (window->vertical_scroll_bar))
4675     {
4676       if (width > 0 && height > 0)
4677         ns_clear_frame_area (f, left, top, width, height);
4679       bar = [[EmacsScroller alloc] initFrame: r window: win];
4680       wset_vertical_scroll_bar (window, make_save_ptr (bar));
4681       update_p = YES;
4682     }
4683   else
4684     {
4685       NSRect oldRect;
4686       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4687       oldRect = [bar frame];
4688       r.size.width = oldRect.size.width;
4689       if (FRAME_LIVE_P (f) && !NSEqualRects (oldRect, r))
4690         {
4691           if (oldRect.origin.x != r.origin.x)
4692               ns_clear_frame_area (f, left, top, width, height);
4693           [bar setFrame: r];
4694         }
4695     }
4697   if (update_p)
4698     [bar setPosition: position portion: portion whole: whole];
4699   unblock_input ();
4703 static void
4704 ns_set_horizontal_scroll_bar (struct window *window,
4705                               int portion, int whole, int position)
4706 /* --------------------------------------------------------------------------
4707       External (hook): Update or add scrollbar
4708    -------------------------------------------------------------------------- */
4710   Lisp_Object win;
4711   NSRect r, v;
4712   struct frame *f = XFRAME (WINDOW_FRAME (window));
4713   EmacsView *view = FRAME_NS_VIEW (f);
4714   EmacsScroller *bar;
4715   int top, height, left, width;
4716   int window_x, window_width;
4717   BOOL update_p = YES;
4719   /* optimization; display engine sends WAY too many of these.. */
4720   if (!NILP (window->horizontal_scroll_bar))
4721     {
4722       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4723       if ([bar checkSamePosition: position portion: portion whole: whole])
4724         {
4725           if (view->scrollbarsNeedingUpdate == 0)
4726             {
4727               if (!windows_or_buffers_changed)
4728                   return;
4729             }
4730           else
4731             view->scrollbarsNeedingUpdate--;
4732           update_p = NO;
4733         }
4734     }
4736   NSTRACE ("ns_set_horizontal_scroll_bar");
4738   /* Get dimensions.  */
4739   window_box (window, ANY_AREA, &window_x, 0, &window_width, 0);
4740   left = window_x;
4741   width = window_width;
4742   height = NS_SCROLL_BAR_HEIGHT (f);
4743   top = WINDOW_SCROLL_BAR_AREA_Y (window);
4745   r = NSMakeRect (left, top, width, height);
4746   /* the parent view is flipped, so we need to flip y value */
4747   v = [view frame];
4748   r.origin.y = (v.size.height - r.size.height - r.origin.y);
4750   XSETWINDOW (win, window);
4751   block_input ();
4753   if (NILP (window->horizontal_scroll_bar))
4754     {
4755       if (width > 0 && height > 0)
4756         ns_clear_frame_area (f, left, top, width, height);
4758       bar = [[EmacsScroller alloc] initFrame: r window: win];
4759       wset_horizontal_scroll_bar (window, make_save_ptr (bar));
4760       update_p = YES;
4761     }
4762   else
4763     {
4764       NSRect oldRect;
4765       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4766       oldRect = [bar frame];
4767       if (FRAME_LIVE_P (f) && !NSEqualRects (oldRect, r))
4768         {
4769           if (oldRect.origin.y != r.origin.y)
4770             ns_clear_frame_area (f, left, top, width, height);
4771           [bar setFrame: r];
4772           update_p = YES;
4773         }
4774     }
4776   /* If there are both horizontal and vertical scroll-bars they leave
4777      a square that belongs to neither. We need to clear it otherwise
4778      it fills with junk. */
4779   if (!NILP (window->vertical_scroll_bar))
4780     ns_clear_frame_area (f, WINDOW_SCROLL_BAR_AREA_X (window), top,
4781                          NS_SCROLL_BAR_HEIGHT (f), height);
4783   if (update_p)
4784     [bar setPosition: position portion: portion whole: whole];
4785   unblock_input ();
4789 static void
4790 ns_condemn_scroll_bars (struct frame *f)
4791 /* --------------------------------------------------------------------------
4792      External (hook): arrange for all frame's scrollbars to be removed
4793      at next call to judge_scroll_bars, except for those redeemed.
4794    -------------------------------------------------------------------------- */
4796   int i;
4797   id view;
4798   NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews];
4800   NSTRACE ("ns_condemn_scroll_bars");
4802   for (i =[subviews count]-1; i >= 0; i--)
4803     {
4804       view = [subviews objectAtIndex: i];
4805       if ([view isKindOfClass: [EmacsScroller class]])
4806         [view condemn];
4807     }
4811 static void
4812 ns_redeem_scroll_bar (struct window *window)
4813 /* --------------------------------------------------------------------------
4814      External (hook): arrange to spare this window's scrollbar
4815      at next call to judge_scroll_bars.
4816    -------------------------------------------------------------------------- */
4818   id bar;
4819   NSTRACE ("ns_redeem_scroll_bar");
4820   if (!NILP (window->vertical_scroll_bar)
4821       && WINDOW_HAS_VERTICAL_SCROLL_BAR (window))
4822     {
4823       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4824       [bar reprieve];
4825     }
4827   if (!NILP (window->horizontal_scroll_bar)
4828       && WINDOW_HAS_HORIZONTAL_SCROLL_BAR (window))
4829     {
4830       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4831       [bar reprieve];
4832     }
4836 static void
4837 ns_judge_scroll_bars (struct frame *f)
4838 /* --------------------------------------------------------------------------
4839      External (hook): destroy all scrollbars on frame that weren't
4840      redeemed after call to condemn_scroll_bars.
4841    -------------------------------------------------------------------------- */
4843   int i;
4844   id view;
4845   EmacsView *eview = FRAME_NS_VIEW (f);
4846   NSArray *subviews = [[eview superview] subviews];
4847   BOOL removed = NO;
4849   NSTRACE ("ns_judge_scroll_bars");
4850   for (i = [subviews count]-1; i >= 0; --i)
4851     {
4852       view = [subviews objectAtIndex: i];
4853       if (![view isKindOfClass: [EmacsScroller class]]) continue;
4854       if ([view judge])
4855         removed = YES;
4856     }
4858   if (removed)
4859     [eview updateFrameSize: NO];
4862 /* ==========================================================================
4864     Initialization
4866    ========================================================================== */
4869 x_display_pixel_height (struct ns_display_info *dpyinfo)
4871   NSArray *screens = [NSScreen screens];
4872   NSEnumerator *enumerator = [screens objectEnumerator];
4873   NSScreen *screen;
4874   NSRect frame;
4876   frame = NSZeroRect;
4877   while ((screen = [enumerator nextObject]) != nil)
4878     frame = NSUnionRect (frame, [screen frame]);
4880   return NSHeight (frame);
4884 x_display_pixel_width (struct ns_display_info *dpyinfo)
4886   NSArray *screens = [NSScreen screens];
4887   NSEnumerator *enumerator = [screens objectEnumerator];
4888   NSScreen *screen;
4889   NSRect frame;
4891   frame = NSZeroRect;
4892   while ((screen = [enumerator nextObject]) != nil)
4893     frame = NSUnionRect (frame, [screen frame]);
4895   return NSWidth (frame);
4899 static Lisp_Object ns_string_to_lispmod (const char *s)
4900 /* --------------------------------------------------------------------------
4901      Convert modifier name to lisp symbol
4902    -------------------------------------------------------------------------- */
4904   if (!strncmp (SSDATA (SYMBOL_NAME (Qmeta)), s, 10))
4905     return Qmeta;
4906   else if (!strncmp (SSDATA (SYMBOL_NAME (Qsuper)), s, 10))
4907     return Qsuper;
4908   else if (!strncmp (SSDATA (SYMBOL_NAME (Qcontrol)), s, 10))
4909     return Qcontrol;
4910   else if (!strncmp (SSDATA (SYMBOL_NAME (Qalt)), s, 10))
4911     return Qalt;
4912   else if (!strncmp (SSDATA (SYMBOL_NAME (Qhyper)), s, 10))
4913     return Qhyper;
4914   else if (!strncmp (SSDATA (SYMBOL_NAME (Qnone)), s, 10))
4915     return Qnone;
4916   else
4917     return Qnil;
4921 static void
4922 ns_default (const char *parameter, Lisp_Object *result,
4923            Lisp_Object yesval, Lisp_Object noval,
4924            BOOL is_float, BOOL is_modstring)
4925 /* --------------------------------------------------------------------------
4926       Check a parameter value in user's preferences
4927    -------------------------------------------------------------------------- */
4929   const char *value = ns_get_defaults_value (parameter);
4931   if (value)
4932     {
4933       double f;
4934       char *pos;
4935       if (c_strcasecmp (value, "YES") == 0)
4936         *result = yesval;
4937       else if (c_strcasecmp (value, "NO") == 0)
4938         *result = noval;
4939       else if (is_float && (f = strtod (value, &pos), pos != value))
4940         *result = make_float (f);
4941       else if (is_modstring && value)
4942         *result = ns_string_to_lispmod (value);
4943       else fprintf (stderr,
4944                    "Bad value for default \"%s\": \"%s\"\n", parameter, value);
4945     }
4949 static void
4950 ns_initialize_display_info (struct ns_display_info *dpyinfo)
4951 /* --------------------------------------------------------------------------
4952       Initialize global info and storage for display.
4953    -------------------------------------------------------------------------- */
4955     NSScreen *screen = [NSScreen mainScreen];
4956     NSWindowDepth depth = [screen depth];
4958     dpyinfo->resx = 72.27; /* used 75.0, but this makes pt == pixel, expected */
4959     dpyinfo->resy = 72.27;
4960     dpyinfo->color_p = ![NSDeviceWhiteColorSpace isEqualToString:
4961                                                   NSColorSpaceFromDepth (depth)]
4962                 && ![NSCalibratedWhiteColorSpace isEqualToString:
4963                                                  NSColorSpaceFromDepth (depth)];
4964     dpyinfo->n_planes = NSBitsPerPixelFromDepth (depth);
4965     dpyinfo->color_table = xmalloc (sizeof *dpyinfo->color_table);
4966     dpyinfo->color_table->colors = NULL;
4967     dpyinfo->root_window = 42; /* a placeholder.. */
4968     dpyinfo->x_highlight_frame = dpyinfo->x_focus_frame = NULL;
4969     dpyinfo->n_fonts = 0;
4970     dpyinfo->smallest_font_height = 1;
4971     dpyinfo->smallest_char_width = 1;
4973     reset_mouse_highlight (&dpyinfo->mouse_highlight);
4977 /* This and next define (many of the) public functions in this file. */
4978 /* x_... are generic versions in xdisp.c that we, and other terms, get away
4979          with using despite presence in the "system dependent" redisplay
4980          interface.  In addition, many of the ns_ methods have code that is
4981          shared with all terms, indicating need for further refactoring. */
4982 extern frame_parm_handler ns_frame_parm_handlers[];
4983 static struct redisplay_interface ns_redisplay_interface =
4985   ns_frame_parm_handlers,
4986   x_produce_glyphs,
4987   x_write_glyphs,
4988   x_insert_glyphs,
4989   x_clear_end_of_line,
4990   ns_scroll_run,
4991   ns_after_update_window_line,
4992   ns_update_window_begin,
4993   ns_update_window_end,
4994   0, /* flush_display */
4995   x_clear_window_mouse_face,
4996   x_get_glyph_overhangs,
4997   x_fix_overlapping_area,
4998   ns_draw_fringe_bitmap,
4999   0, /* define_fringe_bitmap */ /* FIXME: simplify ns_draw_fringe_bitmap */
5000   0, /* destroy_fringe_bitmap */
5001   ns_compute_glyph_string_overhangs,
5002   ns_draw_glyph_string,
5003   ns_define_frame_cursor,
5004   ns_clear_frame_area,
5005   ns_draw_window_cursor,
5006   ns_draw_vertical_window_border,
5007   ns_draw_window_divider,
5008   ns_shift_glyphs_for_insert,
5009   ns_show_hourglass,
5010   ns_hide_hourglass
5014 static void
5015 ns_delete_display (struct ns_display_info *dpyinfo)
5017   /* TODO... */
5021 /* This function is called when the last frame on a display is deleted. */
5022 static void
5023 ns_delete_terminal (struct terminal *terminal)
5025   struct ns_display_info *dpyinfo = terminal->display_info.ns;
5027   NSTRACE ("ns_delete_terminal");
5029   /* Protect against recursive calls.  delete_frame in
5030      delete_terminal calls us back when it deletes our last frame.  */
5031   if (!terminal->name)
5032     return;
5034   block_input ();
5036   x_destroy_all_bitmaps (dpyinfo);
5037   ns_delete_display (dpyinfo);
5038   unblock_input ();
5042 static struct terminal *
5043 ns_create_terminal (struct ns_display_info *dpyinfo)
5044 /* --------------------------------------------------------------------------
5045       Set up use of NS before we make the first connection.
5046    -------------------------------------------------------------------------- */
5048   struct terminal *terminal;
5050   NSTRACE ("ns_create_terminal");
5052   terminal = create_terminal (output_ns, &ns_redisplay_interface);
5054   terminal->display_info.ns = dpyinfo;
5055   dpyinfo->terminal = terminal;
5057   terminal->clear_frame_hook = ns_clear_frame;
5058   terminal->ring_bell_hook = ns_ring_bell;
5059   terminal->update_begin_hook = ns_update_begin;
5060   terminal->update_end_hook = ns_update_end;
5061   terminal->read_socket_hook = ns_read_socket;
5062   terminal->frame_up_to_date_hook = ns_frame_up_to_date;
5063   terminal->mouse_position_hook = ns_mouse_position;
5064   terminal->frame_rehighlight_hook = ns_frame_rehighlight;
5065   terminal->frame_raise_lower_hook = ns_frame_raise_lower;
5066   terminal->fullscreen_hook = ns_fullscreen_hook;
5067   terminal->menu_show_hook = ns_menu_show;
5068   terminal->popup_dialog_hook = ns_popup_dialog;
5069   terminal->set_vertical_scroll_bar_hook = ns_set_vertical_scroll_bar;
5070   terminal->set_horizontal_scroll_bar_hook = ns_set_horizontal_scroll_bar;
5071   terminal->condemn_scroll_bars_hook = ns_condemn_scroll_bars;
5072   terminal->redeem_scroll_bar_hook = ns_redeem_scroll_bar;
5073   terminal->judge_scroll_bars_hook = ns_judge_scroll_bars;
5074   terminal->delete_frame_hook = x_destroy_window;
5075   terminal->delete_terminal_hook = ns_delete_terminal;
5076   /* Other hooks are NULL by default.  */
5078   return terminal;
5082 struct ns_display_info *
5083 ns_term_init (Lisp_Object display_name)
5084 /* --------------------------------------------------------------------------
5085      Start the Application and get things rolling.
5086    -------------------------------------------------------------------------- */
5088   struct terminal *terminal;
5089   struct ns_display_info *dpyinfo;
5090   static int ns_initialized = 0;
5091   Lisp_Object tmp;
5093   if (ns_initialized) return x_display_list;
5094   ns_initialized = 1;
5096   block_input ();
5098   NSTRACE ("ns_term_init");
5100   [outerpool release];
5101   outerpool = [[NSAutoreleasePool alloc] init];
5103   /* count object allocs (About, click icon); on macOS use ObjectAlloc tool */
5104   /*GSDebugAllocationActive (YES); */
5105   block_input ();
5107   baud_rate = 38400;
5108   Fset_input_interrupt_mode (Qnil);
5110   if (selfds[0] == -1)
5111     {
5112       if (emacs_pipe (selfds) != 0)
5113         {
5114           fprintf (stderr, "Failed to create pipe: %s\n",
5115                    emacs_strerror (errno));
5116           emacs_abort ();
5117         }
5119       fcntl (selfds[0], F_SETFL, O_NONBLOCK|fcntl (selfds[0], F_GETFL));
5120       FD_ZERO (&select_readfds);
5121       FD_ZERO (&select_writefds);
5122       pthread_mutex_init (&select_mutex, NULL);
5123     }
5125   ns_pending_files = [[NSMutableArray alloc] init];
5126   ns_pending_service_names = [[NSMutableArray alloc] init];
5127   ns_pending_service_args = [[NSMutableArray alloc] init];
5129 /* Start app and create the main menu, window, view.
5130      Needs to be here because ns_initialize_display_info () uses AppKit classes.
5131      The view will then ask the NSApp to stop and return to Emacs. */
5132   [EmacsApp sharedApplication];
5133   if (NSApp == nil)
5134     return NULL;
5135   [NSApp setDelegate: NSApp];
5137   /* Start the select thread.  */
5138   [NSThread detachNewThreadSelector:@selector (fd_handler:)
5139                            toTarget:NSApp
5140                          withObject:nil];
5142   /* debugging: log all notifications */
5143   /*   [[NSNotificationCenter defaultCenter] addObserver: NSApp
5144                                          selector: @selector (logNotification:)
5145                                              name: nil object: nil]; */
5147   dpyinfo = xzalloc (sizeof *dpyinfo);
5149   ns_initialize_display_info (dpyinfo);
5150   terminal = ns_create_terminal (dpyinfo);
5152   terminal->kboard = allocate_kboard (Qns);
5153   /* Don't let the initial kboard remain current longer than necessary.
5154      That would cause problems if a file loaded on startup tries to
5155      prompt in the mini-buffer.  */
5156   if (current_kboard == initial_kboard)
5157     current_kboard = terminal->kboard;
5158   terminal->kboard->reference_count++;
5160   dpyinfo->next = x_display_list;
5161   x_display_list = dpyinfo;
5163   dpyinfo->name_list_element = Fcons (display_name, Qnil);
5165   terminal->name = xlispstrdup (display_name);
5167   unblock_input ();
5169   if (!inhibit_x_resources)
5170     {
5171       ns_default ("GSFontAntiAlias", &ns_antialias_text,
5172                  Qt, Qnil, NO, NO);
5173       tmp = Qnil;
5174       /* this is a standard variable */
5175       ns_default ("AppleAntiAliasingThreshold", &tmp,
5176                  make_float (10.0), make_float (6.0), YES, NO);
5177       ns_antialias_threshold = NILP (tmp) ? 10.0 : extract_float (tmp);
5178     }
5180   NSTRACE_MSG ("Colors");
5182   {
5183     NSColorList *cl = [NSColorList colorListNamed: @"Emacs"];
5185     if ( cl == nil )
5186       {
5187         Lisp_Object color_file, color_map, color;
5188         unsigned long c;
5189         char *name;
5191         color_file = Fexpand_file_name (build_string ("rgb.txt"),
5192                          Fsymbol_value (intern ("data-directory")));
5194         color_map = Fx_load_color_file (color_file);
5195         if (NILP (color_map))
5196           fatal ("Could not read %s.\n", SDATA (color_file));
5198         cl = [[NSColorList alloc] initWithName: @"Emacs"];
5199         for ( ; CONSP (color_map); color_map = XCDR (color_map))
5200           {
5201             color = XCAR (color_map);
5202             name = SSDATA (XCAR (color));
5203             c = XINT (XCDR (color));
5204             [cl setColor:
5205                   [NSColor colorForEmacsRed: RED_FROM_ULONG (c) / 255.0
5206                                       green: GREEN_FROM_ULONG (c) / 255.0
5207                                        blue: BLUE_FROM_ULONG (c) / 255.0
5208                                       alpha: 1.0]
5209                   forKey: [NSString stringWithUTF8String: name]];
5210           }
5211         [cl writeToFile: nil];
5212       }
5213   }
5215   NSTRACE_MSG ("Versions");
5217   {
5218 #ifdef NS_IMPL_GNUSTEP
5219     Vwindow_system_version = build_string (gnustep_base_version);
5220 #else
5221     /*PSnextrelease (128, c); */
5222     char c[DBL_BUFSIZE_BOUND];
5223     int len = dtoastr (c, sizeof c, 0, 0, NSAppKitVersionNumber);
5224     Vwindow_system_version = make_unibyte_string (c, len);
5225 #endif
5226   }
5228   delete_keyboard_wait_descriptor (0);
5230   ns_app_name = [[NSProcessInfo processInfo] processName];
5232   /* Set up macOS app menu */
5234   NSTRACE_MSG ("Menu init");
5236 #ifdef NS_IMPL_COCOA
5237   {
5238     NSMenu *appMenu;
5239     NSMenuItem *item;
5240     /* set up the application menu */
5241     svcsMenu = [[EmacsMenu alloc] initWithTitle: @"Services"];
5242     [svcsMenu setAutoenablesItems: NO];
5243     appMenu = [[EmacsMenu alloc] initWithTitle: @"Emacs"];
5244     [appMenu setAutoenablesItems: NO];
5245     mainMenu = [[EmacsMenu alloc] initWithTitle: @""];
5246     dockMenu = [[EmacsMenu alloc] initWithTitle: @""];
5248     [appMenu insertItemWithTitle: @"About Emacs"
5249                           action: @selector (orderFrontStandardAboutPanel:)
5250                    keyEquivalent: @""
5251                          atIndex: 0];
5252     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 1];
5253     [appMenu insertItemWithTitle: @"Preferences..."
5254                           action: @selector (showPreferencesWindow:)
5255                    keyEquivalent: @","
5256                          atIndex: 2];
5257     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 3];
5258     item = [appMenu insertItemWithTitle: @"Services"
5259                                  action: @selector (menuDown:)
5260                           keyEquivalent: @""
5261                                 atIndex: 4];
5262     [appMenu setSubmenu: svcsMenu forItem: item];
5263     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 5];
5264     [appMenu insertItemWithTitle: @"Hide Emacs"
5265                           action: @selector (hide:)
5266                    keyEquivalent: @"h"
5267                          atIndex: 6];
5268     item =  [appMenu insertItemWithTitle: @"Hide Others"
5269                           action: @selector (hideOtherApplications:)
5270                    keyEquivalent: @"h"
5271                          atIndex: 7];
5272     [item setKeyEquivalentModifierMask: NSEventModifierFlagCommand | NSEventModifierFlagOption];
5273     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 8];
5274     [appMenu insertItemWithTitle: @"Quit Emacs"
5275                           action: @selector (terminate:)
5276                    keyEquivalent: @"q"
5277                          atIndex: 9];
5279     item = [mainMenu insertItemWithTitle: ns_app_name
5280                                   action: @selector (menuDown:)
5281                            keyEquivalent: @""
5282                                  atIndex: 0];
5283     [mainMenu setSubmenu: appMenu forItem: item];
5284     [dockMenu insertItemWithTitle: @"New Frame"
5285                            action: @selector (newFrame:)
5286                     keyEquivalent: @""
5287                           atIndex: 0];
5289     [NSApp setMainMenu: mainMenu];
5290     [NSApp setAppleMenu: appMenu];
5291     [NSApp setServicesMenu: svcsMenu];
5292     /* Needed at least on Cocoa, to get dock menu to show windows */
5293     [NSApp setWindowsMenu: [[NSMenu alloc] init]];
5295     [[NSNotificationCenter defaultCenter]
5296       addObserver: mainMenu
5297          selector: @selector (trackingNotification:)
5298              name: NSMenuDidBeginTrackingNotification object: mainMenu];
5299     [[NSNotificationCenter defaultCenter]
5300       addObserver: mainMenu
5301          selector: @selector (trackingNotification:)
5302              name: NSMenuDidEndTrackingNotification object: mainMenu];
5303   }
5304 #endif /* macOS menu setup */
5306   /* Register our external input/output types, used for determining
5307      applicable services and also drag/drop eligibility. */
5309   NSTRACE_MSG ("Input/output types");
5311   ns_send_types = [[NSArray arrayWithObjects: NSStringPboardType, nil] retain];
5312   ns_return_types = [[NSArray arrayWithObjects: NSStringPboardType, nil]
5313                       retain];
5314   ns_drag_types = [[NSArray arrayWithObjects:
5315                             NSStringPboardType,
5316                             NSTabularTextPboardType,
5317                             NSFilenamesPboardType,
5318                             NSURLPboardType, nil] retain];
5320   /* If fullscreen is in init/default-frame-alist, focus isn't set
5321      right for fullscreen windows, so set this.  */
5322   [NSApp activateIgnoringOtherApps:YES];
5324   NSTRACE_MSG ("Call NSApp run");
5326   [NSApp run];
5327   ns_do_open_file = YES;
5329 #ifdef NS_IMPL_GNUSTEP
5330   /* GNUstep steals SIGCHLD for use in NSTask, but we don't use NSTask.
5331      We must re-catch it so subprocess works.  */
5332   catch_child_signal ();
5333 #endif
5335   NSTRACE_MSG ("ns_term_init done");
5337   unblock_input ();
5339   return dpyinfo;
5343 void
5344 ns_term_shutdown (int sig)
5346   [[NSUserDefaults standardUserDefaults] synchronize];
5348   /* code not reached in emacs.c after this is called by shut_down_emacs: */
5349   if (STRINGP (Vauto_save_list_file_name))
5350     unlink (SSDATA (Vauto_save_list_file_name));
5352   if (sig == 0 || sig == SIGTERM)
5353     {
5354       [NSApp terminate: NSApp];
5355     }
5356   else // force a stack trace to happen
5357     {
5358       emacs_abort ();
5359     }
5363 /* ==========================================================================
5365     EmacsApp implementation
5367    ========================================================================== */
5370 @implementation EmacsApp
5372 - (id)init
5374   NSTRACE ("[EmacsApp init]");
5376   if ((self = [super init]))
5377     {
5378 #ifdef NS_IMPL_COCOA
5379       self->isFirst = YES;
5380 #endif
5381 #ifdef NS_IMPL_GNUSTEP
5382       self->applicationDidFinishLaunchingCalled = NO;
5383 #endif
5384     }
5386   return self;
5389 #ifdef NS_IMPL_COCOA
5390 - (void)run
5392   NSTRACE ("[EmacsApp run]");
5394 #ifndef NSAppKitVersionNumber10_9
5395 #define NSAppKitVersionNumber10_9 1265
5396 #endif
5398     if ((int)NSAppKitVersionNumber != NSAppKitVersionNumber10_9)
5399       {
5400         [super run];
5401         return;
5402       }
5404   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5406   if (isFirst) [self finishLaunching];
5407   isFirst = NO;
5409   shouldKeepRunning = YES;
5410   do
5411     {
5412       [pool release];
5413       pool = [[NSAutoreleasePool alloc] init];
5415       NSEvent *event =
5416         [self nextEventMatchingMask:NSEventMaskAny
5417                           untilDate:[NSDate distantFuture]
5418                              inMode:NSDefaultRunLoopMode
5419                             dequeue:YES];
5421       [self sendEvent:event];
5422       [self updateWindows];
5423     } while (shouldKeepRunning);
5425   [pool release];
5428 - (void)stop: (id)sender
5430   NSTRACE ("[EmacsApp stop:]");
5432     shouldKeepRunning = NO;
5433     // Stop possible dialog also.  Noop if no dialog present.
5434     // The file dialog still leaks 7k - 10k on 10.9 though.
5435     [super stop:sender];
5437 #endif /* NS_IMPL_COCOA */
5439 - (void)logNotification: (NSNotification *)notification
5441   NSTRACE ("[EmacsApp logNotification:]");
5443   const char *name = [[notification name] UTF8String];
5444   if (!strstr (name, "Update") && !strstr (name, "NSMenu")
5445       && !strstr (name, "WindowNumber"))
5446     NSLog (@"notification: '%@'", [notification name]);
5450 - (void)sendEvent: (NSEvent *)theEvent
5451 /* --------------------------------------------------------------------------
5452      Called when NSApp is running for each event received.  Used to stop
5453      the loop when we choose, since there's no way to just run one iteration.
5454    -------------------------------------------------------------------------- */
5456   int type = [theEvent type];
5457   NSWindow *window = [theEvent window];
5459   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsApp sendEvent:]");
5460   NSTRACE_MSG ("Type: %d", type);
5462 #ifdef NS_IMPL_GNUSTEP
5463   // Keyboard events aren't propagated to file dialogs for some reason.
5464   if ([NSApp modalWindow] != nil &&
5465       (type == NSEventTypeKeyDown || type == NSEventTypeKeyUp || type == NSEventTypeFlagsChanged))
5466     {
5467       [[NSApp modalWindow] sendEvent: theEvent];
5468       return;
5469     }
5470 #endif
5472   if (represented_filename != nil && represented_frame)
5473     {
5474       NSString *fstr = represented_filename;
5475       NSView *view = FRAME_NS_VIEW (represented_frame);
5476 #ifdef NS_IMPL_COCOA
5477       /* work around a bug observed on 10.3 and later where
5478          setTitleWithRepresentedFilename does not clear out previous state
5479          if given filename does not exist */
5480       if (! [[NSFileManager defaultManager] fileExistsAtPath: fstr])
5481         [[view window] setRepresentedFilename: @""];
5482 #endif
5483       [[view window] setRepresentedFilename: fstr];
5484       [represented_filename release];
5485       represented_filename = nil;
5486       represented_frame = NULL;
5487     }
5489   if (type == NSEventTypeApplicationDefined)
5490     {
5491       switch ([theEvent data2])
5492         {
5493 #ifdef NS_IMPL_COCOA
5494         case NSAPP_DATA2_RUNASSCRIPT:
5495           ns_run_ascript ();
5496           [self stop: self];
5497           return;
5498 #endif
5499         case NSAPP_DATA2_RUNFILEDIALOG:
5500           ns_run_file_dialog ();
5501           [self stop: self];
5502           return;
5503         }
5504     }
5506   if (type == NSEventTypeCursorUpdate && window == nil)
5507     {
5508       fprintf (stderr, "Dropping external cursor update event.\n");
5509       return;
5510     }
5512   if (type == NSEventTypeApplicationDefined)
5513     {
5514       /* Events posted by ns_send_appdefined interrupt the run loop here.
5515          But, if a modal window is up, an appdefined can still come through,
5516          (e.g., from a makeKeyWindow event) but stopping self also stops the
5517          modal loop. Just defer it until later. */
5518       if ([NSApp modalWindow] == nil)
5519         {
5520           last_appdefined_event_data = [theEvent data1];
5521           [self stop: self];
5522         }
5523       else
5524         {
5525           send_appdefined = YES;
5526         }
5527     }
5530 #ifdef NS_IMPL_COCOA
5531   /* If no dialog and none of our frames have focus and it is a move, skip it.
5532      It is a mouse move in an auxiliary menu, i.e. on the top right on macOS,
5533      such as Wifi, sound, date or similar.
5534      This prevents "spooky" highlighting in the frame under the menu.  */
5535   if (type == NSEventTypeMouseMoved && [NSApp modalWindow] == nil)
5536     {
5537       struct ns_display_info *di;
5538       BOOL has_focus = NO;
5539       for (di = x_display_list; ! has_focus && di; di = di->next)
5540         has_focus = di->x_focus_frame != 0;
5541       if (! has_focus)
5542         return;
5543     }
5544 #endif
5546   NSTRACE_UNSILENCE();
5548   [super sendEvent: theEvent];
5552 - (void)showPreferencesWindow: (id)sender
5554   struct frame *emacsframe = SELECTED_FRAME ();
5555   NSEvent *theEvent = [NSApp currentEvent];
5557   if (!emacs_event)
5558     return;
5559   emacs_event->kind = NS_NONKEY_EVENT;
5560   emacs_event->code = KEY_NS_SHOW_PREFS;
5561   emacs_event->modifiers = 0;
5562   EV_TRAILER (theEvent);
5566 - (void)newFrame: (id)sender
5568   NSTRACE ("[EmacsApp newFrame:]");
5570   struct frame *emacsframe = SELECTED_FRAME ();
5571   NSEvent *theEvent = [NSApp currentEvent];
5573   if (!emacs_event)
5574     return;
5575   emacs_event->kind = NS_NONKEY_EVENT;
5576   emacs_event->code = KEY_NS_NEW_FRAME;
5577   emacs_event->modifiers = 0;
5578   EV_TRAILER (theEvent);
5582 /* Open a file (used by below, after going into queue read by ns_read_socket) */
5583 - (BOOL) openFile: (NSString *)fileName
5585   NSTRACE ("[EmacsApp openFile:]");
5587   struct frame *emacsframe = SELECTED_FRAME ();
5588   NSEvent *theEvent = [NSApp currentEvent];
5590   if (!emacs_event)
5591     return NO;
5593   emacs_event->kind = NS_NONKEY_EVENT;
5594   emacs_event->code = KEY_NS_OPEN_FILE_LINE;
5595   ns_input_file = append2 (ns_input_file, build_string ([fileName UTF8String]));
5596   ns_input_line = Qnil; /* can be start or cons start,end */
5597   emacs_event->modifiers =0;
5598   EV_TRAILER (theEvent);
5600   return YES;
5604 /* **************************************************************************
5606       EmacsApp delegate implementation
5608    ************************************************************************** */
5610 - (void)applicationDidFinishLaunching: (NSNotification *)notification
5611 /* --------------------------------------------------------------------------
5612      When application is loaded, terminate event loop in ns_term_init
5613    -------------------------------------------------------------------------- */
5615   NSTRACE ("[EmacsApp applicationDidFinishLaunching:]");
5617 #ifdef NS_IMPL_GNUSTEP
5618   ((EmacsApp *)self)->applicationDidFinishLaunchingCalled = YES;
5619 #endif
5620   [NSApp setServicesProvider: NSApp];
5622   [self antialiasThresholdDidChange:nil];
5623 #ifdef NS_IMPL_COCOA
5624   [[NSNotificationCenter defaultCenter]
5625     addObserver:self
5626        selector:@selector(antialiasThresholdDidChange:)
5627            name:NSAntialiasThresholdChangedNotification
5628          object:nil];
5629 #endif
5631 #ifdef NS_IMPL_COCOA
5632   if ([NSApp activationPolicy] == NSApplicationActivationPolicyProhibited) {
5633     /* Set the app's activation policy to regular when we run outside
5634        of a bundle.  This is already done for us by Info.plist when we
5635        run inside a bundle. */
5636     [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
5637     [NSApp setApplicationIconImage:
5638              [EmacsImage
5639                allocInitFromFile:
5640                  build_string("icons/hicolor/128x128/apps/emacs.png")]];
5641   }
5642 #endif
5644   ns_send_appdefined (-2);
5647 - (void)antialiasThresholdDidChange:(NSNotification *)notification
5649 #ifdef NS_IMPL_COCOA
5650   macfont_update_antialias_threshold ();
5651 #endif
5655 /* Termination sequences:
5656     C-x C-c:
5657     Cmd-Q:
5658     MenuBar | File | Exit:
5659     Select Quit from App menubar:
5660         -terminate
5661         KEY_NS_POWER_OFF, (save-buffers-kill-emacs)
5662         ns_term_shutdown()
5664     Select Quit from Dock menu:
5665     Logout attempt:
5666         -appShouldTerminate
5667           Cancel -> Nothing else
5668           Accept ->
5670           -terminate
5671           KEY_NS_POWER_OFF, (save-buffers-kill-emacs)
5672           ns_term_shutdown()
5676 - (void) terminate: (id)sender
5678   NSTRACE ("[EmacsApp terminate:]");
5680   struct frame *emacsframe = SELECTED_FRAME ();
5682   if (!emacs_event)
5683     return;
5685   emacs_event->kind = NS_NONKEY_EVENT;
5686   emacs_event->code = KEY_NS_POWER_OFF;
5687   emacs_event->arg = Qt; /* mark as non-key event */
5688   EV_TRAILER ((id)nil);
5691 static bool
5692 runAlertPanel(NSString *title,
5693               NSString *msgFormat,
5694               NSString *defaultButton,
5695               NSString *alternateButton)
5697 #ifdef NS_IMPL_GNUSTEP
5698   return NSRunAlertPanel(title, msgFormat, defaultButton, alternateButton, nil)
5699     == NSAlertDefaultReturn;
5700 #else
5701   NSAlert *alert = [[NSAlert alloc] init];
5702   [alert setAlertStyle: NSAlertStyleCritical];
5703   [alert setMessageText: msgFormat];
5704   [alert addButtonWithTitle: defaultButton];
5705   [alert addButtonWithTitle: alternateButton];
5706   NSInteger ret = [alert runModal];
5707   [alert release];
5708   return ret == NSAlertFirstButtonReturn;
5709 #endif
5713 - (NSApplicationTerminateReply)applicationShouldTerminate: (id)sender
5715   NSTRACE ("[EmacsApp applicationShouldTerminate:]");
5717   bool ret;
5719   if (NILP (ns_confirm_quit)) //   || ns_shutdown_properly  --> TO DO
5720     return NSTerminateNow;
5722   ret = runAlertPanel(ns_app_name,
5723                       @"Exit requested.  Would you like to Save Buffers and Exit, or Cancel the request?",
5724                       @"Save Buffers and Exit", @"Cancel");
5726   return ret ? NSTerminateNow : NSTerminateCancel;
5729 static int
5730 not_in_argv (NSString *arg)
5732   int k;
5733   const char *a = [arg UTF8String];
5734   for (k = 1; k < initial_argc; ++k)
5735     if (strcmp (a, initial_argv[k]) == 0) return 0;
5736   return 1;
5739 /*   Notification from the Workspace to open a file */
5740 - (BOOL)application: sender openFile: (NSString *)file
5742   if (ns_do_open_file || not_in_argv (file))
5743     [ns_pending_files addObject: file];
5744   return YES;
5748 /*   Open a file as a temporary file */
5749 - (BOOL)application: sender openTempFile: (NSString *)file
5751   if (ns_do_open_file || not_in_argv (file))
5752     [ns_pending_files addObject: file];
5753   return YES;
5757 /*   Notification from the Workspace to open a file noninteractively (?) */
5758 - (BOOL)application: sender openFileWithoutUI: (NSString *)file
5760   if (ns_do_open_file || not_in_argv (file))
5761     [ns_pending_files addObject: file];
5762   return YES;
5765 /*   Notification from the Workspace to open multiple files */
5766 - (void)application: sender openFiles: (NSArray *)fileList
5768   NSEnumerator *files = [fileList objectEnumerator];
5769   NSString *file;
5770   /* Don't open files from the command line unconditionally,
5771      Cocoa parses the command line wrong, --option value tries to open value
5772      if --option is the last option.  */
5773   while ((file = [files nextObject]) != nil)
5774     if (ns_do_open_file || not_in_argv (file))
5775       [ns_pending_files addObject: file];
5777   [self replyToOpenOrPrint: NSApplicationDelegateReplySuccess];
5782 /* Handle dock menu requests.  */
5783 - (NSMenu *)applicationDockMenu: (NSApplication *) sender
5785   return dockMenu;
5789 /* TODO: these may help w/IO switching btwn terminal and NSApp */
5790 - (void)applicationWillBecomeActive: (NSNotification *)notification
5792   NSTRACE ("[EmacsApp applicationWillBecomeActive:]");
5793   //ns_app_active=YES;
5796 - (void)applicationDidBecomeActive: (NSNotification *)notification
5798   NSTRACE ("[EmacsApp applicationDidBecomeActive:]");
5800 #ifdef NS_IMPL_GNUSTEP
5801   if (! applicationDidFinishLaunchingCalled)
5802     [self applicationDidFinishLaunching:notification];
5803 #endif
5804   //ns_app_active=YES;
5806   ns_update_auto_hide_menu_bar ();
5807   // No constraining takes place when the application is not active.
5808   ns_constrain_all_frames ();
5810 - (void)applicationDidResignActive: (NSNotification *)notification
5812   NSTRACE ("[EmacsApp applicationDidResignActive:]");
5814   //ns_app_active=NO;
5815   ns_send_appdefined (-1);
5820 /* ==========================================================================
5822     EmacsApp aux handlers for managing event loop
5824    ========================================================================== */
5827 - (void)timeout_handler: (NSTimer *)timedEntry
5828 /* --------------------------------------------------------------------------
5829      The timeout specified to ns_select has passed.
5830    -------------------------------------------------------------------------- */
5832   /*NSTRACE ("timeout_handler"); */
5833   ns_send_appdefined (-2);
5836 - (void)sendFromMainThread:(id)unused
5838   ns_send_appdefined (nextappdefined);
5841 - (void)fd_handler:(id)unused
5842 /* --------------------------------------------------------------------------
5843      Check data waiting on file descriptors and terminate if so
5844    -------------------------------------------------------------------------- */
5846   int result;
5847   int waiting = 1, nfds;
5848   char c;
5850   fd_set readfds, writefds, *wfds;
5851   struct timespec timeout, *tmo;
5852   NSAutoreleasePool *pool = nil;
5854   /* NSTRACE ("fd_handler"); */
5856   for (;;)
5857     {
5858       [pool release];
5859       pool = [[NSAutoreleasePool alloc] init];
5861       if (waiting)
5862         {
5863           fd_set fds;
5864           FD_ZERO (&fds);
5865           FD_SET (selfds[0], &fds);
5866           result = select (selfds[0]+1, &fds, NULL, NULL, NULL);
5867           if (result > 0 && read (selfds[0], &c, 1) == 1 && c == 'g')
5868             waiting = 0;
5869         }
5870       else
5871         {
5872           pthread_mutex_lock (&select_mutex);
5873           nfds = select_nfds;
5875           if (select_valid & SELECT_HAVE_READ)
5876             readfds = select_readfds;
5877           else
5878             FD_ZERO (&readfds);
5880           if (select_valid & SELECT_HAVE_WRITE)
5881             {
5882               writefds = select_writefds;
5883               wfds = &writefds;
5884             }
5885           else
5886             wfds = NULL;
5887           if (select_valid & SELECT_HAVE_TMO)
5888             {
5889               timeout = select_timeout;
5890               tmo = &timeout;
5891             }
5892           else
5893             tmo = NULL;
5895           pthread_mutex_unlock (&select_mutex);
5897           FD_SET (selfds[0], &readfds);
5898           if (selfds[0] >= nfds) nfds = selfds[0]+1;
5900           result = pselect (nfds, &readfds, wfds, NULL, tmo, NULL);
5902           if (result == 0)
5903             ns_send_appdefined (-2);
5904           else if (result > 0)
5905             {
5906               if (FD_ISSET (selfds[0], &readfds))
5907                 {
5908                   if (read (selfds[0], &c, 1) == 1 && c == 's')
5909                     waiting = 1;
5910                 }
5911               else
5912                 {
5913                   pthread_mutex_lock (&select_mutex);
5914                   if (select_valid & SELECT_HAVE_READ)
5915                     select_readfds = readfds;
5916                   if (select_valid & SELECT_HAVE_WRITE)
5917                     select_writefds = writefds;
5918                   if (select_valid & SELECT_HAVE_TMO)
5919                     select_timeout = timeout;
5920                   pthread_mutex_unlock (&select_mutex);
5922                   ns_send_appdefined (result);
5923                 }
5924             }
5925           waiting = 1;
5926         }
5927     }
5932 /* ==========================================================================
5934     Service provision
5936    ========================================================================== */
5938 /* called from system: queue for next pass through event loop */
5939 - (void)requestService: (NSPasteboard *)pboard
5940               userData: (NSString *)userData
5941                  error: (NSString **)error
5943   [ns_pending_service_names addObject: userData];
5944   [ns_pending_service_args addObject: [NSString stringWithUTF8String:
5945       SSDATA (ns_string_from_pasteboard (pboard))]];
5949 /* called from ns_read_socket to clear queue */
5950 - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
5952   struct frame *emacsframe = SELECTED_FRAME ();
5953   NSEvent *theEvent = [NSApp currentEvent];
5955   NSTRACE ("[EmacsApp fulfillService:withArg:]");
5957   if (!emacs_event)
5958     return NO;
5960   emacs_event->kind = NS_NONKEY_EVENT;
5961   emacs_event->code = KEY_NS_SPI_SERVICE_CALL;
5962   ns_input_spi_name = build_string ([name UTF8String]);
5963   ns_input_spi_arg = build_string ([arg UTF8String]);
5964   emacs_event->modifiers = EV_MODIFIERS (theEvent);
5965   EV_TRAILER (theEvent);
5967   return YES;
5971 @end  /* EmacsApp */
5975 /* ==========================================================================
5977     EmacsView implementation
5979    ========================================================================== */
5982 @implementation EmacsView
5984 /* needed to inform when window closed from LISP */
5985 - (void) setWindowClosing: (BOOL)closing
5987   NSTRACE ("[EmacsView setWindowClosing:%d]", closing);
5989   windowClosing = closing;
5993 - (void)dealloc
5995   NSTRACE ("[EmacsView dealloc]");
5996   [toolbar release];
5997   if (fs_state == FULLSCREEN_BOTH)
5998     [nonfs_window release];
5999   [super dealloc];
6003 /* called on font panel selection */
6004 - (void)changeFont: (id)sender
6006   NSEvent *e = [[self window] currentEvent];
6007   struct face *face = FACE_FROM_ID (emacsframe, DEFAULT_FACE_ID);
6008   struct font *font = face->font;
6009   id newFont;
6010   CGFloat size;
6011   NSFont *nsfont;
6013   NSTRACE ("[EmacsView changeFont:]");
6015   if (!emacs_event)
6016     return;
6018 #ifdef NS_IMPL_GNUSTEP
6019   nsfont = ((struct nsfont_info *)font)->nsfont;
6020 #endif
6021 #ifdef NS_IMPL_COCOA
6022   nsfont = (NSFont *) macfont_get_nsctfont (font);
6023 #endif
6025   if ((newFont = [sender convertFont: nsfont]))
6026     {
6027       SET_FRAME_GARBAGED (emacsframe); /* now needed as of 2008/10 */
6029       emacs_event->kind = NS_NONKEY_EVENT;
6030       emacs_event->modifiers = 0;
6031       emacs_event->code = KEY_NS_CHANGE_FONT;
6033       size = [newFont pointSize];
6034       ns_input_fontsize = make_number (lrint (size));
6035       ns_input_font = build_string ([[newFont familyName] UTF8String]);
6036       EV_TRAILER (e);
6037     }
6041 - (BOOL)acceptsFirstResponder
6043   NSTRACE ("[EmacsView acceptsFirstResponder]");
6044   return YES;
6048 - (void)resetCursorRects
6050   NSRect visible = [self visibleRect];
6051   NSCursor *currentCursor = FRAME_POINTER_TYPE (emacsframe);
6052   NSTRACE ("[EmacsView resetCursorRects]");
6054   if (currentCursor == nil)
6055     currentCursor = [NSCursor arrowCursor];
6057   if (!NSIsEmptyRect (visible))
6058     [self addCursorRect: visible cursor: currentCursor];
6059   [currentCursor setOnMouseEntered: YES];
6064 /*****************************************************************************/
6065 /* Keyboard handling. */
6066 #define NS_KEYLOG 0
6068 - (void)keyDown: (NSEvent *)theEvent
6070   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
6071   int code;
6072   unsigned fnKeysym = 0;
6073   static NSMutableArray *nsEvArray;
6074   int left_is_none;
6075   unsigned int flags = [theEvent modifierFlags];
6077   NSTRACE ("[EmacsView keyDown:]");
6079   /* Rhapsody and macOS give up and down events for the arrow keys */
6080   if (ns_fake_keydown == YES)
6081     ns_fake_keydown = NO;
6082   else if ([theEvent type] != NSEventTypeKeyDown)
6083     return;
6085   if (!emacs_event)
6086     return;
6088  if (![[self window] isKeyWindow]
6089      && [[theEvent window] isKindOfClass: [EmacsWindow class]]
6090      /* we must avoid an infinite loop here. */
6091      && (EmacsView *)[[theEvent window] delegate] != self)
6092    {
6093      /* XXX: There is an occasional condition in which, when Emacs display
6094          updates a different frame from the current one, and temporarily
6095          selects it, then processes some interrupt-driven input
6096          (dispnew.c:3878), OS will send the event to the correct NSWindow, but
6097          for some reason that window has its first responder set to the NSView
6098          most recently updated (I guess), which is not the correct one. */
6099      [(EmacsView *)[[theEvent window] delegate] keyDown: theEvent];
6100      return;
6101    }
6103   if (nsEvArray == nil)
6104     nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1];
6106   [NSCursor setHiddenUntilMouseMoves: YES];
6108   if (hlinfo->mouse_face_hidden && INTEGERP (Vmouse_highlight))
6109     {
6110       clear_mouse_face (hlinfo);
6111       hlinfo->mouse_face_hidden = 1;
6112     }
6114   if (!processingCompose)
6115     {
6116       /* When using screen sharing, no left or right information is sent,
6117          so use Left key in those cases.  */
6118       int is_left_key, is_right_key;
6120       code = ([[theEvent charactersIgnoringModifiers] length] == 0) ?
6121         0 : [[theEvent charactersIgnoringModifiers] characterAtIndex: 0];
6123       /* (Carbon way: [theEvent keyCode]) */
6125       /* is it a "function key"? */
6126       /* Note: Sometimes a plain key will have the NSEventModifierFlagNumericPad
6127          flag set (this is probably a bug in the OS).
6128       */
6129       if (code < 0x00ff && (flags&NSEventModifierFlagNumericPad))
6130         {
6131           fnKeysym = ns_convert_key ([theEvent keyCode] | NSEventModifierFlagNumericPad);
6132         }
6133       if (fnKeysym == 0)
6134         {
6135           fnKeysym = ns_convert_key (code);
6136         }
6138       if (fnKeysym)
6139         {
6140           /* COUNTERHACK: map 'Delete' on upper-right main KB to 'Backspace',
6141              because Emacs treats Delete and KP-Delete same (in simple.el). */
6142           if ((fnKeysym == 0xFFFF && [theEvent keyCode] == 0x33)
6143 #ifdef NS_IMPL_GNUSTEP
6144               /*  GNUstep uses incompatible keycodes, even for those that are
6145                   supposed to be hardware independent.  Just check for delete.
6146                   Keypad delete does not have keysym 0xFFFF.
6147                   See https://savannah.gnu.org/bugs/?25395
6148               */
6149               || (fnKeysym == 0xFFFF && code == 127)
6150 #endif
6151             )
6152             code = 0xFF08; /* backspace */
6153           else
6154             code = fnKeysym;
6155         }
6157       /* are there modifiers? */
6158       emacs_event->modifiers = 0;
6160       if (flags & NSEventModifierFlagHelp)
6161           emacs_event->modifiers |= hyper_modifier;
6163       if (flags & NSEventModifierFlagShift)
6164         emacs_event->modifiers |= shift_modifier;
6166       is_right_key = (flags & NSRightCommandKeyMask) == NSRightCommandKeyMask;
6167       is_left_key = (flags & NSLeftCommandKeyMask) == NSLeftCommandKeyMask
6168         || (! is_right_key && (flags & NSEventModifierFlagCommand) == NSEventModifierFlagCommand);
6170       if (is_right_key)
6171         emacs_event->modifiers |= parse_solitary_modifier
6172           (EQ (ns_right_command_modifier, Qleft)
6173            ? ns_command_modifier
6174            : ns_right_command_modifier);
6176       if (is_left_key)
6177         {
6178           emacs_event->modifiers |= parse_solitary_modifier
6179             (ns_command_modifier);
6181           /* if super (default), take input manager's word so things like
6182              dvorak / qwerty layout work */
6183           if (EQ (ns_command_modifier, Qsuper)
6184               && !fnKeysym
6185               && [[theEvent characters] length] != 0)
6186             {
6187               /* XXX: the code we get will be unshifted, so if we have
6188                  a shift modifier, must convert ourselves */
6189               if (!(flags & NSEventModifierFlagShift))
6190                 code = [[theEvent characters] characterAtIndex: 0];
6191 #if 0
6192               /* this is ugly and also requires linking w/Carbon framework
6193                  (for LMGetKbdType) so for now leave this rare (?) case
6194                  undealt with.. in future look into CGEvent methods */
6195               else
6196                 {
6197                   long smv = GetScriptManagerVariable (smKeyScript);
6198                   Handle uchrHandle = GetResource
6199                     ('uchr', GetScriptVariable (smv, smScriptKeys));
6200                   UInt32 dummy = 0;
6201                   UCKeyTranslate ((UCKeyboardLayout *) *uchrHandle,
6202                                  [[theEvent characters] characterAtIndex: 0],
6203                                  kUCKeyActionDisplay,
6204                                  (flags & ~NSEventModifierFlagCommand) >> 8,
6205                                  LMGetKbdType (), kUCKeyTranslateNoDeadKeysMask,
6206                                  &dummy, 1, &dummy, &code);
6207                   code &= 0xFF;
6208                 }
6209 #endif
6210             }
6211         }
6213       is_right_key = (flags & NSRightControlKeyMask) == NSRightControlKeyMask;
6214       is_left_key = (flags & NSLeftControlKeyMask) == NSLeftControlKeyMask
6215         || (! is_right_key && (flags & NSEventModifierFlagControl) == NSEventModifierFlagControl);
6217       if (is_right_key)
6218           emacs_event->modifiers |= parse_solitary_modifier
6219               (EQ (ns_right_control_modifier, Qleft)
6220                ? ns_control_modifier
6221                : ns_right_control_modifier);
6223       if (is_left_key)
6224         emacs_event->modifiers |= parse_solitary_modifier
6225           (ns_control_modifier);
6227       if (flags & NS_FUNCTION_KEY_MASK && !fnKeysym)
6228           emacs_event->modifiers |=
6229             parse_solitary_modifier (ns_function_modifier);
6231       left_is_none = NILP (ns_alternate_modifier)
6232         || EQ (ns_alternate_modifier, Qnone);
6234       is_right_key = (flags & NSRightAlternateKeyMask)
6235         == NSRightAlternateKeyMask;
6236       is_left_key = (flags & NSLeftAlternateKeyMask) == NSLeftAlternateKeyMask
6237         || (! is_right_key
6238             && (flags & NSEventModifierFlagOption) == NSEventModifierFlagOption);
6240       if (is_right_key)
6241         {
6242           if ((NILP (ns_right_alternate_modifier)
6243                || EQ (ns_right_alternate_modifier, Qnone)
6244                || (EQ (ns_right_alternate_modifier, Qleft) && left_is_none))
6245               && !fnKeysym)
6246             {   /* accept pre-interp alt comb */
6247               if ([[theEvent characters] length] > 0)
6248                 code = [[theEvent characters] characterAtIndex: 0];
6249               /*HACK: clear lone shift modifier to stop next if from firing */
6250               if (emacs_event->modifiers == shift_modifier)
6251                 emacs_event->modifiers = 0;
6252             }
6253           else
6254             emacs_event->modifiers |= parse_solitary_modifier
6255               (EQ (ns_right_alternate_modifier, Qleft)
6256                ? ns_alternate_modifier
6257                : ns_right_alternate_modifier);
6258         }
6260       if (is_left_key) /* default = meta */
6261         {
6262           if (left_is_none && !fnKeysym)
6263             {   /* accept pre-interp alt comb */
6264               if ([[theEvent characters] length] > 0)
6265                 code = [[theEvent characters] characterAtIndex: 0];
6266               /*HACK: clear lone shift modifier to stop next if from firing */
6267               if (emacs_event->modifiers == shift_modifier)
6268                 emacs_event->modifiers = 0;
6269             }
6270           else
6271               emacs_event->modifiers |=
6272                 parse_solitary_modifier (ns_alternate_modifier);
6273         }
6275   if (NS_KEYLOG)
6276     fprintf (stderr, "keyDown: code =%x\tfnKey =%x\tflags = %x\tmods = %x\n",
6277              (unsigned) code, fnKeysym, flags, emacs_event->modifiers);
6279       /* if it was a function key or had modifiers, pass it directly to emacs */
6280       if (fnKeysym || (emacs_event->modifiers
6281                        && (emacs_event->modifiers != shift_modifier)
6282                        && [[theEvent charactersIgnoringModifiers] length] > 0))
6283 /*[[theEvent characters] length] */
6284         {
6285           emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
6286           if (code < 0x20)
6287             code |= (1<<28)|(3<<16);
6288           else if (code == 0x7f)
6289             code |= (1<<28)|(3<<16);
6290           else if (!fnKeysym)
6291             emacs_event->kind = code > 0xFF
6292               ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT;
6294           emacs_event->code = code;
6295           EV_TRAILER (theEvent);
6296           processingCompose = NO;
6297           return;
6298         }
6299     }
6302   if (NS_KEYLOG && !processingCompose)
6303     fprintf (stderr, "keyDown: Begin compose sequence.\n");
6305   processingCompose = YES;
6306   [nsEvArray addObject: theEvent];
6307   [self interpretKeyEvents: nsEvArray];
6308   [nsEvArray removeObject: theEvent];
6312 /* <NSTextInput> implementation (called through super interpretKeyEvents:]). */
6315 /* <NSTextInput>: called when done composing;
6316    NOTE: also called when we delete over working text, followed immed.
6317          by doCommandBySelector: deleteBackward: */
6318 - (void)insertText: (id)aString
6320   NSString *s;
6321   NSUInteger len;
6323   NSTRACE ("[EmacsView insertText:]");
6325   if ([aString isKindOfClass:[NSAttributedString class]])
6326     s = [aString string];
6327   else
6328     s = aString;
6330   len = [s length];
6332   if (NS_KEYLOG)
6333     NSLog (@"insertText '%@'\tlen = %lu", aString, (unsigned long) len);
6334   processingCompose = NO;
6336   if (!emacs_event)
6337     return;
6339   /* first, clear any working text */
6340   if (workingText != nil)
6341     [self deleteWorkingText];
6343   /* It might be preferable to use getCharacters:range: below,
6344      cf. https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CocoaPerformance/Articles/StringDrawing.html#//apple_ref/doc/uid/TP40001445-112378.
6345      However, we probably can't use SAFE_NALLOCA here because it might
6346      exit nonlocally.  */
6348   /* now insert the string as keystrokes */
6349   for (NSUInteger i = 0; i < len; i++)
6350     {
6351       NSUInteger code = [s characterAtIndex:i];
6352       if (UTF_16_HIGH_SURROGATE_P (code) && i < len - 1)
6353         {
6354           unichar low = [s characterAtIndex:i + 1];
6355           if (UTF_16_LOW_SURROGATE_P (low))
6356             {
6357               code = surrogates_to_codepoint (low, code);
6358               ++i;
6359             }
6360         }
6361       /* TODO: still need this? */
6362       if (code == 0x2DC)
6363         code = '~'; /* 0x7E */
6364       if (code != 32) /* Space */
6365         emacs_event->modifiers = 0;
6366       emacs_event->kind
6367         = code > 0xFF ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT;
6368       emacs_event->code = code;
6369       EV_TRAILER ((id)nil);
6370     }
6374 /* <NSTextInput>: inserts display of composing characters */
6375 - (void)setMarkedText: (id)aString selectedRange: (NSRange)selRange
6377   NSString *str = [aString respondsToSelector: @selector (string)] ?
6378     [aString string] : aString;
6380   NSTRACE ("[EmacsView setMarkedText:selectedRange:]");
6382   if (NS_KEYLOG)
6383     NSLog (@"setMarkedText '%@' len =%lu range %lu from %lu",
6384            str, (unsigned long)[str length],
6385            (unsigned long)selRange.length,
6386            (unsigned long)selRange.location);
6388   if (workingText != nil)
6389     [self deleteWorkingText];
6390   if ([str length] == 0)
6391     return;
6393   if (!emacs_event)
6394     return;
6396   processingCompose = YES;
6397   workingText = [str copy];
6398   ns_working_text = build_string ([workingText UTF8String]);
6400   emacs_event->kind = NS_TEXT_EVENT;
6401   emacs_event->code = KEY_NS_PUT_WORKING_TEXT;
6402   EV_TRAILER ((id)nil);
6406 /* delete display of composing characters [not in <NSTextInput>] */
6407 - (void)deleteWorkingText
6409   NSTRACE ("[EmacsView deleteWorkingText]");
6411   if (workingText == nil)
6412     return;
6413   if (NS_KEYLOG)
6414     NSLog(@"deleteWorkingText len =%lu\n", (unsigned long)[workingText length]);
6415   [workingText release];
6416   workingText = nil;
6417   processingCompose = NO;
6419   if (!emacs_event)
6420     return;
6422   emacs_event->kind = NS_TEXT_EVENT;
6423   emacs_event->code = KEY_NS_UNPUT_WORKING_TEXT;
6424   EV_TRAILER ((id)nil);
6428 - (BOOL)hasMarkedText
6430   NSTRACE ("[EmacsView hasMarkedText]");
6432   return workingText != nil;
6436 - (NSRange)markedRange
6438   NSTRACE ("[EmacsView markedRange]");
6440   NSRange rng = workingText != nil
6441     ? NSMakeRange (0, [workingText length]) : NSMakeRange (NSNotFound, 0);
6442   if (NS_KEYLOG)
6443     NSLog (@"markedRange request");
6444   return rng;
6448 - (void)unmarkText
6450   NSTRACE ("[EmacsView unmarkText]");
6452   if (NS_KEYLOG)
6453     NSLog (@"unmark (accept) text");
6454   [self deleteWorkingText];
6455   processingCompose = NO;
6459 /* used to position char selection windows, etc. */
6460 - (NSRect)firstRectForCharacterRange: (NSRange)theRange
6462   NSRect rect;
6463   NSPoint pt;
6464   struct window *win = XWINDOW (FRAME_SELECTED_WINDOW (emacsframe));
6466   NSTRACE ("[EmacsView firstRectForCharacterRange:]");
6468   if (NS_KEYLOG)
6469     NSLog (@"firstRectForCharRange request");
6471   rect.size.width = theRange.length * FRAME_COLUMN_WIDTH (emacsframe);
6472   rect.size.height = FRAME_LINE_HEIGHT (emacsframe);
6473   pt.x = WINDOW_TEXT_TO_FRAME_PIXEL_X (win, win->phys_cursor.x);
6474   pt.y = WINDOW_TO_FRAME_PIXEL_Y (win, win->phys_cursor.y
6475                                        +FRAME_LINE_HEIGHT (emacsframe));
6477   pt = [self convertPoint: pt toView: nil];
6479 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
6480 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6481   if ([[self window] respondsToSelector: @selector(convertRectToScreen:)])
6482     {
6483 #endif
6484       rect.origin = pt;
6485       rect = [(EmacsWindow *) [self window] convertRectToScreen: rect];
6486 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6487     }
6488   else
6489 #endif
6490 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
6491 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 \
6492   || defined (NS_IMPL_GNUSTEP)
6493     {
6494       pt = [[self window] convertBaseToScreen: pt];
6495       rect.origin = pt;
6496     }
6497 #endif
6499   return rect;
6503 - (NSInteger)conversationIdentifier
6505   return (NSInteger)self;
6509 - (void)doCommandBySelector: (SEL)aSelector
6511   NSTRACE ("[EmacsView doCommandBySelector:]");
6513   if (NS_KEYLOG)
6514     NSLog (@"doCommandBySelector: %@", NSStringFromSelector (aSelector));
6516   processingCompose = NO;
6517   if (aSelector == @selector (deleteBackward:))
6518     {
6519       /* happens when user backspaces over an ongoing composition:
6520          throw a 'delete' into the event queue */
6521       if (!emacs_event)
6522         return;
6523       emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
6524       emacs_event->code = 0xFF08;
6525       EV_TRAILER ((id)nil);
6526     }
6529 - (NSArray *)validAttributesForMarkedText
6531   static NSArray *arr = nil;
6532   if (arr == nil) arr = [NSArray new];
6533  /* [[NSArray arrayWithObject: NSUnderlineStyleAttributeName] retain]; */
6534   return arr;
6537 - (NSRange)selectedRange
6539   if (NS_KEYLOG)
6540     NSLog (@"selectedRange request");
6541   return NSMakeRange (NSNotFound, 0);
6544 #if defined (NS_IMPL_COCOA) || GNUSTEP_GUI_MAJOR_VERSION > 0 || \
6545     GNUSTEP_GUI_MINOR_VERSION > 22
6546 - (NSUInteger)characterIndexForPoint: (NSPoint)thePoint
6547 #else
6548 - (unsigned int)characterIndexForPoint: (NSPoint)thePoint
6549 #endif
6551   if (NS_KEYLOG)
6552     NSLog (@"characterIndexForPoint request");
6553   return 0;
6556 - (NSAttributedString *)attributedSubstringFromRange: (NSRange)theRange
6558   static NSAttributedString *str = nil;
6559   if (str == nil) str = [NSAttributedString new];
6560   if (NS_KEYLOG)
6561     NSLog (@"attributedSubstringFromRange request");
6562   return str;
6565 /* End <NSTextInput> impl. */
6566 /*****************************************************************************/
6569 /* This is what happens when the user presses a mouse button.  */
6570 - (void)mouseDown: (NSEvent *)theEvent
6572   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6573   NSPoint p = [self convertPoint: [theEvent locationInWindow] fromView: nil];
6575   NSTRACE ("[EmacsView mouseDown:]");
6577   [self deleteWorkingText];
6579   if (!emacs_event)
6580     return;
6582   dpyinfo->last_mouse_frame = emacsframe;
6583   /* appears to be needed to prevent spurious movement events generated on
6584      button clicks */
6585   emacsframe->mouse_moved = 0;
6587   if ([theEvent type] == NSEventTypeScrollWheel)
6588     {
6589 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
6590 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6591       if ([theEvent respondsToSelector:@selector(hasPreciseScrollingDeltas)])
6592         {
6593 #endif
6594           /* If the input device is a touchpad or similar, use precise
6595            * scrolling deltas.  These are measured in pixels, so we
6596            * have to add them up until they exceed one line height,
6597            * then we can send a scroll wheel event.
6598            *
6599            * If the device only has coarse scrolling deltas, like a
6600            * real mousewheel, the deltas represent a ratio of whole
6601            * lines, so round up the number of lines.  This means we
6602            * always send one scroll event per click, but can still
6603            * scroll more than one line if the OS tells us to.
6604            */
6605           bool horizontal;
6606           int lines = 0;
6607           int scrollUp = NO;
6609           /* FIXME: At the top or bottom of the buffer we should
6610            * ignore momentum-phase events.  */
6611           if (! ns_use_mwheel_momentum
6612               && [theEvent momentumPhase] != NSEventPhaseNone)
6613             return;
6615           if ([theEvent hasPreciseScrollingDeltas])
6616             {
6617               static int totalDeltaX, totalDeltaY;
6618               int lineHeight;
6620               if (NUMBERP (ns_mwheel_line_height))
6621                 lineHeight = XINT (ns_mwheel_line_height);
6622               else
6623                 {
6624                   /* FIXME: Use actual line height instead of the default.  */
6625                   lineHeight = default_line_pixel_height
6626                     (XWINDOW (FRAME_SELECTED_WINDOW (emacsframe)));
6627                 }
6629               if ([theEvent phase] == NSEventPhaseBegan)
6630                 {
6631                   totalDeltaX = 0;
6632                   totalDeltaY = 0;
6633                 }
6635               totalDeltaX += [theEvent scrollingDeltaX];
6636               totalDeltaY += [theEvent scrollingDeltaY];
6638               /* Calculate the number of lines, if any, to scroll, and
6639                * reset the total delta for the direction we're NOT
6640                * scrolling so that small movements don't add up.  */
6641               if (abs (totalDeltaX) > abs (totalDeltaY)
6642                   && abs (totalDeltaX) > lineHeight)
6643                 {
6644                   horizontal = YES;
6645                   scrollUp = totalDeltaX > 0;
6647                   lines = abs (totalDeltaX / lineHeight);
6648                   totalDeltaX = totalDeltaX % lineHeight;
6649                   totalDeltaY = 0;
6650                 }
6651               else if (abs (totalDeltaY) >= abs (totalDeltaX)
6652                        && abs (totalDeltaY) > lineHeight)
6653                 {
6654                   horizontal = NO;
6655                   scrollUp = totalDeltaY > 0;
6657                   lines = abs (totalDeltaY / lineHeight);
6658                   totalDeltaY = totalDeltaY % lineHeight;
6659                   totalDeltaX = 0;
6660                 }
6662               if (lines > 1 && ! ns_use_mwheel_acceleration)
6663                 lines = 1;
6664             }
6665           else
6666             {
6667               CGFloat delta;
6669               if ([theEvent scrollingDeltaY] == 0)
6670                 {
6671                   horizontal = YES;
6672                   delta = [theEvent scrollingDeltaX];
6673                 }
6674               else
6675                 {
6676                   horizontal = NO;
6677                   delta = [theEvent scrollingDeltaY];
6678                 }
6680               lines = (ns_use_mwheel_acceleration)
6681                 ? ceil (fabs (delta)) : 1;
6683               scrollUp = delta > 0;
6684             }
6686           if (lines == 0)
6687             return;
6689           emacs_event->kind = horizontal ? HORIZ_WHEEL_EVENT : WHEEL_EVENT;
6690           emacs_event->arg = (make_number (lines));
6692           emacs_event->code = 0;
6693           emacs_event->modifiers = EV_MODIFIERS (theEvent) |
6694             (scrollUp ? up_modifier : down_modifier);
6695 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6696         }
6697       else
6698 #endif
6699 #endif /* defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
6700 #if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6701         {
6702           CGFloat delta = [theEvent deltaY];
6703           /* Mac notebooks send wheel events w/delta =0 when trackpad scrolling */
6704           if (delta == 0)
6705             {
6706               delta = [theEvent deltaX];
6707               if (delta == 0)
6708                 {
6709                   NSTRACE_MSG ("deltaIsZero");
6710                   return;
6711                 }
6712               emacs_event->kind = HORIZ_WHEEL_EVENT;
6713             }
6714           else
6715             emacs_event->kind = WHEEL_EVENT;
6717           emacs_event->code = 0;
6718           emacs_event->modifiers = EV_MODIFIERS (theEvent) |
6719             ((delta > 0) ? up_modifier : down_modifier);
6720         }
6721 #endif
6722     }
6723   else
6724     {
6725       emacs_event->kind = MOUSE_CLICK_EVENT;
6726       emacs_event->code = EV_BUTTON (theEvent);
6727       emacs_event->modifiers = EV_MODIFIERS (theEvent)
6728                              | EV_UDMODIFIERS (theEvent);
6729     }
6731   XSETINT (emacs_event->x, lrint (p.x));
6732   XSETINT (emacs_event->y, lrint (p.y));
6733   EV_TRAILER (theEvent);
6734   return;
6738 - (void)rightMouseDown: (NSEvent *)theEvent
6740   NSTRACE ("[EmacsView rightMouseDown:]");
6741   [self mouseDown: theEvent];
6745 - (void)otherMouseDown: (NSEvent *)theEvent
6747   NSTRACE ("[EmacsView otherMouseDown:]");
6748   [self mouseDown: theEvent];
6752 - (void)mouseUp: (NSEvent *)theEvent
6754   NSTRACE ("[EmacsView mouseUp:]");
6755   [self mouseDown: theEvent];
6759 - (void)rightMouseUp: (NSEvent *)theEvent
6761   NSTRACE ("[EmacsView rightMouseUp:]");
6762   [self mouseDown: theEvent];
6766 - (void)otherMouseUp: (NSEvent *)theEvent
6768   NSTRACE ("[EmacsView otherMouseUp:]");
6769   [self mouseDown: theEvent];
6773 - (void) scrollWheel: (NSEvent *)theEvent
6775   NSTRACE ("[EmacsView scrollWheel:]");
6776   [self mouseDown: theEvent];
6780 /* Tell emacs the mouse has moved. */
6781 - (void)mouseMoved: (NSEvent *)e
6783   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
6784   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6785   Lisp_Object frame;
6786   NSPoint pt;
6788   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsView mouseMoved:]");
6790   dpyinfo->last_mouse_movement_time = EV_TIMESTAMP (e);
6791   pt = [self convertPoint: [e locationInWindow] fromView: nil];
6792   dpyinfo->last_mouse_motion_x = pt.x;
6793   dpyinfo->last_mouse_motion_y = pt.y;
6795   /* update any mouse face */
6796   if (hlinfo->mouse_face_hidden)
6797     {
6798       hlinfo->mouse_face_hidden = 0;
6799       clear_mouse_face (hlinfo);
6800     }
6802   /* tooltip handling */
6803   previous_help_echo_string = help_echo_string;
6804   help_echo_string = Qnil;
6806   if (!NILP (Vmouse_autoselect_window))
6807     {
6808       NSTRACE_MSG ("mouse_autoselect_window");
6809       static Lisp_Object last_mouse_window;
6810       Lisp_Object window
6811         = window_from_coordinates (emacsframe, pt.x, pt.y, 0, 0);
6813       if (WINDOWP (window)
6814           && !EQ (window, last_mouse_window)
6815           && !EQ (window, selected_window)
6816           && (!NILP (focus_follows_mouse)
6817               || (EQ (XWINDOW (window)->frame,
6818                       XWINDOW (selected_window)->frame))))
6819         {
6820           NSTRACE_MSG ("in_window");
6821           emacs_event->kind = SELECT_WINDOW_EVENT;
6822           emacs_event->frame_or_window = window;
6823           EV_TRAILER2 (e);
6824         }
6825       /* Remember the last window where we saw the mouse.  */
6826       last_mouse_window = window;
6827     }
6829   if (!note_mouse_movement (emacsframe, pt.x, pt.y))
6830     help_echo_string = previous_help_echo_string;
6832   XSETFRAME (frame, emacsframe);
6833   if (!NILP (help_echo_string) || !NILP (previous_help_echo_string))
6834     {
6835       /* NOTE: help_echo_{window,pos,object} are set in xdisp.c
6836          (note_mouse_highlight), which is called through the
6837          note_mouse_movement () call above */
6838       any_help_event_p = YES;
6839       gen_help_event (help_echo_string, frame, help_echo_window,
6840                       help_echo_object, help_echo_pos);
6841     }
6843   if (emacsframe->mouse_moved && send_appdefined)
6844     ns_send_appdefined (-1);
6848 - (void)mouseDragged: (NSEvent *)e
6850   NSTRACE ("[EmacsView mouseDragged:]");
6851   [self mouseMoved: e];
6855 - (void)rightMouseDragged: (NSEvent *)e
6857   NSTRACE ("[EmacsView rightMouseDragged:]");
6858   [self mouseMoved: e];
6862 - (void)otherMouseDragged: (NSEvent *)e
6864   NSTRACE ("[EmacsView otherMouseDragged:]");
6865   [self mouseMoved: e];
6869 - (BOOL)windowShouldClose: (id)sender
6871   NSEvent *e =[[self window] currentEvent];
6873   NSTRACE ("[EmacsView windowShouldClose:]");
6874   windowClosing = YES;
6875   if (!emacs_event)
6876     return NO;
6877   emacs_event->kind = DELETE_WINDOW_EVENT;
6878   emacs_event->modifiers = 0;
6879   emacs_event->code = 0;
6880   EV_TRAILER (e);
6881   /* Don't close this window, let this be done from lisp code.  */
6882   return NO;
6885 - (void) updateFrameSize: (BOOL) delay
6887   NSWindow *window = [self window];
6888   NSRect wr = [window frame];
6889   int extra = 0;
6890   int oldc = cols, oldr = rows;
6891   int oldw = FRAME_PIXEL_WIDTH (emacsframe);
6892   int oldh = FRAME_PIXEL_HEIGHT (emacsframe);
6893   int neww, newh;
6895   NSTRACE ("[EmacsView updateFrameSize:]");
6896   NSTRACE_SIZE ("Original size", NSMakeSize (oldw, oldh));
6897   NSTRACE_RECT ("Original frame", wr);
6898   NSTRACE_MSG  ("Original columns: %d", cols);
6899   NSTRACE_MSG  ("Original rows: %d", rows);
6901   if (! [self isFullscreen])
6902     {
6903       int toolbar_height;
6904 #ifdef NS_IMPL_GNUSTEP
6905       // GNUstep does not always update the tool bar height.  Force it.
6906       if (toolbar && [toolbar isVisible])
6907           update_frame_tool_bar (emacsframe);
6908 #endif
6910       toolbar_height = FRAME_TOOLBAR_HEIGHT (emacsframe);
6911       if (toolbar_height < 0)
6912         toolbar_height = 35;
6914       extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
6915         + toolbar_height;
6916     }
6918   if (wait_for_tool_bar)
6919     {
6920       /* The toolbar height is always 0 in fullscreen and undecorated
6921          frames, so don't wait for it to become available. */
6922       if (FRAME_TOOLBAR_HEIGHT (emacsframe) == 0
6923           && FRAME_UNDECORATED (emacsframe) == false
6924           && ! [self isFullscreen])
6925         {
6926           NSTRACE_MSG ("Waiting for toolbar");
6927           return;
6928         }
6929       wait_for_tool_bar = NO;
6930     }
6932   neww = (int)wr.size.width - emacsframe->border_width;
6933   newh = (int)wr.size.height - extra;
6935   NSTRACE_SIZE ("New size", NSMakeSize (neww, newh));
6936   NSTRACE_MSG ("FRAME_TOOLBAR_HEIGHT: %d", FRAME_TOOLBAR_HEIGHT (emacsframe));
6937   NSTRACE_MSG ("FRAME_NS_TITLEBAR_HEIGHT: %d", FRAME_NS_TITLEBAR_HEIGHT (emacsframe));
6939   cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, neww);
6940   rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe, newh);
6942   if (cols < MINWIDTH)
6943     cols = MINWIDTH;
6945   if (rows < MINHEIGHT)
6946     rows = MINHEIGHT;
6948   NSTRACE_MSG ("New columns: %d", cols);
6949   NSTRACE_MSG ("New rows: %d", rows);
6951   if (oldr != rows || oldc != cols || neww != oldw || newh != oldh)
6952     {
6953       NSView *view = FRAME_NS_VIEW (emacsframe);
6955       change_frame_size (emacsframe,
6956                          FRAME_PIXEL_TO_TEXT_WIDTH (emacsframe, neww),
6957                          FRAME_PIXEL_TO_TEXT_HEIGHT (emacsframe, newh),
6958                          0, delay, 0, 1);
6959       SET_FRAME_GARBAGED (emacsframe);
6960       cancel_mouse_face (emacsframe);
6962       /* The next two lines set the frame to the same size as we've
6963          already set above.  We need to do this when we switch back
6964          from non-native fullscreen, in other circumstances it appears
6965          to be a noop.  (bug#28872) */
6966       wr = NSMakeRect (0, 0, neww, newh);
6967       [view setFrame: wr];
6969       // to do: consider using [NSNotificationCenter postNotificationName:].
6970       [self windowDidMove: // Update top/left.
6971               [NSNotification notificationWithName:NSWindowDidMoveNotification
6972                                             object:[view window]]];
6973     }
6974   else
6975     {
6976       NSTRACE_MSG ("No change");
6977     }
6980 - (NSSize)windowWillResize: (NSWindow *)sender toSize: (NSSize)frameSize
6981 /* normalize frame to gridded text size */
6983   int extra = 0;
6985   NSTRACE ("[EmacsView windowWillResize:toSize: " NSTRACE_FMT_SIZE "]",
6986            NSTRACE_ARG_SIZE (frameSize));
6987   NSTRACE_RECT   ("[sender frame]", [sender frame]);
6988   NSTRACE_FSTYPE ("fs_state", fs_state);
6990   if (!FRAME_LIVE_P (emacsframe))
6991     return frameSize;
6993   if (fs_state == FULLSCREEN_MAXIMIZED
6994       && (maximized_width != (int)frameSize.width
6995           || maximized_height != (int)frameSize.height))
6996     [self setFSValue: FULLSCREEN_NONE];
6997   else if (fs_state == FULLSCREEN_WIDTH
6998            && maximized_width != (int)frameSize.width)
6999     [self setFSValue: FULLSCREEN_NONE];
7000   else if (fs_state == FULLSCREEN_HEIGHT
7001            && maximized_height != (int)frameSize.height)
7002     [self setFSValue: FULLSCREEN_NONE];
7004   if (fs_state == FULLSCREEN_NONE)
7005     maximized_width = maximized_height = -1;
7007   if (! [self isFullscreen])
7008     {
7009       extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
7010         + FRAME_TOOLBAR_HEIGHT (emacsframe);
7011     }
7013   cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, frameSize.width);
7014   if (cols < MINWIDTH)
7015     cols = MINWIDTH;
7017   rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe,
7018                                            frameSize.height - extra);
7019   if (rows < MINHEIGHT)
7020     rows = MINHEIGHT;
7021 #ifdef NS_IMPL_COCOA
7022   {
7023     /* this sets window title to have size in it; the wm does this under GS */
7024     NSRect r = [[self window] frame];
7025     if (r.size.height == frameSize.height && r.size.width == frameSize.width)
7026       {
7027         if (old_title != 0)
7028           {
7029             xfree (old_title);
7030             old_title = 0;
7031           }
7032       }
7033     else if (fs_state == FULLSCREEN_NONE && ! maximizing_resize
7034              && [[self window] title] != NULL)
7035       {
7036         char *size_title;
7037         NSWindow *window = [self window];
7038         if (old_title == 0)
7039           {
7040             char *t = strdup ([[[self window] title] UTF8String]);
7041             char *pos = strstr (t, "  â€”  ");
7042             if (pos)
7043               *pos = '\0';
7044             old_title = t;
7045           }
7046         size_title = xmalloc (strlen (old_title) + 40);
7047         esprintf (size_title, "%s  â€”  (%d x %d)", old_title, cols, rows);
7048         [window setTitle: [NSString stringWithUTF8String: size_title]];
7049         [window display];
7050         xfree (size_title);
7051       }
7052   }
7053 #endif /* NS_IMPL_COCOA */
7055   NSTRACE_MSG ("cols: %d  rows: %d", cols, rows);
7057   /* Restrict the new size to the text gird.
7059      Don't restrict the width if the user only adjusted the height, and
7060      vice versa.  (Without this, the frame would shrink, and move
7061      slightly, if the window was resized by dragging one of its
7062      borders.) */
7063   if (!frame_resize_pixelwise)
7064     {
7065       NSRect r = [[self window] frame];
7067       if (r.size.width != frameSize.width)
7068         {
7069           frameSize.width =
7070             FRAME_TEXT_COLS_TO_PIXEL_WIDTH  (emacsframe, cols);
7071         }
7073       if (r.size.height != frameSize.height)
7074         {
7075           frameSize.height =
7076             FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (emacsframe, rows) + extra;
7077         }
7078     }
7080   NSTRACE_RETURN_SIZE (frameSize);
7082   return frameSize;
7086 - (void)windowDidResize: (NSNotification *)notification
7088   NSTRACE ("[EmacsView windowDidResize:]");
7089   if (!FRAME_LIVE_P (emacsframe))
7090     {
7091       NSTRACE_MSG ("Ignored (frame dead)");
7092       return;
7093     }
7094   if (emacsframe->output_data.ns->in_animation)
7095     {
7096       NSTRACE_MSG ("Ignored (in animation)");
7097       return;
7098     }
7100   if (! [self fsIsNative])
7101     {
7102       NSWindow *theWindow = [notification object];
7103       /* We can get notification on the non-FS window when in
7104          fullscreen mode.  */
7105       if ([self window] != theWindow) return;
7106     }
7108   NSTRACE_RECT ("frame", [[notification object] frame]);
7110 #ifdef NS_IMPL_GNUSTEP
7111   NSWindow *theWindow = [notification object];
7113    /* In GNUstep, at least currently, it's possible to get a didResize
7114       without getting a willResize.. therefore we need to act as if we got
7115       the willResize now */
7116   NSSize sz = [theWindow frame].size;
7117   sz = [self windowWillResize: theWindow toSize: sz];
7118 #endif /* NS_IMPL_GNUSTEP */
7120   if (cols > 0 && rows > 0)
7121     {
7122       [self updateFrameSize: YES];
7123     }
7125   ns_send_appdefined (-1);
7128 #ifdef NS_IMPL_COCOA
7129 - (void)viewDidEndLiveResize
7131   NSTRACE ("[EmacsView viewDidEndLiveResize]");
7133   [super viewDidEndLiveResize];
7134   if (old_title != 0)
7135     {
7136       [[self window] setTitle: [NSString stringWithUTF8String: old_title]];
7137       xfree (old_title);
7138       old_title = 0;
7139     }
7140   maximizing_resize = NO;
7142 #endif /* NS_IMPL_COCOA */
7145 - (void)windowDidBecomeKey: (NSNotification *)notification
7146 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
7148   [self windowDidBecomeKey];
7152 - (void)windowDidBecomeKey      /* for direct calls */
7154   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
7155   struct frame *old_focus = dpyinfo->x_focus_frame;
7157   NSTRACE ("[EmacsView windowDidBecomeKey]");
7159   if (emacsframe != old_focus)
7160     dpyinfo->x_focus_frame = emacsframe;
7162   ns_frame_rehighlight (emacsframe);
7164   if (emacs_event)
7165     {
7166       emacs_event->kind = FOCUS_IN_EVENT;
7167       EV_TRAILER ((id)nil);
7168     }
7172 - (void)windowDidResignKey: (NSNotification *)notification
7173 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
7175   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
7176   BOOL is_focus_frame = dpyinfo->x_focus_frame == emacsframe;
7177   NSTRACE ("[EmacsView windowDidResignKey:]");
7179   if (is_focus_frame)
7180     dpyinfo->x_focus_frame = 0;
7182   emacsframe->mouse_moved = 0;
7183   ns_frame_rehighlight (emacsframe);
7185   /* FIXME: for some reason needed on second and subsequent clicks away
7186             from sole-frame Emacs to get hollow box to show */
7187   if (!windowClosing && [[self window] isVisible] == YES)
7188     {
7189       x_update_cursor (emacsframe, 1);
7190       x_set_frame_alpha (emacsframe);
7191     }
7193   if (any_help_event_p)
7194     {
7195       Lisp_Object frame;
7196       XSETFRAME (frame, emacsframe);
7197       help_echo_string = Qnil;
7198       gen_help_event (Qnil, frame, Qnil, Qnil, 0);
7199     }
7201   if (emacs_event && is_focus_frame)
7202     {
7203       [self deleteWorkingText];
7204       emacs_event->kind = FOCUS_OUT_EVENT;
7205       EV_TRAILER ((id)nil);
7206     }
7210 - (void)windowWillMiniaturize: sender
7212   NSTRACE ("[EmacsView windowWillMiniaturize:]");
7216 - (void)setFrame:(NSRect)frameRect
7218   NSTRACE ("[EmacsView setFrame:" NSTRACE_FMT_RECT "]",
7219            NSTRACE_ARG_RECT (frameRect));
7221   [super setFrame:(NSRect)frameRect];
7225 - (BOOL)isFlipped
7227   return YES;
7231 - (BOOL)isOpaque
7233   return NO;
7237 - (void)createToolbar: (struct frame *)f
7239   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
7240   NSWindow *window = [view window];
7242   toolbar = [[EmacsToolbar alloc] initForView: self withIdentifier:
7243                    [NSString stringWithFormat: @"Emacs Frame %d",
7244                              ns_window_num]];
7245   [toolbar setVisible: NO];
7246   [window setToolbar: toolbar];
7248   /* Don't set frame garbaged until tool bar is up to date?
7249      This avoids an extra clear and redraw (flicker) at frame creation.  */
7250   if (FRAME_EXTERNAL_TOOL_BAR (f)) wait_for_tool_bar = YES;
7251   else wait_for_tool_bar = NO;
7254 #ifdef NS_IMPL_COCOA
7255   {
7256     NSButton *toggleButton;
7257     toggleButton = [window standardWindowButton: NSWindowToolbarButton];
7258     [toggleButton setTarget: self];
7259     [toggleButton setAction: @selector (toggleToolbar: )];
7260   }
7261 #endif
7265 - (instancetype) initFrameFromEmacs: (struct frame *)f
7267   NSRect r, wr;
7268   Lisp_Object tem;
7269   NSWindow *win;
7270   NSColor *col;
7271   NSString *name;
7273   NSTRACE ("[EmacsView initFrameFromEmacs:]");
7274   NSTRACE_MSG ("cols:%d lines:%d", f->text_cols, f->text_lines);
7276   windowClosing = NO;
7277   processingCompose = NO;
7278   scrollbarsNeedingUpdate = 0;
7279   fs_state = FULLSCREEN_NONE;
7280   fs_before_fs = next_maximized = -1;
7282   fs_is_native = NO;
7283 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7284 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7285   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7286 #endif
7287     fs_is_native = ns_use_native_fullscreen;
7288 #endif
7290   maximized_width = maximized_height = -1;
7291   nonfs_window = nil;
7293   ns_userRect = NSMakeRect (0, 0, 0, 0);
7294   r = NSMakeRect (0, 0, FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, f->text_cols),
7295                  FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, f->text_lines));
7296   [self initWithFrame: r];
7297   [self setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
7299   FRAME_NS_VIEW (f) = self;
7300   emacsframe = f;
7301 #ifdef NS_IMPL_COCOA
7302   old_title = 0;
7303   maximizing_resize = NO;
7304 #endif
7306   win = [[EmacsWindow alloc]
7307             initWithContentRect: r
7308                       styleMask: (FRAME_UNDECORATED (f)
7309                                   ? FRAME_UNDECORATED_FLAGS
7310                                   : FRAME_DECORATED_FLAGS)
7311                         backing: NSBackingStoreBuffered
7312                           defer: YES];
7314 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7315 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7316   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7317 #endif
7318     [win setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
7319 #endif
7321   wr = [win frame];
7322   bwidth = f->border_width = wr.size.width - r.size.width;
7324   [win setAcceptsMouseMovedEvents: YES];
7325   [win setDelegate: self];
7326 #if !defined (NS_IMPL_COCOA) || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7327 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7328   if ([win respondsToSelector: @selector(useOptimizedDrawing:)])
7329 #endif
7330     [win useOptimizedDrawing: YES];
7331 #endif
7333   [[win contentView] addSubview: self];
7335   if (ns_drag_types)
7336     [self registerForDraggedTypes: ns_drag_types];
7338   tem = f->name;
7339   name = [NSString stringWithUTF8String:
7340                    NILP (tem) ? "Emacs" : SSDATA (tem)];
7341   [win setTitle: name];
7343   /* toolbar support */
7344   if (! FRAME_UNDECORATED (f))
7345     [self createToolbar: f];
7347 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
7348 #ifndef NSAppKitVersionNumber10_10
7349 #define NSAppKitVersionNumber10_10 1343
7350 #endif
7352   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_10
7353       && FRAME_NS_APPEARANCE (f) != ns_appearance_aqua)
7354     win.appearance = [NSAppearance
7355                           appearanceNamed: NSAppearanceNameVibrantDark];
7356 #endif
7358 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
7359   if ([win respondsToSelector: @selector(titlebarAppearsTransparent)])
7360     win.titlebarAppearsTransparent = FRAME_NS_TRANSPARENT_TITLEBAR (f);
7361 #endif
7363   tem = f->icon_name;
7364   if (!NILP (tem))
7365     [win setMiniwindowTitle:
7366            [NSString stringWithUTF8String: SSDATA (tem)]];
7368   if (FRAME_PARENT_FRAME (f) != NULL)
7369     {
7370       NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
7371       [parent addChildWindow: win
7372                      ordered: NSWindowAbove];
7373     }
7375   if (FRAME_Z_GROUP (f) != z_group_none)
7376       win.level = NSNormalWindowLevel
7377         + (FRAME_Z_GROUP_BELOW (f) ? -1 : 1);
7379   {
7380     NSScreen *screen = [win screen];
7382     if (screen != 0)
7383       {
7384         NSPoint pt = NSMakePoint
7385           (IN_BOUND (-SCREENMAX, f->left_pos
7386                      + NS_PARENT_WINDOW_LEFT_POS (f), SCREENMAX),
7387            IN_BOUND (-SCREENMAX,
7388                      NS_PARENT_WINDOW_TOP_POS (f) - f->top_pos,
7389                      SCREENMAX));
7391         [win setFrameTopLeftPoint: pt];
7393         NSTRACE_RECT ("new frame", [win frame]);
7394       }
7395   }
7397   [win makeFirstResponder: self];
7399   col = ns_lookup_indexed_color (NS_FACE_BACKGROUND
7400                                  (FACE_FROM_ID (emacsframe, DEFAULT_FACE_ID)),
7401                                  emacsframe);
7402   [win setBackgroundColor: col];
7403   if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7404     [win setOpaque: NO];
7406 #if !defined (NS_IMPL_COCOA) \
7407   || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7408 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7409   if ([self respondsToSelector: @selector(allocateGState)])
7410 #endif
7411     [self allocateGState];
7412 #endif
7413   [NSApp registerServicesMenuSendTypes: ns_send_types
7414                            returnTypes: [NSArray array]];
7416   /* macOS Sierra automatically enables tabbed windows.  We can't
7417      allow this to be enabled until it's available on a Free system.
7418      Currently it only happens by accident and is buggy anyway. */
7419 #if defined (NS_IMPL_COCOA) \
7420   && MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
7421 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101200
7422   if ([win respondsToSelector: @selector(setTabbingMode:)])
7423 #endif
7424     [win setTabbingMode: NSWindowTabbingModeDisallowed];
7425 #endif
7427   ns_window_num++;
7428   return self;
7432 - (void)windowDidMove: sender
7434   NSWindow *win = [self window];
7435   NSRect r = [win frame];
7436   NSArray *screens = [NSScreen screens];
7437   NSScreen *screen = [screens objectAtIndex: 0];
7439   NSTRACE ("[EmacsView windowDidMove:]");
7441   if (!emacsframe->output_data.ns)
7442     return;
7443   if (screen != nil)
7444     {
7445       emacsframe->left_pos = r.origin.x - NS_PARENT_WINDOW_LEFT_POS (emacsframe);
7446       emacsframe->top_pos =
7447         NS_PARENT_WINDOW_TOP_POS (emacsframe) - (r.origin.y + r.size.height);
7449       if (emacs_event)
7450         {
7451           emacs_event->kind = MOVE_FRAME_EVENT;
7452           EV_TRAILER ((id)nil);
7453         }
7454     }
7458 /* Called AFTER method below, but before our windowWillResize call there leads
7459    to windowDidResize -> x_set_window_size.  Update emacs' notion of frame
7460    location so set_window_size moves the frame. */
7461 - (BOOL)windowShouldZoom: (NSWindow *)sender toFrame: (NSRect)newFrame
7463   NSTRACE (("[EmacsView windowShouldZoom:toFrame:" NSTRACE_FMT_RECT "]"
7464             NSTRACE_FMT_RETURN "YES"),
7465            NSTRACE_ARG_RECT (newFrame));
7467   emacsframe->output_data.ns->zooming = 1;
7468   return YES;
7472 /* Override to do something slightly nonstandard, but nice.  First click on
7473    zoom button will zoom vertically.  Second will zoom completely.  Third
7474    returns to original. */
7475 - (NSRect)windowWillUseStandardFrame:(NSWindow *)sender
7476                         defaultFrame:(NSRect)defaultFrame
7478   // TODO: Rename to "currentFrame" and assign "result" properly in
7479   // all paths.
7480   NSRect result = [sender frame];
7482   NSTRACE (("[EmacsView windowWillUseStandardFrame:defaultFrame:"
7483             NSTRACE_FMT_RECT "]"),
7484            NSTRACE_ARG_RECT (defaultFrame));
7485   NSTRACE_FSTYPE ("fs_state", fs_state);
7486   NSTRACE_FSTYPE ("fs_before_fs", fs_before_fs);
7487   NSTRACE_FSTYPE ("next_maximized", next_maximized);
7488   NSTRACE_RECT   ("ns_userRect", ns_userRect);
7489   NSTRACE_RECT   ("[sender frame]", [sender frame]);
7491   if (fs_before_fs != -1) /* Entering fullscreen */
7492     {
7493       NSTRACE_MSG ("Entering fullscreen");
7494       result = defaultFrame;
7495     }
7496   else
7497     {
7498       // Save the window size and position (frame) before the resize.
7499       if (fs_state != FULLSCREEN_MAXIMIZED
7500           && fs_state != FULLSCREEN_WIDTH)
7501         {
7502           ns_userRect.size.width = result.size.width;
7503           ns_userRect.origin.x   = result.origin.x;
7504         }
7506       if (fs_state != FULLSCREEN_MAXIMIZED
7507           && fs_state != FULLSCREEN_HEIGHT)
7508         {
7509           ns_userRect.size.height = result.size.height;
7510           ns_userRect.origin.y    = result.origin.y;
7511         }
7513       NSTRACE_RECT ("ns_userRect (2)", ns_userRect);
7515       if (next_maximized == FULLSCREEN_HEIGHT
7516           || (next_maximized == -1
7517               && abs ((int)(defaultFrame.size.height - result.size.height))
7518               > FRAME_LINE_HEIGHT (emacsframe)))
7519         {
7520           /* first click */
7521           NSTRACE_MSG ("FULLSCREEN_HEIGHT");
7522           maximized_height = result.size.height = defaultFrame.size.height;
7523           maximized_width = -1;
7524           result.origin.y = defaultFrame.origin.y;
7525           if (ns_userRect.size.height != 0)
7526             {
7527               result.origin.x = ns_userRect.origin.x;
7528               result.size.width = ns_userRect.size.width;
7529             }
7530           [self setFSValue: FULLSCREEN_HEIGHT];
7531 #ifdef NS_IMPL_COCOA
7532           maximizing_resize = YES;
7533 #endif
7534         }
7535       else if (next_maximized == FULLSCREEN_WIDTH)
7536         {
7537           NSTRACE_MSG ("FULLSCREEN_WIDTH");
7538           maximized_width = result.size.width = defaultFrame.size.width;
7539           maximized_height = -1;
7540           result.origin.x = defaultFrame.origin.x;
7541           if (ns_userRect.size.width != 0)
7542             {
7543               result.origin.y = ns_userRect.origin.y;
7544               result.size.height = ns_userRect.size.height;
7545             }
7546           [self setFSValue: FULLSCREEN_WIDTH];
7547         }
7548       else if (next_maximized == FULLSCREEN_MAXIMIZED
7549                || (next_maximized == -1
7550                    && abs ((int)(defaultFrame.size.width - result.size.width))
7551                    > FRAME_COLUMN_WIDTH (emacsframe)))
7552         {
7553           NSTRACE_MSG ("FULLSCREEN_MAXIMIZED");
7555           result = defaultFrame;  /* second click */
7556           maximized_width = result.size.width;
7557           maximized_height = result.size.height;
7558           [self setFSValue: FULLSCREEN_MAXIMIZED];
7559 #ifdef NS_IMPL_COCOA
7560           maximizing_resize = YES;
7561 #endif
7562         }
7563       else
7564         {
7565           /* restore */
7566           NSTRACE_MSG ("Restore");
7567           result = ns_userRect.size.height ? ns_userRect : result;
7568           NSTRACE_RECT ("restore (2)", result);
7569           ns_userRect = NSMakeRect (0, 0, 0, 0);
7570 #ifdef NS_IMPL_COCOA
7571           maximizing_resize = fs_state != FULLSCREEN_NONE;
7572 #endif
7573           [self setFSValue: FULLSCREEN_NONE];
7574           maximized_width = maximized_height = -1;
7575         }
7576     }
7578   if (fs_before_fs == -1) next_maximized = -1;
7580   NSTRACE_RECT   ("Final ns_userRect", ns_userRect);
7581   NSTRACE_MSG    ("Final maximized_width: %d", maximized_width);
7582   NSTRACE_MSG    ("Final maximized_height: %d", maximized_height);
7583   NSTRACE_FSTYPE ("Final next_maximized", next_maximized);
7585   [self windowWillResize: sender toSize: result.size];
7587   NSTRACE_RETURN_RECT (result);
7589   return result;
7593 - (void)windowDidDeminiaturize: sender
7595   NSTRACE ("[EmacsView windowDidDeminiaturize:]");
7596   if (!emacsframe->output_data.ns)
7597     return;
7599   SET_FRAME_ICONIFIED (emacsframe, 0);
7600   SET_FRAME_VISIBLE (emacsframe, 1);
7601   windows_or_buffers_changed = 63;
7603   if (emacs_event)
7604     {
7605       emacs_event->kind = DEICONIFY_EVENT;
7606       EV_TRAILER ((id)nil);
7607     }
7611 - (void)windowDidExpose: sender
7613   NSTRACE ("[EmacsView windowDidExpose:]");
7614   if (!emacsframe->output_data.ns)
7615     return;
7617   SET_FRAME_VISIBLE (emacsframe, 1);
7618   SET_FRAME_GARBAGED (emacsframe);
7620   if (send_appdefined)
7621     ns_send_appdefined (-1);
7625 - (void)windowDidMiniaturize: sender
7627   NSTRACE ("[EmacsView windowDidMiniaturize:]");
7628   if (!emacsframe->output_data.ns)
7629     return;
7631   SET_FRAME_ICONIFIED (emacsframe, 1);
7632   SET_FRAME_VISIBLE (emacsframe, 0);
7634   if (emacs_event)
7635     {
7636       emacs_event->kind = ICONIFY_EVENT;
7637       EV_TRAILER ((id)nil);
7638     }
7641 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7642 - (NSApplicationPresentationOptions)window:(NSWindow *)window
7643       willUseFullScreenPresentationOptions:
7644   (NSApplicationPresentationOptions)proposedOptions
7646   return proposedOptions|NSApplicationPresentationAutoHideToolbar;
7648 #endif
7650 - (void)windowWillEnterFullScreen:(NSNotification *)notification
7652   NSTRACE ("[EmacsView windowWillEnterFullScreen:]");
7653   [self windowWillEnterFullScreen];
7655 - (void)windowWillEnterFullScreen /* provided for direct calls */
7657   NSTRACE ("[EmacsView windowWillEnterFullScreen]");
7658   fs_before_fs = fs_state;
7661 - (void)windowDidEnterFullScreen:(NSNotification *)notification
7663   NSTRACE ("[EmacsView windowDidEnterFullScreen:]");
7664   [self windowDidEnterFullScreen];
7667 - (void)windowDidEnterFullScreen /* provided for direct calls */
7669   NSTRACE ("[EmacsView windowDidEnterFullScreen]");
7670   [self setFSValue: FULLSCREEN_BOTH];
7671   if (! [self fsIsNative])
7672     {
7673       [self windowDidBecomeKey];
7674       [nonfs_window orderOut:self];
7675     }
7676   else
7677     {
7678       BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (emacsframe) ? YES : NO;
7679 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 \
7680   && MAC_OS_X_VERSION_MIN_REQUIRED <= 1070
7681       unsigned val = (unsigned)[NSApp presentationOptions];
7683       // Mac OS X 10.7 bug fix, the menu won't appear without this.
7684       // val is non-zero on other macOS versions.
7685       if (val == 0)
7686         {
7687           NSApplicationPresentationOptions options
7688             = NSApplicationPresentationAutoHideDock
7689             | NSApplicationPresentationAutoHideMenuBar
7690             | NSApplicationPresentationFullScreen
7691             | NSApplicationPresentationAutoHideToolbar;
7693           [NSApp setPresentationOptions: options];
7694         }
7695 #endif
7696       [toolbar setVisible:tbar_visible];
7697     }
7700 - (void)windowWillExitFullScreen:(NSNotification *)notification
7702   NSTRACE ("[EmacsView windowWillExitFullScreen:]");
7703   [self windowWillExitFullScreen];
7706 - (void)windowWillExitFullScreen /* provided for direct calls */
7708   NSTRACE ("[EmacsView windowWillExitFullScreen]");
7709   if (!FRAME_LIVE_P (emacsframe))
7710     {
7711       NSTRACE_MSG ("Ignored (frame dead)");
7712       return;
7713     }
7714   if (next_maximized != -1)
7715     fs_before_fs = next_maximized;
7718 - (void)windowDidExitFullScreen:(NSNotification *)notification
7720   NSTRACE ("[EmacsView windowDidExitFullScreen:]");
7721   [self windowDidExitFullScreen];
7724 - (void)windowDidExitFullScreen /* provided for direct calls */
7726   NSTRACE ("[EmacsView windowDidExitFullScreen]");
7727   if (!FRAME_LIVE_P (emacsframe))
7728     {
7729       NSTRACE_MSG ("Ignored (frame dead)");
7730       return;
7731     }
7732   [self setFSValue: fs_before_fs];
7733   fs_before_fs = -1;
7734 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7735   [self updateCollectionBehavior];
7736 #endif
7737   if (FRAME_EXTERNAL_TOOL_BAR (emacsframe))
7738     {
7739       [toolbar setVisible:YES];
7740       update_frame_tool_bar (emacsframe);
7741       [self updateFrameSize:YES];
7742       [[self window] display];
7743     }
7744   else
7745     [toolbar setVisible:NO];
7747   if (next_maximized != -1)
7748     [[self window] performZoom:self];
7751 - (BOOL)fsIsNative
7753   return fs_is_native;
7756 - (BOOL)isFullscreen
7758   BOOL res;
7760   if (! fs_is_native)
7761     {
7762       res = (nonfs_window != nil);
7763     }
7764   else
7765     {
7766 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7767       res = (([[self window] styleMask] & NSWindowStyleMaskFullScreen) != 0);
7768 #else
7769       res = NO;
7770 #endif
7771     }
7773   NSTRACE ("[EmacsView isFullscreen] " NSTRACE_FMT_RETURN " %d",
7774            (int) res);
7776   return res;
7779 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7780 - (void)updateCollectionBehavior
7782   NSTRACE ("[EmacsView updateCollectionBehavior]");
7784   if (! [self isFullscreen])
7785     {
7786       NSWindow *win = [self window];
7787       NSWindowCollectionBehavior b = [win collectionBehavior];
7788       if (ns_use_native_fullscreen)
7789         b |= NSWindowCollectionBehaviorFullScreenPrimary;
7790       else
7791         b &= ~NSWindowCollectionBehaviorFullScreenPrimary;
7793       [win setCollectionBehavior: b];
7794 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7795       if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7796 #endif
7797         fs_is_native = ns_use_native_fullscreen;
7798     }
7800 #endif
7802 - (void)toggleFullScreen: (id)sender
7804   NSWindow *w, *fw;
7805   BOOL onFirstScreen;
7806   struct frame *f;
7807   NSRect r, wr;
7808   NSColor *col;
7810   NSTRACE ("[EmacsView toggleFullScreen:]");
7812   if (fs_is_native)
7813     {
7814 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7815 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7816       if ([[self window] respondsToSelector: @selector(toggleFullScreen:)])
7817 #endif
7818         [[self window] toggleFullScreen:sender];
7819 #endif
7820       return;
7821     }
7823   w = [self window];
7824   onFirstScreen = [[w screen] isEqual:[[NSScreen screens] objectAtIndex:0]];
7825   f = emacsframe;
7826   wr = [w frame];
7827   col = ns_lookup_indexed_color (NS_FACE_BACKGROUND
7828                                  (FACE_FROM_ID (f, DEFAULT_FACE_ID)),
7829                                  f);
7831   if (fs_state != FULLSCREEN_BOTH)
7832     {
7833       NSScreen *screen = [w screen];
7835 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
7836       /* Hide ghost menu bar on secondary monitor? */
7837       if (! onFirstScreen
7838 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
7839           && [NSScreen respondsToSelector: @selector(screensHaveSeparateSpaces)]
7840 #endif
7841           )
7842         onFirstScreen = [NSScreen screensHaveSeparateSpaces];
7843 #endif
7844       /* Hide dock and menubar if we are on the primary screen.  */
7845       if (onFirstScreen)
7846         {
7847 #ifdef NS_IMPL_COCOA
7848           NSApplicationPresentationOptions options
7849             = NSApplicationPresentationAutoHideDock
7850             | NSApplicationPresentationAutoHideMenuBar;
7852           [NSApp setPresentationOptions: options];
7853 #else
7854           [NSMenu setMenuBarVisible:NO];
7855 #endif
7856         }
7858       fw = [[EmacsFSWindow alloc]
7859                        initWithContentRect:[w contentRectForFrameRect:wr]
7860                                  styleMask:NSWindowStyleMaskBorderless
7861                                    backing:NSBackingStoreBuffered
7862                                      defer:YES
7863                                     screen:screen];
7865       [fw setContentView:[w contentView]];
7866       [fw setTitle:[w title]];
7867       [fw setDelegate:self];
7868       [fw setAcceptsMouseMovedEvents: YES];
7869 #if !defined (NS_IMPL_COCOA) \
7870   || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7871 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7872       if ([fw respondsToSelector: @selector(useOptimizedDrawing:)])
7873 #endif
7874         [fw useOptimizedDrawing: YES];
7875 #endif
7876       [fw setBackgroundColor: col];
7877       if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7878         [fw setOpaque: NO];
7880       f->border_width = 0;
7882       nonfs_window = w;
7884       [self windowWillEnterFullScreen];
7885       [fw makeKeyAndOrderFront:NSApp];
7886       [fw makeFirstResponder:self];
7887       [w orderOut:self];
7888       r = [fw frameRectForContentRect:[screen frame]];
7889       [fw setFrame: r display:YES animate:ns_use_fullscreen_animation];
7890       [self windowDidEnterFullScreen];
7891       [fw display];
7892     }
7893   else
7894     {
7895       fw = w;
7896       w = nonfs_window;
7897       nonfs_window = nil;
7899       if (onFirstScreen)
7900         {
7901 #ifdef NS_IMPL_COCOA
7902           [NSApp setPresentationOptions: NSApplicationPresentationDefault];
7903 #else
7904           [NSMenu setMenuBarVisible:YES];
7905 #endif
7906         }
7908       [w setContentView:[fw contentView]];
7909       [w setBackgroundColor: col];
7910       if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7911         [w setOpaque: NO];
7913       f->border_width = bwidth;
7915       // to do: consider using [NSNotificationCenter postNotificationName:] to send notifications.
7917       [self windowWillExitFullScreen];
7918       [fw setFrame: [w frame] display:YES animate:ns_use_fullscreen_animation];
7919       [fw close];
7920       [w makeKeyAndOrderFront:NSApp];
7921       [self windowDidExitFullScreen];
7922       [self updateFrameSize:YES];
7923     }
7926 - (void)handleFS
7928   NSTRACE ("[EmacsView handleFS]");
7930   if (fs_state != emacsframe->want_fullscreen)
7931     {
7932       if (fs_state == FULLSCREEN_BOTH)
7933         {
7934           NSTRACE_MSG ("fs_state == FULLSCREEN_BOTH");
7935           [self toggleFullScreen:self];
7936         }
7938       switch (emacsframe->want_fullscreen)
7939         {
7940         case FULLSCREEN_BOTH:
7941           NSTRACE_MSG ("FULLSCREEN_BOTH");
7942           [self toggleFullScreen:self];
7943           break;
7944         case FULLSCREEN_WIDTH:
7945           NSTRACE_MSG ("FULLSCREEN_WIDTH");
7946           next_maximized = FULLSCREEN_WIDTH;
7947           if (fs_state != FULLSCREEN_BOTH)
7948             [[self window] performZoom:self];
7949           break;
7950         case FULLSCREEN_HEIGHT:
7951           NSTRACE_MSG ("FULLSCREEN_HEIGHT");
7952           next_maximized = FULLSCREEN_HEIGHT;
7953           if (fs_state != FULLSCREEN_BOTH)
7954             [[self window] performZoom:self];
7955           break;
7956         case FULLSCREEN_MAXIMIZED:
7957           NSTRACE_MSG ("FULLSCREEN_MAXIMIZED");
7958           next_maximized = FULLSCREEN_MAXIMIZED;
7959           if (fs_state != FULLSCREEN_BOTH)
7960             [[self window] performZoom:self];
7961           break;
7962         case FULLSCREEN_NONE:
7963           NSTRACE_MSG ("FULLSCREEN_NONE");
7964           if (fs_state != FULLSCREEN_BOTH)
7965             {
7966               next_maximized = FULLSCREEN_NONE;
7967               [[self window] performZoom:self];
7968             }
7969           break;
7970         }
7972       emacsframe->want_fullscreen = FULLSCREEN_NONE;
7973     }
7977 - (void) setFSValue: (int)value
7979   NSTRACE ("[EmacsView setFSValue:" NSTRACE_FMT_FSTYPE "]",
7980            NSTRACE_ARG_FSTYPE(value));
7982   Lisp_Object lval = Qnil;
7983   switch (value)
7984     {
7985     case FULLSCREEN_BOTH:
7986       lval = Qfullboth;
7987       break;
7988     case FULLSCREEN_WIDTH:
7989       lval = Qfullwidth;
7990       break;
7991     case FULLSCREEN_HEIGHT:
7992       lval = Qfullheight;
7993       break;
7994     case FULLSCREEN_MAXIMIZED:
7995       lval = Qmaximized;
7996       break;
7997     }
7998   store_frame_param (emacsframe, Qfullscreen, lval);
7999   fs_state = value;
8002 - (void)mouseEntered: (NSEvent *)theEvent
8004   NSTRACE ("[EmacsView mouseEntered:]");
8005   if (emacsframe)
8006     FRAME_DISPLAY_INFO (emacsframe)->last_mouse_movement_time
8007       = EV_TIMESTAMP (theEvent);
8011 - (void)mouseExited: (NSEvent *)theEvent
8013   Mouse_HLInfo *hlinfo = emacsframe ? MOUSE_HL_INFO (emacsframe) : NULL;
8015   NSTRACE ("[EmacsView mouseExited:]");
8017   if (!hlinfo)
8018     return;
8020   FRAME_DISPLAY_INFO (emacsframe)->last_mouse_movement_time
8021     = EV_TIMESTAMP (theEvent);
8023   if (emacsframe == hlinfo->mouse_face_mouse_frame)
8024     {
8025       clear_mouse_face (hlinfo);
8026       hlinfo->mouse_face_mouse_frame = 0;
8027     }
8031 - (instancetype)menuDown: sender
8033   NSTRACE ("[EmacsView menuDown:]");
8034   if (context_menu_value == -1)
8035     context_menu_value = [sender tag];
8036   else
8037     {
8038       NSInteger tag = [sender tag];
8039       find_and_call_menu_selection (emacsframe, emacsframe->menu_bar_items_used,
8040                                     emacsframe->menu_bar_vector,
8041                                     (void *)tag);
8042     }
8044   ns_send_appdefined (-1);
8045   return self;
8049 - (EmacsToolbar *)toolbar
8051   return toolbar;
8055 /* this gets called on toolbar button click */
8056 - (instancetype)toolbarClicked: (id)item
8058   NSEvent *theEvent;
8059   int idx = [item tag] * TOOL_BAR_ITEM_NSLOTS;
8061   NSTRACE ("[EmacsView toolbarClicked:]");
8063   if (!emacs_event)
8064     return self;
8066   /* send first event (for some reason two needed) */
8067   theEvent = [[self window] currentEvent];
8068   emacs_event->kind = TOOL_BAR_EVENT;
8069   XSETFRAME (emacs_event->arg, emacsframe);
8070   EV_TRAILER (theEvent);
8072   emacs_event->kind = TOOL_BAR_EVENT;
8073 /*   XSETINT (emacs_event->code, 0); */
8074   emacs_event->arg = AREF (emacsframe->tool_bar_items,
8075                            idx + TOOL_BAR_ITEM_KEY);
8076   emacs_event->modifiers = EV_MODIFIERS (theEvent);
8077   EV_TRAILER (theEvent);
8078   return self;
8082 - (instancetype)toggleToolbar: (id)sender
8084   NSTRACE ("[EmacsView toggleToolbar:]");
8086   if (!emacs_event)
8087     return self;
8089   emacs_event->kind = NS_NONKEY_EVENT;
8090   emacs_event->code = KEY_NS_TOGGLE_TOOLBAR;
8091   EV_TRAILER ((id)nil);
8092   return self;
8096 - (void)drawRect: (NSRect)rect
8098   int x = NSMinX (rect), y = NSMinY (rect);
8099   int width = NSWidth (rect), height = NSHeight (rect);
8101   NSTRACE ("[EmacsView drawRect:" NSTRACE_FMT_RECT "]",
8102            NSTRACE_ARG_RECT(rect));
8104   if (!emacsframe || !emacsframe->output_data.ns)
8105     return;
8107   ns_clear_frame_area (emacsframe, x, y, width, height);
8108   block_input ();
8109   expose_frame (emacsframe, x, y, width, height);
8110   unblock_input ();
8112   /*
8113     drawRect: may be called (at least in Mac OS X 10.5) for invisible
8114     views as well for some reason.  Thus, do not infer visibility
8115     here.
8117     emacsframe->async_visible = 1;
8118     emacsframe->async_iconified = 0;
8119   */
8123 /* NSDraggingDestination protocol methods.  Actually this is not really a
8124    protocol, but a category of Object.  O well...  */
8126 -(NSDragOperation) draggingEntered: (id <NSDraggingInfo>) sender
8128   NSTRACE ("[EmacsView draggingEntered:]");
8129   return NSDragOperationGeneric;
8133 -(BOOL)prepareForDragOperation: (id <NSDraggingInfo>) sender
8135   return YES;
8139 -(BOOL)performDragOperation: (id <NSDraggingInfo>) sender
8141   id pb;
8142   int x, y;
8143   NSString *type;
8144   NSEvent *theEvent = [[self window] currentEvent];
8145   NSPoint position;
8146   NSDragOperation op = [sender draggingSourceOperationMask];
8147   int modifiers = 0;
8149   NSTRACE ("[EmacsView performDragOperation:]");
8151   if (!emacs_event)
8152     return NO;
8154   position = [self convertPoint: [sender draggingLocation] fromView: nil];
8155   x = lrint (position.x);  y = lrint (position.y);
8157   pb = [sender draggingPasteboard];
8158   type = [pb availableTypeFromArray: ns_drag_types];
8160   if (! (op & (NSDragOperationMove|NSDragOperationDelete)) &&
8161       // URL drags contain all operations (0xf), don't allow all to be set.
8162       (op & 0xf) != 0xf)
8163     {
8164       if (op & NSDragOperationLink)
8165         modifiers |= NSEventModifierFlagControl;
8166       if (op & NSDragOperationCopy)
8167         modifiers |= NSEventModifierFlagOption;
8168       if (op & NSDragOperationGeneric)
8169         modifiers |= NSEventModifierFlagCommand;
8170     }
8172   modifiers = EV_MODIFIERS2 (modifiers);
8173   if (type == 0)
8174     {
8175       return NO;
8176     }
8177   else if ([type isEqualToString: NSFilenamesPboardType])
8178     {
8179       NSArray *files;
8180       NSEnumerator *fenum;
8181       NSString *file;
8183       if (!(files = [pb propertyListForType: type]))
8184         return NO;
8186       fenum = [files objectEnumerator];
8187       while ( (file = [fenum nextObject]) )
8188         {
8189           emacs_event->kind = DRAG_N_DROP_EVENT;
8190           XSETINT (emacs_event->x, x);
8191           XSETINT (emacs_event->y, y);
8192           emacs_event->modifiers = modifiers;
8193           emacs_event->arg =  list2 (Qfile, build_string ([file UTF8String]));
8194           EV_TRAILER (theEvent);
8195         }
8196       return YES;
8197     }
8198   else if ([type isEqualToString: NSURLPboardType])
8199     {
8200       NSURL *url = [NSURL URLFromPasteboard: pb];
8201       if (url == nil) return NO;
8203       emacs_event->kind = DRAG_N_DROP_EVENT;
8204       XSETINT (emacs_event->x, x);
8205       XSETINT (emacs_event->y, y);
8206       emacs_event->modifiers = modifiers;
8207       emacs_event->arg =  list2 (Qurl,
8208                                  build_string ([[url absoluteString]
8209                                                  UTF8String]));
8210       EV_TRAILER (theEvent);
8212       if ([url isFileURL] != NO)
8213         {
8214           NSString *file = [url path];
8215           ns_input_file = append2 (ns_input_file,
8216                                    build_string ([file UTF8String]));
8217         }
8218       return YES;
8219     }
8220   else if ([type isEqualToString: NSStringPboardType]
8221            || [type isEqualToString: NSTabularTextPboardType])
8222     {
8223       NSString *data;
8225       if (! (data = [pb stringForType: type]))
8226         return NO;
8228       emacs_event->kind = DRAG_N_DROP_EVENT;
8229       XSETINT (emacs_event->x, x);
8230       XSETINT (emacs_event->y, y);
8231       emacs_event->modifiers = modifiers;
8232       emacs_event->arg =  list2 (Qnil, build_string ([data UTF8String]));
8233       EV_TRAILER (theEvent);
8234       return YES;
8235     }
8236   else
8237     {
8238       fprintf (stderr, "Invalid data type in dragging pasteboard");
8239       return NO;
8240     }
8244 - (id) validRequestorForSendType: (NSString *)typeSent
8245                       returnType: (NSString *)typeReturned
8247   NSTRACE ("[EmacsView validRequestorForSendType:returnType:]");
8248   if (typeSent != nil && [ns_send_types indexOfObject: typeSent] != NSNotFound
8249       && typeReturned == nil)
8250     {
8251       if (! NILP (ns_get_local_selection (QPRIMARY, QUTF8_STRING)))
8252         return self;
8253     }
8255   return [super validRequestorForSendType: typeSent
8256                                returnType: typeReturned];
8260 /* The next two methods are part of NSServicesRequests informal protocol,
8261    supposedly called when a services menu item is chosen from this app.
8262    But this should not happen because we override the services menu with our
8263    own entries which call ns-perform-service.
8264    Nonetheless, it appeared to happen (under strange circumstances): bug#1435.
8265    So let's at least stub them out until further investigation can be done. */
8267 - (BOOL) readSelectionFromPasteboard: (NSPasteboard *)pb
8269   /* we could call ns_string_from_pasteboard(pboard) here but then it should
8270      be written into the buffer in place of the existing selection..
8271      ordinary service calls go through functions defined in ns-win.el */
8272   return NO;
8275 - (BOOL) writeSelectionToPasteboard: (NSPasteboard *)pb types: (NSArray *)types
8277   NSArray *typesDeclared;
8278   Lisp_Object val;
8280   NSTRACE ("[EmacsView writeSelectionToPasteboard:types:]");
8282   /* We only support NSStringPboardType */
8283   if ([types containsObject:NSStringPboardType] == NO) {
8284     return NO;
8285   }
8287   val = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8288   if (CONSP (val) && SYMBOLP (XCAR (val)))
8289     {
8290       val = XCDR (val);
8291       if (CONSP (val) && NILP (XCDR (val)))
8292         val = XCAR (val);
8293     }
8294   if (! STRINGP (val))
8295     return NO;
8297   typesDeclared = [NSArray arrayWithObject:NSStringPboardType];
8298   [pb declareTypes:typesDeclared owner:nil];
8299   ns_string_to_pasteboard (pb, val);
8300   return YES;
8304 /* setMini =YES means set from internal (gives a finder icon), NO means set nil
8305    (gives a miniaturized version of the window); currently we use the latter for
8306    frames whose active buffer doesn't correspond to any file
8307    (e.g., '*scratch*') */
8308 - (instancetype)setMiniwindowImage: (BOOL) setMini
8310   id image = [[self window] miniwindowImage];
8311   NSTRACE ("[EmacsView setMiniwindowImage:%d]", setMini);
8313   /* NOTE: under Cocoa miniwindowImage always returns nil, documentation
8314      about "AppleDockIconEnabled" notwithstanding, however the set message
8315      below has its effect nonetheless. */
8316   if (image != emacsframe->output_data.ns->miniimage)
8317     {
8318       if (image && [image isKindOfClass: [EmacsImage class]])
8319         [image release];
8320       [[self window] setMiniwindowImage:
8321                        setMini ? emacsframe->output_data.ns->miniimage : nil];
8322     }
8324   return self;
8328 - (void) setRows: (int) r andColumns: (int) c
8330   NSTRACE ("[EmacsView setRows:%d andColumns:%d]", r, c);
8331   rows = r;
8332   cols = c;
8335 - (int) fullscreenState
8337   return fs_state;
8340 @end  /* EmacsView */
8344 /* ==========================================================================
8346     EmacsWindow implementation
8348    ========================================================================== */
8350 @implementation EmacsWindow
8352 #ifdef NS_IMPL_COCOA
8353 - (id)accessibilityAttributeValue:(NSString *)attribute
8355   Lisp_Object str = Qnil;
8356   struct frame *f = SELECTED_FRAME ();
8357   struct buffer *curbuf = XBUFFER (XWINDOW (f->selected_window)->contents);
8359   NSTRACE ("[EmacsWindow accessibilityAttributeValue:]");
8361   if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
8362     return NSAccessibilityTextFieldRole;
8364   if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]
8365       && curbuf && ! NILP (BVAR (curbuf, mark_active)))
8366     {
8367       str = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8368     }
8369   else if (curbuf && [attribute isEqualToString:NSAccessibilityValueAttribute])
8370     {
8371       if (! NILP (BVAR (curbuf, mark_active)))
8372           str = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8374       if (NILP (str))
8375         {
8376           ptrdiff_t start_byte = BUF_BEGV_BYTE (curbuf);
8377           ptrdiff_t byte_range = BUF_ZV_BYTE (curbuf) - start_byte;
8378           ptrdiff_t range = BUF_ZV (curbuf) - BUF_BEGV (curbuf);
8380           if (! NILP (BVAR (curbuf, enable_multibyte_characters)))
8381             str = make_uninit_multibyte_string (range, byte_range);
8382           else
8383             str = make_uninit_string (range);
8384           /* To check: This returns emacs-utf-8, which is a superset of utf-8.
8385              Is this a problem?  */
8386           memcpy (SDATA (str), BYTE_POS_ADDR (start_byte), byte_range);
8387         }
8388     }
8391   if (! NILP (str))
8392     {
8393       if (CONSP (str) && SYMBOLP (XCAR (str)))
8394         {
8395           str = XCDR (str);
8396           if (CONSP (str) && NILP (XCDR (str)))
8397             str = XCAR (str);
8398         }
8399       if (STRINGP (str))
8400         {
8401           const char *utfStr = SSDATA (str);
8402           NSString *nsStr = [NSString stringWithUTF8String: utfStr];
8403           return nsStr;
8404         }
8405     }
8407   return [super accessibilityAttributeValue:attribute];
8409 #endif /* NS_IMPL_COCOA */
8411 /* Constrain size and placement of a frame.
8413    By returning the original "frameRect", the frame is not
8414    constrained. This can lead to unwanted situations where, for
8415    example, the menu bar covers the frame.
8417    The default implementation (accessed using "super") constrains the
8418    frame to the visible area of SCREEN, minus the menu bar (if
8419    present) and the Dock.  Note that default implementation also calls
8420    windowWillResize, with the frame it thinks should have.  (This can
8421    make the frame exit maximized mode.)
8423    Note that this should work in situations where multiple monitors
8424    are present.  Common configurations are side-by-side monitors and a
8425    monitor on top of another (e.g. when a laptop is placed under a
8426    large screen). */
8427 - (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
8429   NSTRACE ("[EmacsWindow constrainFrameRect:" NSTRACE_FMT_RECT " toScreen:]",
8430              NSTRACE_ARG_RECT (frameRect));
8432 #ifdef NS_IMPL_COCOA
8433 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
8434   // If separate spaces is on, it is like each screen is independent.  There is
8435   // no spanning of frames across screens.
8436   if (
8437 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
8438       [NSScreen respondsToSelector: @selector(screensHaveSeparateSpaces)] &&
8439 #endif
8440       [NSScreen screensHaveSeparateSpaces])
8441     {
8442       NSTRACE_MSG ("Screens have separate spaces");
8443       frameRect = [super constrainFrameRect:frameRect toScreen:screen];
8444       NSTRACE_RETURN_RECT (frameRect);
8445       return frameRect;
8446     }
8447   else
8448 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 */
8450     // Check that the proposed frameRect is visible in at least one
8451     // screen.  If it is not, ask the system to reposition it (only
8452     // for non-child windows).
8454     if (!FRAME_PARENT_FRAME (((EmacsView *)[self delegate])->emacsframe))
8455     {
8456       NSArray *screens = [NSScreen screens];
8457       NSUInteger nr_screens = [screens count];
8459       int i;
8460       BOOL frame_on_screen = NO;
8462       for (i = 0; i < nr_screens; ++i)
8463         {
8464           NSScreen *s = [screens objectAtIndex: i];
8465           NSRect scrRect = [s frame];
8467           if (NSIntersectsRect(frameRect, scrRect))
8468             {
8469               frame_on_screen = YES;
8470               break;
8471             }
8472         }
8474       if (!frame_on_screen)
8475         {
8476           NSTRACE_MSG ("Frame outside screens; constraining");
8477           frameRect = [super constrainFrameRect:frameRect toScreen:screen];
8478           NSTRACE_RETURN_RECT (frameRect);
8479           return frameRect;
8480         }
8481     }
8482 #endif
8484   return constrain_frame_rect(frameRect,
8485                               [(EmacsView *)[self delegate] isFullscreen]);
8489 - (void)performZoom:(id)sender
8491   NSTRACE ("[EmacsWindow performZoom:]");
8493   return [super performZoom:sender];
8496 - (void)zoom:(id)sender
8498   NSTRACE ("[EmacsWindow zoom:]");
8500   ns_update_auto_hide_menu_bar();
8502   // Below are three zoom implementations.  In the final commit, the
8503   // idea is that the last should be included.
8505 #if 0
8506   // Native zoom done using the standard zoom animation.  Size of the
8507   // resulting frame reduced to accommodate the Dock and, if present,
8508   // the menu-bar.
8509   [super zoom:sender];
8511 #elif 0
8512   // Native zoom done using the standard zoom animation, plus an
8513   // explicit resize to cover the full screen, except the menu-bar and
8514   // dock, if present.
8515   [super zoom:sender];
8517   // After the native zoom, resize the resulting frame to fill the
8518   // entire screen, except the menu-bar.
8519   //
8520   // This works for all practical purposes.  (The only minor oddity is
8521   // when transiting from full-height frame to a maximized, the
8522   // animation reduces the height of the frame slightly (to the 4
8523   // pixels needed to accommodate the Doc) before it snaps back into
8524   // full height.  The user would need a very trained eye to spot
8525   // this.)
8526   NSScreen * screen = [self screen];
8527   if (screen != nil)
8528     {
8529       int fs_state = [(EmacsView *)[self delegate] fullscreenState];
8531       NSTRACE_FSTYPE ("fullscreenState", fs_state);
8533       NSRect sr = [screen frame];
8534       struct EmacsMargins margins
8535         = ns_screen_margins_ignoring_hidden_dock(screen);
8537       NSRect wr = [self frame];
8538       NSTRACE_RECT ("Rect after zoom", wr);
8540       NSRect newWr = wr;
8542       if (fs_state == FULLSCREEN_MAXIMIZED
8543           || fs_state == FULLSCREEN_HEIGHT)
8544         {
8545           newWr.origin.y = sr.origin.y + margins.bottom;
8546           newWr.size.height = sr.size.height - margins.top - margins.bottom;
8547         }
8549       if (fs_state == FULLSCREEN_MAXIMIZED
8550           || fs_state == FULLSCREEN_WIDTH)
8551         {
8552           newWr.origin.x = sr.origin.x + margins.left;
8553           newWr.size.width = sr.size.width - margins.right - margins.left;
8554         }
8556       if (newWr.size.width     != wr.size.width
8557           || newWr.size.height != wr.size.height
8558           || newWr.origin.x    != wr.origin.x
8559           || newWr.origin.y    != wr.origin.y)
8560         {
8561           NSTRACE_MSG ("New frame different");
8562           [self setFrame: newWr display: NO];
8563         }
8564     }
8565 #else
8566   // Non-native zoom which is done instantaneously.  The resulting
8567   // frame covers the entire screen, except the menu-bar and dock, if
8568   // present.
8569   NSScreen * screen = [self screen];
8570   if (screen != nil)
8571     {
8572       NSRect sr = [screen frame];
8573       struct EmacsMargins margins
8574         = ns_screen_margins_ignoring_hidden_dock(screen);
8576       sr.size.height -= (margins.top + margins.bottom);
8577       sr.size.width  -= (margins.left + margins.right);
8578       sr.origin.x += margins.left;
8579       sr.origin.y += margins.bottom;
8581       sr = [[self delegate] windowWillUseStandardFrame:self
8582                                           defaultFrame:sr];
8583       [self setFrame: sr display: NO];
8584     }
8585 #endif
8588 - (void)setFrame:(NSRect)windowFrame
8589          display:(BOOL)displayViews
8591   NSTRACE ("[EmacsWindow setFrame:" NSTRACE_FMT_RECT " display:%d]",
8592            NSTRACE_ARG_RECT (windowFrame), displayViews);
8594   [super setFrame:windowFrame display:displayViews];
8597 - (void)setFrame:(NSRect)windowFrame
8598          display:(BOOL)displayViews
8599          animate:(BOOL)performAnimation
8601   NSTRACE ("[EmacsWindow setFrame:" NSTRACE_FMT_RECT
8602            " display:%d performAnimation:%d]",
8603            NSTRACE_ARG_RECT (windowFrame), displayViews, performAnimation);
8605   [super setFrame:windowFrame display:displayViews animate:performAnimation];
8608 - (void)setFrameTopLeftPoint:(NSPoint)point
8610   NSTRACE ("[EmacsWindow setFrameTopLeftPoint:" NSTRACE_FMT_POINT "]",
8611            NSTRACE_ARG_POINT (point));
8613   [super setFrameTopLeftPoint:point];
8616 - (BOOL)canBecomeKeyWindow
8618   return !FRAME_NO_ACCEPT_FOCUS (((EmacsView *)[self delegate])->emacsframe);
8620 @end /* EmacsWindow */
8623 @implementation EmacsFSWindow
8625 - (BOOL)canBecomeKeyWindow
8627   return YES;
8630 - (BOOL)canBecomeMainWindow
8632   return YES;
8635 @end
8637 /* ==========================================================================
8639     EmacsScroller implementation
8641    ========================================================================== */
8644 @implementation EmacsScroller
8646 /* for repeat button push */
8647 #define SCROLL_BAR_FIRST_DELAY 0.5
8648 #define SCROLL_BAR_CONTINUOUS_DELAY (1.0 / 15)
8650 + (CGFloat) scrollerWidth
8652   /* TODO: if we want to allow variable widths, this is the place to do it,
8653            however neither GNUstep nor Cocoa support it very well */
8654   CGFloat r;
8655 #if defined (NS_IMPL_COCOA) \
8656   && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
8657 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
8658   if ([NSScroller respondsToSelector:
8659                     @selector(scrollerWidthForControlSize:scrollerStyle:)])
8660 #endif
8661     r = [NSScroller scrollerWidthForControlSize: NSControlSizeRegular
8662                                   scrollerStyle: NSScrollerStyleLegacy];
8663 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
8664   else
8665 #endif
8666 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
8667 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 \
8668   || defined (NS_IMPL_GNUSTEP)
8669     r = [NSScroller scrollerWidth];
8670 #endif
8671   return r;
8674 - (instancetype)initFrame: (NSRect )r window: (Lisp_Object)nwin
8676   NSTRACE ("[EmacsScroller initFrame: window:]");
8678   if (r.size.width > r.size.height)
8679       horizontal = YES;
8680   else
8681       horizontal = NO;
8683   [super initWithFrame: r/*NSMakeRect (0, 0, 0, 0)*/];
8684   [self setContinuous: YES];
8685   [self setEnabled: YES];
8687   /* Ensure auto resizing of scrollbars occurs within the emacs frame's view
8688      locked against the top and bottom edges, and right edge on macOS, where
8689      scrollers are on right. */
8690 #ifdef NS_IMPL_GNUSTEP
8691   [self setAutoresizingMask: NSViewMaxXMargin | NSViewHeightSizable];
8692 #else
8693   [self setAutoresizingMask: NSViewMinXMargin | NSViewHeightSizable];
8694 #endif
8696   window = XWINDOW (nwin);
8697   condemned = NO;
8698   if (horizontal)
8699     pixel_length = NSWidth (r);
8700   else
8701     pixel_length = NSHeight (r);
8702   if (pixel_length == 0) pixel_length = 1;
8703   min_portion = 20 / pixel_length;
8705   frame = XFRAME (window->frame);
8706   if (FRAME_LIVE_P (frame))
8707     {
8708       int i;
8709       EmacsView *view = FRAME_NS_VIEW (frame);
8710       NSView *sview = [[view window] contentView];
8711       NSArray *subs = [sview subviews];
8713       /* disable optimization stopping redraw of other scrollbars */
8714       view->scrollbarsNeedingUpdate = 0;
8715       for (i =[subs count]-1; i >= 0; i--)
8716         if ([[subs objectAtIndex: i] isKindOfClass: [EmacsScroller class]])
8717           view->scrollbarsNeedingUpdate++;
8718       [sview addSubview: self];
8719     }
8721 /*  [self setFrame: r]; */
8723   return self;
8727 - (void)setFrame: (NSRect)newRect
8729   NSTRACE ("[EmacsScroller setFrame:]");
8731 /*  block_input (); */
8732   if (horizontal)
8733     pixel_length = NSWidth (newRect);
8734   else
8735     pixel_length = NSHeight (newRect);
8736   if (pixel_length == 0) pixel_length = 1;
8737   min_portion = 20 / pixel_length;
8738   [super setFrame: newRect];
8739 /*  unblock_input (); */
8743 - (void)dealloc
8745   NSTRACE ("[EmacsScroller dealloc]");
8746   if (window)
8747     {
8748       if (horizontal)
8749         wset_horizontal_scroll_bar (window, Qnil);
8750       else
8751         wset_vertical_scroll_bar (window, Qnil);
8752     }
8753   window = 0;
8754   [super dealloc];
8758 - (instancetype)condemn
8760   NSTRACE ("[EmacsScroller condemn]");
8761   condemned =YES;
8762   return self;
8766 - (instancetype)reprieve
8768   NSTRACE ("[EmacsScroller reprieve]");
8769   condemned =NO;
8770   return self;
8774 -(bool)judge
8776   NSTRACE ("[EmacsScroller judge]");
8777   bool ret = condemned;
8778   if (condemned)
8779     {
8780       EmacsView *view;
8781       block_input ();
8782       /* ensure other scrollbar updates after deletion */
8783       view = (EmacsView *)FRAME_NS_VIEW (frame);
8784       if (view != nil)
8785         view->scrollbarsNeedingUpdate++;
8786       if (window)
8787         {
8788           if (horizontal)
8789             wset_horizontal_scroll_bar (window, Qnil);
8790           else
8791             wset_vertical_scroll_bar (window, Qnil);
8792         }
8793       window = 0;
8794       [self removeFromSuperview];
8795       [self release];
8796       unblock_input ();
8797     }
8798   return ret;
8802 - (void)resetCursorRects
8804   NSRect visible = [self visibleRect];
8805   NSTRACE ("[EmacsScroller resetCursorRects]");
8807   if (!NSIsEmptyRect (visible))
8808     [self addCursorRect: visible cursor: [NSCursor arrowCursor]];
8809   [[NSCursor arrowCursor] setOnMouseEntered: YES];
8813 - (int) checkSamePosition: (int) position portion: (int) portion
8814                     whole: (int) whole
8816   return em_position ==position && em_portion ==portion && em_whole ==whole
8817     && portion != whole; /* needed for resize empty buf */
8821 - (instancetype)setPosition: (int)position portion: (int)portion whole: (int)whole
8823   NSTRACE ("[EmacsScroller setPosition:portion:whole:]");
8825   em_position = position;
8826   em_portion = portion;
8827   em_whole = whole;
8829   if (portion >= whole)
8830     {
8831 #ifdef NS_IMPL_COCOA
8832       [self setKnobProportion: 1.0];
8833       [self setDoubleValue: 1.0];
8834 #else
8835       [self setFloatValue: 0.0 knobProportion: 1.0];
8836 #endif
8837     }
8838   else
8839     {
8840       float pos;
8841       CGFloat por;
8842       portion = max ((float)whole*min_portion/pixel_length, portion);
8843       pos = (float)position / (whole - portion);
8844       por = (CGFloat)portion/whole;
8845 #ifdef NS_IMPL_COCOA
8846       [self setKnobProportion: por];
8847       [self setDoubleValue: pos];
8848 #else
8849       [self setFloatValue: pos knobProportion: por];
8850 #endif
8851     }
8853   return self;
8856 /* set up emacs_event */
8857 - (void) sendScrollEventAtLoc: (float)loc fromEvent: (NSEvent *)e
8859   Lisp_Object win;
8861   NSTRACE ("[EmacsScroller sendScrollEventAtLoc:fromEvent:]");
8863   if (!emacs_event)
8864     return;
8866   emacs_event->part = last_hit_part;
8867   emacs_event->code = 0;
8868   emacs_event->modifiers = EV_MODIFIERS (e) | down_modifier;
8869   XSETWINDOW (win, window);
8870   emacs_event->frame_or_window = win;
8871   emacs_event->timestamp = EV_TIMESTAMP (e);
8872   emacs_event->arg = Qnil;
8874   if (horizontal)
8875     {
8876       emacs_event->kind = HORIZONTAL_SCROLL_BAR_CLICK_EVENT;
8877       XSETINT (emacs_event->x, em_whole * loc / pixel_length);
8878       XSETINT (emacs_event->y, em_whole);
8879     }
8880   else
8881     {
8882       emacs_event->kind = SCROLL_BAR_CLICK_EVENT;
8883       XSETINT (emacs_event->x, loc);
8884       XSETINT (emacs_event->y, pixel_length-20);
8885     }
8887   if (q_event_ptr)
8888     {
8889       n_emacs_events_pending++;
8890       kbd_buffer_store_event_hold (emacs_event, q_event_ptr);
8891     }
8892   else
8893     hold_event (emacs_event);
8894   EVENT_INIT (*emacs_event);
8895   ns_send_appdefined (-1);
8899 /* called manually thru timer to implement repeated button action w/hold-down */
8900 - (instancetype)repeatScroll: (NSTimer *)scrollEntry
8902   NSEvent *e = [[self window] currentEvent];
8903   NSPoint p =  [[self window] mouseLocationOutsideOfEventStream];
8904   BOOL inKnob = [self testPart: p] == NSScrollerKnob;
8906   NSTRACE ("[EmacsScroller repeatScroll:]");
8908   /* clear timer if need be */
8909   if (inKnob || [scroll_repeat_entry timeInterval] == SCROLL_BAR_FIRST_DELAY)
8910     {
8911         [scroll_repeat_entry invalidate];
8912         [scroll_repeat_entry release];
8913         scroll_repeat_entry = nil;
8915         if (inKnob)
8916           return self;
8918         scroll_repeat_entry
8919           = [[NSTimer scheduledTimerWithTimeInterval:
8920                         SCROLL_BAR_CONTINUOUS_DELAY
8921                                             target: self
8922                                           selector: @selector (repeatScroll:)
8923                                           userInfo: 0
8924                                            repeats: YES]
8925               retain];
8926     }
8928   [self sendScrollEventAtLoc: 0 fromEvent: e];
8929   return self;
8933 /* Asynchronous mouse tracking for scroller.  This allows us to dispatch
8934    mouseDragged events without going into a modal loop. */
8935 - (void)mouseDown: (NSEvent *)e
8937   NSRect sr, kr;
8938   /* hitPart is only updated AFTER event is passed on */
8939   NSScrollerPart part = [self testPart: [e locationInWindow]];
8940   CGFloat loc, kloc, pos UNINIT;
8941   int edge = 0;
8943   NSTRACE ("[EmacsScroller mouseDown:]");
8945   switch (part)
8946     {
8947     case NSScrollerDecrementPage:
8948       last_hit_part = horizontal ? scroll_bar_before_handle : scroll_bar_above_handle; break;
8949     case NSScrollerIncrementPage:
8950       last_hit_part = horizontal ? scroll_bar_after_handle : scroll_bar_below_handle; break;
8951     case NSScrollerDecrementLine:
8952       last_hit_part = horizontal ? scroll_bar_left_arrow : scroll_bar_up_arrow; break;
8953     case NSScrollerIncrementLine:
8954       last_hit_part = horizontal ? scroll_bar_right_arrow : scroll_bar_down_arrow; break;
8955     case NSScrollerKnob:
8956       last_hit_part = horizontal ? scroll_bar_horizontal_handle : scroll_bar_handle; break;
8957     case NSScrollerKnobSlot:  /* GNUstep-only */
8958       last_hit_part = scroll_bar_move_ratio; break;
8959     default:  /* NSScrollerNoPart? */
8960       fprintf (stderr, "EmacsScroller-mouseDown: unexpected part %ld\n",
8961                (long) part);
8962       return;
8963     }
8965   if (part == NSScrollerKnob || part == NSScrollerKnobSlot)
8966     {
8967       /* handle, or on GNUstep possibly slot */
8968       NSEvent *fake_event;
8969       int length;
8971       /* compute float loc in slot and mouse offset on knob */
8972       sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot]
8973                       toView: nil];
8974       if (horizontal)
8975         {
8976           length = NSWidth (sr);
8977           loc = ([e locationInWindow].x - NSMinX (sr));
8978         }
8979       else
8980         {
8981           length = NSHeight (sr);
8982           loc = length - ([e locationInWindow].y - NSMinY (sr));
8983         }
8985       if (loc <= 0.0)
8986         {
8987           loc = 0.0;
8988           edge = -1;
8989         }
8990       else if (loc >= length)
8991         {
8992           loc = length;
8993           edge = 1;
8994         }
8996       if (edge)
8997         kloc = 0.5 * edge;
8998       else
8999         {
9000           kr = [self convertRect: [self rectForPart: NSScrollerKnob]
9001                           toView: nil];
9002           if (horizontal)
9003             kloc = ([e locationInWindow].x - NSMinX (kr));
9004           else
9005             kloc = NSHeight (kr) - ([e locationInWindow].y - NSMinY (kr));
9006         }
9007       last_mouse_offset = kloc;
9009       /* if knob, tell emacs a location offset by knob pos
9010          (to indicate top of handle) */
9011       if (part == NSScrollerKnob)
9012         pos = (loc - last_mouse_offset);
9013       else
9014         /* else this is a slot click on GNUstep: go straight there */
9015         pos = loc;
9017       /* If there are buttons in the scroller area, we need to
9018          recalculate pos as emacs expects the scroller slot to take up
9019          the entire available length.  */
9020       if (length != pixel_length)
9021         pos = pos * pixel_length / length;
9023       /* send a fake mouse-up to super to preempt modal -trackKnob: mode */
9024       fake_event = [NSEvent mouseEventWithType: NSEventTypeLeftMouseUp
9025                                       location: [e locationInWindow]
9026                                  modifierFlags: [e modifierFlags]
9027                                      timestamp: [e timestamp]
9028                                   windowNumber: [e windowNumber]
9029                                        context: nil
9030                                    eventNumber: [e eventNumber]
9031                                     clickCount: [e clickCount]
9032                                       pressure: [e pressure]];
9033       [super mouseUp: fake_event];
9034     }
9035   else
9036     {
9037       pos = 0;      /* ignored */
9039       /* set a timer to repeat, as we can't let superclass do this modally */
9040       scroll_repeat_entry
9041         = [[NSTimer scheduledTimerWithTimeInterval: SCROLL_BAR_FIRST_DELAY
9042                                             target: self
9043                                           selector: @selector (repeatScroll:)
9044                                           userInfo: 0
9045                                            repeats: YES]
9046             retain];
9047     }
9049   if (part != NSScrollerKnob)
9050     [self sendScrollEventAtLoc: pos fromEvent: e];
9054 /* Called as we manually track scroller drags, rather than superclass. */
9055 - (void)mouseDragged: (NSEvent *)e
9057     NSRect sr;
9058     double loc, pos;
9059     int length;
9061     NSTRACE ("[EmacsScroller mouseDragged:]");
9063       sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot]
9064                       toView: nil];
9066       if (horizontal)
9067         {
9068           length = NSWidth (sr);
9069           loc = ([e locationInWindow].x - NSMinX (sr));
9070         }
9071       else
9072         {
9073           length = NSHeight (sr);
9074           loc = length - ([e locationInWindow].y - NSMinY (sr));
9075         }
9077       if (loc <= 0.0)
9078         {
9079           loc = 0.0;
9080         }
9081       else if (loc >= length + last_mouse_offset)
9082         {
9083           loc = length + last_mouse_offset;
9084         }
9086       pos = (loc - last_mouse_offset);
9088       /* If there are buttons in the scroller area, we need to
9089          recalculate pos as emacs expects the scroller slot to take up
9090          the entire available length.  */
9091       if (length != pixel_length)
9092         pos = pos * pixel_length / length;
9094       [self sendScrollEventAtLoc: pos fromEvent: e];
9098 - (void)mouseUp: (NSEvent *)e
9100   NSTRACE ("[EmacsScroller mouseUp:]");
9102   if (scroll_repeat_entry)
9103     {
9104       [scroll_repeat_entry invalidate];
9105       [scroll_repeat_entry release];
9106       scroll_repeat_entry = nil;
9107     }
9108   last_hit_part = scroll_bar_above_handle;
9112 /* treat scrollwheel events in the bar as though they were in the main window */
9113 - (void) scrollWheel: (NSEvent *)theEvent
9115   NSTRACE ("[EmacsScroller scrollWheel:]");
9117   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (frame);
9118   [view mouseDown: theEvent];
9121 @end  /* EmacsScroller */
9124 #ifdef NS_IMPL_GNUSTEP
9125 /* Dummy class to get rid of startup warnings.  */
9126 @implementation EmacsDocument
9128 @end
9129 #endif
9132 /* ==========================================================================
9134    Font-related functions; these used to be in nsfaces.m
9136    ========================================================================== */
9139 Lisp_Object
9140 x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
9142   struct font *font = XFONT_OBJECT (font_object);
9143   EmacsView *view = FRAME_NS_VIEW (f);
9144   int font_ascent, font_descent;
9146   if (fontset < 0)
9147     fontset = fontset_from_font (font_object);
9148   FRAME_FONTSET (f) = fontset;
9150   if (FRAME_FONT (f) == font)
9151     /* This font is already set in frame F.  There's nothing more to
9152        do.  */
9153     return font_object;
9155   FRAME_FONT (f) = font;
9157   FRAME_BASELINE_OFFSET (f) = font->baseline_offset;
9158   FRAME_COLUMN_WIDTH (f) = font->average_width;
9159   get_font_ascent_descent (font, &font_ascent, &font_descent);
9160   FRAME_LINE_HEIGHT (f) = font_ascent + font_descent;
9162   /* Compute the scroll bar width in character columns.  */
9163   if (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) > 0)
9164     {
9165       int wid = FRAME_COLUMN_WIDTH (f);
9166       FRAME_CONFIG_SCROLL_BAR_COLS (f)
9167         = (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) + wid - 1) / wid;
9168     }
9169   else
9170     {
9171       int wid = FRAME_COLUMN_WIDTH (f);
9172       FRAME_CONFIG_SCROLL_BAR_COLS (f) = (14 + wid - 1) / wid;
9173     }
9175   /* Compute the scroll bar height in character lines.  */
9176   if (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) > 0)
9177     {
9178       int height = FRAME_LINE_HEIGHT (f);
9179       FRAME_CONFIG_SCROLL_BAR_LINES (f)
9180         = (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) + height - 1) / height;
9181     }
9182   else
9183     {
9184       int height = FRAME_LINE_HEIGHT (f);
9185       FRAME_CONFIG_SCROLL_BAR_LINES (f) = (14 + height - 1) / height;
9186     }
9188   /* Now make the frame display the given font.  */
9189   if (FRAME_NS_WINDOW (f) != 0 && ! [view isFullscreen])
9190     adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
9191                        FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3,
9192                        false, Qfont);
9194   return font_object;
9198 /* XLFD: -foundry-family-weight-slant-swidth-adstyle-pxlsz-ptSz-resx-resy-spc-avgWidth-rgstry-encoding */
9199 /* Note: ns_font_to_xlfd and ns_fontname_to_xlfd no longer needed, removed
9200          in 1.43. */
9202 const char *
9203 ns_xlfd_to_fontname (const char *xlfd)
9204 /* --------------------------------------------------------------------------
9205     Convert an X font name (XLFD) to an NS font name.
9206     Only family is used.
9207     The string returned is temporarily allocated.
9208    -------------------------------------------------------------------------- */
9210   char *name = xmalloc (180);
9211   int i, len;
9212   const char *ret;
9214   if (!strncmp (xlfd, "--", 2))
9215     sscanf (xlfd, "--%*[^-]-%179[^-]-", name);
9216   else
9217     sscanf (xlfd, "-%*[^-]-%179[^-]-", name);
9219   /* stopgap for malformed XLFD input */
9220   if (strlen (name) == 0)
9221     strcpy (name, "Monaco");
9223   /* undo hack in ns_fontname_to_xlfd, converting '$' to '-', '_' to ' '
9224      also uppercase after '-' or ' ' */
9225   name[0] = c_toupper (name[0]);
9226   for (len =strlen (name), i =0; i<len; i++)
9227     {
9228       if (name[i] == '$')
9229         {
9230           name[i] = '-';
9231           if (i+1<len)
9232             name[i+1] = c_toupper (name[i+1]);
9233         }
9234       else if (name[i] == '_')
9235         {
9236           name[i] = ' ';
9237           if (i+1<len)
9238             name[i+1] = c_toupper (name[i+1]);
9239         }
9240     }
9241 /*fprintf (stderr, "converted '%s' to '%s'\n",xlfd,name);  */
9242   ret = [[NSString stringWithUTF8String: name] UTF8String];
9243   xfree (name);
9244   return ret;
9248 void
9249 syms_of_nsterm (void)
9251   NSTRACE ("syms_of_nsterm");
9253   ns_antialias_threshold = 10.0;
9255   /* from 23+ we need to tell emacs what modifiers there are.. */
9256   DEFSYM (Qmodifier_value, "modifier-value");
9257   DEFSYM (Qalt, "alt");
9258   DEFSYM (Qhyper, "hyper");
9259   DEFSYM (Qmeta, "meta");
9260   DEFSYM (Qsuper, "super");
9261   DEFSYM (Qcontrol, "control");
9262   DEFSYM (QUTF8_STRING, "UTF8_STRING");
9264   DEFSYM (Qfile, "file");
9265   DEFSYM (Qurl, "url");
9267   Fput (Qalt, Qmodifier_value, make_number (alt_modifier));
9268   Fput (Qhyper, Qmodifier_value, make_number (hyper_modifier));
9269   Fput (Qmeta, Qmodifier_value, make_number (meta_modifier));
9270   Fput (Qsuper, Qmodifier_value, make_number (super_modifier));
9271   Fput (Qcontrol, Qmodifier_value, make_number (ctrl_modifier));
9273   DEFVAR_LISP ("ns-input-file", ns_input_file,
9274               "The file specified in the last NS event.");
9275   ns_input_file =Qnil;
9277   DEFVAR_LISP ("ns-working-text", ns_working_text,
9278               "String for visualizing working composition sequence.");
9279   ns_working_text =Qnil;
9281   DEFVAR_LISP ("ns-input-font", ns_input_font,
9282               "The font specified in the last NS event.");
9283   ns_input_font =Qnil;
9285   DEFVAR_LISP ("ns-input-fontsize", ns_input_fontsize,
9286               "The fontsize specified in the last NS event.");
9287   ns_input_fontsize =Qnil;
9289   DEFVAR_LISP ("ns-input-line", ns_input_line,
9290                "The line specified in the last NS event.");
9291   ns_input_line =Qnil;
9293   DEFVAR_LISP ("ns-input-spi-name", ns_input_spi_name,
9294                "The service name specified in the last NS event.");
9295   ns_input_spi_name =Qnil;
9297   DEFVAR_LISP ("ns-input-spi-arg", ns_input_spi_arg,
9298                "The service argument specified in the last NS event.");
9299   ns_input_spi_arg =Qnil;
9301   DEFVAR_LISP ("ns-alternate-modifier", ns_alternate_modifier,
9302                "This variable describes the behavior of the alternate or option key.\n\
9303 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9304 that key.\n\
9305 Set to none means that the alternate / option key is not interpreted by Emacs\n\
9306 at all, allowing it to be used at a lower level for accented character entry.");
9307   ns_alternate_modifier = Qmeta;
9309   DEFVAR_LISP ("ns-right-alternate-modifier", ns_right_alternate_modifier,
9310                "This variable describes the behavior of the right alternate or option key.\n\
9311 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9312 that key.\n\
9313 Set to left means be the same key as `ns-alternate-modifier'.\n\
9314 Set to none means that the alternate / option key is not interpreted by Emacs\n\
9315 at all, allowing it to be used at a lower level for accented character entry.");
9316   ns_right_alternate_modifier = Qleft;
9318   DEFVAR_LISP ("ns-command-modifier", ns_command_modifier,
9319                "This variable describes the behavior of the command key.\n\
9320 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9321 that key.");
9322   ns_command_modifier = Qsuper;
9324   DEFVAR_LISP ("ns-right-command-modifier", ns_right_command_modifier,
9325                "This variable describes the behavior of the right command key.\n\
9326 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9327 that key.\n\
9328 Set to left means be the same key as `ns-command-modifier'.\n\
9329 Set to none means that the command / option key is not interpreted by Emacs\n\
9330 at all, allowing it to be used at a lower level for accented character entry.");
9331   ns_right_command_modifier = Qleft;
9333   DEFVAR_LISP ("ns-control-modifier", ns_control_modifier,
9334                "This variable describes the behavior of the control key.\n\
9335 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9336 that key.");
9337   ns_control_modifier = Qcontrol;
9339   DEFVAR_LISP ("ns-right-control-modifier", ns_right_control_modifier,
9340                "This variable describes the behavior of the right control key.\n\
9341 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9342 that key.\n\
9343 Set to left means be the same key as `ns-control-modifier'.\n\
9344 Set to none means that the control / option key is not interpreted by Emacs\n\
9345 at all, allowing it to be used at a lower level for accented character entry.");
9346   ns_right_control_modifier = Qleft;
9348   DEFVAR_LISP ("ns-function-modifier", ns_function_modifier,
9349                "This variable describes the behavior of the function key (on laptops).\n\
9350 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9351 that key.\n\
9352 Set to none means that the function key is not interpreted by Emacs at all,\n\
9353 allowing it to be used at a lower level for accented character entry.");
9354   ns_function_modifier = Qnone;
9356   DEFVAR_LISP ("ns-antialias-text", ns_antialias_text,
9357                "Non-nil (the default) means to render text antialiased.");
9358   ns_antialias_text = Qt;
9360   DEFVAR_LISP ("ns-use-thin-smoothing", ns_use_thin_smoothing,
9361                "Non-nil turns on a font smoothing method that produces thinner strokes.");
9362   ns_use_thin_smoothing = Qnil;
9364   DEFVAR_LISP ("ns-confirm-quit", ns_confirm_quit,
9365                "Whether to confirm application quit using dialog.");
9366   ns_confirm_quit = Qnil;
9368   DEFVAR_LISP ("ns-auto-hide-menu-bar", ns_auto_hide_menu_bar,
9369                doc: /* Non-nil means that the menu bar is hidden, but appears when the mouse is near.
9370 Only works on Mac OS X 10.6 or later.  */);
9371   ns_auto_hide_menu_bar = Qnil;
9373   DEFVAR_BOOL ("ns-use-native-fullscreen", ns_use_native_fullscreen,
9374      doc: /*Non-nil means to use native fullscreen on Mac OS X 10.7 and later.
9375 Nil means use fullscreen the old (< 10.7) way.  The old way works better with
9376 multiple monitors, but lacks tool bar.  This variable is ignored on
9377 Mac OS X < 10.7.  Default is t.  */);
9378   ns_use_native_fullscreen = YES;
9379   ns_last_use_native_fullscreen = ns_use_native_fullscreen;
9381   DEFVAR_BOOL ("ns-use-fullscreen-animation", ns_use_fullscreen_animation,
9382      doc: /*Non-nil means use animation on non-native fullscreen.
9383 For native fullscreen, this does nothing.
9384 Default is nil.  */);
9385   ns_use_fullscreen_animation = NO;
9387   DEFVAR_BOOL ("ns-use-srgb-colorspace", ns_use_srgb_colorspace,
9388      doc: /*Non-nil means to use sRGB colorspace on Mac OS X 10.7 and later.
9389 Note that this does not apply to images.
9390 This variable is ignored on Mac OS X < 10.7 and GNUstep.  */);
9391   ns_use_srgb_colorspace = YES;
9393   DEFVAR_BOOL ("ns-use-mwheel-acceleration",
9394                ns_use_mwheel_acceleration,
9395      doc: /*Non-nil means use macOS's standard mouse wheel acceleration.
9396 This variable is ignored on macOS < 10.7 and GNUstep.  Default is t.  */);
9397   ns_use_mwheel_acceleration = YES;
9399   DEFVAR_LISP ("ns-mwheel-line-height", ns_mwheel_line_height,
9400                doc: /*The number of pixels touchpad scrolling considers one line.
9401 Nil or a non-number means use the default frame line height.
9402 This variable is ignored on macOS < 10.7 and GNUstep.  Default is nil.  */);
9403   ns_mwheel_line_height = Qnil;
9405   DEFVAR_BOOL ("ns-use-mwheel-momentum", ns_use_mwheel_momentum,
9406                doc: /*Non-nil means mouse wheel scrolling uses momentum.
9407 This variable is ignored on macOS < 10.7 and GNUstep.  Default is t.  */);
9408   ns_use_mwheel_momentum = YES;
9410   /* TODO: move to common code */
9411   DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars,
9412                doc: /* Which toolkit scroll bars Emacs uses, if any.
9413 A value of nil means Emacs doesn't use toolkit scroll bars.
9414 With the X Window system, the value is a symbol describing the
9415 X toolkit.  Possible values are: gtk, motif, xaw, or xaw3d.
9416 With MS Windows or Nextstep, the value is t.  */);
9417   Vx_toolkit_scroll_bars = Qt;
9419   DEFVAR_BOOL ("x-use-underline-position-properties",
9420                x_use_underline_position_properties,
9421      doc: /*Non-nil means make use of UNDERLINE_POSITION font properties.
9422 A value of nil means ignore them.  If you encounter fonts with bogus
9423 UNDERLINE_POSITION font properties, for example 7x13 on XFree prior
9424 to 4.1, set this to nil. */);
9425   x_use_underline_position_properties = 0;
9427   DEFVAR_BOOL ("x-underline-at-descent-line",
9428                x_underline_at_descent_line,
9429      doc: /* Non-nil means to draw the underline at the same place as the descent line.
9430 (If `line-spacing' is in effect, that moves the underline lower by
9431 that many pixels.)
9432 A value of nil means to draw the underline according to the value of the
9433 variable `x-use-underline-position-properties', which is usually at the
9434 baseline level.  The default value is nil.  */);
9435   x_underline_at_descent_line = 0;
9437   /* Tell Emacs about this window system.  */
9438   Fprovide (Qns, Qnil);
9440   DEFSYM (Qcocoa, "cocoa");
9441   DEFSYM (Qgnustep, "gnustep");
9443 #ifdef NS_IMPL_COCOA
9444   Fprovide (Qcocoa, Qnil);
9445   syms_of_macfont ();
9446 #else
9447   Fprovide (Qgnustep, Qnil);
9448   syms_of_nsfont ();
9449 #endif