* lisp/net/ange-ftp.el: Use lexical-binding
[emacs.git] / src / nsterm.m
blob07ac8f978f96e04d5ecca7721e87ab8e3779549a
1 /* NeXT/Open/GNUstep / macOS communication module.      -*- coding: utf-8 -*-
3 Copyright (C) 1989, 1993-1994, 2005-2006, 2008-2017 Free Software
4 Foundation, Inc.
6 This file is part of GNU Emacs.
8 GNU Emacs is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or (at
11 your option) any later version.
13 GNU Emacs is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
22 Originally by Carl Edman
23 Updated by Christian Limpach (chris@nice.ch)
24 OpenStep/Rhapsody port by Scott Bender (sbender@harmony-ds.com)
25 macOS/Aqua port by Christophe de Dinechin (descubes@earthlink.net)
26 GNUstep port and post-20 update by Adrian Robert (arobert@cogsci.ucsd.edu)
29 /* This should be the first include, as it may set up #defines affecting
30    interpretation of even the system includes. */
31 #include <config.h>
33 #include <fcntl.h>
34 #include <math.h>
35 #include <pthread.h>
36 #include <sys/types.h>
37 #include <time.h>
38 #include <signal.h>
39 #include <unistd.h>
41 #include <c-ctype.h>
42 #include <c-strcase.h>
43 #include <ftoastr.h>
45 #include "lisp.h"
46 #include "blockinput.h"
47 #include "sysselect.h"
48 #include "nsterm.h"
49 #include "systime.h"
50 #include "character.h"
51 #include "fontset.h"
52 #include "composite.h"
53 #include "ccl.h"
55 #include "termhooks.h"
56 #include "termchar.h"
57 #include "menu.h"
58 #include "window.h"
59 #include "keyboard.h"
60 #include "buffer.h"
61 #include "font.h"
63 #ifdef NS_IMPL_GNUSTEP
64 #include "process.h"
65 #endif
67 #ifdef NS_IMPL_COCOA
68 #include "macfont.h"
69 #endif
71 static EmacsMenu *dockMenu;
72 #ifdef NS_IMPL_COCOA
73 static EmacsMenu *mainMenu;
74 #endif
76 /* ==========================================================================
78    NSTRACE, Trace support.
80    ========================================================================== */
82 #if NSTRACE_ENABLED
84 /* The following use "volatile" since they can be accessed from
85    parallel threads. */
86 volatile int nstrace_num = 0;
87 volatile int nstrace_depth = 0;
89 /* When 0, no trace is emitted.  This is used by NSTRACE_WHEN and
90    NSTRACE_UNLESS to silence functions called.
92    TODO: This should really be a thread-local variable, to avoid that
93    a function with disabled trace thread silence trace output in
94    another.  However, in practice this seldom is a problem. */
95 volatile int nstrace_enabled_global = 1;
97 /* Called when nstrace_enabled goes out of scope. */
98 void nstrace_leave(int * pointer_to_nstrace_enabled)
100   if (*pointer_to_nstrace_enabled)
101     {
102       --nstrace_depth;
103     }
107 /* Called when nstrace_saved_enabled_global goes out of scope. */
108 void nstrace_restore_global_trace_state(int * pointer_to_saved_enabled_global)
110   nstrace_enabled_global = *pointer_to_saved_enabled_global;
114 char const * nstrace_fullscreen_type_name (int fs_type)
116   switch (fs_type)
117     {
118     case -1:                   return "-1";
119     case FULLSCREEN_NONE:      return "FULLSCREEN_NONE";
120     case FULLSCREEN_WIDTH:     return "FULLSCREEN_WIDTH";
121     case FULLSCREEN_HEIGHT:    return "FULLSCREEN_HEIGHT";
122     case FULLSCREEN_BOTH:      return "FULLSCREEN_BOTH";
123     case FULLSCREEN_MAXIMIZED: return "FULLSCREEN_MAXIMIZED";
124     default:                   return "FULLSCREEN_?????";
125     }
127 #endif
130 /* ==========================================================================
132    NSColor, EmacsColor category.
134    ========================================================================== */
135 @implementation NSColor (EmacsColor)
136 + (NSColor *)colorForEmacsRed:(CGFloat)red green:(CGFloat)green
137                          blue:(CGFloat)blue alpha:(CGFloat)alpha
139 #if defined (NS_IMPL_COCOA) \
140   && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
141   if (ns_use_srgb_colorspace
142 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
143       && [NSColor respondsToSelector:
144                     @selector(colorWithSRGBRed:green:blue:alpha:)]
145 #endif
146       )
147     return [NSColor colorWithSRGBRed: red
148                                green: green
149                                 blue: blue
150                                alpha: alpha];
151 #endif
152   return [NSColor colorWithCalibratedRed: red
153                                    green: green
154                                     blue: blue
155                                    alpha: alpha];
158 - (NSColor *)colorUsingDefaultColorSpace
160   /* FIXMES: We're checking for colorWithSRGBRed here so this will
161      only work in the same place as in the method above.  It should
162      really be a check whether we're on macOS 10.7 or above. */
163 #if defined (NS_IMPL_COCOA) \
164   && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
165   if (ns_use_srgb_colorspace
166 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
167       && [NSColor respondsToSelector:
168                     @selector(colorWithSRGBRed:green:blue:alpha:)]
169 #endif
170       )
171     return [self colorUsingColorSpace: [NSColorSpace sRGBColorSpace]];
172 #endif
173   return [self colorUsingColorSpaceName: NSCalibratedRGBColorSpace];
176 @end
178 /* ==========================================================================
180     Local declarations
182    ========================================================================== */
184 /* Convert a symbol indexed with an NSxxx value to a value as defined
185    in keyboard.c (lispy_function_key). I hope this is a correct way
186    of doing things... */
187 static unsigned convert_ns_to_X_keysym[] =
189   NSHomeFunctionKey,            0x50,
190   NSLeftArrowFunctionKey,       0x51,
191   NSUpArrowFunctionKey,         0x52,
192   NSRightArrowFunctionKey,      0x53,
193   NSDownArrowFunctionKey,       0x54,
194   NSPageUpFunctionKey,          0x55,
195   NSPageDownFunctionKey,        0x56,
196   NSEndFunctionKey,             0x57,
197   NSBeginFunctionKey,           0x58,
198   NSSelectFunctionKey,          0x60,
199   NSPrintFunctionKey,           0x61,
200   NSClearLineFunctionKey,       0x0B,
201   NSExecuteFunctionKey,         0x62,
202   NSInsertFunctionKey,          0x63,
203   NSUndoFunctionKey,            0x65,
204   NSRedoFunctionKey,            0x66,
205   NSMenuFunctionKey,            0x67,
206   NSFindFunctionKey,            0x68,
207   NSHelpFunctionKey,            0x6A,
208   NSBreakFunctionKey,           0x6B,
210   NSF1FunctionKey,              0xBE,
211   NSF2FunctionKey,              0xBF,
212   NSF3FunctionKey,              0xC0,
213   NSF4FunctionKey,              0xC1,
214   NSF5FunctionKey,              0xC2,
215   NSF6FunctionKey,              0xC3,
216   NSF7FunctionKey,              0xC4,
217   NSF8FunctionKey,              0xC5,
218   NSF9FunctionKey,              0xC6,
219   NSF10FunctionKey,             0xC7,
220   NSF11FunctionKey,             0xC8,
221   NSF12FunctionKey,             0xC9,
222   NSF13FunctionKey,             0xCA,
223   NSF14FunctionKey,             0xCB,
224   NSF15FunctionKey,             0xCC,
225   NSF16FunctionKey,             0xCD,
226   NSF17FunctionKey,             0xCE,
227   NSF18FunctionKey,             0xCF,
228   NSF19FunctionKey,             0xD0,
229   NSF20FunctionKey,             0xD1,
230   NSF21FunctionKey,             0xD2,
231   NSF22FunctionKey,             0xD3,
232   NSF23FunctionKey,             0xD4,
233   NSF24FunctionKey,             0xD5,
235   NSBackspaceCharacter,         0x08,  /* 8: Not on some KBs. */
236   NSDeleteCharacter,            0xFF,  /* 127: Big 'delete' key upper right. */
237   NSDeleteFunctionKey,          0x9F,  /* 63272: Del forw key off main array. */
239   NSTabCharacter,               0x09,
240   0x19,                         0x09,  /* left tab->regular since pass shift */
241   NSCarriageReturnCharacter,    0x0D,
242   NSNewlineCharacter,           0x0D,
243   NSEnterCharacter,             0x8D,
245   0x41|NSEventModifierFlagNumericPad,   0xAE,  /* KP_Decimal */
246   0x43|NSEventModifierFlagNumericPad,   0xAA,  /* KP_Multiply */
247   0x45|NSEventModifierFlagNumericPad,   0xAB,  /* KP_Add */
248   0x4B|NSEventModifierFlagNumericPad,   0xAF,  /* KP_Divide */
249   0x4E|NSEventModifierFlagNumericPad,   0xAD,  /* KP_Subtract */
250   0x51|NSEventModifierFlagNumericPad,   0xBD,  /* KP_Equal */
251   0x52|NSEventModifierFlagNumericPad,   0xB0,  /* KP_0 */
252   0x53|NSEventModifierFlagNumericPad,   0xB1,  /* KP_1 */
253   0x54|NSEventModifierFlagNumericPad,   0xB2,  /* KP_2 */
254   0x55|NSEventModifierFlagNumericPad,   0xB3,  /* KP_3 */
255   0x56|NSEventModifierFlagNumericPad,   0xB4,  /* KP_4 */
256   0x57|NSEventModifierFlagNumericPad,   0xB5,  /* KP_5 */
257   0x58|NSEventModifierFlagNumericPad,   0xB6,  /* KP_6 */
258   0x59|NSEventModifierFlagNumericPad,   0xB7,  /* KP_7 */
259   0x5B|NSEventModifierFlagNumericPad,   0xB8,  /* KP_8 */
260   0x5C|NSEventModifierFlagNumericPad,   0xB9,  /* KP_9 */
262   0x1B,                         0x1B   /* escape */
265 /* On macOS picks up the default NSGlobalDomain AppleAntiAliasingThreshold,
266    the maximum font size to NOT antialias.  On GNUstep there is currently
267    no way to control this behavior. */
268 float ns_antialias_threshold;
270 NSArray *ns_send_types = 0, *ns_return_types = 0;
271 static NSArray *ns_drag_types = 0;
272 NSString *ns_app_name = @"Emacs";  /* default changed later */
274 /* Display variables */
275 struct ns_display_info *x_display_list; /* Chain of existing displays */
276 long context_menu_value = 0;
278 /* display update */
279 static struct frame *ns_updating_frame;
280 static NSView *focus_view = NULL;
281 static int ns_window_num = 0;
282 #ifdef NS_IMPL_GNUSTEP
283 static NSRect uRect;            // TODO: This is dead, remove it?
284 #endif
285 static BOOL gsaved = NO;
286 static BOOL ns_fake_keydown = NO;
287 #ifdef NS_IMPL_COCOA
288 static BOOL ns_menu_bar_is_hidden = NO;
289 #endif
290 /*static int debug_lock = 0; */
292 /* event loop */
293 static BOOL send_appdefined = YES;
294 #define NO_APPDEFINED_DATA (-8)
295 static int last_appdefined_event_data = NO_APPDEFINED_DATA;
296 static NSTimer *timed_entry = 0;
297 static NSTimer *scroll_repeat_entry = nil;
298 static fd_set select_readfds, select_writefds;
299 enum { SELECT_HAVE_READ = 1, SELECT_HAVE_WRITE = 2, SELECT_HAVE_TMO = 4 };
300 static int select_nfds = 0, select_valid = 0;
301 static struct timespec select_timeout = { 0, 0 };
302 static int selfds[2] = { -1, -1 };
303 static pthread_mutex_t select_mutex;
304 static NSAutoreleasePool *outerpool;
305 static struct input_event *emacs_event = NULL;
306 static struct input_event *q_event_ptr = NULL;
307 static int n_emacs_events_pending = 0;
308 static NSMutableArray *ns_pending_files, *ns_pending_service_names,
309   *ns_pending_service_args;
310 static BOOL ns_do_open_file = NO;
311 static BOOL ns_last_use_native_fullscreen;
313 /* Non-zero means that a HELP_EVENT has been generated since Emacs
314    start.  */
316 static BOOL any_help_event_p = NO;
318 static struct {
319   struct input_event *q;
320   int nr, cap;
321 } hold_event_q = {
322   NULL, 0, 0
325 static NSString *represented_filename = nil;
326 static struct frame *represented_frame = 0;
328 #ifdef NS_IMPL_COCOA
330  * State for pending menu activation:
331  * MENU_NONE     Normal state
332  * MENU_PENDING  A menu has been clicked on, but has been canceled so we can
333  *               run lisp to update the menu.
334  * MENU_OPENING  Menu is up to date, and the click event is redone so the menu
335  *               will open.
336  */
337 #define MENU_NONE 0
338 #define MENU_PENDING 1
339 #define MENU_OPENING 2
340 static int menu_will_open_state = MENU_NONE;
342 /* Saved position for menu click.  */
343 static CGPoint menu_mouse_point;
344 #endif
346 /* Convert modifiers in a NeXTstep event to emacs style modifiers.  */
347 #define NS_FUNCTION_KEY_MASK 0x800000
348 #define NSLeftControlKeyMask    (0x000001 | NSEventModifierFlagControl)
349 #define NSRightControlKeyMask   (0x002000 | NSEventModifierFlagControl)
350 #define NSLeftCommandKeyMask    (0x000008 | NSEventModifierFlagCommand)
351 #define NSRightCommandKeyMask   (0x000010 | NSEventModifierFlagCommand)
352 #define NSLeftAlternateKeyMask  (0x000020 | NSEventModifierFlagOption)
353 #define NSRightAlternateKeyMask (0x000040 | NSEventModifierFlagOption)
354 #define EV_MODIFIERS2(flags)                          \
355     (((flags & NSEventModifierFlagHelp) ?           \
356            hyper_modifier : 0)                        \
357      | (!EQ (ns_right_alternate_modifier, Qleft) && \
358         ((flags & NSRightAlternateKeyMask) \
359          == NSRightAlternateKeyMask) ? \
360            parse_solitary_modifier (ns_right_alternate_modifier) : 0) \
361      | ((flags & NSEventModifierFlagOption) ?                 \
362            parse_solitary_modifier (ns_alternate_modifier) : 0)   \
363      | ((flags & NSEventModifierFlagShift) ?     \
364            shift_modifier : 0)                        \
365      | (!EQ (ns_right_control_modifier, Qleft) && \
366         ((flags & NSRightControlKeyMask) \
367          == NSRightControlKeyMask) ? \
368            parse_solitary_modifier (ns_right_control_modifier) : 0) \
369      | ((flags & NSEventModifierFlagControl) ?      \
370            parse_solitary_modifier (ns_control_modifier) : 0)     \
371      | ((flags & NS_FUNCTION_KEY_MASK) ?  \
372            parse_solitary_modifier (ns_function_modifier) : 0)    \
373      | (!EQ (ns_right_command_modifier, Qleft) && \
374         ((flags & NSRightCommandKeyMask) \
375          == NSRightCommandKeyMask) ? \
376            parse_solitary_modifier (ns_right_command_modifier) : 0) \
377      | ((flags & NSEventModifierFlagCommand) ?      \
378            parse_solitary_modifier (ns_command_modifier):0))
379 #define EV_MODIFIERS(e) EV_MODIFIERS2 ([e modifierFlags])
381 #define EV_UDMODIFIERS(e)                                      \
382     ((([e type] == NSEventTypeLeftMouseDown) ? down_modifier : 0)       \
383      | (([e type] == NSEventTypeRightMouseDown) ? down_modifier : 0)    \
384      | (([e type] == NSEventTypeOtherMouseDown) ? down_modifier : 0)    \
385      | (([e type] == NSEventTypeLeftMouseDragged) ? down_modifier : 0)  \
386      | (([e type] == NSEventTypeRightMouseDragged) ? down_modifier : 0) \
387      | (([e type] == NSEventTypeOtherMouseDragged) ? down_modifier : 0) \
388      | (([e type] == NSEventTypeLeftMouseUp)   ? up_modifier   : 0)     \
389      | (([e type] == NSEventTypeRightMouseUp)   ? up_modifier   : 0)    \
390      | (([e type] == NSEventTypeOtherMouseUp)   ? up_modifier   : 0))
392 #define EV_BUTTON(e)                                                         \
393     ((([e type] == NSEventTypeLeftMouseDown) || ([e type] == NSEventTypeLeftMouseUp)) ? 0 :    \
394       (([e type] == NSEventTypeRightMouseDown) || ([e type] == NSEventTypeRightMouseUp)) ? 2 : \
395      [e buttonNumber] - 1)
397 /* Convert the time field to a timestamp in milliseconds. */
398 #define EV_TIMESTAMP(e) ([e timestamp] * 1000)
400 /* This is a piece of code which is common to all the event handling
401    methods.  Maybe it should even be a function.  */
402 #define EV_TRAILER(e)                                                   \
403   {                                                                     \
404     XSETFRAME (emacs_event->frame_or_window, emacsframe);               \
405     EV_TRAILER2 (e);                                                    \
406   }
408 #define EV_TRAILER2(e)                                                  \
409   {                                                                     \
410       if (e) emacs_event->timestamp = EV_TIMESTAMP (e);                 \
411       if (q_event_ptr)                                                  \
412         {                                                               \
413           Lisp_Object tem = Vinhibit_quit;                              \
414           Vinhibit_quit = Qt;                                           \
415           n_emacs_events_pending++;                                     \
416           kbd_buffer_store_event_hold (emacs_event, q_event_ptr);       \
417           Vinhibit_quit = tem;                                          \
418         }                                                               \
419       else                                                              \
420         hold_event (emacs_event);                                       \
421       EVENT_INIT (*emacs_event);                                        \
422       ns_send_appdefined (-1);                                          \
423     }
426 /* These flags will be OR'd or XOR'd with the NSWindow's styleMask
427    property depending on what we're doing. */
428 #define FRAME_DECORATED_FLAGS (NSWindowStyleMaskTitled              \
429                                | NSWindowStyleMaskResizable         \
430                                | NSWindowStyleMaskMiniaturizable    \
431                                | NSWindowStyleMaskClosable)
432 #define FRAME_UNDECORATED_FLAGS NSWindowStyleMaskBorderless
434 /* TODO: get rid of need for these forward declarations */
435 static void ns_condemn_scroll_bars (struct frame *f);
436 static void ns_judge_scroll_bars (struct frame *f);
439 /* ==========================================================================
441     Utilities
443    ========================================================================== */
445 void
446 ns_set_represented_filename (NSString *fstr, struct frame *f)
448   represented_filename = [fstr retain];
449   represented_frame = f;
452 void
453 ns_init_events (struct input_event *ev)
455   EVENT_INIT (*ev);
456   emacs_event = ev;
459 void
460 ns_finish_events (void)
462   emacs_event = NULL;
465 static void
466 hold_event (struct input_event *event)
468   if (hold_event_q.nr == hold_event_q.cap)
469     {
470       if (hold_event_q.cap == 0) hold_event_q.cap = 10;
471       else hold_event_q.cap *= 2;
472       hold_event_q.q =
473         xrealloc (hold_event_q.q, hold_event_q.cap * sizeof *hold_event_q.q);
474     }
476   hold_event_q.q[hold_event_q.nr++] = *event;
477   /* Make sure ns_read_socket is called, i.e. we have input.  */
478   raise (SIGIO);
479   send_appdefined = YES;
482 static Lisp_Object
483 append2 (Lisp_Object list, Lisp_Object item)
484 /* --------------------------------------------------------------------------
485    Utility to append to a list
486    -------------------------------------------------------------------------- */
488   return CALLN (Fnconc, list, list1 (item));
492 const char *
493 ns_etc_directory (void)
494 /* If running as a self-contained app bundle, return as a string the
495    filename of the etc directory, if present; else nil.  */
497   NSBundle *bundle = [NSBundle mainBundle];
498   NSString *resourceDir = [bundle resourcePath];
499   NSString *resourcePath;
500   NSFileManager *fileManager = [NSFileManager defaultManager];
501   BOOL isDir;
503   resourcePath = [resourceDir stringByAppendingPathComponent: @"etc"];
504   if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
505     {
506       if (isDir) return [resourcePath UTF8String];
507     }
508   return NULL;
512 const char *
513 ns_exec_path (void)
514 /* If running as a self-contained app bundle, return as a path string
515    the filenames of the libexec and bin directories, ie libexec:bin.
516    Otherwise, return nil.
517    Normally, Emacs does not add its own bin/ directory to the PATH.
518    However, a self-contained NS build has a different layout, with
519    bin/ and libexec/ subdirectories in the directory that contains
520    Emacs.app itself.
521    We put libexec first, because init_callproc_1 uses the first
522    element to initialize exec-directory.  An alternative would be
523    for init_callproc to check for invocation-directory/libexec.
526   NSBundle *bundle = [NSBundle mainBundle];
527   NSString *resourceDir = [bundle resourcePath];
528   NSString *binDir = [bundle bundlePath];
529   NSString *resourcePath, *resourcePaths;
530   NSRange range;
531   NSString *pathSeparator = [NSString stringWithFormat: @"%c", SEPCHAR];
532   NSFileManager *fileManager = [NSFileManager defaultManager];
533   NSArray *paths;
534   NSEnumerator *pathEnum;
535   BOOL isDir;
537   range = [resourceDir rangeOfString: @"Contents"];
538   if (range.location != NSNotFound)
539     {
540       binDir = [binDir stringByAppendingPathComponent: @"Contents"];
541 #ifdef NS_IMPL_COCOA
542       binDir = [binDir stringByAppendingPathComponent: @"MacOS"];
543 #endif
544     }
546   paths = [binDir stringsByAppendingPaths:
547                 [NSArray arrayWithObjects: @"libexec", @"bin", nil]];
548   pathEnum = [paths objectEnumerator];
549   resourcePaths = @"";
551   while ((resourcePath = [pathEnum nextObject]))
552     {
553       if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
554         if (isDir)
555           {
556             if ([resourcePaths length] > 0)
557               resourcePaths
558                 = [resourcePaths stringByAppendingString: pathSeparator];
559             resourcePaths
560               = [resourcePaths stringByAppendingString: resourcePath];
561           }
562     }
563   if ([resourcePaths length] > 0) return [resourcePaths UTF8String];
565   return NULL;
569 const char *
570 ns_load_path (void)
571 /* If running as a self-contained app bundle, return as a path string
572    the filenames of the site-lisp and lisp directories.
573    Ie, site-lisp:lisp.  Otherwise, return nil.  */
575   NSBundle *bundle = [NSBundle mainBundle];
576   NSString *resourceDir = [bundle resourcePath];
577   NSString *resourcePath, *resourcePaths;
578   NSString *pathSeparator = [NSString stringWithFormat: @"%c", SEPCHAR];
579   NSFileManager *fileManager = [NSFileManager defaultManager];
580   BOOL isDir;
581   NSArray *paths = [resourceDir stringsByAppendingPaths:
582                               [NSArray arrayWithObjects:
583                                          @"site-lisp", @"lisp", nil]];
584   NSEnumerator *pathEnum = [paths objectEnumerator];
585   resourcePaths = @"";
587   /* Hack to skip site-lisp.  */
588   if (no_site_lisp) resourcePath = [pathEnum nextObject];
590   while ((resourcePath = [pathEnum nextObject]))
591     {
592       if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
593         if (isDir)
594           {
595             if ([resourcePaths length] > 0)
596               resourcePaths
597                 = [resourcePaths stringByAppendingString: pathSeparator];
598             resourcePaths
599               = [resourcePaths stringByAppendingString: resourcePath];
600           }
601     }
602   if ([resourcePaths length] > 0) return [resourcePaths UTF8String];
604   return NULL;
608 void
609 ns_init_locale (void)
610 /* macOS doesn't set any environment variables for the locale when run
611    from the GUI. Get the locale from the OS and set LANG. */
613   NSLocale *locale = [NSLocale currentLocale];
615   NSTRACE ("ns_init_locale");
617   @try
618     {
619       /* It seems macOS should probably use UTF-8 everywhere.
620          'localeIdentifier' does not specify the encoding, and I can't
621          find any way to get the OS to tell us which encoding to use,
622          so hard-code '.UTF-8'. */
623       NSString *localeID = [NSString stringWithFormat:@"%@.UTF-8",
624                                      [locale localeIdentifier]];
626       /* Set LANG to locale, but not if LANG is already set. */
627       setenv("LANG", [localeID UTF8String], 0);
628     }
629   @catch (NSException *e)
630     {
631       NSLog (@"Locale detection failed: %@: %@", [e name], [e reason]);
632     }
636 void
637 ns_release_object (void *obj)
638 /* --------------------------------------------------------------------------
639     Release an object (callable from C)
640    -------------------------------------------------------------------------- */
642     [(id)obj release];
646 void
647 ns_retain_object (void *obj)
648 /* --------------------------------------------------------------------------
649     Retain an object (callable from C)
650    -------------------------------------------------------------------------- */
652     [(id)obj retain];
656 void *
657 ns_alloc_autorelease_pool (void)
658 /* --------------------------------------------------------------------------
659      Allocate a pool for temporary objects (callable from C)
660    -------------------------------------------------------------------------- */
662   return [[NSAutoreleasePool alloc] init];
666 void
667 ns_release_autorelease_pool (void *pool)
668 /* --------------------------------------------------------------------------
669      Free a pool and temporary objects it refers to (callable from C)
670    -------------------------------------------------------------------------- */
672   ns_release_object (pool);
676 static BOOL
677 ns_menu_bar_should_be_hidden (void)
678 /* True, if the menu bar should be hidden.  */
680   return !NILP (ns_auto_hide_menu_bar)
681     && [NSApp respondsToSelector:@selector(setPresentationOptions:)];
685 struct EmacsMargins
687   CGFloat top;
688   CGFloat bottom;
689   CGFloat left;
690   CGFloat right;
694 static struct EmacsMargins
695 ns_screen_margins (NSScreen *screen)
696 /* The parts of SCREEN used by the operating system.  */
698   NSTRACE ("ns_screen_margins");
700   struct EmacsMargins margins;
702   NSRect screenFrame = [screen frame];
703   NSRect screenVisibleFrame = [screen visibleFrame];
705   /* Sometimes, visibleFrame isn't up-to-date with respect to a hidden
706      menu bar, check this explicitly.  */
707   if (ns_menu_bar_should_be_hidden())
708     {
709       margins.top = 0;
710     }
711   else
712     {
713       CGFloat frameTop = screenFrame.origin.y + screenFrame.size.height;
714       CGFloat visibleFrameTop = (screenVisibleFrame.origin.y
715                                  + screenVisibleFrame.size.height);
717       margins.top = frameTop - visibleFrameTop;
718     }
720   {
721     CGFloat frameRight = screenFrame.origin.x + screenFrame.size.width;
722     CGFloat visibleFrameRight = (screenVisibleFrame.origin.x
723                                  + screenVisibleFrame.size.width);
724     margins.right = frameRight - visibleFrameRight;
725   }
727   margins.bottom = screenVisibleFrame.origin.y - screenFrame.origin.y;
728   margins.left   = screenVisibleFrame.origin.x - screenFrame.origin.x;
730   NSTRACE_MSG ("left:%g right:%g top:%g bottom:%g",
731                margins.left,
732                margins.right,
733                margins.top,
734                margins.bottom);
736   return margins;
740 /* A screen margin between 1 and DOCK_IGNORE_LIMIT (inclusive) is
741    assumed to contain a hidden dock.  macOS currently use 4 pixels for
742    this, however, to be future compatible, a larger value is used.  */
743 #define DOCK_IGNORE_LIMIT 6
745 static struct EmacsMargins
746 ns_screen_margins_ignoring_hidden_dock (NSScreen *screen)
747 /* The parts of SCREEN used by the operating system, excluding the parts
748 reserved for an hidden dock.  */
750   NSTRACE ("ns_screen_margins_ignoring_hidden_dock");
752   struct EmacsMargins margins = ns_screen_margins(screen);
754   /* macOS (currently) reserved 4 pixels along the edge where a hidden
755      dock is located.  Unfortunately, it's not possible to find the
756      location and information about if the dock is hidden.  Instead,
757      it is assumed that if the margin of an edge is less than
758      DOCK_IGNORE_LIMIT, it contains a hidden dock.  */
759   if (margins.left <= DOCK_IGNORE_LIMIT)
760     {
761       margins.left = 0;
762     }
763   if (margins.right <= DOCK_IGNORE_LIMIT)
764     {
765       margins.right = 0;
766     }
767   if (margins.top <= DOCK_IGNORE_LIMIT)
768     {
769       margins.top = 0;
770     }
771   /* Note: This doesn't occur in current versions of macOS, but
772      included for completeness and future compatibility.  */
773   if (margins.bottom <= DOCK_IGNORE_LIMIT)
774     {
775       margins.bottom = 0;
776     }
778   NSTRACE_MSG ("left:%g right:%g top:%g bottom:%g",
779                margins.left,
780                margins.right,
781                margins.top,
782                margins.bottom);
784   return margins;
788 static CGFloat
789 ns_menu_bar_height (NSScreen *screen)
790 /* The height of the menu bar, if visible.
792    Note: Don't use this when fullscreen is enabled -- the screen
793    sometimes includes, sometimes excludes the menu bar area.  */
795   struct EmacsMargins margins = ns_screen_margins(screen);
797   CGFloat res = margins.top;
799   NSTRACE ("ns_menu_bar_height " NSTRACE_FMT_RETURN " %.0f", res);
801   return res;
805 /* ==========================================================================
807     Focus (clipping) and screen update
809    ========================================================================== */
812 // Window constraining
813 // -------------------
815 // To ensure that the windows are not placed under the menu bar, they
816 // are typically moved by the call-back constrainFrameRect. However,
817 // by overriding it, it's possible to inhibit this, leaving the window
818 // in it's original position.
820 // It's possible to hide the menu bar. However, technically, it's only
821 // possible to hide it when the application is active. To ensure that
822 // this work properly, the menu bar and window constraining are
823 // deferred until the application becomes active.
825 // Even though it's not possible to manually move a window above the
826 // top of the screen, it is allowed if it's done programmatically,
827 // when the menu is hidden. This allows the editable area to cover the
828 // full screen height.
830 // Test cases
831 // ----------
833 // Use the following extra files:
835 //    init.el:
836 //       ;; Hide menu and place frame slightly above the top of the screen.
837 //       (setq ns-auto-hide-menu-bar t)
838 //       (set-frame-position (selected-frame) 0 -20)
840 // Test 1:
842 //    emacs -Q -l init.el
844 //    Result: No menu bar, and the title bar should be above the screen.
846 // Test 2:
848 //    emacs -Q
850 //    Result: Menu bar visible, frame placed immediately below the menu.
853 static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
855   NSTRACE ("constrain_frame_rect(" NSTRACE_FMT_RECT ")",
856              NSTRACE_ARG_RECT (frameRect));
858   // --------------------
859   // Collect information about the screen the frame is covering.
860   //
862   NSArray *screens = [NSScreen screens];
863   NSUInteger nr_screens = [screens count];
865   int i;
867   // The height of the menu bar, if present in any screen the frame is
868   // displayed in.
869   int menu_bar_height = 0;
871   // A rectangle covering all the screen the frame is displayed in.
872   NSRect multiscreenRect = NSMakeRect(0, 0, 0, 0);
873   for (i = 0; i < nr_screens; ++i )
874     {
875       NSScreen *s = [screens objectAtIndex: i];
876       NSRect scrRect = [s frame];
878       NSTRACE_MSG ("Screen %d: " NSTRACE_FMT_RECT,
879                    i, NSTRACE_ARG_RECT (scrRect));
881       if (NSIntersectionRect (frameRect, scrRect).size.height != 0)
882         {
883           multiscreenRect = NSUnionRect (multiscreenRect, scrRect);
885           if (!isFullscreen)
886             {
887               CGFloat screen_menu_bar_height = ns_menu_bar_height (s);
888               menu_bar_height = max(menu_bar_height, screen_menu_bar_height);
889             }
890         }
891     }
893   NSTRACE_RECT ("multiscreenRect", multiscreenRect);
895   NSTRACE_MSG ("menu_bar_height: %d", menu_bar_height);
897   if (multiscreenRect.size.width == 0
898       || multiscreenRect.size.height == 0)
899     {
900       // Failed to find any monitor, give up.
901       NSTRACE_MSG ("multiscreenRect empty");
902       NSTRACE_RETURN_RECT (frameRect);
903       return frameRect;
904     }
907   // --------------------
908   // Find a suitable placement.
909   //
911   if (ns_menu_bar_should_be_hidden())
912     {
913       // When the menu bar is hidden, the user may place part of the
914       // frame above the top of the screen, for example to hide the
915       // title bar.
916       //
917       // Hence, keep the original position.
918     }
919   else
920     {
921       // Ensure that the frame is below the menu bar, or below the top
922       // of the screen.
923       //
924       // This assume that the menu bar is placed at the top in the
925       // rectangle that covers the monitors.  (It doesn't have to be,
926       // but if it's not it's hard to do anything useful.)
927       CGFloat topOfWorkArea = (multiscreenRect.origin.y
928                                + multiscreenRect.size.height
929                                - menu_bar_height);
931       CGFloat topOfFrame = frameRect.origin.y + frameRect.size.height;
932       if (topOfFrame > topOfWorkArea)
933         {
934           frameRect.origin.y -= topOfFrame - topOfWorkArea;
935           NSTRACE_RECT ("After placement adjust", frameRect);
936         }
937     }
939   // Include the following section to restrict frame to the screens.
940   // (If so, update it to allow the frame to stretch down below the
941   // screen.)
942 #if 0
943   // --------------------
944   // Ensure frame doesn't stretch below the screens.
945   //
947   CGFloat diff = multiscreenRect.origin.y - frameRect.origin.y;
949   if (diff > 0)
950     {
951       frameRect.origin.y = multiscreenRect.origin.y;
952       frameRect.size.height -= diff;
953     }
954 #endif
956   NSTRACE_RETURN_RECT (frameRect);
957   return frameRect;
961 static void
962 ns_constrain_all_frames (void)
963 /* --------------------------------------------------------------------------
964      Ensure that the menu bar doesn't cover any frames.
965    -------------------------------------------------------------------------- */
967   Lisp_Object tail, frame;
969   NSTRACE ("ns_constrain_all_frames");
971   block_input ();
973   FOR_EACH_FRAME (tail, frame)
974     {
975       struct frame *f = XFRAME (frame);
976       if (FRAME_NS_P (f))
977         {
978           EmacsView *view = FRAME_NS_VIEW (f);
980           if (![view isFullscreen])
981             {
982               [[view window]
983                 setFrame:constrain_frame_rect([[view window] frame], false)
984                  display:NO];
985             }
986         }
987     }
989   unblock_input ();
993 static void
994 ns_update_auto_hide_menu_bar (void)
995 /* --------------------------------------------------------------------------
996      Show or hide the menu bar, based on user setting.
997    -------------------------------------------------------------------------- */
999 #ifdef NS_IMPL_COCOA
1000   NSTRACE ("ns_update_auto_hide_menu_bar");
1002   block_input ();
1004   if (NSApp != nil && [NSApp isActive])
1005     {
1006       // Note, "setPresentationOptions" triggers an error unless the
1007       // application is active.
1008       BOOL menu_bar_should_be_hidden = ns_menu_bar_should_be_hidden ();
1010       if (menu_bar_should_be_hidden != ns_menu_bar_is_hidden)
1011         {
1012           NSApplicationPresentationOptions options
1013             = NSApplicationPresentationDefault;
1015           if (menu_bar_should_be_hidden)
1016             options |= NSApplicationPresentationAutoHideMenuBar
1017               | NSApplicationPresentationAutoHideDock;
1019           [NSApp setPresentationOptions: options];
1021           ns_menu_bar_is_hidden = menu_bar_should_be_hidden;
1023           if (!ns_menu_bar_is_hidden)
1024             {
1025               ns_constrain_all_frames ();
1026             }
1027         }
1028     }
1030   unblock_input ();
1031 #endif
1035 static void
1036 ns_update_begin (struct frame *f)
1037 /* --------------------------------------------------------------------------
1038    Prepare for a grouped sequence of drawing calls
1039    external (RIF) call; whole frame, called before update_window_begin
1040    -------------------------------------------------------------------------- */
1042   EmacsView *view = FRAME_NS_VIEW (f);
1043   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_begin");
1045   ns_update_auto_hide_menu_bar ();
1047 #ifdef NS_IMPL_COCOA
1048   if ([view isFullscreen] && [view fsIsNative])
1049   {
1050     // Fix reappearing tool bar in fullscreen for Mac OS X 10.7
1051     BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (f) ? YES : NO;
1052     NSToolbar *toolbar = [FRAME_NS_VIEW (f) toolbar];
1053     if (! tbar_visible != ! [toolbar isVisible])
1054       [toolbar setVisible: tbar_visible];
1055   }
1056 #endif
1058   ns_updating_frame = f;
1059   [view lockFocus];
1061   /* drawRect may have been called for say the minibuffer, and then clip path
1062      is for the minibuffer.  But the display engine may draw more because
1063      we have set the frame as garbaged.  So reset clip path to the whole
1064      view.  */
1065 #ifdef NS_IMPL_COCOA
1066   {
1067     NSBezierPath *bp;
1068     NSRect r = [view frame];
1069     NSRect cr = [[view window] frame];
1070     /* If a large frame size is set, r may be larger than the window frame
1071        before constrained.  In that case don't change the clip path, as we
1072        will clear in to the tool bar and title bar.  */
1073     if (r.size.height
1074         + FRAME_NS_TITLEBAR_HEIGHT (f)
1075         + FRAME_TOOLBAR_HEIGHT (f) <= cr.size.height)
1076       {
1077         bp = [[NSBezierPath bezierPathWithRect: r] retain];
1078         [bp setClip];
1079         [bp release];
1080       }
1081   }
1082 #endif
1084 #ifdef NS_IMPL_GNUSTEP
1085   uRect = NSMakeRect (0, 0, 0, 0);
1086 #endif
1090 static void
1091 ns_update_window_begin (struct window *w)
1092 /* --------------------------------------------------------------------------
1093    Prepare for a grouped sequence of drawing calls
1094    external (RIF) call; for one window, called after update_begin
1095    -------------------------------------------------------------------------- */
1097   struct frame *f = XFRAME (WINDOW_FRAME (w));
1098   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
1100   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_window_begin");
1101   w->output_cursor = w->cursor;
1103   block_input ();
1105   if (f == hlinfo->mouse_face_mouse_frame)
1106     {
1107       /* Don't do highlighting for mouse motion during the update.  */
1108       hlinfo->mouse_face_defer = 1;
1110         /* If the frame needs to be redrawn,
1111            simply forget about any prior mouse highlighting.  */
1112       if (FRAME_GARBAGED_P (f))
1113         hlinfo->mouse_face_window = Qnil;
1115       /* (further code for mouse faces ifdef'd out in other terms elided) */
1116     }
1118   unblock_input ();
1122 static void
1123 ns_update_window_end (struct window *w, bool cursor_on_p,
1124                       bool mouse_face_overwritten_p)
1125 /* --------------------------------------------------------------------------
1126    Finished a grouped sequence of drawing calls
1127    external (RIF) call; for one window called before update_end
1128    -------------------------------------------------------------------------- */
1130   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_window_end");
1132   /* note: this fn is nearly identical in all terms */
1133   if (!w->pseudo_window_p)
1134     {
1135       block_input ();
1137       if (cursor_on_p)
1138         display_and_set_cursor (w, 1,
1139                                 w->output_cursor.hpos, w->output_cursor.vpos,
1140                                 w->output_cursor.x, w->output_cursor.y);
1142       if (draw_window_fringes (w, 1))
1143         {
1144           if (WINDOW_RIGHT_DIVIDER_WIDTH (w))
1145             x_draw_right_divider (w);
1146           else
1147             x_draw_vertical_border (w);
1148         }
1150       unblock_input ();
1151     }
1153   /* If a row with mouse-face was overwritten, arrange for
1154      frame_up_to_date to redisplay the mouse highlight.  */
1155   if (mouse_face_overwritten_p)
1156     reset_mouse_highlight (MOUSE_HL_INFO (XFRAME (w->frame)));
1160 static void
1161 ns_update_end (struct frame *f)
1162 /* --------------------------------------------------------------------------
1163    Finished a grouped sequence of drawing calls
1164    external (RIF) call; for whole frame, called after update_window_end
1165    -------------------------------------------------------------------------- */
1167   EmacsView *view = FRAME_NS_VIEW (f);
1169   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_end");
1171 /*   if (f == MOUSE_HL_INFO (f)->mouse_face_mouse_frame) */
1172   MOUSE_HL_INFO (f)->mouse_face_defer = 0;
1174   block_input ();
1176   [view unlockFocus];
1177   [[view window] flushWindow];
1179   unblock_input ();
1180   ns_updating_frame = NULL;
1183 static void
1184 ns_focus (struct frame *f, NSRect *r, int n)
1185 /* --------------------------------------------------------------------------
1186    Internal: Focus on given frame.  During small local updates this is used to
1187      draw, however during large updates, ns_update_begin and ns_update_end are
1188      called to wrap the whole thing, in which case these calls are stubbed out.
1189      Except, on GNUstep, we accumulate the rectangle being drawn into, because
1190      the back end won't do this automatically, and will just end up flushing
1191      the entire window.
1192    -------------------------------------------------------------------------- */
1194   NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_focus");
1195   if (r != NULL)
1196     {
1197       NSTRACE_RECT ("r", *r);
1198     }
1200   if (f != ns_updating_frame)
1201     {
1202       NSView *view = FRAME_NS_VIEW (f);
1203       if (view != focus_view)
1204         {
1205           if (focus_view != NULL)
1206             {
1207               [focus_view unlockFocus];
1208               [[focus_view window] flushWindow];
1209 /*debug_lock--; */
1210             }
1212           if (view)
1213             [view lockFocus];
1214           focus_view = view;
1215 /*if (view) debug_lock++; */
1216         }
1217     }
1219   /* clipping */
1220   if (r)
1221     {
1222       [[NSGraphicsContext currentContext] saveGraphicsState];
1223       if (n == 2)
1224         NSRectClipList (r, 2);
1225       else
1226         NSRectClip (*r);
1227       gsaved = YES;
1228     }
1232 static void
1233 ns_unfocus (struct frame *f)
1234 /* --------------------------------------------------------------------------
1235      Internal: Remove focus on given frame
1236    -------------------------------------------------------------------------- */
1238   NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_unfocus");
1240   if (gsaved)
1241     {
1242       [[NSGraphicsContext currentContext] restoreGraphicsState];
1243       gsaved = NO;
1244     }
1246   if (f != ns_updating_frame)
1247     {
1248       if (focus_view != NULL)
1249         {
1250           [focus_view unlockFocus];
1251           [[focus_view window] flushWindow];
1252           focus_view = NULL;
1253 /*debug_lock--; */
1254         }
1255     }
1259 static void
1260 ns_clip_to_row (struct window *w, struct glyph_row *row,
1261                 enum glyph_row_area area, BOOL gc)
1262 /* --------------------------------------------------------------------------
1263      Internal (but parallels other terms): Focus drawing on given row
1264    -------------------------------------------------------------------------- */
1266   struct frame *f = XFRAME (WINDOW_FRAME (w));
1267   NSRect clip_rect;
1268   int window_x, window_y, window_width;
1270   window_box (w, area, &window_x, &window_y, &window_width, 0);
1272   clip_rect.origin.x = window_x;
1273   clip_rect.origin.y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, row->y));
1274   clip_rect.origin.y = max (clip_rect.origin.y, window_y);
1275   clip_rect.size.width = window_width;
1276   clip_rect.size.height = row->visible_height;
1278   ns_focus (f, &clip_rect, 1);
1282 /* ==========================================================================
1284     Visible bell and beep.
1286    ========================================================================== */
1289 // This bell implementation shows the visual bell image asynchronously
1290 // from the rest of Emacs. This is done by adding a NSView to the
1291 // superview of the Emacs window and removing it using a timer.
1293 // Unfortunately, some Emacs operations, like scrolling, is done using
1294 // low-level primitives that copy the content of the window, including
1295 // the bell image. To some extent, this is handled by removing the
1296 // image prior to scrolling and marking that the window is in need for
1297 // redisplay.
1299 // To test this code, make sure that there is no artifacts of the bell
1300 // image in the following situations. Use a non-empty buffer (like the
1301 // tutorial) to ensure that a scroll is performed:
1303 // * Single-window: C-g C-v
1305 // * Side-by-windows: C-x 3 C-g C-v
1307 // * Windows above each other: C-x 2 C-g C-v
1309 @interface EmacsBell : NSImageView
1311   // Number of currently active bell:s.
1312   unsigned int nestCount;
1313   NSView * mView;
1314   bool isAttached;
1316 - (void)show:(NSView *)view;
1317 - (void)hide;
1318 - (void)remove;
1319 @end
1321 @implementation EmacsBell
1323 - (id)init
1325   NSTRACE ("[EmacsBell init]");
1326   if ((self = [super init]))
1327     {
1328       nestCount = 0;
1329       isAttached = false;
1330 #ifdef NS_IMPL_GNUSTEP
1331       // GNUstep doesn't provide named images.  This was reported in
1332       // 2011, see https://savannah.gnu.org/bugs/?33396
1333       //
1334       // As a drop in replacement, a semitransparent gray square is used.
1335       self.image = [[NSImage alloc] initWithSize:NSMakeSize(32 * 5, 32 * 5)];
1336       [self.image lockFocus];
1337       [[NSColor colorForEmacsRed:0.5 green:0.5 blue:0.5 alpha:0.5] set];
1338       NSRectFill(NSMakeRect(0, 0, 32, 32));
1339       [self.image unlockFocus];
1340 #else
1341       self.image = [NSImage imageNamed:NSImageNameCaution];
1342       [self.image setSize:NSMakeSize(self.image.size.width * 5,
1343                                      self.image.size.height * 5)];
1344 #endif
1345     }
1346   return self;
1349 - (void)show:(NSView *)view
1351   NSTRACE ("[EmacsBell show:]");
1352   NSTRACE_MSG ("nestCount: %u", nestCount);
1354   // Show the image, unless it's already shown.
1355   if (nestCount == 0)
1356     {
1357       NSRect rect = [view bounds];
1358       NSPoint pos;
1359       pos.x = rect.origin.x + (rect.size.width  - self.image.size.width )/2;
1360       pos.y = rect.origin.y + (rect.size.height - self.image.size.height)/2;
1362       [self setFrameOrigin:pos];
1363       [self setFrameSize:self.image.size];
1365       isAttached = true;
1366       mView = view;
1367       [[[view window] contentView] addSubview:self
1368                                    positioned:NSWindowAbove
1369                                    relativeTo:nil];
1370     }
1372   ++nestCount;
1374   [self performSelector:@selector(hide) withObject:self afterDelay:0.5];
1378 - (void)hide
1380   // Note: Trace output from this method isn't shown, reason unknown.
1381   // NSTRACE ("[EmacsBell hide]");
1383   if (nestCount > 0)
1384     --nestCount;
1386   // Remove the image once the last bell became inactive.
1387   if (nestCount == 0)
1388     {
1389       [self remove];
1390     }
1394 -(void)remove
1396   NSTRACE ("[EmacsBell remove]");
1397   if (isAttached)
1398     {
1399       NSTRACE_MSG ("removeFromSuperview");
1400       [self removeFromSuperview];
1401       mView.needsDisplay = YES;
1402       isAttached = false;
1403     }
1406 @end
1409 static EmacsBell * bell_view = nil;
1411 static void
1412 ns_ring_bell (struct frame *f)
1413 /* --------------------------------------------------------------------------
1414      "Beep" routine
1415    -------------------------------------------------------------------------- */
1417   NSTRACE ("ns_ring_bell");
1418   if (visible_bell)
1419     {
1420       struct frame *frame = SELECTED_FRAME ();
1421       NSView *view;
1423       if (bell_view == nil)
1424         {
1425           bell_view = [[EmacsBell alloc] init];
1426           [bell_view retain];
1427         }
1429       block_input ();
1431       view = FRAME_NS_VIEW (frame);
1432       if (view != nil)
1433         {
1434           [bell_view show:view];
1435         }
1437       unblock_input ();
1438     }
1439   else
1440     {
1441       NSBeep ();
1442     }
1446 static void
1447 hide_bell (void)
1448 /* --------------------------------------------------------------------------
1449      Ensure the bell is hidden.
1450    -------------------------------------------------------------------------- */
1452   NSTRACE ("hide_bell");
1454   if (bell_view != nil)
1455     {
1456       [bell_view remove];
1457     }
1461 /* ==========================================================================
1463     Frame / window manager related functions
1465    ========================================================================== */
1468 static void
1469 ns_raise_frame (struct frame *f, BOOL make_key)
1470 /* --------------------------------------------------------------------------
1471      Bring window to foreground and if make_key is YES, give it focus.
1472    -------------------------------------------------------------------------- */
1474   NSView *view;
1476   check_window_system (f);
1477   view = FRAME_NS_VIEW (f);
1478   block_input ();
1479   if (FRAME_VISIBLE_P (f))
1480     {
1481       if (make_key)
1482         [[view window] makeKeyAndOrderFront: NSApp];
1483       else
1484         [[view window] orderFront: NSApp];
1485     }
1486   unblock_input ();
1490 static void
1491 ns_lower_frame (struct frame *f)
1492 /* --------------------------------------------------------------------------
1493      Send window to back
1494    -------------------------------------------------------------------------- */
1496   NSView *view;
1498   check_window_system (f);
1499   view = FRAME_NS_VIEW (f);
1500   block_input ();
1501   [[view window] orderBack: NSApp];
1502   unblock_input ();
1506 static void
1507 ns_frame_raise_lower (struct frame *f, bool raise)
1508 /* --------------------------------------------------------------------------
1509      External (hook)
1510    -------------------------------------------------------------------------- */
1512   NSTRACE ("ns_frame_raise_lower");
1514   if (raise)
1515     ns_raise_frame (f, YES);
1516   else
1517     ns_lower_frame (f);
1521 static void
1522 ns_frame_rehighlight (struct frame *frame)
1523 /* --------------------------------------------------------------------------
1524      External (hook): called on things like window switching within frame
1525    -------------------------------------------------------------------------- */
1527   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
1528   struct frame *old_highlight = dpyinfo->x_highlight_frame;
1530   NSTRACE ("ns_frame_rehighlight");
1531   if (dpyinfo->x_focus_frame)
1532     {
1533       dpyinfo->x_highlight_frame
1534         = (FRAMEP (FRAME_FOCUS_FRAME (dpyinfo->x_focus_frame))
1535            ? XFRAME (FRAME_FOCUS_FRAME (dpyinfo->x_focus_frame))
1536            : dpyinfo->x_focus_frame);
1537       if (!FRAME_LIVE_P (dpyinfo->x_highlight_frame))
1538         {
1539           fset_focus_frame (dpyinfo->x_focus_frame, Qnil);
1540           dpyinfo->x_highlight_frame = dpyinfo->x_focus_frame;
1541         }
1542     }
1543   else
1544       dpyinfo->x_highlight_frame = 0;
1546   if (dpyinfo->x_highlight_frame &&
1547          dpyinfo->x_highlight_frame != old_highlight)
1548     {
1549       if (old_highlight)
1550         {
1551           x_update_cursor (old_highlight, 1);
1552           x_set_frame_alpha (old_highlight);
1553         }
1554       if (dpyinfo->x_highlight_frame)
1555         {
1556           x_update_cursor (dpyinfo->x_highlight_frame, 1);
1557           x_set_frame_alpha (dpyinfo->x_highlight_frame);
1558         }
1559     }
1563 void
1564 x_make_frame_visible (struct frame *f)
1565 /* --------------------------------------------------------------------------
1566      External: Show the window (X11 semantics)
1567    -------------------------------------------------------------------------- */
1569   NSTRACE ("x_make_frame_visible");
1570   /* XXX: at some points in past this was not needed, as the only place that
1571      called this (frame.c:Fraise_frame ()) also called raise_lower;
1572      if this ends up the case again, comment this out again. */
1573   if (!FRAME_VISIBLE_P (f))
1574     {
1575       EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
1576       NSWindow *window = [view window];
1578       SET_FRAME_VISIBLE (f, 1);
1579       ns_raise_frame (f, ! FRAME_NO_FOCUS_ON_MAP (f));
1581       /* Making a new frame from a fullscreen frame will make the new frame
1582          fullscreen also.  So skip handleFS as this will print an error.  */
1583       if ([view fsIsNative] && f->want_fullscreen == FULLSCREEN_BOTH
1584           && [view isFullscreen])
1585         return;
1587       if (f->want_fullscreen != FULLSCREEN_NONE)
1588         {
1589           block_input ();
1590           [view handleFS];
1591           unblock_input ();
1592         }
1594       /* Making a frame invisible seems to break the parent->child
1595          relationship, so reinstate it. */
1596       if ([window parentWindow] == nil && FRAME_PARENT_FRAME (f) != NULL)
1597         {
1598           NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
1600           block_input ();
1601           [parent addChildWindow: window
1602                          ordered: NSWindowAbove];
1603           unblock_input ();
1605           /* If the parent frame moved while the child frame was
1606              invisible, the child frame's position won't have been
1607              updated.  Make sure it's in the right place now. */
1608           x_set_offset(f, f->left_pos, f->top_pos, 0);
1609         }
1610     }
1614 void
1615 x_make_frame_invisible (struct frame *f)
1616 /* --------------------------------------------------------------------------
1617      External: Hide the window (X11 semantics)
1618    -------------------------------------------------------------------------- */
1620   NSView *view;
1621   NSTRACE ("x_make_frame_invisible");
1622   check_window_system (f);
1623   view = FRAME_NS_VIEW (f);
1624   [[view window] orderOut: NSApp];
1625   SET_FRAME_VISIBLE (f, 0);
1626   SET_FRAME_ICONIFIED (f, 0);
1630 void
1631 x_iconify_frame (struct frame *f)
1632 /* --------------------------------------------------------------------------
1633      External: Iconify window
1634    -------------------------------------------------------------------------- */
1636   NSView *view;
1637   struct ns_display_info *dpyinfo;
1639   NSTRACE ("x_iconify_frame");
1640   check_window_system (f);
1641   view = FRAME_NS_VIEW (f);
1642   dpyinfo = FRAME_DISPLAY_INFO (f);
1644   if (dpyinfo->x_highlight_frame == f)
1645     dpyinfo->x_highlight_frame = 0;
1647   if ([[view window] windowNumber] <= 0)
1648     {
1649       /* the window is still deferred.  Make it very small, bring it
1650          on screen and order it out. */
1651       NSRect s = { { 100, 100}, {0, 0} };
1652       NSRect t;
1653       t = [[view window] frame];
1654       [[view window] setFrame: s display: NO];
1655       [[view window] orderBack: NSApp];
1656       [[view window] orderOut: NSApp];
1657       [[view window] setFrame: t display: NO];
1658     }
1660   /* Processing input while Emacs is being minimized can cause a
1661      crash, so block it for the duration. */
1662   block_input();
1663   [[view window] miniaturize: NSApp];
1664   unblock_input();
1667 /* Free X resources of frame F.  */
1669 void
1670 x_free_frame_resources (struct frame *f)
1672   NSView *view;
1673   struct ns_display_info *dpyinfo;
1674   Mouse_HLInfo *hlinfo;
1676   NSTRACE ("x_free_frame_resources");
1677   check_window_system (f);
1678   view = FRAME_NS_VIEW (f);
1679   dpyinfo = FRAME_DISPLAY_INFO (f);
1680   hlinfo = MOUSE_HL_INFO (f);
1682   [(EmacsView *)view setWindowClosing: YES]; /* may not have been informed */
1684   block_input ();
1686   free_frame_menubar (f);
1687   free_frame_faces (f);
1689   if (f == dpyinfo->x_focus_frame)
1690     dpyinfo->x_focus_frame = 0;
1691   if (f == dpyinfo->x_highlight_frame)
1692     dpyinfo->x_highlight_frame = 0;
1693   if (f == hlinfo->mouse_face_mouse_frame)
1694     reset_mouse_highlight (hlinfo);
1696   if (f->output_data.ns->miniimage != nil)
1697     [f->output_data.ns->miniimage release];
1699   [[view window] close];
1700   [view release];
1702   xfree (f->output_data.ns);
1704   unblock_input ();
1707 void
1708 x_destroy_window (struct frame *f)
1709 /* --------------------------------------------------------------------------
1710      External: Delete the window
1711    -------------------------------------------------------------------------- */
1713   NSTRACE ("x_destroy_window");
1715   /* If this frame has a parent window, detach it as not doing so can
1716      cause a crash in GNUStep. */
1717   if (FRAME_PARENT_FRAME (f) != NULL)
1718     {
1719       NSWindow *child = [FRAME_NS_VIEW (f) window];
1720       NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
1722       [parent removeChildWindow: child];
1723     }
1725   check_window_system (f);
1726   x_free_frame_resources (f);
1727   ns_window_num--;
1731 void
1732 x_set_offset (struct frame *f, int xoff, int yoff, int change_grav)
1733 /* --------------------------------------------------------------------------
1734      External: Position the window
1735    -------------------------------------------------------------------------- */
1737   NSView *view = FRAME_NS_VIEW (f);
1738   NSArray *screens = [NSScreen screens];
1739   NSScreen *fscreen = [screens objectAtIndex: 0];
1740   NSScreen *screen = [[view window] screen];
1742   NSTRACE ("x_set_offset");
1744   block_input ();
1746   f->left_pos = xoff;
1747   f->top_pos = yoff;
1749   if (view != nil && screen && fscreen)
1750     {
1751       f->left_pos = f->size_hint_flags & XNegative
1752         ? [screen visibleFrame].size.width + f->left_pos - FRAME_PIXEL_WIDTH (f)
1753         : f->left_pos;
1754       /* We use visibleFrame here to take menu bar into account.
1755          Ideally we should also adjust left/top with visibleFrame.origin.  */
1757       f->top_pos = f->size_hint_flags & YNegative
1758         ? ([screen visibleFrame].size.height + f->top_pos
1759            - FRAME_PIXEL_HEIGHT (f) - FRAME_NS_TITLEBAR_HEIGHT (f)
1760            - FRAME_TOOLBAR_HEIGHT (f))
1761         : f->top_pos;
1762 #ifdef NS_IMPL_GNUSTEP
1763       if (FRAME_PARENT_FRAME (f) == NULL)
1764         {
1765           if (f->left_pos < 100)
1766             f->left_pos = 100;  /* don't overlap menu */
1767         }
1768 #endif
1769       /* Constrain the setFrameTopLeftPoint so we don't move behind the
1770          menu bar.  */
1771       NSPoint pt = NSMakePoint (SCREENMAXBOUND (f->left_pos
1772                                                 + NS_PARENT_WINDOW_LEFT_POS (f)),
1773                                 SCREENMAXBOUND (NS_PARENT_WINDOW_TOP_POS (f)
1774                                                 - f->top_pos));
1775       NSTRACE_POINT ("setFrameTopLeftPoint", pt);
1776       [[view window] setFrameTopLeftPoint: pt];
1777       f->size_hint_flags &= ~(XNegative|YNegative);
1778     }
1780   unblock_input ();
1784 void
1785 x_set_window_size (struct frame *f,
1786                    bool change_gravity,
1787                    int width,
1788                    int height,
1789                    bool pixelwise)
1790 /* --------------------------------------------------------------------------
1791      Adjust window pixel size based on given character grid size
1792      Impl is a bit more complex than other terms, need to do some
1793      internal clipping.
1794    -------------------------------------------------------------------------- */
1796   EmacsView *view = FRAME_NS_VIEW (f);
1797   NSWindow *window = [view window];
1798   NSRect wr = [window frame];
1799   int pixelwidth, pixelheight;
1800   int orig_height = wr.size.height;
1802   NSTRACE ("x_set_window_size");
1804   if (view == nil)
1805     return;
1807   NSTRACE_RECT ("current", wr);
1808   NSTRACE_MSG ("Width:%d Height:%d Pixelwise:%d", width, height, pixelwise);
1809   NSTRACE_MSG ("Font %d x %d", FRAME_COLUMN_WIDTH (f), FRAME_LINE_HEIGHT (f));
1811   block_input ();
1813   if (pixelwise)
1814     {
1815       pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
1816       pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
1817     }
1818   else
1819     {
1820       pixelwidth =  FRAME_TEXT_COLS_TO_PIXEL_WIDTH   (f, width);
1821       pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, height);
1822     }
1824   wr.size.width = pixelwidth + f->border_width;
1825   wr.size.height = pixelheight;
1826   if (! [view isFullscreen])
1827     wr.size.height += FRAME_NS_TITLEBAR_HEIGHT (f)
1828       + FRAME_TOOLBAR_HEIGHT (f);
1830   /* Do not try to constrain to this screen.  We may have multiple
1831      screens, and want Emacs to span those.  Constraining to screen
1832      prevents that, and that is not nice to the user.  */
1833  if (f->output_data.ns->zooming)
1834    f->output_data.ns->zooming = 0;
1835  else
1836    wr.origin.y += orig_height - wr.size.height;
1838  frame_size_history_add
1839    (f, Qx_set_window_size_1, width, height,
1840     list5 (Fcons (make_number (pixelwidth), make_number (pixelheight)),
1841            Fcons (make_number (wr.size.width), make_number (wr.size.height)),
1842            make_number (f->border_width),
1843            make_number (FRAME_NS_TITLEBAR_HEIGHT (f)),
1844            make_number (FRAME_TOOLBAR_HEIGHT (f))));
1846   [window setFrame: wr display: YES];
1848   [view updateFrameSize: NO];
1849   unblock_input ();
1852 #ifdef NS_IMPL_COCOA
1853 void
1854 x_set_undecorated (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1855 /* --------------------------------------------------------------------------
1856      Set frame F's `undecorated' parameter.  If non-nil, F's window-system
1857      window is drawn without decorations, title, minimize/maximize boxes
1858      and external borders.  This usually means that the window cannot be
1859      dragged, resized, iconified, maximized or deleted with the mouse.  If
1860      nil, draw the frame with all the elements listed above unless these
1861      have been suspended via window manager settings.
1863      GNUStep cannot change an existing window's style.
1864    -------------------------------------------------------------------------- */
1866   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
1867   NSWindow *window = [view window];
1869   NSTRACE ("x_set_undecorated");
1871   if (!EQ (new_value, old_value))
1872     {
1873       block_input ();
1875       if (NILP (new_value))
1876         {
1877           FRAME_UNDECORATED (f) = false;
1878           [window setStyleMask: ((window.styleMask | FRAME_DECORATED_FLAGS)
1879                                   ^ FRAME_UNDECORATED_FLAGS)];
1881           [view createToolbar: f];
1882         }
1883       else
1884         {
1885           [window setToolbar: nil];
1886           /* Do I need to release the toolbar here? */
1888           FRAME_UNDECORATED (f) = true;
1889           [window setStyleMask: ((window.styleMask | FRAME_UNDECORATED_FLAGS)
1890                                  ^ FRAME_DECORATED_FLAGS)];
1891         }
1893       /* At this point it seems we don't have an active NSResponder,
1894          so some key presses (TAB) are swallowed by the system. */
1895       [window makeFirstResponder: view];
1897       [view updateFrameSize: NO];
1898       unblock_input ();
1899     }
1901 #endif /* NS_IMPL_COCOA */
1903 void
1904 x_set_parent_frame (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1905 /* --------------------------------------------------------------------------
1906      Set frame F's `parent-frame' parameter.  If non-nil, make F a child
1907      frame of the frame specified by that parameter.  Technically, this
1908      makes F's window-system window a child window of the parent frame's
1909      window-system window.  If nil, make F's window-system window a
1910      top-level window--a child of its display's root window.
1912      A child frame's `left' and `top' parameters specify positions
1913      relative to the top-left corner of its parent frame's native
1914      rectangle.  On macOS moving a parent frame moves all its child
1915      frames too, keeping their position relative to the parent
1916      unaltered.  When a parent frame is iconified or made invisible, its
1917      child frames are made invisible.  When a parent frame is deleted,
1918      its child frames are deleted too.
1920      Whether a child frame has a tool bar may be window-system or window
1921      manager dependent.  It's advisable to disable it via the frame
1922      parameter settings.
1924      Some window managers may not honor this parameter.
1925    -------------------------------------------------------------------------- */
1927   struct frame *p = NULL;
1928   NSWindow *parent, *child;
1930   NSTRACE ("x_set_parent_frame");
1932   if (!NILP (new_value)
1933       && (!FRAMEP (new_value)
1934           || !FRAME_LIVE_P (p = XFRAME (new_value))
1935           || !FRAME_X_P (p)))
1936     {
1937       store_frame_param (f, Qparent_frame, old_value);
1938       error ("Invalid specification of `parent-frame'");
1939     }
1941   if (p != FRAME_PARENT_FRAME (f))
1942     {
1943       parent = [FRAME_NS_VIEW (p) window];
1944       child = [FRAME_NS_VIEW (f) window];
1946       block_input ();
1947       [parent addChildWindow: child
1948                      ordered: NSWindowAbove];
1949       unblock_input ();
1951       fset_parent_frame (f, new_value);
1952     }
1955 void
1956 x_set_no_focus_on_map (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1957 /* Set frame F's `no-focus-on-map' parameter which, if non-nil, means
1958  * that F's window-system window does not want to receive input focus
1959  * when it is mapped.  (A frame's window is mapped when the frame is
1960  * displayed for the first time and when the frame changes its state
1961  * from `iconified' or `invisible' to `visible'.)
1963  * Some window managers may not honor this parameter. */
1965   NSTRACE ("x_set_no_focus_on_map");
1967   if (!EQ (new_value, old_value))
1968     {
1969       FRAME_NO_FOCUS_ON_MAP (f) = !NILP (new_value);
1970     }
1973 void
1974 x_set_no_accept_focus (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1975 /*  Set frame F's `no-accept-focus' parameter which, if non-nil, hints
1976  * that F's window-system window does not want to receive input focus
1977  * via mouse clicks or by moving the mouse into it.
1979  * If non-nil, this may have the unwanted side-effect that a user cannot
1980  * scroll a non-selected frame with the mouse.
1982  * Some window managers may not honor this parameter. */
1984   NSTRACE ("x_set_no_accept_focus");
1986   if (!EQ (new_value, old_value))
1987     FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value);
1990 void
1991 x_set_z_group (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1992 /* Set frame F's `z-group' parameter.  If `above', F's window-system
1993    window is displayed above all windows that do not have the `above'
1994    property set.  If nil, F's window is shown below all windows that
1995    have the `above' property set and above all windows that have the
1996    `below' property set.  If `below', F's window is displayed below
1997    all windows that do.
1999    Some window managers may not honor this parameter. */
2001   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2002   NSWindow *window = [view window];
2004   NSTRACE ("x_set_z_group");
2006   if (NILP (new_value))
2007     {
2008       window.level = NSNormalWindowLevel;
2009       FRAME_Z_GROUP (f) = z_group_none;
2010     }
2011   else if (EQ (new_value, Qabove))
2012     {
2013       window.level = NSNormalWindowLevel + 1;
2014       FRAME_Z_GROUP (f) = z_group_above;
2015     }
2016   else if (EQ (new_value, Qabove_suspended))
2017     {
2018       /* Not sure what level this should be. */
2019       window.level = NSNormalWindowLevel + 1;
2020       FRAME_Z_GROUP (f) = z_group_above_suspended;
2021     }
2022   else if (EQ (new_value, Qbelow))
2023     {
2024       window.level = NSNormalWindowLevel - 1;
2025       FRAME_Z_GROUP (f) = z_group_below;
2026     }
2027   else
2028     error ("Invalid z-group specification");
2031 #ifdef NS_IMPL_COCOA
2032 void
2033 ns_set_appearance (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
2035 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
2036   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2037   NSWindow *window = [view window];
2039   NSTRACE ("ns_set_appearance");
2041 #ifndef NSAppKitVersionNumber10_10
2042 #define NSAppKitVersionNumber10_10 1343
2043 #endif
2045   if (NSAppKitVersionNumber < NSAppKitVersionNumber10_10)
2046     return;
2048   if (EQ (new_value, Qdark))
2049     {
2050       window.appearance = [NSAppearance
2051                             appearanceNamed: NSAppearanceNameVibrantDark];
2052       FRAME_NS_APPEARANCE (f) = ns_appearance_vibrant_dark;
2053     }
2054   else
2055     {
2056       window.appearance = [NSAppearance
2057                             appearanceNamed: NSAppearanceNameAqua];
2058       FRAME_NS_APPEARANCE (f) = ns_appearance_aqua;
2059     }
2060 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 */
2063 void
2064 ns_set_transparent_titlebar (struct frame *f, Lisp_Object new_value,
2065                              Lisp_Object old_value)
2067 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
2068   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2069   NSWindow *window = [view window];
2071   NSTRACE ("ns_set_transparent_titlebar");
2073   if ([window respondsToSelector: @selector(titlebarAppearsTransparent)]
2074       && !EQ (new_value, old_value))
2075     {
2076       window.titlebarAppearsTransparent = !NILP (new_value);
2077       FRAME_NS_TRANSPARENT_TITLEBAR (f) = !NILP (new_value);
2078     }
2079 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 */
2081 #endif /* NS_IMPL_COCOA */
2083 static void
2084 ns_fullscreen_hook (struct frame *f)
2086   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2088   NSTRACE ("ns_fullscreen_hook");
2090   if (!FRAME_VISIBLE_P (f))
2091     return;
2093    if (! [view fsIsNative] && f->want_fullscreen == FULLSCREEN_BOTH)
2094     {
2095       /* Old style fs don't initiate correctly if created from
2096          init/default-frame alist, so use a timer (not nice...).
2097       */
2098       [NSTimer scheduledTimerWithTimeInterval: 0.5 target: view
2099                                      selector: @selector (handleFS)
2100                                      userInfo: nil repeats: NO];
2101       return;
2102     }
2104   block_input ();
2105   [view handleFS];
2106   unblock_input ();
2109 /* ==========================================================================
2111     Color management
2113    ========================================================================== */
2116 NSColor *
2117 ns_lookup_indexed_color (unsigned long idx, struct frame *f)
2119   struct ns_color_table *color_table = FRAME_DISPLAY_INFO (f)->color_table;
2120   if (idx < 1 || idx >= color_table->avail)
2121     return nil;
2122   return color_table->colors[idx];
2126 unsigned long
2127 ns_index_color (NSColor *color, struct frame *f)
2129   struct ns_color_table *color_table = FRAME_DISPLAY_INFO (f)->color_table;
2130   ptrdiff_t idx;
2131   ptrdiff_t i;
2133   if (!color_table->colors)
2134     {
2135       color_table->size = NS_COLOR_CAPACITY;
2136       color_table->avail = 1; /* skip idx=0 as marker */
2137       color_table->colors = xmalloc (color_table->size * sizeof (NSColor *));
2138       color_table->colors[0] = nil;
2139       color_table->empty_indices = [[NSMutableSet alloc] init];
2140     }
2142   /* Do we already have this color?  */
2143   for (i = 1; i < color_table->avail; i++)
2144     if (color_table->colors[i] && [color_table->colors[i] isEqual: color])
2145       return i;
2147   if ([color_table->empty_indices count] > 0)
2148     {
2149       NSNumber *index = [color_table->empty_indices anyObject];
2150       [color_table->empty_indices removeObject: index];
2151       idx = [index unsignedLongValue];
2152     }
2153   else
2154     {
2155       if (color_table->avail == color_table->size)
2156         color_table->colors =
2157           xpalloc (color_table->colors, &color_table->size, 1,
2158                    min (ULONG_MAX, PTRDIFF_MAX), sizeof *color_table->colors);
2159       idx = color_table->avail++;
2160     }
2162   color_table->colors[idx] = color;
2163   [color retain];
2164 /*fprintf(stderr, "color_table: allocated %d\n",idx);*/
2165   return idx;
2169 static int
2170 ns_get_color (const char *name, NSColor **col)
2171 /* --------------------------------------------------------------------------
2172      Parse a color name
2173    -------------------------------------------------------------------------- */
2174 /* On *Step, we attempt to mimic the X11 platform here, down to installing an
2175    X11 rgb.txt-compatible color list in Emacs.clr (see ns_term_init()).
2176    See: http://thread.gmane.org/gmane.emacs.devel/113050/focus=113272). */
2178   NSColor *new = nil;
2179   static char hex[20];
2180   int scaling = 0;
2181   float r = -1.0, g, b;
2182   NSString *nsname = [NSString stringWithUTF8String: name];
2184   NSTRACE ("ns_get_color(%s, **)", name);
2186   block_input ();
2188   if ([nsname isEqualToString: @"ns_selection_bg_color"])
2189     {
2190 #ifdef NS_IMPL_COCOA
2191       NSString *defname = [[NSUserDefaults standardUserDefaults]
2192                             stringForKey: @"AppleHighlightColor"];
2193       if (defname != nil)
2194         nsname = defname;
2195       else
2196 #endif
2197       if ((new = [NSColor selectedTextBackgroundColor]) != nil)
2198         {
2199           *col = [new colorUsingDefaultColorSpace];
2200           unblock_input ();
2201           return 0;
2202         }
2203       else
2204         nsname = NS_SELECTION_BG_COLOR_DEFAULT;
2206       name = [nsname UTF8String];
2207     }
2208   else if ([nsname isEqualToString: @"ns_selection_fg_color"])
2209     {
2210       /* NOTE: macOS applications normally don't set foreground
2211          selection, but text may be unreadable if we don't.
2212       */
2213       if ((new = [NSColor selectedTextColor]) != nil)
2214         {
2215           *col = [new colorUsingDefaultColorSpace];
2216           unblock_input ();
2217           return 0;
2218         }
2220       nsname = NS_SELECTION_FG_COLOR_DEFAULT;
2221       name = [nsname UTF8String];
2222     }
2224   /* First, check for some sort of numeric specification. */
2225   hex[0] = '\0';
2227   if (name[0] == '0' || name[0] == '1' || name[0] == '.')  /* RGB decimal */
2228     {
2229       NSScanner *scanner = [NSScanner scannerWithString: nsname];
2230       [scanner scanFloat: &r];
2231       [scanner scanFloat: &g];
2232       [scanner scanFloat: &b];
2233     }
2234   else if (!strncmp(name, "rgb:", 4))  /* A newer X11 format -- rgb:r/g/b */
2235     scaling = (snprintf (hex, sizeof hex, "%s", name + 4) - 2) / 3;
2236   else if (name[0] == '#')        /* An old X11 format; convert to newer */
2237     {
2238       int len = (strlen(name) - 1);
2239       int start = (len % 3 == 0) ? 1 : len / 4 + 1;
2240       int i;
2241       scaling = strlen(name+start) / 3;
2242       for (i = 0; i < 3; i++)
2243         sprintf (hex + i * (scaling + 1), "%.*s/", scaling,
2244                  name + start + i * scaling);
2245       hex[3 * (scaling + 1) - 1] = '\0';
2246     }
2248   if (hex[0])
2249     {
2250       unsigned int rr, gg, bb;
2251       float fscale = scaling == 4 ? 65535.0 : (scaling == 2 ? 255.0 : 15.0);
2252       if (sscanf (hex, "%x/%x/%x", &rr, &gg, &bb))
2253         {
2254           r = rr / fscale;
2255           g = gg / fscale;
2256           b = bb / fscale;
2257         }
2258     }
2260   if (r >= 0.0F)
2261     {
2262       *col = [NSColor colorForEmacsRed: r green: g blue: b alpha: 1.0];
2263       unblock_input ();
2264       return 0;
2265     }
2267   /* Otherwise, color is expected to be from a list */
2268   {
2269     NSEnumerator *lenum, *cenum;
2270     NSString *name;
2271     NSColorList *clist;
2273 #ifdef NS_IMPL_GNUSTEP
2274     /* XXX: who is wrong, the requestor or the implementation? */
2275     if ([nsname compare: @"Highlight" options: NSCaseInsensitiveSearch]
2276         == NSOrderedSame)
2277       nsname = @"highlightColor";
2278 #endif
2280     lenum = [[NSColorList availableColorLists] objectEnumerator];
2281     while ( (clist = [lenum nextObject]) && new == nil)
2282       {
2283         cenum = [[clist allKeys] objectEnumerator];
2284         while ( (name = [cenum nextObject]) && new == nil )
2285           {
2286             if ([name compare: nsname
2287                       options: NSCaseInsensitiveSearch] == NSOrderedSame )
2288               new = [clist colorWithKey: name];
2289           }
2290       }
2291   }
2293   if (new)
2294     *col = [new colorUsingDefaultColorSpace];
2295   unblock_input ();
2296   return new ? 0 : 1;
2301 ns_lisp_to_color (Lisp_Object color, NSColor **col)
2302 /* --------------------------------------------------------------------------
2303      Convert a Lisp string object to a NS color
2304    -------------------------------------------------------------------------- */
2306   NSTRACE ("ns_lisp_to_color");
2307   if (STRINGP (color))
2308     return ns_get_color (SSDATA (color), col);
2309   else if (SYMBOLP (color))
2310     return ns_get_color (SSDATA (SYMBOL_NAME (color)), col);
2311   return 1;
2315 void
2316 ns_query_color(void *col, XColor *color_def, int setPixel)
2317 /* --------------------------------------------------------------------------
2318          Get ARGB values out of NSColor col and put them into color_def.
2319          If setPixel, set the pixel to a concatenated version.
2320          and set color_def pixel to the resulting index.
2321    -------------------------------------------------------------------------- */
2323   EmacsCGFloat r, g, b, a;
2325   [((NSColor *)col) getRed: &r green: &g blue: &b alpha: &a];
2326   color_def->red   = r * 65535;
2327   color_def->green = g * 65535;
2328   color_def->blue  = b * 65535;
2330   if (setPixel == YES)
2331     color_def->pixel
2332       = ARGB_TO_ULONG((int)(a*255),
2333                       (int)(r*255), (int)(g*255), (int)(b*255));
2337 bool
2338 ns_defined_color (struct frame *f,
2339                   const char *name,
2340                   XColor *color_def,
2341                   bool alloc,
2342                   bool makeIndex)
2343 /* --------------------------------------------------------------------------
2344          Return true if named color found, and set color_def rgb accordingly.
2345          If makeIndex and alloc are nonzero put the color in the color_table,
2346          and set color_def pixel to the resulting index.
2347          If makeIndex is zero, set color_def pixel to ARGB.
2348          Return false if not found
2349    -------------------------------------------------------------------------- */
2351   NSColor *col;
2352   NSTRACE_WHEN (NSTRACE_GROUP_COLOR, "ns_defined_color");
2354   block_input ();
2355   if (ns_get_color (name, &col) != 0) /* Color not found  */
2356     {
2357       unblock_input ();
2358       return 0;
2359     }
2360   if (makeIndex && alloc)
2361     color_def->pixel = ns_index_color (col, f);
2362   ns_query_color (col, color_def, !makeIndex);
2363   unblock_input ();
2364   return 1;
2368 void
2369 x_set_frame_alpha (struct frame *f)
2370 /* --------------------------------------------------------------------------
2371      change the entire-frame transparency
2372    -------------------------------------------------------------------------- */
2374   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
2375   double alpha = 1.0;
2376   double alpha_min = 1.0;
2378   NSTRACE ("x_set_frame_alpha");
2380   if (dpyinfo->x_highlight_frame == f)
2381     alpha = f->alpha[0];
2382   else
2383     alpha = f->alpha[1];
2385   if (FLOATP (Vframe_alpha_lower_limit))
2386     alpha_min = XFLOAT_DATA (Vframe_alpha_lower_limit);
2387   else if (INTEGERP (Vframe_alpha_lower_limit))
2388     alpha_min = (XINT (Vframe_alpha_lower_limit)) / 100.0;
2390   if (alpha < 0.0)
2391     return;
2392   else if (1.0 < alpha)
2393     alpha = 1.0;
2394   else if (0.0 <= alpha && alpha < alpha_min && alpha_min <= 1.0)
2395     alpha = alpha_min;
2397 #ifdef NS_IMPL_COCOA
2398   {
2399     EmacsView *view = FRAME_NS_VIEW (f);
2400   [[view window] setAlphaValue: alpha];
2401   }
2402 #endif
2406 /* ==========================================================================
2408     Mouse handling
2410    ========================================================================== */
2413 void
2414 frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y)
2415 /* --------------------------------------------------------------------------
2416      Programmatically reposition mouse pointer in pixel coordinates
2417    -------------------------------------------------------------------------- */
2419   NSTRACE ("frame_set_mouse_pixel_position");
2421   /* FIXME: what about GNUstep? */
2422 #ifdef NS_IMPL_COCOA
2423   CGPoint mouse_pos =
2424     CGPointMake(f->left_pos + pix_x,
2425                 f->top_pos + pix_y +
2426                 FRAME_NS_TITLEBAR_HEIGHT(f) + FRAME_TOOLBAR_HEIGHT(f));
2427   CGWarpMouseCursorPosition (mouse_pos);
2428 #endif
2431 static int
2432 note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y)
2433 /*   ------------------------------------------------------------------------
2434      Called by EmacsView on mouseMovement events.  Passes on
2435      to emacs mainstream code if we moved off of a rect of interest
2436      known as last_mouse_glyph.
2437      ------------------------------------------------------------------------ */
2439   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
2440   NSRect *r;
2442 //  NSTRACE ("note_mouse_movement");
2444   dpyinfo->last_mouse_motion_frame = frame;
2445   r = &dpyinfo->last_mouse_glyph;
2447   /* Note, this doesn't get called for enter/leave, since we don't have a
2448      position.  Those are taken care of in the corresponding NSView methods. */
2450   /* has movement gone beyond last rect we were tracking? */
2451   if (x < r->origin.x || x >= r->origin.x + r->size.width
2452       || y < r->origin.y || y >= r->origin.y + r->size.height)
2453     {
2454       ns_update_begin (frame);
2455       frame->mouse_moved = 1;
2456       note_mouse_highlight (frame, x, y);
2457       remember_mouse_glyph (frame, x, y, r);
2458       ns_update_end (frame);
2459       return 1;
2460     }
2462   return 0;
2466 static void
2467 ns_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window,
2468                    enum scroll_bar_part *part, Lisp_Object *x, Lisp_Object *y,
2469                    Time *time)
2470 /* --------------------------------------------------------------------------
2471     External (hook): inform emacs about mouse position and hit parts.
2472     If a scrollbar is being dragged, set bar_window, part, x, y, time.
2473     x & y should be position in the scrollbar (the whole bar, not the handle)
2474     and length of scrollbar respectively
2475    -------------------------------------------------------------------------- */
2477   id view;
2478   NSPoint position;
2479   Lisp_Object frame, tail;
2480   struct frame *f;
2481   struct ns_display_info *dpyinfo;
2483   NSTRACE ("ns_mouse_position");
2485   if (*fp == NULL)
2486     {
2487       fprintf (stderr, "Warning: ns_mouse_position () called with null *fp.\n");
2488       return;
2489     }
2491   dpyinfo = FRAME_DISPLAY_INFO (*fp);
2493   block_input ();
2495   /* Clear the mouse-moved flag for every frame on this display.  */
2496   FOR_EACH_FRAME (tail, frame)
2497     if (FRAME_NS_P (XFRAME (frame))
2498         && FRAME_NS_DISPLAY (XFRAME (frame)) == FRAME_NS_DISPLAY (*fp))
2499       XFRAME (frame)->mouse_moved = 0;
2501   dpyinfo->last_mouse_scroll_bar = nil;
2502   if (dpyinfo->last_mouse_frame
2503       && FRAME_LIVE_P (dpyinfo->last_mouse_frame))
2504     f = dpyinfo->last_mouse_frame;
2505   else
2506     f = dpyinfo->x_focus_frame ? dpyinfo->x_focus_frame : SELECTED_FRAME ();
2508   if (f && FRAME_NS_P (f))
2509     {
2510       view = FRAME_NS_VIEW (*fp);
2512       position = [[view window] mouseLocationOutsideOfEventStream];
2513       position = [view convertPoint: position fromView: nil];
2514       remember_mouse_glyph (f, position.x, position.y,
2515                             &dpyinfo->last_mouse_glyph);
2516       NSTRACE_POINT ("position", position);
2518       if (bar_window) *bar_window = Qnil;
2519       if (part) *part = scroll_bar_above_handle;
2521       if (x) XSETINT (*x, lrint (position.x));
2522       if (y) XSETINT (*y, lrint (position.y));
2523       if (time)
2524         *time = dpyinfo->last_mouse_movement_time;
2525       *fp = f;
2526     }
2528   unblock_input ();
2532 static void
2533 ns_frame_up_to_date (struct frame *f)
2534 /* --------------------------------------------------------------------------
2535     External (hook): Fix up mouse highlighting right after a full update.
2536     Can't use FRAME_MOUSE_UPDATE due to ns_frame_begin and ns_frame_end calls.
2537    -------------------------------------------------------------------------- */
2539   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_frame_up_to_date");
2541   if (FRAME_NS_P (f))
2542     {
2543       Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
2544       if (f == hlinfo->mouse_face_mouse_frame)
2545         {
2546           block_input ();
2547           ns_update_begin(f);
2548           note_mouse_highlight (hlinfo->mouse_face_mouse_frame,
2549                                 hlinfo->mouse_face_mouse_x,
2550                                 hlinfo->mouse_face_mouse_y);
2551           ns_update_end(f);
2552           unblock_input ();
2553         }
2554     }
2558 static void
2559 ns_define_frame_cursor (struct frame *f, Cursor cursor)
2560 /* --------------------------------------------------------------------------
2561     External (RIF): set frame mouse pointer type.
2562    -------------------------------------------------------------------------- */
2564   NSTRACE ("ns_define_frame_cursor");
2565   if (FRAME_POINTER_TYPE (f) != cursor)
2566     {
2567       EmacsView *view = FRAME_NS_VIEW (f);
2568       FRAME_POINTER_TYPE (f) = cursor;
2569       [[view window] invalidateCursorRectsForView: view];
2570       /* Redisplay assumes this function also draws the changed frame
2571          cursor, but this function doesn't, so do it explicitly.  */
2572       x_update_cursor (f, 1);
2573     }
2578 /* ==========================================================================
2580     Keyboard handling
2582    ========================================================================== */
2585 static unsigned
2586 ns_convert_key (unsigned code)
2587 /* --------------------------------------------------------------------------
2588     Internal call used by NSView-keyDown.
2589    -------------------------------------------------------------------------- */
2591   const unsigned last_keysym = ARRAYELTS (convert_ns_to_X_keysym);
2592   unsigned keysym;
2593   /* An array would be faster, but less easy to read. */
2594   for (keysym = 0; keysym < last_keysym; keysym += 2)
2595     if (code == convert_ns_to_X_keysym[keysym])
2596       return 0xFF00 | convert_ns_to_X_keysym[keysym+1];
2597   return 0;
2598 /* if decide to use keyCode and Carbon table, use this line:
2599      return code > 0xff ? 0 : 0xFF00 | ns_keycode_to_xkeysym_table[code]; */
2603 char *
2604 x_get_keysym_name (int keysym)
2605 /* --------------------------------------------------------------------------
2606     Called by keyboard.c.  Not sure if the return val is important, except
2607     that it be unique.
2608    -------------------------------------------------------------------------- */
2610   static char value[16];
2611   NSTRACE ("x_get_keysym_name");
2612   sprintf (value, "%d", keysym);
2613   return value;
2618 /* ==========================================================================
2620     Block drawing operations
2622    ========================================================================== */
2625 static void
2626 ns_redraw_scroll_bars (struct frame *f)
2628   int i;
2629   id view;
2630   NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews];
2631   NSTRACE ("ns_redraw_scroll_bars");
2632   for (i =[subviews count]-1; i >= 0; i--)
2633     {
2634       view = [subviews objectAtIndex: i];
2635       if (![view isKindOfClass: [EmacsScroller class]]) continue;
2636       [view display];
2637     }
2641 void
2642 ns_clear_frame (struct frame *f)
2643 /* --------------------------------------------------------------------------
2644       External (hook): Erase the entire frame
2645    -------------------------------------------------------------------------- */
2647   NSView *view = FRAME_NS_VIEW (f);
2648   NSRect r;
2650   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_clear_frame");
2652  /* comes on initial frame because we have
2653     after-make-frame-functions = select-frame */
2654  if (!FRAME_DEFAULT_FACE (f))
2655    return;
2657   mark_window_cursors_off (XWINDOW (FRAME_ROOT_WINDOW (f)));
2659   r = [view bounds];
2661   block_input ();
2662   ns_focus (f, &r, 1);
2663   [ns_lookup_indexed_color (NS_FACE_BACKGROUND
2664                             (FACE_FROM_ID (f, DEFAULT_FACE_ID)), f) set];
2665   NSRectFill (r);
2666   ns_unfocus (f);
2668   /* as of 2006/11 or so this is now needed */
2669   ns_redraw_scroll_bars (f);
2670   unblock_input ();
2674 static void
2675 ns_clear_frame_area (struct frame *f, int x, int y, int width, int height)
2676 /* --------------------------------------------------------------------------
2677     External (RIF):  Clear section of frame
2678    -------------------------------------------------------------------------- */
2680   NSRect r = NSMakeRect (x, y, width, height);
2681   NSView *view = FRAME_NS_VIEW (f);
2682   struct face *face = FRAME_DEFAULT_FACE (f);
2684   if (!view || !face)
2685     return;
2687   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_clear_frame_area");
2689   r = NSIntersectionRect (r, [view frame]);
2690   ns_focus (f, &r, 1);
2691   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), f) set];
2693   NSRectFill (r);
2695   ns_unfocus (f);
2696   return;
2699 static void
2700 ns_copy_bits (struct frame *f, NSRect src, NSRect dest)
2702   NSTRACE ("ns_copy_bits");
2704   if (FRAME_NS_VIEW (f))
2705     {
2706       hide_bell();              // Ensure the bell image isn't scrolled.
2708       ns_focus (f, &dest, 1);
2709       [FRAME_NS_VIEW (f) scrollRect: src
2710                                  by: NSMakeSize (dest.origin.x - src.origin.x,
2711                                                  dest.origin.y - src.origin.y)];
2712       ns_unfocus (f);
2713     }
2716 static void
2717 ns_scroll_run (struct window *w, struct run *run)
2718 /* --------------------------------------------------------------------------
2719     External (RIF):  Insert or delete n lines at line vpos
2720    -------------------------------------------------------------------------- */
2722   struct frame *f = XFRAME (w->frame);
2723   int x, y, width, height, from_y, to_y, bottom_y;
2725   NSTRACE ("ns_scroll_run");
2727   /* begin copy from other terms */
2728   /* Get frame-relative bounding box of the text display area of W,
2729      without mode lines.  Include in this box the left and right
2730      fringe of W.  */
2731   window_box (w, ANY_AREA, &x, &y, &width, &height);
2733   from_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->current_y);
2734   to_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->desired_y);
2735   bottom_y = y + height;
2737   if (to_y < from_y)
2738     {
2739       /* Scrolling up.  Make sure we don't copy part of the mode
2740          line at the bottom.  */
2741       if (from_y + run->height > bottom_y)
2742         height = bottom_y - from_y;
2743       else
2744         height = run->height;
2745     }
2746   else
2747     {
2748       /* Scrolling down.  Make sure we don't copy over the mode line.
2749          at the bottom.  */
2750       if (to_y + run->height > bottom_y)
2751         height = bottom_y - to_y;
2752       else
2753         height = run->height;
2754     }
2755   /* end copy from other terms */
2757   if (height == 0)
2758       return;
2760   block_input ();
2762   x_clear_cursor (w);
2764   {
2765     NSRect srcRect = NSMakeRect (x, from_y, width, height);
2766     NSRect dstRect = NSMakeRect (x, to_y, width, height);
2768     ns_copy_bits (f, srcRect , dstRect);
2769   }
2771   unblock_input ();
2775 static void
2776 ns_after_update_window_line (struct window *w, struct glyph_row *desired_row)
2777 /* --------------------------------------------------------------------------
2778     External (RIF): preparatory to fringe update after text was updated
2779    -------------------------------------------------------------------------- */
2781   struct frame *f;
2782   int width, height;
2784   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_after_update_window_line");
2786   /* begin copy from other terms */
2787   eassert (w);
2789   if (!desired_row->mode_line_p && !w->pseudo_window_p)
2790     desired_row->redraw_fringe_bitmaps_p = 1;
2792   /* When a window has disappeared, make sure that no rest of
2793      full-width rows stays visible in the internal border.  */
2794   if (windows_or_buffers_changed
2795       && desired_row->full_width_p
2796       && (f = XFRAME (w->frame),
2797           width = FRAME_INTERNAL_BORDER_WIDTH (f),
2798           width != 0)
2799       && (height = desired_row->visible_height,
2800           height > 0))
2801     {
2802       int y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, desired_row->y));
2804       block_input ();
2805       ns_clear_frame_area (f, 0, y, width, height);
2806       ns_clear_frame_area (f,
2807                            FRAME_PIXEL_WIDTH (f) - width,
2808                            y, width, height);
2809       unblock_input ();
2810     }
2814 static void
2815 ns_shift_glyphs_for_insert (struct frame *f,
2816                            int x, int y, int width, int height,
2817                            int shift_by)
2818 /* --------------------------------------------------------------------------
2819     External (RIF): copy an area horizontally, don't worry about clearing src
2820    -------------------------------------------------------------------------- */
2822   NSRect srcRect = NSMakeRect (x, y, width, height);
2823   NSRect dstRect = NSMakeRect (x+shift_by, y, width, height);
2825   NSTRACE ("ns_shift_glyphs_for_insert");
2827   ns_copy_bits (f, srcRect, dstRect);
2832 /* ==========================================================================
2834     Character encoding and metrics
2836    ========================================================================== */
2839 static void
2840 ns_compute_glyph_string_overhangs (struct glyph_string *s)
2841 /* --------------------------------------------------------------------------
2842      External (RIF); compute left/right overhang of whole string and set in s
2843    -------------------------------------------------------------------------- */
2845   struct font *font = s->font;
2847   if (s->char2b)
2848     {
2849       struct font_metrics metrics;
2850       unsigned int codes[2];
2851       codes[0] = *(s->char2b);
2852       codes[1] = *(s->char2b + s->nchars - 1);
2854       font->driver->text_extents (font, codes, 2, &metrics);
2855       s->left_overhang = -metrics.lbearing;
2856       s->right_overhang
2857         = metrics.rbearing > metrics.width
2858         ? metrics.rbearing - metrics.width : 0;
2859     }
2860   else
2861     {
2862       s->left_overhang = 0;
2863       if (EQ (font->driver->type, Qns))
2864         s->right_overhang = ((struct nsfont_info *)font)->ital ?
2865           FONT_HEIGHT (font) * 0.2 : 0;
2866       else
2867         s->right_overhang = 0;
2868     }
2873 /* ==========================================================================
2875     Fringe and cursor drawing
2877    ========================================================================== */
2880 extern int max_used_fringe_bitmap;
2881 static void
2882 ns_draw_fringe_bitmap (struct window *w, struct glyph_row *row,
2883                       struct draw_fringe_bitmap_params *p)
2884 /* --------------------------------------------------------------------------
2885     External (RIF); fringe-related
2886    -------------------------------------------------------------------------- */
2888   /* Fringe bitmaps comes in two variants, normal and periodic.  A
2889      periodic bitmap is used to create a continuous pattern.  Since a
2890      bitmap is rendered one text line at a time, the start offset (dh)
2891      of the bitmap varies.  Concretely, this is used for the empty
2892      line indicator.
2894      For a bitmap, "h + dh" is the full height and is always
2895      invariant.  For a normal bitmap "dh" is zero.
2897      For example, when the period is three and the full height is 72
2898      the following combinations exists:
2900        h=72 dh=0
2901        h=71 dh=1
2902        h=70 dh=2 */
2904   struct frame *f = XFRAME (WINDOW_FRAME (w));
2905   struct face *face = p->face;
2906   static EmacsImage **bimgs = NULL;
2907   static int nBimgs = 0;
2909   NSTRACE_WHEN (NSTRACE_GROUP_FRINGE, "ns_draw_fringe_bitmap");
2910   NSTRACE_MSG ("which:%d cursor:%d overlay:%d width:%d height:%d period:%d",
2911                p->which, p->cursor_p, p->overlay_p, p->wd, p->h, p->dh);
2913   /* grow bimgs if needed */
2914   if (nBimgs < max_used_fringe_bitmap)
2915     {
2916       bimgs = xrealloc (bimgs, max_used_fringe_bitmap * sizeof *bimgs);
2917       memset (bimgs + nBimgs, 0,
2918               (max_used_fringe_bitmap - nBimgs) * sizeof *bimgs);
2919       nBimgs = max_used_fringe_bitmap;
2920     }
2922   /* Must clip because of partially visible lines.  */
2923   ns_clip_to_row (w, row, ANY_AREA, YES);
2925   if (!p->overlay_p)
2926     {
2927       int bx = p->bx, by = p->by, nx = p->nx, ny = p->ny;
2929       if (bx >= 0 && nx > 0)
2930         {
2931           NSRect r = NSMakeRect (bx, by, nx, ny);
2932           NSRectClip (r);
2933           [ns_lookup_indexed_color (face->background, f) set];
2934           NSRectFill (r);
2935         }
2936     }
2938   if (p->which)
2939     {
2940       NSRect r = NSMakeRect (p->x, p->y, p->wd, p->h);
2941       EmacsImage *img = bimgs[p->which - 1];
2943       if (!img)
2944         {
2945           // Note: For "periodic" images, allocate one EmacsImage for
2946           // the base image, and use it for all dh:s.
2947           unsigned short *bits = p->bits;
2948           int full_height = p->h + p->dh;
2949           int i;
2950           unsigned char *cbits = xmalloc (full_height);
2952           for (i = 0; i < full_height; i++)
2953             cbits[i] = bits[i];
2954           img = [[EmacsImage alloc] initFromXBM: cbits width: 8
2955                                          height: full_height
2956                                              fg: 0 bg: 0];
2957           bimgs[p->which - 1] = img;
2958           xfree (cbits);
2959         }
2961       NSTRACE_RECT ("r", r);
2963       NSRectClip (r);
2964       /* Since we composite the bitmap instead of just blitting it, we need
2965          to erase the whole background. */
2966       [ns_lookup_indexed_color(face->background, f) set];
2967       NSRectFill (r);
2969       {
2970         NSColor *bm_color;
2971         if (!p->cursor_p)
2972           bm_color = ns_lookup_indexed_color(face->foreground, f);
2973         else if (p->overlay_p)
2974           bm_color = ns_lookup_indexed_color(face->background, f);
2975         else
2976           bm_color = f->output_data.ns->cursor_color;
2977         [img setXBMColor: bm_color];
2978       }
2980 #ifdef NS_IMPL_COCOA
2981       // Note: For periodic images, the full image height is "h + hd".
2982       // By using the height h, a suitable part of the image is used.
2983       NSRect fromRect = NSMakeRect(0, 0, p->wd, p->h);
2985       NSTRACE_RECT ("fromRect", fromRect);
2987       [img drawInRect: r
2988               fromRect: fromRect
2989              operation: NSCompositingOperationSourceOver
2990               fraction: 1.0
2991            respectFlipped: YES
2992                 hints: nil];
2993 #else
2994       {
2995         NSPoint pt = r.origin;
2996         pt.y += p->h;
2997         [img compositeToPoint: pt operation: NSCompositingOperationSourceOver];
2998       }
2999 #endif
3000     }
3001   ns_unfocus (f);
3005 static void
3006 ns_draw_window_cursor (struct window *w, struct glyph_row *glyph_row,
3007                        int x, int y, enum text_cursor_kinds cursor_type,
3008                        int cursor_width, bool on_p, bool active_p)
3009 /* --------------------------------------------------------------------------
3010      External call (RIF): draw cursor.
3011      Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
3012    -------------------------------------------------------------------------- */
3014   NSRect r, s;
3015   int fx, fy, h, cursor_height;
3016   struct frame *f = WINDOW_XFRAME (w);
3017   struct glyph *phys_cursor_glyph;
3018   struct glyph *cursor_glyph;
3019   struct face *face;
3020   NSColor *hollow_color = FRAME_BACKGROUND_COLOR (f);
3022   /* If cursor is out of bounds, don't draw garbage.  This can happen
3023      in mini-buffer windows when switching between echo area glyphs
3024      and mini-buffer.  */
3026   NSTRACE ("ns_draw_window_cursor");
3028   if (!on_p)
3029     return;
3031   w->phys_cursor_type = cursor_type;
3032   w->phys_cursor_on_p = on_p;
3034   if (cursor_type == NO_CURSOR)
3035     {
3036       w->phys_cursor_width = 0;
3037       return;
3038     }
3040   if ((phys_cursor_glyph = get_phys_cursor_glyph (w)) == NULL)
3041     {
3042       if (glyph_row->exact_window_width_line_p
3043           && w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])
3044         {
3045           glyph_row->cursor_in_fringe_p = 1;
3046           draw_fringe_bitmap (w, glyph_row, 0);
3047         }
3048       return;
3049     }
3051   /* We draw the cursor (with NSRectFill), then draw the glyph on top
3052      (other terminals do it the other way round).  We must set
3053      w->phys_cursor_width to the cursor width.  For bar cursors, that
3054      is CURSOR_WIDTH; for box cursors, it is the glyph width.  */
3055   get_phys_cursor_geometry (w, glyph_row, phys_cursor_glyph, &fx, &fy, &h);
3057   /* The above get_phys_cursor_geometry call set w->phys_cursor_width
3058      to the glyph width; replace with CURSOR_WIDTH for (V)BAR cursors. */
3059   if (cursor_type == BAR_CURSOR)
3060     {
3061       if (cursor_width < 1)
3062         cursor_width = max (FRAME_CURSOR_WIDTH (f), 1);
3064       /* The bar cursor should never be wider than the glyph. */
3065       if (cursor_width < w->phys_cursor_width)
3066         w->phys_cursor_width = cursor_width;
3067     }
3068   /* If we have an HBAR, "cursor_width" MAY specify height. */
3069   else if (cursor_type == HBAR_CURSOR)
3070     {
3071       cursor_height = (cursor_width < 1) ? lrint (0.25 * h) : cursor_width;
3072       if (cursor_height > glyph_row->height)
3073         cursor_height = glyph_row->height;
3074       if (h > cursor_height) // Cursor smaller than line height, move down
3075         fy += h - cursor_height;
3076       h = cursor_height;
3077     }
3079   r.origin.x = fx, r.origin.y = fy;
3080   r.size.height = h;
3081   r.size.width = w->phys_cursor_width;
3083   /* Prevent the cursor from being drawn outside the text area. */
3084   ns_clip_to_row (w, glyph_row, TEXT_AREA, NO); /* do ns_focus(f, &r, 1); if remove */
3087   face = FACE_FROM_ID_OR_NULL (f, phys_cursor_glyph->face_id);
3088   if (face && NS_FACE_BACKGROUND (face)
3089       == ns_index_color (FRAME_CURSOR_COLOR (f), f))
3090     {
3091       [ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), f) set];
3092       hollow_color = FRAME_CURSOR_COLOR (f);
3093     }
3094   else
3095     [FRAME_CURSOR_COLOR (f) set];
3097 #ifdef NS_IMPL_COCOA
3098   /* TODO: This makes drawing of cursor plus that of phys_cursor_glyph
3099            atomic.  Cleaner ways of doing this should be investigated.
3100            One way would be to set a global variable DRAWING_CURSOR
3101            when making the call to draw_phys..(), don't focus in that
3102            case, then move the ns_unfocus() here after that call. */
3103   NSDisableScreenUpdates ();
3104 #endif
3106   switch (cursor_type)
3107     {
3108     case DEFAULT_CURSOR:
3109     case NO_CURSOR:
3110       break;
3111     case FILLED_BOX_CURSOR:
3112       NSRectFill (r);
3113       break;
3114     case HOLLOW_BOX_CURSOR:
3115       NSRectFill (r);
3116       [hollow_color set];
3117       NSRectFill (NSInsetRect (r, 1, 1));
3118       [FRAME_CURSOR_COLOR (f) set];
3119       break;
3120     case HBAR_CURSOR:
3121       NSRectFill (r);
3122       break;
3123     case BAR_CURSOR:
3124       s = r;
3125       /* If the character under cursor is R2L, draw the bar cursor
3126          on the right of its glyph, rather than on the left.  */
3127       cursor_glyph = get_phys_cursor_glyph (w);
3128       if ((cursor_glyph->resolved_level & 1) != 0)
3129         s.origin.x += cursor_glyph->pixel_width - s.size.width;
3131       NSRectFill (s);
3132       break;
3133     }
3134   ns_unfocus (f);
3136   /* draw the character under the cursor */
3137   if (cursor_type != NO_CURSOR)
3138     draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
3140 #ifdef NS_IMPL_COCOA
3141   NSEnableScreenUpdates ();
3142 #endif
3147 static void
3148 ns_draw_vertical_window_border (struct window *w, int x, int y0, int y1)
3149 /* --------------------------------------------------------------------------
3150      External (RIF): Draw a vertical line.
3151    -------------------------------------------------------------------------- */
3153   struct frame *f = XFRAME (WINDOW_FRAME (w));
3154   struct face *face;
3155   NSRect r = NSMakeRect (x, y0, 1, y1-y0);
3157   NSTRACE ("ns_draw_vertical_window_border");
3159   face = FACE_FROM_ID_OR_NULL (f, VERTICAL_BORDER_FACE_ID);
3161   ns_focus (f, &r, 1);
3162   if (face)
3163     [ns_lookup_indexed_color(face->foreground, f) set];
3165   NSRectFill(r);
3166   ns_unfocus (f);
3170 static void
3171 ns_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1)
3172 /* --------------------------------------------------------------------------
3173      External (RIF): Draw a window divider.
3174    -------------------------------------------------------------------------- */
3176   struct frame *f = XFRAME (WINDOW_FRAME (w));
3177   struct face *face = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FACE_ID);
3178   struct face *face_first
3179     = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FIRST_PIXEL_FACE_ID);
3180   struct face *face_last
3181     = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_LAST_PIXEL_FACE_ID);
3182   unsigned long color = face ? face->foreground : FRAME_FOREGROUND_PIXEL (f);
3183   unsigned long color_first = (face_first
3184                                ? face_first->foreground
3185                                : FRAME_FOREGROUND_PIXEL (f));
3186   unsigned long color_last = (face_last
3187                               ? face_last->foreground
3188                               : FRAME_FOREGROUND_PIXEL (f));
3189   NSRect divider = NSMakeRect (x0, y0, x1-x0, y1-y0);
3191   NSTRACE ("ns_draw_window_divider");
3193   ns_focus (f, &divider, 1);
3195   if ((y1 - y0 > x1 - x0) && (x1 - x0 >= 3))
3196     /* A vertical divider, at least three pixels wide: Draw first and
3197        last pixels differently.  */
3198     {
3199       [ns_lookup_indexed_color(color_first, f) set];
3200       NSRectFill(NSMakeRect (x0, y0, 1, y1 - y0));
3201       [ns_lookup_indexed_color(color, f) set];
3202       NSRectFill(NSMakeRect (x0 + 1, y0, x1 - x0 - 2, y1 - y0));
3203       [ns_lookup_indexed_color(color_last, f) set];
3204       NSRectFill(NSMakeRect (x1 - 1, y0, 1, y1 - y0));
3205     }
3206   else if ((x1 - x0 > y1 - y0) && (y1 - y0 >= 3))
3207     /* A horizontal divider, at least three pixels high: Draw first and
3208        last pixels differently.  */
3209     {
3210       [ns_lookup_indexed_color(color_first, f) set];
3211       NSRectFill(NSMakeRect (x0, y0, x1 - x0, 1));
3212       [ns_lookup_indexed_color(color, f) set];
3213       NSRectFill(NSMakeRect (x0, y0 + 1, x1 - x0, y1 - y0 - 2));
3214       [ns_lookup_indexed_color(color_last, f) set];
3215       NSRectFill(NSMakeRect (x0, y1 - 1, x1 - x0, 1));
3216     }
3217   else
3218     {
3219       /* In any other case do not draw the first and last pixels
3220          differently.  */
3221       [ns_lookup_indexed_color(color, f) set];
3222       NSRectFill(divider);
3223     }
3225   ns_unfocus (f);
3228 static void
3229 ns_show_hourglass (struct frame *f)
3231   /* TODO: add NSProgressIndicator to all frames.  */
3234 static void
3235 ns_hide_hourglass (struct frame *f)
3237   /* TODO: remove NSProgressIndicator from all frames.  */
3240 /* ==========================================================================
3242     Glyph drawing operations
3244    ========================================================================== */
3246 static int
3247 ns_get_glyph_string_clip_rect (struct glyph_string *s, NativeRectangle *nr)
3248 /* --------------------------------------------------------------------------
3249     Wrapper utility to account for internal border width on full-width lines,
3250     and allow top full-width rows to hit the frame top.  nr should be pointer
3251     to two successive NSRects.  Number of rects actually used is returned.
3252    -------------------------------------------------------------------------- */
3254   int n = get_glyph_string_clip_rects (s, nr, 2);
3255   return n;
3258 /* --------------------------------------------------------------------
3259    Draw a wavy line under glyph string s. The wave fills wave_height
3260    pixels from y.
3262                     x          wave_length = 2
3263                                  --
3264                 y    *   *   *   *   *
3265                      |* * * * * * * * *
3266     wave_height = 3  | *   *   *   *
3267   --------------------------------------------------------------------- */
3269 static void
3270 ns_draw_underwave (struct glyph_string *s, EmacsCGFloat width, EmacsCGFloat x)
3272   int wave_height = 3, wave_length = 2;
3273   int y, dx, dy, odd, xmax;
3274   NSPoint a, b;
3275   NSRect waveClip;
3277   dx = wave_length;
3278   dy = wave_height - 1;
3279   y =  s->ybase - wave_height + 3;
3280   xmax = x + width;
3282   /* Find and set clipping rectangle */
3283   waveClip = NSMakeRect (x, y, width, wave_height);
3284   [[NSGraphicsContext currentContext] saveGraphicsState];
3285   NSRectClip (waveClip);
3287   /* Draw the waves */
3288   a.x = x - ((int)(x) % dx) + (EmacsCGFloat) 0.5;
3289   b.x = a.x + dx;
3290   odd = (int)(a.x/dx) % 2;
3291   a.y = b.y = y + 0.5;
3293   if (odd)
3294     a.y += dy;
3295   else
3296     b.y += dy;
3298   while (a.x <= xmax)
3299     {
3300       [NSBezierPath strokeLineFromPoint:a toPoint:b];
3301       a.x = b.x, a.y = b.y;
3302       b.x += dx, b.y = y + 0.5 + odd*dy;
3303       odd = !odd;
3304     }
3306   /* Restore previous clipping rectangle(s) */
3307   [[NSGraphicsContext currentContext] restoreGraphicsState];
3312 static void
3313 ns_draw_text_decoration (struct glyph_string *s, struct face *face,
3314                          NSColor *defaultCol, CGFloat width, CGFloat x)
3315 /* --------------------------------------------------------------------------
3316    Draw underline, overline, and strike-through on glyph string s.
3317    -------------------------------------------------------------------------- */
3319   if (s->for_overlaps)
3320     return;
3322   /* Do underline. */
3323   if (face->underline_p)
3324     {
3325       if (s->face->underline_type == FACE_UNDER_WAVE)
3326         {
3327           if (face->underline_defaulted_p)
3328             [defaultCol set];
3329           else
3330             [ns_lookup_indexed_color (face->underline_color, s->f) set];
3332           ns_draw_underwave (s, width, x);
3333         }
3334       else if (s->face->underline_type == FACE_UNDER_LINE)
3335         {
3337           NSRect r;
3338           unsigned long thickness, position;
3340           /* If the prev was underlined, match its appearance. */
3341           if (s->prev && s->prev->face->underline_p
3342               && s->prev->face->underline_type == FACE_UNDER_LINE
3343               && s->prev->underline_thickness > 0)
3344             {
3345               thickness = s->prev->underline_thickness;
3346               position = s->prev->underline_position;
3347             }
3348           else
3349             {
3350               struct font *font = font_for_underline_metrics (s);
3351               unsigned long descent = s->y + s->height - s->ybase;
3353               /* Use underline thickness of font, defaulting to 1. */
3354               thickness = (font && font->underline_thickness > 0)
3355                 ? font->underline_thickness : 1;
3357               /* Determine the offset of underlining from the baseline. */
3358               if (x_underline_at_descent_line)
3359                 position = descent - thickness;
3360               else if (x_use_underline_position_properties
3361                        && font && font->underline_position >= 0)
3362                 position = font->underline_position;
3363               else if (font)
3364                 position = lround (font->descent / 2);
3365               else
3366                 position = underline_minimum_offset;
3368               position = max (position, underline_minimum_offset);
3370               /* Ensure underlining is not cropped. */
3371               if (descent <= position)
3372                 {
3373                   position = descent - 1;
3374                   thickness = 1;
3375                 }
3376               else if (descent < position + thickness)
3377                 thickness = 1;
3378             }
3380           s->underline_thickness = thickness;
3381           s->underline_position = position;
3383           r = NSMakeRect (x, s->ybase + position, width, thickness);
3385           if (face->underline_defaulted_p)
3386             [defaultCol set];
3387           else
3388             [ns_lookup_indexed_color (face->underline_color, s->f) set];
3389           NSRectFill (r);
3390         }
3391     }
3392   /* Do overline. We follow other terms in using a thickness of 1
3393      and ignoring overline_margin. */
3394   if (face->overline_p)
3395     {
3396       NSRect r;
3397       r = NSMakeRect (x, s->y, width, 1);
3399       if (face->overline_color_defaulted_p)
3400         [defaultCol set];
3401       else
3402         [ns_lookup_indexed_color (face->overline_color, s->f) set];
3403       NSRectFill (r);
3404     }
3406   /* Do strike-through.  We follow other terms for thickness and
3407      vertical position.*/
3408   if (face->strike_through_p)
3409     {
3410       NSRect r;
3411       /* Y-coordinate and height of the glyph string's first glyph.
3412          We cannot use s->y and s->height because those could be
3413          larger if there are taller display elements (e.g., characters
3414          displayed with a larger font) in the same glyph row.  */
3415       int glyph_y = s->ybase - s->first_glyph->ascent;
3416       int glyph_height = s->first_glyph->ascent + s->first_glyph->descent;
3417       /* Strike-through width and offset from the glyph string's
3418          top edge.  */
3419       unsigned long h = 1;
3420       unsigned long dy;
3422       dy = lrint ((glyph_height - h) / 2);
3423       r = NSMakeRect (x, glyph_y + dy, width, 1);
3425       if (face->strike_through_color_defaulted_p)
3426         [defaultCol set];
3427       else
3428         [ns_lookup_indexed_color (face->strike_through_color, s->f) set];
3429       NSRectFill (r);
3430     }
3433 static void
3434 ns_draw_box (NSRect r, CGFloat thickness, NSColor *col,
3435              char left_p, char right_p)
3436 /* --------------------------------------------------------------------------
3437     Draw an unfilled rect inside r, optionally leaving left and/or right open.
3438     Note we can't just use an NSDrawRect command, because of the possibility
3439     of some sides not being drawn, and because the rect will be filled.
3440    -------------------------------------------------------------------------- */
3442   NSRect s = r;
3443   [col set];
3445   /* top, bottom */
3446   s.size.height = thickness;
3447   NSRectFill (s);
3448   s.origin.y += r.size.height - thickness;
3449   NSRectFill (s);
3451   s.size.height = r.size.height;
3452   s.origin.y = r.origin.y;
3454   /* left, right (optional) */
3455   s.size.width = thickness;
3456   if (left_p)
3457     NSRectFill (s);
3458   if (right_p)
3459     {
3460       s.origin.x += r.size.width - thickness;
3461       NSRectFill (s);
3462     }
3466 static void
3467 ns_draw_relief (NSRect r, int thickness, char raised_p,
3468                char top_p, char bottom_p, char left_p, char right_p,
3469                struct glyph_string *s)
3470 /* --------------------------------------------------------------------------
3471     Draw a relief rect inside r, optionally leaving some sides open.
3472     Note we can't just use an NSDrawBezel command, because of the possibility
3473     of some sides not being drawn, and because the rect will be filled.
3474    -------------------------------------------------------------------------- */
3476   static NSColor *baseCol = nil, *lightCol = nil, *darkCol = nil;
3477   NSColor *newBaseCol = nil;
3478   NSRect sr = r;
3480   NSTRACE ("ns_draw_relief");
3482   /* set up colors */
3484   if (s->face->use_box_color_for_shadows_p)
3485     {
3486       newBaseCol = ns_lookup_indexed_color (s->face->box_color, s->f);
3487     }
3488 /*     else if (s->first_glyph->type == IMAGE_GLYPH
3489            && s->img->pixmap
3490            && !IMAGE_BACKGROUND_TRANSPARENT (s->img, s->f, 0))
3491        {
3492          newBaseCol = IMAGE_BACKGROUND  (s->img, s->f, 0);
3493        } */
3494   else
3495     {
3496       newBaseCol = ns_lookup_indexed_color (s->face->background, s->f);
3497     }
3499   if (newBaseCol == nil)
3500     newBaseCol = [NSColor grayColor];
3502   if (newBaseCol != baseCol)  /* TODO: better check */
3503     {
3504       [baseCol release];
3505       baseCol = [newBaseCol retain];
3506       [lightCol release];
3507       lightCol = [[baseCol highlightWithLevel: 0.2] retain];
3508       [darkCol release];
3509       darkCol = [[baseCol shadowWithLevel: 0.3] retain];
3510     }
3512   [(raised_p ? lightCol : darkCol) set];
3514   /* TODO: mitering. Using NSBezierPath doesn't work because of color switch. */
3516   /* top */
3517   sr.size.height = thickness;
3518   if (top_p) NSRectFill (sr);
3520   /* left */
3521   sr.size.height = r.size.height;
3522   sr.size.width = thickness;
3523   if (left_p) NSRectFill (sr);
3525   [(raised_p ? darkCol : lightCol) set];
3527   /* bottom */
3528   sr.size.width = r.size.width;
3529   sr.size.height = thickness;
3530   sr.origin.y += r.size.height - thickness;
3531   if (bottom_p) NSRectFill (sr);
3533   /* right */
3534   sr.size.height = r.size.height;
3535   sr.origin.y = r.origin.y;
3536   sr.size.width = thickness;
3537   sr.origin.x += r.size.width - thickness;
3538   if (right_p) NSRectFill (sr);
3542 static void
3543 ns_dumpglyphs_box_or_relief (struct glyph_string *s)
3544 /* --------------------------------------------------------------------------
3545       Function modeled after x_draw_glyph_string_box ().
3546       Sets up parameters for drawing.
3547    -------------------------------------------------------------------------- */
3549   int right_x, last_x;
3550   char left_p, right_p;
3551   struct glyph *last_glyph;
3552   NSRect r;
3553   int thickness;
3554   struct face *face;
3556   if (s->hl == DRAW_MOUSE_FACE)
3557     {
3558       face = FACE_FROM_ID_OR_NULL (s->f,
3559                                    MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3560       if (!face)
3561         face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3562     }
3563   else
3564     face = s->face;
3566   thickness = face->box_line_width;
3568   NSTRACE ("ns_dumpglyphs_box_or_relief");
3570   last_x = ((s->row->full_width_p && !s->w->pseudo_window_p)
3571             ? WINDOW_RIGHT_EDGE_X (s->w)
3572             : window_box_right (s->w, s->area));
3573   last_glyph = (s->cmp || s->img
3574                 ? s->first_glyph : s->first_glyph + s->nchars-1);
3576   right_x = ((s->row->full_width_p && s->extends_to_end_of_line_p
3577               ? last_x - 1 : min (last_x, s->x + s->background_width) - 1));
3579   left_p = (s->first_glyph->left_box_line_p
3580             || (s->hl == DRAW_MOUSE_FACE
3581                 && (s->prev == NULL || s->prev->hl != s->hl)));
3582   right_p = (last_glyph->right_box_line_p
3583              || (s->hl == DRAW_MOUSE_FACE
3584                  && (s->next == NULL || s->next->hl != s->hl)));
3586   r = NSMakeRect (s->x, s->y, right_x - s->x + 1, s->height);
3588   /* TODO: Sometimes box_color is 0 and this seems wrong; should investigate. */
3589   if (s->face->box == FACE_SIMPLE_BOX && s->face->box_color)
3590     {
3591       ns_draw_box (r, abs (thickness),
3592                    ns_lookup_indexed_color (face->box_color, s->f),
3593                   left_p, right_p);
3594     }
3595   else
3596     {
3597       ns_draw_relief (r, abs (thickness), s->face->box == FACE_RAISED_BOX,
3598                      1, 1, left_p, right_p, s);
3599     }
3603 static void
3604 ns_maybe_dumpglyphs_background (struct glyph_string *s, char force_p)
3605 /* --------------------------------------------------------------------------
3606       Modeled after x_draw_glyph_string_background, which draws BG in
3607       certain cases.  Others are left to the text rendering routine.
3608    -------------------------------------------------------------------------- */
3610   NSTRACE ("ns_maybe_dumpglyphs_background");
3612   if (!s->background_filled_p/* || s->hl == DRAW_MOUSE_FACE*/)
3613     {
3614       int box_line_width = max (s->face->box_line_width, 0);
3615       if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width
3616           /* When xdisp.c ignores FONT_HEIGHT, we cannot trust font
3617              dimensions, since the actual glyphs might be much
3618              smaller.  So in that case we always clear the rectangle
3619              with background color.  */
3620           || FONT_TOO_HIGH (s->font)
3621           || s->font_not_found_p || s->extends_to_end_of_line_p || force_p)
3622         {
3623           struct face *face;
3624           if (s->hl == DRAW_MOUSE_FACE)
3625             {
3626               face
3627                 = FACE_FROM_ID_OR_NULL (s->f,
3628                                         MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3629               if (!face)
3630                 face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3631             }
3632           else
3633             face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3634           if (!face->stipple)
3635             [(NS_FACE_BACKGROUND (face) != 0
3636               ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
3637               : FRAME_BACKGROUND_COLOR (s->f)) set];
3638           else
3639             {
3640               struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
3641               [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set];
3642             }
3644           if (s->hl != DRAW_CURSOR)
3645             {
3646               NSRect r = NSMakeRect (s->x, s->y + box_line_width,
3647                                     s->background_width,
3648                                     s->height-2*box_line_width);
3649               NSRectFill (r);
3650             }
3652           s->background_filled_p = 1;
3653         }
3654     }
3658 static void
3659 ns_dumpglyphs_image (struct glyph_string *s, NSRect r)
3660 /* --------------------------------------------------------------------------
3661       Renders an image and associated borders.
3662    -------------------------------------------------------------------------- */
3664   EmacsImage *img = s->img->pixmap;
3665   int box_line_vwidth = max (s->face->box_line_width, 0);
3666   int x = s->x, y = s->ybase - image_ascent (s->img, s->face, &s->slice);
3667   int bg_x, bg_y, bg_height;
3668   int th;
3669   char raised_p;
3670   NSRect br;
3671   struct face *face;
3672   NSColor *tdCol;
3674   NSTRACE ("ns_dumpglyphs_image");
3676   if (s->face->box != FACE_NO_BOX
3677       && s->first_glyph->left_box_line_p && s->slice.x == 0)
3678     x += abs (s->face->box_line_width);
3680   bg_x = x;
3681   bg_y =  s->slice.y == 0 ? s->y : s->y + box_line_vwidth;
3682   bg_height = s->height;
3683   /* other terms have this, but was causing problems w/tabbar mode */
3684   /* - 2 * box_line_vwidth; */
3686   if (s->slice.x == 0) x += s->img->hmargin;
3687   if (s->slice.y == 0) y += s->img->vmargin;
3689   /* Draw BG: if we need larger area than image itself cleared, do that,
3690      otherwise, since we composite the image under NS (instead of mucking
3691      with its background color), we must clear just the image area. */
3692   if (s->hl == DRAW_MOUSE_FACE)
3693     {
3694       face = FACE_FROM_ID_OR_NULL (s->f,
3695                                    MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3696       if (!face)
3697        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3698     }
3699   else
3700     face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3702   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f) set];
3704   if (bg_height > s->slice.height || s->img->hmargin || s->img->vmargin
3705       || s->img->mask || s->img->pixmap == 0 || s->width != s->background_width)
3706     {
3707       br = NSMakeRect (bg_x, bg_y, s->background_width, bg_height);
3708       s->background_filled_p = 1;
3709     }
3710   else
3711     {
3712       br = NSMakeRect (x, y, s->slice.width, s->slice.height);
3713     }
3715   NSRectFill (br);
3717   /* Draw the image.. do we need to draw placeholder if img ==nil? */
3718   if (img != nil)
3719     {
3720 #ifdef NS_IMPL_COCOA
3721       NSRect dr = NSMakeRect (x, y, s->slice.width, s->slice.height);
3722       NSRect ir = NSMakeRect (s->slice.x,
3723                               s->img->height - s->slice.y - s->slice.height,
3724                               s->slice.width, s->slice.height);
3725       [img drawInRect: dr
3726              fromRect: ir
3727              operation: NSCompositingOperationSourceOver
3728               fraction: 1.0
3729            respectFlipped: YES
3730                 hints: nil];
3731 #else
3732       [img compositeToPoint: NSMakePoint (x, y + s->slice.height)
3733                   operation: NSCompositingOperationSourceOver];
3734 #endif
3735     }
3737   if (s->hl == DRAW_CURSOR)
3738     {
3739     [FRAME_CURSOR_COLOR (s->f) set];
3740     if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
3741       tdCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
3742     else
3743       /* Currently on NS img->mask is always 0. Since
3744          get_window_cursor_type specifies a hollow box cursor when on
3745          a non-masked image we never reach this clause. But we put it
3746          in, in anticipation of better support for image masks on
3747          NS. */
3748       tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3749     }
3750   else
3751     {
3752       tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3753     }
3755   /* Draw underline, overline, strike-through. */
3756   ns_draw_text_decoration (s, face, tdCol, br.size.width, br.origin.x);
3758   /* Draw relief, if requested */
3759   if (s->img->relief || s->hl ==DRAW_IMAGE_RAISED || s->hl ==DRAW_IMAGE_SUNKEN)
3760     {
3761       if (s->hl == DRAW_IMAGE_SUNKEN || s->hl == DRAW_IMAGE_RAISED)
3762         {
3763           th = tool_bar_button_relief >= 0 ?
3764             tool_bar_button_relief : DEFAULT_TOOL_BAR_BUTTON_RELIEF;
3765           raised_p = (s->hl == DRAW_IMAGE_RAISED);
3766         }
3767       else
3768         {
3769           th = abs (s->img->relief);
3770           raised_p = (s->img->relief > 0);
3771         }
3773       r.origin.x = x - th;
3774       r.origin.y = y - th;
3775       r.size.width = s->slice.width + 2*th-1;
3776       r.size.height = s->slice.height + 2*th-1;
3777       ns_draw_relief (r, th, raised_p,
3778                       s->slice.y == 0,
3779                       s->slice.y + s->slice.height == s->img->height,
3780                       s->slice.x == 0,
3781                       s->slice.x + s->slice.width == s->img->width, s);
3782     }
3784   /* If there is no mask, the background won't be seen,
3785      so draw a rectangle on the image for the cursor.
3786      Do this for all images, getting transparency right is not reliable.  */
3787   if (s->hl == DRAW_CURSOR)
3788     {
3789       int thickness = abs (s->img->relief);
3790       if (thickness == 0) thickness = 1;
3791       ns_draw_box (br, thickness, FRAME_CURSOR_COLOR (s->f), 1, 1);
3792     }
3796 static void
3797 ns_dumpglyphs_stretch (struct glyph_string *s)
3799   NSRect r[2];
3800   int n, i;
3801   struct face *face;
3802   NSColor *fgCol, *bgCol;
3804   if (!s->background_filled_p)
3805     {
3806       n = ns_get_glyph_string_clip_rect (s, r);
3807       *r = NSMakeRect (s->x, s->y, s->background_width, s->height);
3809       ns_focus (s->f, r, n);
3811       if (s->hl == DRAW_MOUSE_FACE)
3812        {
3813          face = FACE_FROM_ID_OR_NULL (s->f,
3814                                       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3815          if (!face)
3816            face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3817        }
3818       else
3819        face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3821       bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
3822       fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3824       for (i = 0; i < n; ++i)
3825         {
3826           if (!s->row->full_width_p)
3827             {
3828               int overrun, leftoverrun;
3830               /* truncate to avoid overwriting fringe and/or scrollbar */
3831               overrun = max (0, (s->x + s->background_width)
3832                              - (WINDOW_BOX_RIGHT_EDGE_X (s->w)
3833                                 - WINDOW_RIGHT_FRINGE_WIDTH (s->w)));
3834               r[i].size.width -= overrun;
3836               /* truncate to avoid overwriting to left of the window box */
3837               leftoverrun = (WINDOW_BOX_LEFT_EDGE_X (s->w)
3838                              + WINDOW_LEFT_FRINGE_WIDTH (s->w)) - s->x;
3840               if (leftoverrun > 0)
3841                 {
3842                   r[i].origin.x += leftoverrun;
3843                   r[i].size.width -= leftoverrun;
3844                 }
3846               /* XXX: Try to work between problem where a stretch glyph on
3847                  a partially-visible bottom row will clear part of the
3848                  modeline, and another where list-buffers headers and similar
3849                  rows erroneously have visible_height set to 0.  Not sure
3850                  where this is coming from as other terms seem not to show. */
3851               r[i].size.height = min (s->height, s->row->visible_height);
3852             }
3854           [bgCol set];
3856           /* NOTE: under NS this is NOT used to draw cursors, but we must avoid
3857              overwriting cursor (usually when cursor on a tab) */
3858           if (s->hl == DRAW_CURSOR)
3859             {
3860               CGFloat x, width;
3862               x = r[i].origin.x;
3863               width = s->w->phys_cursor_width;
3864               r[i].size.width -= width;
3865               r[i].origin.x += width;
3867               NSRectFill (r[i]);
3869               /* Draw overlining, etc. on the cursor. */
3870               if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
3871                 ns_draw_text_decoration (s, face, bgCol, width, x);
3872               else
3873                 ns_draw_text_decoration (s, face, fgCol, width, x);
3874             }
3875           else
3876             {
3877               NSRectFill (r[i]);
3878             }
3880           /* Draw overlining, etc. on the stretch glyph (or the part
3881              of the stretch glyph after the cursor). */
3882           ns_draw_text_decoration (s, face, fgCol, r[i].size.width,
3883                                    r[i].origin.x);
3884         }
3885       ns_unfocus (s->f);
3886       s->background_filled_p = 1;
3887     }
3891 static void
3892 ns_draw_glyph_string_foreground (struct glyph_string *s)
3894   int x, flags;
3895   struct font *font = s->font;
3897   /* If first glyph of S has a left box line, start drawing the text
3898      of S to the right of that box line.  */
3899   if (s->face && s->face->box != FACE_NO_BOX
3900       && s->first_glyph->left_box_line_p)
3901     x = s->x + eabs (s->face->box_line_width);
3902   else
3903     x = s->x;
3905   flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
3906     (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
3907      (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
3908       NS_DUMPGLYPH_NORMAL));
3910   font->driver->draw
3911     (s, s->cmp_from, s->nchars, x, s->ybase,
3912      (flags == NS_DUMPGLYPH_NORMAL && !s->background_filled_p)
3913      || flags == NS_DUMPGLYPH_MOUSEFACE);
3917 static void
3918 ns_draw_composite_glyph_string_foreground (struct glyph_string *s)
3920   int i, j, x;
3921   struct font *font = s->font;
3923   /* If first glyph of S has a left box line, start drawing the text
3924      of S to the right of that box line.  */
3925   if (s->face && s->face->box != FACE_NO_BOX
3926       && s->first_glyph->left_box_line_p)
3927     x = s->x + eabs (s->face->box_line_width);
3928   else
3929     x = s->x;
3931   /* S is a glyph string for a composition.  S->cmp_from is the index
3932      of the first character drawn for glyphs of this composition.
3933      S->cmp_from == 0 means we are drawing the very first character of
3934      this composition.  */
3936   /* Draw a rectangle for the composition if the font for the very
3937      first character of the composition could not be loaded.  */
3938   if (s->font_not_found_p)
3939     {
3940       if (s->cmp_from == 0)
3941         {
3942           NSRect r = NSMakeRect (s->x, s->y, s->width-1, s->height -1);
3943           ns_draw_box (r, 1, FRAME_CURSOR_COLOR (s->f), 1, 1);
3944         }
3945     }
3946   else if (! s->first_glyph->u.cmp.automatic)
3947     {
3948       int y = s->ybase;
3950       for (i = 0, j = s->cmp_from; i < s->nchars; i++, j++)
3951         /* TAB in a composition means display glyphs with padding
3952            space on the left or right.  */
3953         if (COMPOSITION_GLYPH (s->cmp, j) != '\t')
3954           {
3955             int xx = x + s->cmp->offsets[j * 2];
3956             int yy = y - s->cmp->offsets[j * 2 + 1];
3958             font->driver->draw (s, j, j + 1, xx, yy, false);
3959             if (s->face->overstrike)
3960               font->driver->draw (s, j, j + 1, xx + 1, yy, false);
3961           }
3962     }
3963   else
3964     {
3965       Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
3966       Lisp_Object glyph;
3967       int y = s->ybase;
3968       int width = 0;
3970       for (i = j = s->cmp_from; i < s->cmp_to; i++)
3971         {
3972           glyph = LGSTRING_GLYPH (gstring, i);
3973           if (NILP (LGLYPH_ADJUSTMENT (glyph)))
3974             width += LGLYPH_WIDTH (glyph);
3975           else
3976             {
3977               int xoff, yoff, wadjust;
3979               if (j < i)
3980                 {
3981                   font->driver->draw (s, j, i, x, y, false);
3982                   if (s->face->overstrike)
3983                     font->driver->draw (s, j, i, x + 1, y, false);
3984                   x += width;
3985                 }
3986               xoff = LGLYPH_XOFF (glyph);
3987               yoff = LGLYPH_YOFF (glyph);
3988               wadjust = LGLYPH_WADJUST (glyph);
3989               font->driver->draw (s, i, i + 1, x + xoff, y + yoff, false);
3990               if (s->face->overstrike)
3991                 font->driver->draw (s, i, i + 1, x + xoff + 1, y + yoff,
3992                                     false);
3993               x += wadjust;
3994               j = i + 1;
3995               width = 0;
3996             }
3997         }
3998       if (j < i)
3999         {
4000           font->driver->draw (s, j, i, x, y, false);
4001           if (s->face->overstrike)
4002             font->driver->draw (s, j, i, x + 1, y, false);
4003         }
4004     }
4007 static void
4008 ns_draw_glyph_string (struct glyph_string *s)
4009 /* --------------------------------------------------------------------------
4010       External (RIF): Main draw-text call.
4011    -------------------------------------------------------------------------- */
4013   /* TODO (optimize): focus for box and contents draw */
4014   NSRect r[2];
4015   int n;
4016   char box_drawn_p = 0;
4017   struct font *font = s->face->font;
4018   if (! font) font = FRAME_FONT (s->f);
4020   NSTRACE_WHEN (NSTRACE_GROUP_GLYPHS, "ns_draw_glyph_string");
4022   if (s->next && s->right_overhang && !s->for_overlaps/*&&s->hl!=DRAW_CURSOR*/)
4023     {
4024       int width;
4025       struct glyph_string *next;
4027       for (width = 0, next = s->next;
4028            next && width < s->right_overhang;
4029            width += next->width, next = next->next)
4030         if (next->first_glyph->type != IMAGE_GLYPH)
4031           {
4032             if (next->first_glyph->type != STRETCH_GLYPH)
4033               {
4034                 n = ns_get_glyph_string_clip_rect (s->next, r);
4035                 ns_focus (s->f, r, n);
4036                 ns_maybe_dumpglyphs_background (s->next, 1);
4037                 ns_unfocus (s->f);
4038               }
4039             else
4040               {
4041                 ns_dumpglyphs_stretch (s->next);
4042               }
4043             next->num_clips = 0;
4044           }
4045     }
4047   if (!s->for_overlaps && s->face->box != FACE_NO_BOX
4048         && (s->first_glyph->type == CHAR_GLYPH
4049             || s->first_glyph->type == COMPOSITE_GLYPH))
4050     {
4051       n = ns_get_glyph_string_clip_rect (s, r);
4052       ns_focus (s->f, r, n);
4053       ns_maybe_dumpglyphs_background (s, 1);
4054       ns_dumpglyphs_box_or_relief (s);
4055       ns_unfocus (s->f);
4056       box_drawn_p = 1;
4057     }
4059   switch (s->first_glyph->type)
4060     {
4062     case IMAGE_GLYPH:
4063       n = ns_get_glyph_string_clip_rect (s, r);
4064       ns_focus (s->f, r, n);
4065       ns_dumpglyphs_image (s, r[0]);
4066       ns_unfocus (s->f);
4067       break;
4069     case STRETCH_GLYPH:
4070       ns_dumpglyphs_stretch (s);
4071       break;
4073     case CHAR_GLYPH:
4074     case COMPOSITE_GLYPH:
4075       n = ns_get_glyph_string_clip_rect (s, r);
4076       ns_focus (s->f, r, n);
4078       if (s->for_overlaps || (s->cmp_from > 0
4079                               && ! s->first_glyph->u.cmp.automatic))
4080         s->background_filled_p = 1;
4081       else
4082         ns_maybe_dumpglyphs_background
4083           (s, s->first_glyph->type == COMPOSITE_GLYPH);
4085       if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
4086         {
4087           unsigned long tmp = NS_FACE_BACKGROUND (s->face);
4088           NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
4089           NS_FACE_FOREGROUND (s->face) = tmp;
4090         }
4092       {
4093         BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
4095         if (isComposite)
4096           ns_draw_composite_glyph_string_foreground (s);
4097         else
4098           ns_draw_glyph_string_foreground (s);
4099       }
4101       {
4102         NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
4103                         ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face),
4104                                                    s->f)
4105                         : FRAME_FOREGROUND_COLOR (s->f));
4106         [col set];
4108         /* Draw underline, overline, strike-through. */
4109         ns_draw_text_decoration (s, s->face, col, s->width, s->x);
4110       }
4112       if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
4113         {
4114           unsigned long tmp = NS_FACE_BACKGROUND (s->face);
4115           NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
4116           NS_FACE_FOREGROUND (s->face) = tmp;
4117         }
4119       ns_unfocus (s->f);
4120       break;
4122     case GLYPHLESS_GLYPH:
4123       n = ns_get_glyph_string_clip_rect (s, r);
4124       ns_focus (s->f, r, n);
4126       if (s->for_overlaps || (s->cmp_from > 0
4127                               && ! s->first_glyph->u.cmp.automatic))
4128         s->background_filled_p = 1;
4129       else
4130         ns_maybe_dumpglyphs_background
4131           (s, s->first_glyph->type == COMPOSITE_GLYPH);
4132       /* ... */
4133       /* Not yet implemented.  */
4134       /* ... */
4135       ns_unfocus (s->f);
4136       break;
4138     default:
4139       emacs_abort ();
4140     }
4142   /* Draw box if not done already. */
4143   if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX)
4144     {
4145       n = ns_get_glyph_string_clip_rect (s, r);
4146       ns_focus (s->f, r, n);
4147       ns_dumpglyphs_box_or_relief (s);
4148       ns_unfocus (s->f);
4149     }
4151   s->num_clips = 0;
4156 /* ==========================================================================
4158     Event loop
4160    ========================================================================== */
4163 static void
4164 ns_send_appdefined (int value)
4165 /* --------------------------------------------------------------------------
4166     Internal: post an appdefined event which EmacsApp-sendEvent will
4167               recognize and take as a command to halt the event loop.
4168    -------------------------------------------------------------------------- */
4170   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_send_appdefined(%d)", value);
4172   // GNUstep needs postEvent to happen on the main thread.
4173   // Cocoa needs nextEventMatchingMask to happen on the main thread too.
4174   if (! [[NSThread currentThread] isMainThread])
4175     {
4176       EmacsApp *app = (EmacsApp *)NSApp;
4177       app->nextappdefined = value;
4178       [app performSelectorOnMainThread:@selector (sendFromMainThread:)
4179                             withObject:nil
4180                          waitUntilDone:NO];
4181       return;
4182     }
4184   /* Only post this event if we haven't already posted one.  This will end
4185        the [NXApp run] main loop after having processed all events queued at
4186        this moment.  */
4188 #ifdef NS_IMPL_COCOA
4189   if (! send_appdefined)
4190     {
4191       /* OS X 10.10.1 swallows the AppDefined event we are sending ourselves
4192          in certain situations (rapid incoming events).
4193          So check if we have one, if not add one.  */
4194       NSEvent *appev = [NSApp nextEventMatchingMask:NSEventMaskApplicationDefined
4195                                           untilDate:[NSDate distantPast]
4196                                              inMode:NSDefaultRunLoopMode
4197                                             dequeue:NO];
4198       if (! appev) send_appdefined = YES;
4199     }
4200 #endif
4202   if (send_appdefined)
4203     {
4204       NSEvent *nxev;
4206       /* We only need one NX_APPDEFINED event to stop NXApp from running.  */
4207       send_appdefined = NO;
4209       /* Don't need wakeup timer any more */
4210       if (timed_entry)
4211         {
4212           [timed_entry invalidate];
4213           [timed_entry release];
4214           timed_entry = nil;
4215         }
4217       nxev = [NSEvent otherEventWithType: NSEventTypeApplicationDefined
4218                                 location: NSMakePoint (0, 0)
4219                            modifierFlags: 0
4220                                timestamp: 0
4221                             windowNumber: [[NSApp mainWindow] windowNumber]
4222                                  context: [NSApp context]
4223                                  subtype: 0
4224                                    data1: value
4225                                    data2: 0];
4227       /* Post an application defined event on the event queue.  When this is
4228          received the [NXApp run] will return, thus having processed all
4229          events which are currently queued.  */
4230       [NSApp postEvent: nxev atStart: NO];
4231     }
4234 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4235 static void
4236 check_native_fs ()
4238   Lisp_Object frame, tail;
4240   if (ns_last_use_native_fullscreen == ns_use_native_fullscreen)
4241     return;
4243   ns_last_use_native_fullscreen = ns_use_native_fullscreen;
4245   FOR_EACH_FRAME (tail, frame)
4246     {
4247       struct frame *f = XFRAME (frame);
4248       if (FRAME_NS_P (f))
4249         {
4250           EmacsView *view = FRAME_NS_VIEW (f);
4251           [view updateCollectionBehavior];
4252         }
4253     }
4255 #endif
4257 /* GNUstep does not have cancelTracking.  */
4258 #ifdef NS_IMPL_COCOA
4259 /* Check if menu open should be canceled or continued as normal.  */
4260 void
4261 ns_check_menu_open (NSMenu *menu)
4263   /* Click in menu bar? */
4264   NSArray *a = [[NSApp mainMenu] itemArray];
4265   int i;
4266   BOOL found = NO;
4268   if (menu == nil) // Menu tracking ended.
4269     {
4270       if (menu_will_open_state == MENU_OPENING)
4271         menu_will_open_state = MENU_NONE;
4272       return;
4273     }
4275   for (i = 0; ! found && i < [a count]; i++)
4276     found = menu == [[a objectAtIndex:i] submenu];
4277   if (found)
4278     {
4279       if (menu_will_open_state == MENU_NONE && emacs_event)
4280         {
4281           NSEvent *theEvent = [NSApp currentEvent];
4282           struct frame *emacsframe = SELECTED_FRAME ();
4284           [menu cancelTracking];
4285           menu_will_open_state = MENU_PENDING;
4286           emacs_event->kind = MENU_BAR_ACTIVATE_EVENT;
4287           EV_TRAILER (theEvent);
4289           CGEventRef ourEvent = CGEventCreate (NULL);
4290           menu_mouse_point = CGEventGetLocation (ourEvent);
4291           CFRelease (ourEvent);
4292         }
4293       else if (menu_will_open_state == MENU_OPENING)
4294         {
4295           menu_will_open_state = MENU_NONE;
4296         }
4297     }
4300 /* Redo saved menu click if state is MENU_PENDING.  */
4301 void
4302 ns_check_pending_open_menu ()
4304   if (menu_will_open_state == MENU_PENDING)
4305     {
4306       CGEventSourceRef source
4307         = CGEventSourceCreate (kCGEventSourceStateHIDSystemState);
4309       CGEventRef event = CGEventCreateMouseEvent (source,
4310                                                   kCGEventLeftMouseDown,
4311                                                   menu_mouse_point,
4312                                                   kCGMouseButtonLeft);
4313       CGEventSetType (event, kCGEventLeftMouseDown);
4314       CGEventPost (kCGHIDEventTap, event);
4315       CFRelease (event);
4316       CFRelease (source);
4318       menu_will_open_state = MENU_OPENING;
4319     }
4321 #endif /* NS_IMPL_COCOA */
4323 static int
4324 ns_read_socket (struct terminal *terminal, struct input_event *hold_quit)
4325 /* --------------------------------------------------------------------------
4326      External (hook): Post an event to ourself and keep reading events until
4327      we read it back again.  In effect process all events which were waiting.
4328      From 21+ we have to manage the event buffer ourselves.
4329    -------------------------------------------------------------------------- */
4331   struct input_event ev;
4332   int nevents;
4334   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_read_socket");
4336 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4337   check_native_fs ();
4338 #endif
4340   if ([NSApp modalWindow] != nil)
4341     return -1;
4343   if (hold_event_q.nr > 0)
4344     {
4345       int i;
4346       for (i = 0; i < hold_event_q.nr; ++i)
4347         kbd_buffer_store_event_hold (&hold_event_q.q[i], hold_quit);
4348       hold_event_q.nr = 0;
4349       return i;
4350     }
4352   if ([NSThread isMainThread])
4353     {
4354       block_input ();
4355       n_emacs_events_pending = 0;
4356       ns_init_events (&ev);
4357       q_event_ptr = hold_quit;
4359       /* we manage autorelease pools by allocate/reallocate each time around
4360          the loop; strict nesting is occasionally violated but seems not to
4361          matter.. earlier methods using full nesting caused major memory leaks */
4362       [outerpool release];
4363       outerpool = [[NSAutoreleasePool alloc] init];
4365       /* If have pending open-file requests, attend to the next one of those. */
4366       if (ns_pending_files && [ns_pending_files count] != 0
4367           && [(EmacsApp *)NSApp openFile: [ns_pending_files objectAtIndex: 0]])
4368         {
4369           [ns_pending_files removeObjectAtIndex: 0];
4370         }
4371       /* Deal with pending service requests. */
4372       else if (ns_pending_service_names && [ns_pending_service_names count] != 0
4373                && [(EmacsApp *)
4374                     NSApp fulfillService: [ns_pending_service_names objectAtIndex: 0]
4375                                  withArg: [ns_pending_service_args objectAtIndex: 0]])
4376         {
4377           [ns_pending_service_names removeObjectAtIndex: 0];
4378           [ns_pending_service_args removeObjectAtIndex: 0];
4379         }
4380       else
4381         {
4382           /* Run and wait for events.  We must always send one NX_APPDEFINED event
4383              to ourself, otherwise [NXApp run] will never exit.  */
4384           send_appdefined = YES;
4385           ns_send_appdefined (-1);
4387           [NSApp run];
4388         }
4390       nevents = n_emacs_events_pending;
4391       n_emacs_events_pending = 0;
4392       ns_finish_events ();
4393       q_event_ptr = NULL;
4394       unblock_input ();
4395     }
4396   else
4397     return -1;
4399   return nevents;
4404 ns_select (int nfds, fd_set *readfds, fd_set *writefds,
4405            fd_set *exceptfds, struct timespec *timeout,
4406            sigset_t *sigmask)
4407 /* --------------------------------------------------------------------------
4408      Replacement for select, checking for events
4409    -------------------------------------------------------------------------- */
4411   int result;
4412   int t, k, nr = 0;
4413   struct input_event event;
4414   char c;
4416   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_select");
4418 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4419   check_native_fs ();
4420 #endif
4422   if (hold_event_q.nr > 0)
4423     {
4424       /* We already have events pending. */
4425       raise (SIGIO);
4426       errno = EINTR;
4427       return -1;
4428     }
4430   for (k = 0; k < nfds+1; k++)
4431     {
4432       if (readfds && FD_ISSET(k, readfds)) ++nr;
4433       if (writefds && FD_ISSET(k, writefds)) ++nr;
4434     }
4436   if (NSApp == nil
4437       || ![NSThread isMainThread]
4438       || (timeout && timeout->tv_sec == 0 && timeout->tv_nsec == 0))
4439     return thread_select(pselect, nfds, readfds, writefds,
4440                          exceptfds, timeout, sigmask);
4441   else
4442     {
4443       struct timespec t = {0, 0};
4444       thread_select(pselect, 0, NULL, NULL, NULL, &t, sigmask);
4445     }
4447   [outerpool release];
4448   outerpool = [[NSAutoreleasePool alloc] init];
4451   send_appdefined = YES;
4452   if (nr > 0)
4453     {
4454       pthread_mutex_lock (&select_mutex);
4455       select_nfds = nfds;
4456       select_valid = 0;
4457       if (readfds)
4458         {
4459           select_readfds = *readfds;
4460           select_valid += SELECT_HAVE_READ;
4461         }
4462       if (writefds)
4463         {
4464           select_writefds = *writefds;
4465           select_valid += SELECT_HAVE_WRITE;
4466         }
4468       if (timeout)
4469         {
4470           select_timeout = *timeout;
4471           select_valid += SELECT_HAVE_TMO;
4472         }
4474       pthread_mutex_unlock (&select_mutex);
4476       /* Inform fd_handler that select should be called */
4477       c = 'g';
4478       emacs_write_sig (selfds[1], &c, 1);
4479     }
4480   else if (nr == 0 && timeout)
4481     {
4482       /* No file descriptor, just a timeout, no need to wake fd_handler  */
4483       double time = timespectod (*timeout);
4484       timed_entry = [[NSTimer scheduledTimerWithTimeInterval: time
4485                                                       target: NSApp
4486                                                     selector:
4487                                   @selector (timeout_handler:)
4488                                                     userInfo: 0
4489                                                      repeats: NO]
4490                       retain];
4491     }
4492   else /* No timeout and no file descriptors, can this happen?  */
4493     {
4494       /* Send appdefined so we exit from the loop */
4495       ns_send_appdefined (-1);
4496     }
4498   block_input ();
4499   ns_init_events (&event);
4501   [NSApp run];
4503   ns_finish_events ();
4504   if (nr > 0 && readfds)
4505     {
4506       c = 's';
4507       emacs_write_sig (selfds[1], &c, 1);
4508     }
4509   unblock_input ();
4511   t = last_appdefined_event_data;
4513   if (t != NO_APPDEFINED_DATA)
4514     {
4515       last_appdefined_event_data = NO_APPDEFINED_DATA;
4517       if (t == -2)
4518         {
4519           /* The NX_APPDEFINED event we received was a timeout. */
4520           result = 0;
4521         }
4522       else if (t == -1)
4523         {
4524           /* The NX_APPDEFINED event we received was the result of
4525              at least one real input event arriving.  */
4526           errno = EINTR;
4527           result = -1;
4528         }
4529       else
4530         {
4531           /* Received back from select () in fd_handler; copy the results */
4532           pthread_mutex_lock (&select_mutex);
4533           if (readfds) *readfds = select_readfds;
4534           if (writefds) *writefds = select_writefds;
4535           pthread_mutex_unlock (&select_mutex);
4536           result = t;
4537         }
4538     }
4539   else
4540     {
4541       errno = EINTR;
4542       result = -1;
4543     }
4545   return result;
4548 #ifdef HAVE_PTHREAD
4549 void
4550 ns_run_loop_break ()
4551 /* Break out of the NS run loop in ns_select or ns_read_socket. */
4553   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_run_loop_break");
4555   /* If we don't have a GUI, don't send the event. */
4556   if (NSApp != NULL)
4557     ns_send_appdefined(-1);
4559 #endif
4562 /* ==========================================================================
4564     Scrollbar handling
4566    ========================================================================== */
4569 static void
4570 ns_set_vertical_scroll_bar (struct window *window,
4571                            int portion, int whole, int position)
4572 /* --------------------------------------------------------------------------
4573       External (hook): Update or add scrollbar
4574    -------------------------------------------------------------------------- */
4576   Lisp_Object win;
4577   NSRect r, v;
4578   struct frame *f = XFRAME (WINDOW_FRAME (window));
4579   EmacsView *view = FRAME_NS_VIEW (f);
4580   EmacsScroller *bar;
4581   int window_y, window_height;
4582   int top, left, height, width;
4583   BOOL update_p = YES;
4585   /* optimization; display engine sends WAY too many of these.. */
4586   if (!NILP (window->vertical_scroll_bar))
4587     {
4588       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4589       if ([bar checkSamePosition: position portion: portion whole: whole])
4590         {
4591           if (view->scrollbarsNeedingUpdate == 0)
4592             {
4593               if (!windows_or_buffers_changed)
4594                   return;
4595             }
4596           else
4597             view->scrollbarsNeedingUpdate--;
4598           update_p = NO;
4599         }
4600     }
4602   NSTRACE ("ns_set_vertical_scroll_bar");
4604   /* Get dimensions.  */
4605   window_box (window, ANY_AREA, 0, &window_y, 0, &window_height);
4606   top = window_y;
4607   height = window_height;
4608   width = NS_SCROLL_BAR_WIDTH (f);
4609   left = WINDOW_SCROLL_BAR_AREA_X (window);
4611   r = NSMakeRect (left, top, width, height);
4612   /* the parent view is flipped, so we need to flip y value */
4613   v = [view frame];
4614   r.origin.y = (v.size.height - r.size.height - r.origin.y);
4616   XSETWINDOW (win, window);
4617   block_input ();
4619   /* we want at least 5 lines to display a scrollbar */
4620   if (WINDOW_TOTAL_LINES (window) < 5)
4621     {
4622       if (!NILP (window->vertical_scroll_bar))
4623         {
4624           bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4625           [bar removeFromSuperview];
4626           wset_vertical_scroll_bar (window, Qnil);
4627           [bar release];
4628         }
4629       ns_clear_frame_area (f, left, top, width, height);
4630       unblock_input ();
4631       return;
4632     }
4634   if (NILP (window->vertical_scroll_bar))
4635     {
4636       if (width > 0 && height > 0)
4637         ns_clear_frame_area (f, left, top, width, height);
4639       bar = [[EmacsScroller alloc] initFrame: r window: win];
4640       wset_vertical_scroll_bar (window, make_save_ptr (bar));
4641       update_p = YES;
4642     }
4643   else
4644     {
4645       NSRect oldRect;
4646       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4647       oldRect = [bar frame];
4648       r.size.width = oldRect.size.width;
4649       if (FRAME_LIVE_P (f) && !NSEqualRects (oldRect, r))
4650         {
4651           if (oldRect.origin.x != r.origin.x)
4652               ns_clear_frame_area (f, left, top, width, height);
4653           [bar setFrame: r];
4654         }
4655     }
4657   if (update_p)
4658     [bar setPosition: position portion: portion whole: whole];
4659   unblock_input ();
4663 static void
4664 ns_set_horizontal_scroll_bar (struct window *window,
4665                               int portion, int whole, int position)
4666 /* --------------------------------------------------------------------------
4667       External (hook): Update or add scrollbar
4668    -------------------------------------------------------------------------- */
4670   Lisp_Object win;
4671   NSRect r, v;
4672   struct frame *f = XFRAME (WINDOW_FRAME (window));
4673   EmacsView *view = FRAME_NS_VIEW (f);
4674   EmacsScroller *bar;
4675   int top, height, left, width;
4676   int window_x, window_width;
4677   BOOL update_p = YES;
4679   /* optimization; display engine sends WAY too many of these.. */
4680   if (!NILP (window->horizontal_scroll_bar))
4681     {
4682       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4683       if ([bar checkSamePosition: position portion: portion whole: whole])
4684         {
4685           if (view->scrollbarsNeedingUpdate == 0)
4686             {
4687               if (!windows_or_buffers_changed)
4688                   return;
4689             }
4690           else
4691             view->scrollbarsNeedingUpdate--;
4692           update_p = NO;
4693         }
4694     }
4696   NSTRACE ("ns_set_horizontal_scroll_bar");
4698   /* Get dimensions.  */
4699   window_box (window, ANY_AREA, &window_x, 0, &window_width, 0);
4700   left = window_x;
4701   width = window_width;
4702   height = NS_SCROLL_BAR_HEIGHT (f);
4703   top = WINDOW_SCROLL_BAR_AREA_Y (window);
4705   r = NSMakeRect (left, top, width, height);
4706   /* the parent view is flipped, so we need to flip y value */
4707   v = [view frame];
4708   r.origin.y = (v.size.height - r.size.height - r.origin.y);
4710   XSETWINDOW (win, window);
4711   block_input ();
4713   if (NILP (window->horizontal_scroll_bar))
4714     {
4715       if (width > 0 && height > 0)
4716         ns_clear_frame_area (f, left, top, width, height);
4718       bar = [[EmacsScroller alloc] initFrame: r window: win];
4719       wset_horizontal_scroll_bar (window, make_save_ptr (bar));
4720       update_p = YES;
4721     }
4722   else
4723     {
4724       NSRect oldRect;
4725       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4726       oldRect = [bar frame];
4727       if (FRAME_LIVE_P (f) && !NSEqualRects (oldRect, r))
4728         {
4729           if (oldRect.origin.y != r.origin.y)
4730             ns_clear_frame_area (f, left, top, width, height);
4731           [bar setFrame: r];
4732           update_p = YES;
4733         }
4734     }
4736   /* If there are both horizontal and vertical scroll-bars they leave
4737      a square that belongs to neither. We need to clear it otherwise
4738      it fills with junk. */
4739   if (!NILP (window->vertical_scroll_bar))
4740     ns_clear_frame_area (f, WINDOW_SCROLL_BAR_AREA_X (window), top,
4741                          NS_SCROLL_BAR_HEIGHT (f), height);
4743   if (update_p)
4744     [bar setPosition: position portion: portion whole: whole];
4745   unblock_input ();
4749 static void
4750 ns_condemn_scroll_bars (struct frame *f)
4751 /* --------------------------------------------------------------------------
4752      External (hook): arrange for all frame's scrollbars to be removed
4753      at next call to judge_scroll_bars, except for those redeemed.
4754    -------------------------------------------------------------------------- */
4756   int i;
4757   id view;
4758   NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews];
4760   NSTRACE ("ns_condemn_scroll_bars");
4762   for (i =[subviews count]-1; i >= 0; i--)
4763     {
4764       view = [subviews objectAtIndex: i];
4765       if ([view isKindOfClass: [EmacsScroller class]])
4766         [view condemn];
4767     }
4771 static void
4772 ns_redeem_scroll_bar (struct window *window)
4773 /* --------------------------------------------------------------------------
4774      External (hook): arrange to spare this window's scrollbar
4775      at next call to judge_scroll_bars.
4776    -------------------------------------------------------------------------- */
4778   id bar;
4779   NSTRACE ("ns_redeem_scroll_bar");
4780   if (!NILP (window->vertical_scroll_bar)
4781       && WINDOW_HAS_VERTICAL_SCROLL_BAR (window))
4782     {
4783       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4784       [bar reprieve];
4785     }
4787   if (!NILP (window->horizontal_scroll_bar)
4788       && WINDOW_HAS_HORIZONTAL_SCROLL_BAR (window))
4789     {
4790       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4791       [bar reprieve];
4792     }
4796 static void
4797 ns_judge_scroll_bars (struct frame *f)
4798 /* --------------------------------------------------------------------------
4799      External (hook): destroy all scrollbars on frame that weren't
4800      redeemed after call to condemn_scroll_bars.
4801    -------------------------------------------------------------------------- */
4803   int i;
4804   id view;
4805   EmacsView *eview = FRAME_NS_VIEW (f);
4806   NSArray *subviews = [[eview superview] subviews];
4807   BOOL removed = NO;
4809   NSTRACE ("ns_judge_scroll_bars");
4810   for (i = [subviews count]-1; i >= 0; --i)
4811     {
4812       view = [subviews objectAtIndex: i];
4813       if (![view isKindOfClass: [EmacsScroller class]]) continue;
4814       if ([view judge])
4815         removed = YES;
4816     }
4818   if (removed)
4819     [eview updateFrameSize: NO];
4822 /* ==========================================================================
4824     Initialization
4826    ========================================================================== */
4829 x_display_pixel_height (struct ns_display_info *dpyinfo)
4831   NSArray *screens = [NSScreen screens];
4832   NSEnumerator *enumerator = [screens objectEnumerator];
4833   NSScreen *screen;
4834   NSRect frame;
4836   frame = NSZeroRect;
4837   while ((screen = [enumerator nextObject]) != nil)
4838     frame = NSUnionRect (frame, [screen frame]);
4840   return NSHeight (frame);
4844 x_display_pixel_width (struct ns_display_info *dpyinfo)
4846   NSArray *screens = [NSScreen screens];
4847   NSEnumerator *enumerator = [screens objectEnumerator];
4848   NSScreen *screen;
4849   NSRect frame;
4851   frame = NSZeroRect;
4852   while ((screen = [enumerator nextObject]) != nil)
4853     frame = NSUnionRect (frame, [screen frame]);
4855   return NSWidth (frame);
4859 static Lisp_Object ns_string_to_lispmod (const char *s)
4860 /* --------------------------------------------------------------------------
4861      Convert modifier name to lisp symbol
4862    -------------------------------------------------------------------------- */
4864   if (!strncmp (SSDATA (SYMBOL_NAME (Qmeta)), s, 10))
4865     return Qmeta;
4866   else if (!strncmp (SSDATA (SYMBOL_NAME (Qsuper)), s, 10))
4867     return Qsuper;
4868   else if (!strncmp (SSDATA (SYMBOL_NAME (Qcontrol)), s, 10))
4869     return Qcontrol;
4870   else if (!strncmp (SSDATA (SYMBOL_NAME (Qalt)), s, 10))
4871     return Qalt;
4872   else if (!strncmp (SSDATA (SYMBOL_NAME (Qhyper)), s, 10))
4873     return Qhyper;
4874   else if (!strncmp (SSDATA (SYMBOL_NAME (Qnone)), s, 10))
4875     return Qnone;
4876   else
4877     return Qnil;
4881 static void
4882 ns_default (const char *parameter, Lisp_Object *result,
4883            Lisp_Object yesval, Lisp_Object noval,
4884            BOOL is_float, BOOL is_modstring)
4885 /* --------------------------------------------------------------------------
4886       Check a parameter value in user's preferences
4887    -------------------------------------------------------------------------- */
4889   const char *value = ns_get_defaults_value (parameter);
4891   if (value)
4892     {
4893       double f;
4894       char *pos;
4895       if (c_strcasecmp (value, "YES") == 0)
4896         *result = yesval;
4897       else if (c_strcasecmp (value, "NO") == 0)
4898         *result = noval;
4899       else if (is_float && (f = strtod (value, &pos), pos != value))
4900         *result = make_float (f);
4901       else if (is_modstring && value)
4902         *result = ns_string_to_lispmod (value);
4903       else fprintf (stderr,
4904                    "Bad value for default \"%s\": \"%s\"\n", parameter, value);
4905     }
4909 static void
4910 ns_initialize_display_info (struct ns_display_info *dpyinfo)
4911 /* --------------------------------------------------------------------------
4912       Initialize global info and storage for display.
4913    -------------------------------------------------------------------------- */
4915     NSScreen *screen = [NSScreen mainScreen];
4916     NSWindowDepth depth = [screen depth];
4918     dpyinfo->resx = 72.27; /* used 75.0, but this makes pt == pixel, expected */
4919     dpyinfo->resy = 72.27;
4920     dpyinfo->color_p = ![NSDeviceWhiteColorSpace isEqualToString:
4921                                                   NSColorSpaceFromDepth (depth)]
4922                 && ![NSCalibratedWhiteColorSpace isEqualToString:
4923                                                  NSColorSpaceFromDepth (depth)];
4924     dpyinfo->n_planes = NSBitsPerPixelFromDepth (depth);
4925     dpyinfo->color_table = xmalloc (sizeof *dpyinfo->color_table);
4926     dpyinfo->color_table->colors = NULL;
4927     dpyinfo->root_window = 42; /* a placeholder.. */
4928     dpyinfo->x_highlight_frame = dpyinfo->x_focus_frame = NULL;
4929     dpyinfo->n_fonts = 0;
4930     dpyinfo->smallest_font_height = 1;
4931     dpyinfo->smallest_char_width = 1;
4933     reset_mouse_highlight (&dpyinfo->mouse_highlight);
4937 /* This and next define (many of the) public functions in this file. */
4938 /* x_... are generic versions in xdisp.c that we, and other terms, get away
4939          with using despite presence in the "system dependent" redisplay
4940          interface.  In addition, many of the ns_ methods have code that is
4941          shared with all terms, indicating need for further refactoring. */
4942 extern frame_parm_handler ns_frame_parm_handlers[];
4943 static struct redisplay_interface ns_redisplay_interface =
4945   ns_frame_parm_handlers,
4946   x_produce_glyphs,
4947   x_write_glyphs,
4948   x_insert_glyphs,
4949   x_clear_end_of_line,
4950   ns_scroll_run,
4951   ns_after_update_window_line,
4952   ns_update_window_begin,
4953   ns_update_window_end,
4954   0, /* flush_display */
4955   x_clear_window_mouse_face,
4956   x_get_glyph_overhangs,
4957   x_fix_overlapping_area,
4958   ns_draw_fringe_bitmap,
4959   0, /* define_fringe_bitmap */ /* FIXME: simplify ns_draw_fringe_bitmap */
4960   0, /* destroy_fringe_bitmap */
4961   ns_compute_glyph_string_overhangs,
4962   ns_draw_glyph_string,
4963   ns_define_frame_cursor,
4964   ns_clear_frame_area,
4965   ns_draw_window_cursor,
4966   ns_draw_vertical_window_border,
4967   ns_draw_window_divider,
4968   ns_shift_glyphs_for_insert,
4969   ns_show_hourglass,
4970   ns_hide_hourglass
4974 static void
4975 ns_delete_display (struct ns_display_info *dpyinfo)
4977   /* TODO... */
4981 /* This function is called when the last frame on a display is deleted. */
4982 static void
4983 ns_delete_terminal (struct terminal *terminal)
4985   struct ns_display_info *dpyinfo = terminal->display_info.ns;
4987   NSTRACE ("ns_delete_terminal");
4989   /* Protect against recursive calls.  delete_frame in
4990      delete_terminal calls us back when it deletes our last frame.  */
4991   if (!terminal->name)
4992     return;
4994   block_input ();
4996   x_destroy_all_bitmaps (dpyinfo);
4997   ns_delete_display (dpyinfo);
4998   unblock_input ();
5002 static struct terminal *
5003 ns_create_terminal (struct ns_display_info *dpyinfo)
5004 /* --------------------------------------------------------------------------
5005       Set up use of NS before we make the first connection.
5006    -------------------------------------------------------------------------- */
5008   struct terminal *terminal;
5010   NSTRACE ("ns_create_terminal");
5012   terminal = create_terminal (output_ns, &ns_redisplay_interface);
5014   terminal->display_info.ns = dpyinfo;
5015   dpyinfo->terminal = terminal;
5017   terminal->clear_frame_hook = ns_clear_frame;
5018   terminal->ring_bell_hook = ns_ring_bell;
5019   terminal->update_begin_hook = ns_update_begin;
5020   terminal->update_end_hook = ns_update_end;
5021   terminal->read_socket_hook = ns_read_socket;
5022   terminal->frame_up_to_date_hook = ns_frame_up_to_date;
5023   terminal->mouse_position_hook = ns_mouse_position;
5024   terminal->frame_rehighlight_hook = ns_frame_rehighlight;
5025   terminal->frame_raise_lower_hook = ns_frame_raise_lower;
5026   terminal->fullscreen_hook = ns_fullscreen_hook;
5027   terminal->menu_show_hook = ns_menu_show;
5028   terminal->popup_dialog_hook = ns_popup_dialog;
5029   terminal->set_vertical_scroll_bar_hook = ns_set_vertical_scroll_bar;
5030   terminal->set_horizontal_scroll_bar_hook = ns_set_horizontal_scroll_bar;
5031   terminal->condemn_scroll_bars_hook = ns_condemn_scroll_bars;
5032   terminal->redeem_scroll_bar_hook = ns_redeem_scroll_bar;
5033   terminal->judge_scroll_bars_hook = ns_judge_scroll_bars;
5034   terminal->delete_frame_hook = x_destroy_window;
5035   terminal->delete_terminal_hook = ns_delete_terminal;
5036   /* Other hooks are NULL by default.  */
5038   return terminal;
5042 struct ns_display_info *
5043 ns_term_init (Lisp_Object display_name)
5044 /* --------------------------------------------------------------------------
5045      Start the Application and get things rolling.
5046    -------------------------------------------------------------------------- */
5048   struct terminal *terminal;
5049   struct ns_display_info *dpyinfo;
5050   static int ns_initialized = 0;
5051   Lisp_Object tmp;
5053   if (ns_initialized) return x_display_list;
5054   ns_initialized = 1;
5056   block_input ();
5058   NSTRACE ("ns_term_init");
5060   [outerpool release];
5061   outerpool = [[NSAutoreleasePool alloc] init];
5063   /* count object allocs (About, click icon); on macOS use ObjectAlloc tool */
5064   /*GSDebugAllocationActive (YES); */
5065   block_input ();
5067   baud_rate = 38400;
5068   Fset_input_interrupt_mode (Qnil);
5070   if (selfds[0] == -1)
5071     {
5072       if (emacs_pipe (selfds) != 0)
5073         {
5074           fprintf (stderr, "Failed to create pipe: %s\n",
5075                    emacs_strerror (errno));
5076           emacs_abort ();
5077         }
5079       fcntl (selfds[0], F_SETFL, O_NONBLOCK|fcntl (selfds[0], F_GETFL));
5080       FD_ZERO (&select_readfds);
5081       FD_ZERO (&select_writefds);
5082       pthread_mutex_init (&select_mutex, NULL);
5083     }
5085   ns_pending_files = [[NSMutableArray alloc] init];
5086   ns_pending_service_names = [[NSMutableArray alloc] init];
5087   ns_pending_service_args = [[NSMutableArray alloc] init];
5089 /* Start app and create the main menu, window, view.
5090      Needs to be here because ns_initialize_display_info () uses AppKit classes.
5091      The view will then ask the NSApp to stop and return to Emacs. */
5092   [EmacsApp sharedApplication];
5093   if (NSApp == nil)
5094     return NULL;
5095   [NSApp setDelegate: NSApp];
5097   /* Start the select thread.  */
5098   [NSThread detachNewThreadSelector:@selector (fd_handler:)
5099                            toTarget:NSApp
5100                          withObject:nil];
5102   /* debugging: log all notifications */
5103   /*   [[NSNotificationCenter defaultCenter] addObserver: NSApp
5104                                          selector: @selector (logNotification:)
5105                                              name: nil object: nil]; */
5107   dpyinfo = xzalloc (sizeof *dpyinfo);
5109   ns_initialize_display_info (dpyinfo);
5110   terminal = ns_create_terminal (dpyinfo);
5112   terminal->kboard = allocate_kboard (Qns);
5113   /* Don't let the initial kboard remain current longer than necessary.
5114      That would cause problems if a file loaded on startup tries to
5115      prompt in the mini-buffer.  */
5116   if (current_kboard == initial_kboard)
5117     current_kboard = terminal->kboard;
5118   terminal->kboard->reference_count++;
5120   dpyinfo->next = x_display_list;
5121   x_display_list = dpyinfo;
5123   dpyinfo->name_list_element = Fcons (display_name, Qnil);
5125   terminal->name = xlispstrdup (display_name);
5127   unblock_input ();
5129   if (!inhibit_x_resources)
5130     {
5131       ns_default ("GSFontAntiAlias", &ns_antialias_text,
5132                  Qt, Qnil, NO, NO);
5133       tmp = Qnil;
5134       /* this is a standard variable */
5135       ns_default ("AppleAntiAliasingThreshold", &tmp,
5136                  make_float (10.0), make_float (6.0), YES, NO);
5137       ns_antialias_threshold = NILP (tmp) ? 10.0 : extract_float (tmp);
5138     }
5140   NSTRACE_MSG ("Colors");
5142   {
5143     NSColorList *cl = [NSColorList colorListNamed: @"Emacs"];
5145     if ( cl == nil )
5146       {
5147         Lisp_Object color_file, color_map, color;
5148         unsigned long c;
5149         char *name;
5151         color_file = Fexpand_file_name (build_string ("rgb.txt"),
5152                          Fsymbol_value (intern ("data-directory")));
5154         color_map = Fx_load_color_file (color_file);
5155         if (NILP (color_map))
5156           fatal ("Could not read %s.\n", SDATA (color_file));
5158         cl = [[NSColorList alloc] initWithName: @"Emacs"];
5159         for ( ; CONSP (color_map); color_map = XCDR (color_map))
5160           {
5161             color = XCAR (color_map);
5162             name = SSDATA (XCAR (color));
5163             c = XINT (XCDR (color));
5164             [cl setColor:
5165                   [NSColor colorForEmacsRed: RED_FROM_ULONG (c) / 255.0
5166                                       green: GREEN_FROM_ULONG (c) / 255.0
5167                                        blue: BLUE_FROM_ULONG (c) / 255.0
5168                                       alpha: 1.0]
5169                   forKey: [NSString stringWithUTF8String: name]];
5170           }
5171         [cl writeToFile: nil];
5172       }
5173   }
5175   NSTRACE_MSG ("Versions");
5177   {
5178 #ifdef NS_IMPL_GNUSTEP
5179     Vwindow_system_version = build_string (gnustep_base_version);
5180 #else
5181     /*PSnextrelease (128, c); */
5182     char c[DBL_BUFSIZE_BOUND];
5183     int len = dtoastr (c, sizeof c, 0, 0, NSAppKitVersionNumber);
5184     Vwindow_system_version = make_unibyte_string (c, len);
5185 #endif
5186   }
5188   delete_keyboard_wait_descriptor (0);
5190   ns_app_name = [[NSProcessInfo processInfo] processName];
5192   /* Set up macOS app menu */
5194   NSTRACE_MSG ("Menu init");
5196 #ifdef NS_IMPL_COCOA
5197   {
5198     NSMenu *appMenu;
5199     NSMenuItem *item;
5200     /* set up the application menu */
5201     svcsMenu = [[EmacsMenu alloc] initWithTitle: @"Services"];
5202     [svcsMenu setAutoenablesItems: NO];
5203     appMenu = [[EmacsMenu alloc] initWithTitle: @"Emacs"];
5204     [appMenu setAutoenablesItems: NO];
5205     mainMenu = [[EmacsMenu alloc] initWithTitle: @""];
5206     dockMenu = [[EmacsMenu alloc] initWithTitle: @""];
5208     [appMenu insertItemWithTitle: @"About Emacs"
5209                           action: @selector (orderFrontStandardAboutPanel:)
5210                    keyEquivalent: @""
5211                          atIndex: 0];
5212     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 1];
5213     [appMenu insertItemWithTitle: @"Preferences..."
5214                           action: @selector (showPreferencesWindow:)
5215                    keyEquivalent: @","
5216                          atIndex: 2];
5217     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 3];
5218     item = [appMenu insertItemWithTitle: @"Services"
5219                                  action: @selector (menuDown:)
5220                           keyEquivalent: @""
5221                                 atIndex: 4];
5222     [appMenu setSubmenu: svcsMenu forItem: item];
5223     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 5];
5224     [appMenu insertItemWithTitle: @"Hide Emacs"
5225                           action: @selector (hide:)
5226                    keyEquivalent: @"h"
5227                          atIndex: 6];
5228     item =  [appMenu insertItemWithTitle: @"Hide Others"
5229                           action: @selector (hideOtherApplications:)
5230                    keyEquivalent: @"h"
5231                          atIndex: 7];
5232     [item setKeyEquivalentModifierMask: NSEventModifierFlagCommand | NSEventModifierFlagOption];
5233     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 8];
5234     [appMenu insertItemWithTitle: @"Quit Emacs"
5235                           action: @selector (terminate:)
5236                    keyEquivalent: @"q"
5237                          atIndex: 9];
5239     item = [mainMenu insertItemWithTitle: ns_app_name
5240                                   action: @selector (menuDown:)
5241                            keyEquivalent: @""
5242                                  atIndex: 0];
5243     [mainMenu setSubmenu: appMenu forItem: item];
5244     [dockMenu insertItemWithTitle: @"New Frame"
5245                            action: @selector (newFrame:)
5246                     keyEquivalent: @""
5247                           atIndex: 0];
5249     [NSApp setMainMenu: mainMenu];
5250     [NSApp setAppleMenu: appMenu];
5251     [NSApp setServicesMenu: svcsMenu];
5252     /* Needed at least on Cocoa, to get dock menu to show windows */
5253     [NSApp setWindowsMenu: [[NSMenu alloc] init]];
5255     [[NSNotificationCenter defaultCenter]
5256       addObserver: mainMenu
5257          selector: @selector (trackingNotification:)
5258              name: NSMenuDidBeginTrackingNotification object: mainMenu];
5259     [[NSNotificationCenter defaultCenter]
5260       addObserver: mainMenu
5261          selector: @selector (trackingNotification:)
5262              name: NSMenuDidEndTrackingNotification object: mainMenu];
5263   }
5264 #endif /* macOS menu setup */
5266   /* Register our external input/output types, used for determining
5267      applicable services and also drag/drop eligibility. */
5269   NSTRACE_MSG ("Input/output types");
5271   ns_send_types = [[NSArray arrayWithObjects: NSStringPboardType, nil] retain];
5272   ns_return_types = [[NSArray arrayWithObjects: NSStringPboardType, nil]
5273                       retain];
5274   ns_drag_types = [[NSArray arrayWithObjects:
5275                             NSStringPboardType,
5276                             NSTabularTextPboardType,
5277                             NSFilenamesPboardType,
5278                             NSURLPboardType, nil] retain];
5280   /* If fullscreen is in init/default-frame-alist, focus isn't set
5281      right for fullscreen windows, so set this.  */
5282   [NSApp activateIgnoringOtherApps:YES];
5284   NSTRACE_MSG ("Call NSApp run");
5286   [NSApp run];
5287   ns_do_open_file = YES;
5289 #ifdef NS_IMPL_GNUSTEP
5290   /* GNUstep steals SIGCHLD for use in NSTask, but we don't use NSTask.
5291      We must re-catch it so subprocess works.  */
5292   catch_child_signal ();
5293 #endif
5295   NSTRACE_MSG ("ns_term_init done");
5297   unblock_input ();
5299   return dpyinfo;
5303 void
5304 ns_term_shutdown (int sig)
5306   [[NSUserDefaults standardUserDefaults] synchronize];
5308   /* code not reached in emacs.c after this is called by shut_down_emacs: */
5309   if (STRINGP (Vauto_save_list_file_name))
5310     unlink (SSDATA (Vauto_save_list_file_name));
5312   if (sig == 0 || sig == SIGTERM)
5313     {
5314       [NSApp terminate: NSApp];
5315     }
5316   else // force a stack trace to happen
5317     {
5318       emacs_abort ();
5319     }
5323 /* ==========================================================================
5325     EmacsApp implementation
5327    ========================================================================== */
5330 @implementation EmacsApp
5332 - (id)init
5334   NSTRACE ("[EmacsApp init]");
5336   if ((self = [super init]))
5337     {
5338 #ifdef NS_IMPL_COCOA
5339       self->isFirst = YES;
5340 #endif
5341 #ifdef NS_IMPL_GNUSTEP
5342       self->applicationDidFinishLaunchingCalled = NO;
5343 #endif
5344     }
5346   return self;
5349 #ifdef NS_IMPL_COCOA
5350 - (void)run
5352   NSTRACE ("[EmacsApp run]");
5354 #ifndef NSAppKitVersionNumber10_9
5355 #define NSAppKitVersionNumber10_9 1265
5356 #endif
5358     if ((int)NSAppKitVersionNumber != NSAppKitVersionNumber10_9)
5359       {
5360         [super run];
5361         return;
5362       }
5364   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5366   if (isFirst) [self finishLaunching];
5367   isFirst = NO;
5369   shouldKeepRunning = YES;
5370   do
5371     {
5372       [pool release];
5373       pool = [[NSAutoreleasePool alloc] init];
5375       NSEvent *event =
5376         [self nextEventMatchingMask:NSEventMaskAny
5377                           untilDate:[NSDate distantFuture]
5378                              inMode:NSDefaultRunLoopMode
5379                             dequeue:YES];
5381       [self sendEvent:event];
5382       [self updateWindows];
5383     } while (shouldKeepRunning);
5385   [pool release];
5388 - (void)stop: (id)sender
5390   NSTRACE ("[EmacsApp stop:]");
5392     shouldKeepRunning = NO;
5393     // Stop possible dialog also.  Noop if no dialog present.
5394     // The file dialog still leaks 7k - 10k on 10.9 though.
5395     [super stop:sender];
5397 #endif /* NS_IMPL_COCOA */
5399 - (void)logNotification: (NSNotification *)notification
5401   NSTRACE ("[EmacsApp logNotification:]");
5403   const char *name = [[notification name] UTF8String];
5404   if (!strstr (name, "Update") && !strstr (name, "NSMenu")
5405       && !strstr (name, "WindowNumber"))
5406     NSLog (@"notification: '%@'", [notification name]);
5410 - (void)sendEvent: (NSEvent *)theEvent
5411 /* --------------------------------------------------------------------------
5412      Called when NSApp is running for each event received.  Used to stop
5413      the loop when we choose, since there's no way to just run one iteration.
5414    -------------------------------------------------------------------------- */
5416   int type = [theEvent type];
5417   NSWindow *window = [theEvent window];
5419   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsApp sendEvent:]");
5420   NSTRACE_MSG ("Type: %d", type);
5422 #ifdef NS_IMPL_GNUSTEP
5423   // Keyboard events aren't propagated to file dialogs for some reason.
5424   if ([NSApp modalWindow] != nil &&
5425       (type == NSEventTypeKeyDown || type == NSEventTypeKeyUp || type == NSEventTypeFlagsChanged))
5426     {
5427       [[NSApp modalWindow] sendEvent: theEvent];
5428       return;
5429     }
5430 #endif
5432   if (represented_filename != nil && represented_frame)
5433     {
5434       NSString *fstr = represented_filename;
5435       NSView *view = FRAME_NS_VIEW (represented_frame);
5436 #ifdef NS_IMPL_COCOA
5437       /* work around a bug observed on 10.3 and later where
5438          setTitleWithRepresentedFilename does not clear out previous state
5439          if given filename does not exist */
5440       if (! [[NSFileManager defaultManager] fileExistsAtPath: fstr])
5441         [[view window] setRepresentedFilename: @""];
5442 #endif
5443       [[view window] setRepresentedFilename: fstr];
5444       [represented_filename release];
5445       represented_filename = nil;
5446       represented_frame = NULL;
5447     }
5449   if (type == NSEventTypeApplicationDefined)
5450     {
5451       switch ([theEvent data2])
5452         {
5453 #ifdef NS_IMPL_COCOA
5454         case NSAPP_DATA2_RUNASSCRIPT:
5455           ns_run_ascript ();
5456           [self stop: self];
5457           return;
5458 #endif
5459         case NSAPP_DATA2_RUNFILEDIALOG:
5460           ns_run_file_dialog ();
5461           [self stop: self];
5462           return;
5463         }
5464     }
5466   if (type == NSEventTypeCursorUpdate && window == nil)
5467     {
5468       fprintf (stderr, "Dropping external cursor update event.\n");
5469       return;
5470     }
5472   if (type == NSEventTypeApplicationDefined)
5473     {
5474       /* Events posted by ns_send_appdefined interrupt the run loop here.
5475          But, if a modal window is up, an appdefined can still come through,
5476          (e.g., from a makeKeyWindow event) but stopping self also stops the
5477          modal loop. Just defer it until later. */
5478       if ([NSApp modalWindow] == nil)
5479         {
5480           last_appdefined_event_data = [theEvent data1];
5481           [self stop: self];
5482         }
5483       else
5484         {
5485           send_appdefined = YES;
5486         }
5487     }
5490 #ifdef NS_IMPL_COCOA
5491   /* If no dialog and none of our frames have focus and it is a move, skip it.
5492      It is a mouse move in an auxiliary menu, i.e. on the top right on macOS,
5493      such as Wifi, sound, date or similar.
5494      This prevents "spooky" highlighting in the frame under the menu.  */
5495   if (type == NSEventTypeMouseMoved && [NSApp modalWindow] == nil)
5496     {
5497       struct ns_display_info *di;
5498       BOOL has_focus = NO;
5499       for (di = x_display_list; ! has_focus && di; di = di->next)
5500         has_focus = di->x_focus_frame != 0;
5501       if (! has_focus)
5502         return;
5503     }
5504 #endif
5506   NSTRACE_UNSILENCE();
5508   [super sendEvent: theEvent];
5512 - (void)showPreferencesWindow: (id)sender
5514   struct frame *emacsframe = SELECTED_FRAME ();
5515   NSEvent *theEvent = [NSApp currentEvent];
5517   if (!emacs_event)
5518     return;
5519   emacs_event->kind = NS_NONKEY_EVENT;
5520   emacs_event->code = KEY_NS_SHOW_PREFS;
5521   emacs_event->modifiers = 0;
5522   EV_TRAILER (theEvent);
5526 - (void)newFrame: (id)sender
5528   NSTRACE ("[EmacsApp newFrame:]");
5530   struct frame *emacsframe = SELECTED_FRAME ();
5531   NSEvent *theEvent = [NSApp currentEvent];
5533   if (!emacs_event)
5534     return;
5535   emacs_event->kind = NS_NONKEY_EVENT;
5536   emacs_event->code = KEY_NS_NEW_FRAME;
5537   emacs_event->modifiers = 0;
5538   EV_TRAILER (theEvent);
5542 /* Open a file (used by below, after going into queue read by ns_read_socket) */
5543 - (BOOL) openFile: (NSString *)fileName
5545   NSTRACE ("[EmacsApp openFile:]");
5547   struct frame *emacsframe = SELECTED_FRAME ();
5548   NSEvent *theEvent = [NSApp currentEvent];
5550   if (!emacs_event)
5551     return NO;
5553   emacs_event->kind = NS_NONKEY_EVENT;
5554   emacs_event->code = KEY_NS_OPEN_FILE_LINE;
5555   ns_input_file = append2 (ns_input_file, build_string ([fileName UTF8String]));
5556   ns_input_line = Qnil; /* can be start or cons start,end */
5557   emacs_event->modifiers =0;
5558   EV_TRAILER (theEvent);
5560   return YES;
5564 /* **************************************************************************
5566       EmacsApp delegate implementation
5568    ************************************************************************** */
5570 - (void)applicationDidFinishLaunching: (NSNotification *)notification
5571 /* --------------------------------------------------------------------------
5572      When application is loaded, terminate event loop in ns_term_init
5573    -------------------------------------------------------------------------- */
5575   NSTRACE ("[EmacsApp applicationDidFinishLaunching:]");
5577 #ifdef NS_IMPL_GNUSTEP
5578   ((EmacsApp *)self)->applicationDidFinishLaunchingCalled = YES;
5579 #endif
5580   [NSApp setServicesProvider: NSApp];
5582   [self antialiasThresholdDidChange:nil];
5583 #ifdef NS_IMPL_COCOA
5584   [[NSNotificationCenter defaultCenter]
5585     addObserver:self
5586        selector:@selector(antialiasThresholdDidChange:)
5587            name:NSAntialiasThresholdChangedNotification
5588          object:nil];
5589 #endif
5591 #ifdef NS_IMPL_COCOA
5592   if ([NSApp activationPolicy] == NSApplicationActivationPolicyProhibited) {
5593     /* Set the app's activation policy to regular when we run outside
5594        of a bundle.  This is already done for us by Info.plist when we
5595        run inside a bundle. */
5596     [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
5597     [NSApp setApplicationIconImage:
5598              [EmacsImage
5599                allocInitFromFile:
5600                  build_string("icons/hicolor/128x128/apps/emacs.png")]];
5601   }
5602 #endif
5604   ns_send_appdefined (-2);
5607 - (void)antialiasThresholdDidChange:(NSNotification *)notification
5609 #ifdef NS_IMPL_COCOA
5610   macfont_update_antialias_threshold ();
5611 #endif
5615 /* Termination sequences:
5616     C-x C-c:
5617     Cmd-Q:
5618     MenuBar | File | Exit:
5619     Select Quit from App menubar:
5620         -terminate
5621         KEY_NS_POWER_OFF, (save-buffers-kill-emacs)
5622         ns_term_shutdown()
5624     Select Quit from Dock menu:
5625     Logout attempt:
5626         -appShouldTerminate
5627           Cancel -> Nothing else
5628           Accept ->
5630           -terminate
5631           KEY_NS_POWER_OFF, (save-buffers-kill-emacs)
5632           ns_term_shutdown()
5636 - (void) terminate: (id)sender
5638   NSTRACE ("[EmacsApp terminate:]");
5640   struct frame *emacsframe = SELECTED_FRAME ();
5642   if (!emacs_event)
5643     return;
5645   emacs_event->kind = NS_NONKEY_EVENT;
5646   emacs_event->code = KEY_NS_POWER_OFF;
5647   emacs_event->arg = Qt; /* mark as non-key event */
5648   EV_TRAILER ((id)nil);
5651 static bool
5652 runAlertPanel(NSString *title,
5653               NSString *msgFormat,
5654               NSString *defaultButton,
5655               NSString *alternateButton)
5657 #ifdef NS_IMPL_GNUSTEP
5658   return NSRunAlertPanel(title, msgFormat, defaultButton, alternateButton, nil)
5659     == NSAlertDefaultReturn;
5660 #else
5661   NSAlert *alert = [[NSAlert alloc] init];
5662   [alert setAlertStyle: NSAlertStyleCritical];
5663   [alert setMessageText: msgFormat];
5664   [alert addButtonWithTitle: defaultButton];
5665   [alert addButtonWithTitle: alternateButton];
5666   NSInteger ret = [alert runModal];
5667   [alert release];
5668   return ret == NSAlertFirstButtonReturn;
5669 #endif
5673 - (NSApplicationTerminateReply)applicationShouldTerminate: (id)sender
5675   NSTRACE ("[EmacsApp applicationShouldTerminate:]");
5677   bool ret;
5679   if (NILP (ns_confirm_quit)) //   || ns_shutdown_properly  --> TO DO
5680     return NSTerminateNow;
5682   ret = runAlertPanel(ns_app_name,
5683                       @"Exit requested.  Would you like to Save Buffers and Exit, or Cancel the request?",
5684                       @"Save Buffers and Exit", @"Cancel");
5686   return ret ? NSTerminateNow : NSTerminateCancel;
5689 static int
5690 not_in_argv (NSString *arg)
5692   int k;
5693   const char *a = [arg UTF8String];
5694   for (k = 1; k < initial_argc; ++k)
5695     if (strcmp (a, initial_argv[k]) == 0) return 0;
5696   return 1;
5699 /*   Notification from the Workspace to open a file */
5700 - (BOOL)application: sender openFile: (NSString *)file
5702   if (ns_do_open_file || not_in_argv (file))
5703     [ns_pending_files addObject: file];
5704   return YES;
5708 /*   Open a file as a temporary file */
5709 - (BOOL)application: sender openTempFile: (NSString *)file
5711   if (ns_do_open_file || not_in_argv (file))
5712     [ns_pending_files addObject: file];
5713   return YES;
5717 /*   Notification from the Workspace to open a file noninteractively (?) */
5718 - (BOOL)application: sender openFileWithoutUI: (NSString *)file
5720   if (ns_do_open_file || not_in_argv (file))
5721     [ns_pending_files addObject: file];
5722   return YES;
5725 /*   Notification from the Workspace to open multiple files */
5726 - (void)application: sender openFiles: (NSArray *)fileList
5728   NSEnumerator *files = [fileList objectEnumerator];
5729   NSString *file;
5730   /* Don't open files from the command line unconditionally,
5731      Cocoa parses the command line wrong, --option value tries to open value
5732      if --option is the last option.  */
5733   while ((file = [files nextObject]) != nil)
5734     if (ns_do_open_file || not_in_argv (file))
5735       [ns_pending_files addObject: file];
5737   [self replyToOpenOrPrint: NSApplicationDelegateReplySuccess];
5742 /* Handle dock menu requests.  */
5743 - (NSMenu *)applicationDockMenu: (NSApplication *) sender
5745   return dockMenu;
5749 /* TODO: these may help w/IO switching btwn terminal and NSApp */
5750 - (void)applicationWillBecomeActive: (NSNotification *)notification
5752   NSTRACE ("[EmacsApp applicationWillBecomeActive:]");
5753   //ns_app_active=YES;
5756 - (void)applicationDidBecomeActive: (NSNotification *)notification
5758   NSTRACE ("[EmacsApp applicationDidBecomeActive:]");
5760 #ifdef NS_IMPL_GNUSTEP
5761   if (! applicationDidFinishLaunchingCalled)
5762     [self applicationDidFinishLaunching:notification];
5763 #endif
5764   //ns_app_active=YES;
5766   ns_update_auto_hide_menu_bar ();
5767   // No constraining takes place when the application is not active.
5768   ns_constrain_all_frames ();
5770 - (void)applicationDidResignActive: (NSNotification *)notification
5772   NSTRACE ("[EmacsApp applicationDidResignActive:]");
5774   //ns_app_active=NO;
5775   ns_send_appdefined (-1);
5780 /* ==========================================================================
5782     EmacsApp aux handlers for managing event loop
5784    ========================================================================== */
5787 - (void)timeout_handler: (NSTimer *)timedEntry
5788 /* --------------------------------------------------------------------------
5789      The timeout specified to ns_select has passed.
5790    -------------------------------------------------------------------------- */
5792   /*NSTRACE ("timeout_handler"); */
5793   ns_send_appdefined (-2);
5796 - (void)sendFromMainThread:(id)unused
5798   ns_send_appdefined (nextappdefined);
5801 - (void)fd_handler:(id)unused
5802 /* --------------------------------------------------------------------------
5803      Check data waiting on file descriptors and terminate if so
5804    -------------------------------------------------------------------------- */
5806   int result;
5807   int waiting = 1, nfds;
5808   char c;
5810   fd_set readfds, writefds, *wfds;
5811   struct timespec timeout, *tmo;
5812   NSAutoreleasePool *pool = nil;
5814   /* NSTRACE ("fd_handler"); */
5816   for (;;)
5817     {
5818       [pool release];
5819       pool = [[NSAutoreleasePool alloc] init];
5821       if (waiting)
5822         {
5823           fd_set fds;
5824           FD_ZERO (&fds);
5825           FD_SET (selfds[0], &fds);
5826           result = select (selfds[0]+1, &fds, NULL, NULL, NULL);
5827           if (result > 0 && read (selfds[0], &c, 1) == 1 && c == 'g')
5828             waiting = 0;
5829         }
5830       else
5831         {
5832           pthread_mutex_lock (&select_mutex);
5833           nfds = select_nfds;
5835           if (select_valid & SELECT_HAVE_READ)
5836             readfds = select_readfds;
5837           else
5838             FD_ZERO (&readfds);
5840           if (select_valid & SELECT_HAVE_WRITE)
5841             {
5842               writefds = select_writefds;
5843               wfds = &writefds;
5844             }
5845           else
5846             wfds = NULL;
5847           if (select_valid & SELECT_HAVE_TMO)
5848             {
5849               timeout = select_timeout;
5850               tmo = &timeout;
5851             }
5852           else
5853             tmo = NULL;
5855           pthread_mutex_unlock (&select_mutex);
5857           FD_SET (selfds[0], &readfds);
5858           if (selfds[0] >= nfds) nfds = selfds[0]+1;
5860           result = pselect (nfds, &readfds, wfds, NULL, tmo, NULL);
5862           if (result == 0)
5863             ns_send_appdefined (-2);
5864           else if (result > 0)
5865             {
5866               if (FD_ISSET (selfds[0], &readfds))
5867                 {
5868                   if (read (selfds[0], &c, 1) == 1 && c == 's')
5869                     waiting = 1;
5870                 }
5871               else
5872                 {
5873                   pthread_mutex_lock (&select_mutex);
5874                   if (select_valid & SELECT_HAVE_READ)
5875                     select_readfds = readfds;
5876                   if (select_valid & SELECT_HAVE_WRITE)
5877                     select_writefds = writefds;
5878                   if (select_valid & SELECT_HAVE_TMO)
5879                     select_timeout = timeout;
5880                   pthread_mutex_unlock (&select_mutex);
5882                   ns_send_appdefined (result);
5883                 }
5884             }
5885           waiting = 1;
5886         }
5887     }
5892 /* ==========================================================================
5894     Service provision
5896    ========================================================================== */
5898 /* called from system: queue for next pass through event loop */
5899 - (void)requestService: (NSPasteboard *)pboard
5900               userData: (NSString *)userData
5901                  error: (NSString **)error
5903   [ns_pending_service_names addObject: userData];
5904   [ns_pending_service_args addObject: [NSString stringWithUTF8String:
5905       SSDATA (ns_string_from_pasteboard (pboard))]];
5909 /* called from ns_read_socket to clear queue */
5910 - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
5912   struct frame *emacsframe = SELECTED_FRAME ();
5913   NSEvent *theEvent = [NSApp currentEvent];
5915   NSTRACE ("[EmacsApp fulfillService:withArg:]");
5917   if (!emacs_event)
5918     return NO;
5920   emacs_event->kind = NS_NONKEY_EVENT;
5921   emacs_event->code = KEY_NS_SPI_SERVICE_CALL;
5922   ns_input_spi_name = build_string ([name UTF8String]);
5923   ns_input_spi_arg = build_string ([arg UTF8String]);
5924   emacs_event->modifiers = EV_MODIFIERS (theEvent);
5925   EV_TRAILER (theEvent);
5927   return YES;
5931 @end  /* EmacsApp */
5935 /* ==========================================================================
5937     EmacsView implementation
5939    ========================================================================== */
5942 @implementation EmacsView
5944 /* needed to inform when window closed from LISP */
5945 - (void) setWindowClosing: (BOOL)closing
5947   NSTRACE ("[EmacsView setWindowClosing:%d]", closing);
5949   windowClosing = closing;
5953 - (void)dealloc
5955   NSTRACE ("[EmacsView dealloc]");
5956   [toolbar release];
5957   if (fs_state == FULLSCREEN_BOTH)
5958     [nonfs_window release];
5959   [super dealloc];
5963 /* called on font panel selection */
5964 - (void)changeFont: (id)sender
5966   NSEvent *e = [[self window] currentEvent];
5967   struct face *face = FACE_FROM_ID (emacsframe, DEFAULT_FACE_ID);
5968   struct font *font = face->font;
5969   id newFont;
5970   CGFloat size;
5971   NSFont *nsfont;
5973   NSTRACE ("[EmacsView changeFont:]");
5975   if (!emacs_event)
5976     return;
5978 #ifdef NS_IMPL_GNUSTEP
5979   nsfont = ((struct nsfont_info *)font)->nsfont;
5980 #endif
5981 #ifdef NS_IMPL_COCOA
5982   nsfont = (NSFont *) macfont_get_nsctfont (font);
5983 #endif
5985   if ((newFont = [sender convertFont: nsfont]))
5986     {
5987       SET_FRAME_GARBAGED (emacsframe); /* now needed as of 2008/10 */
5989       emacs_event->kind = NS_NONKEY_EVENT;
5990       emacs_event->modifiers = 0;
5991       emacs_event->code = KEY_NS_CHANGE_FONT;
5993       size = [newFont pointSize];
5994       ns_input_fontsize = make_number (lrint (size));
5995       ns_input_font = build_string ([[newFont familyName] UTF8String]);
5996       EV_TRAILER (e);
5997     }
6001 - (BOOL)acceptsFirstResponder
6003   NSTRACE ("[EmacsView acceptsFirstResponder]");
6004   return YES;
6008 - (void)resetCursorRects
6010   NSRect visible = [self visibleRect];
6011   NSCursor *currentCursor = FRAME_POINTER_TYPE (emacsframe);
6012   NSTRACE ("[EmacsView resetCursorRects]");
6014   if (currentCursor == nil)
6015     currentCursor = [NSCursor arrowCursor];
6017   if (!NSIsEmptyRect (visible))
6018     [self addCursorRect: visible cursor: currentCursor];
6020 #if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 101300
6021 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
6022   if ([currentCursor respondsToSelector: @selector(setOnMouseEntered)])
6023 #endif
6024     [currentCursor setOnMouseEntered: YES];
6025 #endif
6030 /*****************************************************************************/
6031 /* Keyboard handling. */
6032 #define NS_KEYLOG 0
6034 - (void)keyDown: (NSEvent *)theEvent
6036   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
6037   int code;
6038   unsigned fnKeysym = 0;
6039   static NSMutableArray *nsEvArray;
6040   int left_is_none;
6041   unsigned int flags = [theEvent modifierFlags];
6043   NSTRACE ("[EmacsView keyDown:]");
6045   /* Rhapsody and macOS give up and down events for the arrow keys */
6046   if (ns_fake_keydown == YES)
6047     ns_fake_keydown = NO;
6048   else if ([theEvent type] != NSEventTypeKeyDown)
6049     return;
6051   if (!emacs_event)
6052     return;
6054  if (![[self window] isKeyWindow]
6055      && [[theEvent window] isKindOfClass: [EmacsWindow class]]
6056      /* we must avoid an infinite loop here. */
6057      && (EmacsView *)[[theEvent window] delegate] != self)
6058    {
6059      /* XXX: There is an occasional condition in which, when Emacs display
6060          updates a different frame from the current one, and temporarily
6061          selects it, then processes some interrupt-driven input
6062          (dispnew.c:3878), OS will send the event to the correct NSWindow, but
6063          for some reason that window has its first responder set to the NSView
6064          most recently updated (I guess), which is not the correct one. */
6065      [(EmacsView *)[[theEvent window] delegate] keyDown: theEvent];
6066      return;
6067    }
6069   if (nsEvArray == nil)
6070     nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1];
6072   [NSCursor setHiddenUntilMouseMoves: YES];
6074   if (hlinfo->mouse_face_hidden && INTEGERP (Vmouse_highlight))
6075     {
6076       clear_mouse_face (hlinfo);
6077       hlinfo->mouse_face_hidden = 1;
6078     }
6080   if (!processingCompose)
6081     {
6082       /* When using screen sharing, no left or right information is sent,
6083          so use Left key in those cases.  */
6084       int is_left_key, is_right_key;
6086       code = ([[theEvent charactersIgnoringModifiers] length] == 0) ?
6087         0 : [[theEvent charactersIgnoringModifiers] characterAtIndex: 0];
6089       /* (Carbon way: [theEvent keyCode]) */
6091       /* is it a "function key"? */
6092       /* Note: Sometimes a plain key will have the NSEventModifierFlagNumericPad
6093          flag set (this is probably a bug in the OS).
6094       */
6095       if (code < 0x00ff && (flags&NSEventModifierFlagNumericPad))
6096         {
6097           fnKeysym = ns_convert_key ([theEvent keyCode] | NSEventModifierFlagNumericPad);
6098         }
6099       if (fnKeysym == 0)
6100         {
6101           fnKeysym = ns_convert_key (code);
6102         }
6104       if (fnKeysym)
6105         {
6106           /* COUNTERHACK: map 'Delete' on upper-right main KB to 'Backspace',
6107              because Emacs treats Delete and KP-Delete same (in simple.el). */
6108           if ((fnKeysym == 0xFFFF && [theEvent keyCode] == 0x33)
6109 #ifdef NS_IMPL_GNUSTEP
6110               /*  GNUstep uses incompatible keycodes, even for those that are
6111                   supposed to be hardware independent.  Just check for delete.
6112                   Keypad delete does not have keysym 0xFFFF.
6113                   See https://savannah.gnu.org/bugs/?25395
6114               */
6115               || (fnKeysym == 0xFFFF && code == 127)
6116 #endif
6117             )
6118             code = 0xFF08; /* backspace */
6119           else
6120             code = fnKeysym;
6121         }
6123       /* are there modifiers? */
6124       emacs_event->modifiers = 0;
6126       if (flags & NSEventModifierFlagHelp)
6127           emacs_event->modifiers |= hyper_modifier;
6129       if (flags & NSEventModifierFlagShift)
6130         emacs_event->modifiers |= shift_modifier;
6132       is_right_key = (flags & NSRightCommandKeyMask) == NSRightCommandKeyMask;
6133       is_left_key = (flags & NSLeftCommandKeyMask) == NSLeftCommandKeyMask
6134         || (! is_right_key && (flags & NSEventModifierFlagCommand) == NSEventModifierFlagCommand);
6136       if (is_right_key)
6137         emacs_event->modifiers |= parse_solitary_modifier
6138           (EQ (ns_right_command_modifier, Qleft)
6139            ? ns_command_modifier
6140            : ns_right_command_modifier);
6142       if (is_left_key)
6143         {
6144           emacs_event->modifiers |= parse_solitary_modifier
6145             (ns_command_modifier);
6147           /* if super (default), take input manager's word so things like
6148              dvorak / qwerty layout work */
6149           if (EQ (ns_command_modifier, Qsuper)
6150               && !fnKeysym
6151               && [[theEvent characters] length] != 0)
6152             {
6153               /* XXX: the code we get will be unshifted, so if we have
6154                  a shift modifier, must convert ourselves */
6155               if (!(flags & NSEventModifierFlagShift))
6156                 code = [[theEvent characters] characterAtIndex: 0];
6157 #if 0
6158               /* this is ugly and also requires linking w/Carbon framework
6159                  (for LMGetKbdType) so for now leave this rare (?) case
6160                  undealt with.. in future look into CGEvent methods */
6161               else
6162                 {
6163                   long smv = GetScriptManagerVariable (smKeyScript);
6164                   Handle uchrHandle = GetResource
6165                     ('uchr', GetScriptVariable (smv, smScriptKeys));
6166                   UInt32 dummy = 0;
6167                   UCKeyTranslate ((UCKeyboardLayout *) *uchrHandle,
6168                                  [[theEvent characters] characterAtIndex: 0],
6169                                  kUCKeyActionDisplay,
6170                                  (flags & ~NSEventModifierFlagCommand) >> 8,
6171                                  LMGetKbdType (), kUCKeyTranslateNoDeadKeysMask,
6172                                  &dummy, 1, &dummy, &code);
6173                   code &= 0xFF;
6174                 }
6175 #endif
6176             }
6177         }
6179       is_right_key = (flags & NSRightControlKeyMask) == NSRightControlKeyMask;
6180       is_left_key = (flags & NSLeftControlKeyMask) == NSLeftControlKeyMask
6181         || (! is_right_key && (flags & NSEventModifierFlagControl) == NSEventModifierFlagControl);
6183       if (is_right_key)
6184           emacs_event->modifiers |= parse_solitary_modifier
6185               (EQ (ns_right_control_modifier, Qleft)
6186                ? ns_control_modifier
6187                : ns_right_control_modifier);
6189       if (is_left_key)
6190         emacs_event->modifiers |= parse_solitary_modifier
6191           (ns_control_modifier);
6193       if (flags & NS_FUNCTION_KEY_MASK && !fnKeysym)
6194           emacs_event->modifiers |=
6195             parse_solitary_modifier (ns_function_modifier);
6197       left_is_none = NILP (ns_alternate_modifier)
6198         || EQ (ns_alternate_modifier, Qnone);
6200       is_right_key = (flags & NSRightAlternateKeyMask)
6201         == NSRightAlternateKeyMask;
6202       is_left_key = (flags & NSLeftAlternateKeyMask) == NSLeftAlternateKeyMask
6203         || (! is_right_key
6204             && (flags & NSEventModifierFlagOption) == NSEventModifierFlagOption);
6206       if (is_right_key)
6207         {
6208           if ((NILP (ns_right_alternate_modifier)
6209                || EQ (ns_right_alternate_modifier, Qnone)
6210                || (EQ (ns_right_alternate_modifier, Qleft) && left_is_none))
6211               && !fnKeysym)
6212             {   /* accept pre-interp alt comb */
6213               if ([[theEvent characters] length] > 0)
6214                 code = [[theEvent characters] characterAtIndex: 0];
6215               /*HACK: clear lone shift modifier to stop next if from firing */
6216               if (emacs_event->modifiers == shift_modifier)
6217                 emacs_event->modifiers = 0;
6218             }
6219           else
6220             emacs_event->modifiers |= parse_solitary_modifier
6221               (EQ (ns_right_alternate_modifier, Qleft)
6222                ? ns_alternate_modifier
6223                : ns_right_alternate_modifier);
6224         }
6226       if (is_left_key) /* default = meta */
6227         {
6228           if (left_is_none && !fnKeysym)
6229             {   /* accept pre-interp alt comb */
6230               if ([[theEvent characters] length] > 0)
6231                 code = [[theEvent characters] characterAtIndex: 0];
6232               /*HACK: clear lone shift modifier to stop next if from firing */
6233               if (emacs_event->modifiers == shift_modifier)
6234                 emacs_event->modifiers = 0;
6235             }
6236           else
6237               emacs_event->modifiers |=
6238                 parse_solitary_modifier (ns_alternate_modifier);
6239         }
6241   if (NS_KEYLOG)
6242     fprintf (stderr, "keyDown: code =%x\tfnKey =%x\tflags = %x\tmods = %x\n",
6243              (unsigned) code, fnKeysym, flags, emacs_event->modifiers);
6245       /* if it was a function key or had modifiers, pass it directly to emacs */
6246       if (fnKeysym || (emacs_event->modifiers
6247                        && (emacs_event->modifiers != shift_modifier)
6248                        && [[theEvent charactersIgnoringModifiers] length] > 0))
6249 /*[[theEvent characters] length] */
6250         {
6251           emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
6252           if (code < 0x20)
6253             code |= (1<<28)|(3<<16);
6254           else if (code == 0x7f)
6255             code |= (1<<28)|(3<<16);
6256           else if (!fnKeysym)
6257             emacs_event->kind = code > 0xFF
6258               ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT;
6260           emacs_event->code = code;
6261           EV_TRAILER (theEvent);
6262           processingCompose = NO;
6263           return;
6264         }
6265     }
6268   if (NS_KEYLOG && !processingCompose)
6269     fprintf (stderr, "keyDown: Begin compose sequence.\n");
6271   processingCompose = YES;
6272   [nsEvArray addObject: theEvent];
6273   [self interpretKeyEvents: nsEvArray];
6274   [nsEvArray removeObject: theEvent];
6278 /* <NSTextInput> implementation (called through super interpretKeyEvents:]). */
6281 /* <NSTextInput>: called when done composing;
6282    NOTE: also called when we delete over working text, followed immed.
6283          by doCommandBySelector: deleteBackward: */
6284 - (void)insertText: (id)aString
6286   int code;
6287   int len = [(NSString *)aString length];
6288   int i;
6290   NSTRACE ("[EmacsView insertText:]");
6292   if (NS_KEYLOG)
6293     NSLog (@"insertText '%@'\tlen = %d", aString, len);
6294   processingCompose = NO;
6296   if (!emacs_event)
6297     return;
6299   /* first, clear any working text */
6300   if (workingText != nil)
6301     [self deleteWorkingText];
6303   /* now insert the string as keystrokes */
6304   for (i =0; i<len; i++)
6305     {
6306       code = [aString characterAtIndex: i];
6307       /* TODO: still need this? */
6308       if (code == 0x2DC)
6309         code = '~'; /* 0x7E */
6310       if (code != 32) /* Space */
6311         emacs_event->modifiers = 0;
6312       emacs_event->kind
6313         = code > 0xFF ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT;
6314       emacs_event->code = code;
6315       EV_TRAILER ((id)nil);
6316     }
6320 /* <NSTextInput>: inserts display of composing characters */
6321 - (void)setMarkedText: (id)aString selectedRange: (NSRange)selRange
6323   NSString *str = [aString respondsToSelector: @selector (string)] ?
6324     [aString string] : aString;
6326   NSTRACE ("[EmacsView setMarkedText:selectedRange:]");
6328   if (NS_KEYLOG)
6329     NSLog (@"setMarkedText '%@' len =%lu range %lu from %lu",
6330            str, (unsigned long)[str length],
6331            (unsigned long)selRange.length,
6332            (unsigned long)selRange.location);
6334   if (workingText != nil)
6335     [self deleteWorkingText];
6336   if ([str length] == 0)
6337     return;
6339   if (!emacs_event)
6340     return;
6342   processingCompose = YES;
6343   workingText = [str copy];
6344   ns_working_text = build_string ([workingText UTF8String]);
6346   emacs_event->kind = NS_TEXT_EVENT;
6347   emacs_event->code = KEY_NS_PUT_WORKING_TEXT;
6348   EV_TRAILER ((id)nil);
6352 /* delete display of composing characters [not in <NSTextInput>] */
6353 - (void)deleteWorkingText
6355   NSTRACE ("[EmacsView deleteWorkingText]");
6357   if (workingText == nil)
6358     return;
6359   if (NS_KEYLOG)
6360     NSLog(@"deleteWorkingText len =%lu\n", (unsigned long)[workingText length]);
6361   [workingText release];
6362   workingText = nil;
6363   processingCompose = NO;
6365   if (!emacs_event)
6366     return;
6368   emacs_event->kind = NS_TEXT_EVENT;
6369   emacs_event->code = KEY_NS_UNPUT_WORKING_TEXT;
6370   EV_TRAILER ((id)nil);
6374 - (BOOL)hasMarkedText
6376   NSTRACE ("[EmacsView hasMarkedText]");
6378   return workingText != nil;
6382 - (NSRange)markedRange
6384   NSTRACE ("[EmacsView markedRange]");
6386   NSRange rng = workingText != nil
6387     ? NSMakeRange (0, [workingText length]) : NSMakeRange (NSNotFound, 0);
6388   if (NS_KEYLOG)
6389     NSLog (@"markedRange request");
6390   return rng;
6394 - (void)unmarkText
6396   NSTRACE ("[EmacsView unmarkText]");
6398   if (NS_KEYLOG)
6399     NSLog (@"unmark (accept) text");
6400   [self deleteWorkingText];
6401   processingCompose = NO;
6405 /* used to position char selection windows, etc. */
6406 - (NSRect)firstRectForCharacterRange: (NSRange)theRange
6408   NSRect rect;
6409   NSPoint pt;
6410   struct window *win = XWINDOW (FRAME_SELECTED_WINDOW (emacsframe));
6412   NSTRACE ("[EmacsView firstRectForCharacterRange:]");
6414   if (NS_KEYLOG)
6415     NSLog (@"firstRectForCharRange request");
6417   rect.size.width = theRange.length * FRAME_COLUMN_WIDTH (emacsframe);
6418   rect.size.height = FRAME_LINE_HEIGHT (emacsframe);
6419   pt.x = WINDOW_TEXT_TO_FRAME_PIXEL_X (win, win->phys_cursor.x);
6420   pt.y = WINDOW_TO_FRAME_PIXEL_Y (win, win->phys_cursor.y
6421                                        +FRAME_LINE_HEIGHT (emacsframe));
6423   pt = [self convertPoint: pt toView: nil];
6425 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
6426 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6427   if ([[self window] respondsToSelector: @selector(convertRectToScreen:)])
6428     {
6429 #endif
6430       rect.origin = pt;
6431       rect = [(EmacsWindow *) [self window] convertRectToScreen: rect];
6432 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6433     }
6434   else
6435 #endif
6436 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
6437 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 \
6438   || defined (NS_IMPL_GNUSTEP)
6439     {
6440       pt = [[self window] convertBaseToScreen: pt];
6441       rect.origin = pt;
6442     }
6443 #endif
6445   return rect;
6449 - (NSInteger)conversationIdentifier
6451   return (NSInteger)self;
6455 - (void)doCommandBySelector: (SEL)aSelector
6457   NSTRACE ("[EmacsView doCommandBySelector:]");
6459   if (NS_KEYLOG)
6460     NSLog (@"doCommandBySelector: %@", NSStringFromSelector (aSelector));
6462   processingCompose = NO;
6463   if (aSelector == @selector (deleteBackward:))
6464     {
6465       /* happens when user backspaces over an ongoing composition:
6466          throw a 'delete' into the event queue */
6467       if (!emacs_event)
6468         return;
6469       emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
6470       emacs_event->code = 0xFF08;
6471       EV_TRAILER ((id)nil);
6472     }
6475 - (NSArray *)validAttributesForMarkedText
6477   static NSArray *arr = nil;
6478   if (arr == nil) arr = [NSArray new];
6479  /* [[NSArray arrayWithObject: NSUnderlineStyleAttributeName] retain]; */
6480   return arr;
6483 - (NSRange)selectedRange
6485   if (NS_KEYLOG)
6486     NSLog (@"selectedRange request");
6487   return NSMakeRange (NSNotFound, 0);
6490 #if defined (NS_IMPL_COCOA) || GNUSTEP_GUI_MAJOR_VERSION > 0 || \
6491     GNUSTEP_GUI_MINOR_VERSION > 22
6492 - (NSUInteger)characterIndexForPoint: (NSPoint)thePoint
6493 #else
6494 - (unsigned int)characterIndexForPoint: (NSPoint)thePoint
6495 #endif
6497   if (NS_KEYLOG)
6498     NSLog (@"characterIndexForPoint request");
6499   return 0;
6502 - (NSAttributedString *)attributedSubstringFromRange: (NSRange)theRange
6504   static NSAttributedString *str = nil;
6505   if (str == nil) str = [NSAttributedString new];
6506   if (NS_KEYLOG)
6507     NSLog (@"attributedSubstringFromRange request");
6508   return str;
6511 /* End <NSTextInput> impl. */
6512 /*****************************************************************************/
6515 /* This is what happens when the user presses a mouse button.  */
6516 - (void)mouseDown: (NSEvent *)theEvent
6518   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6519   NSPoint p = [self convertPoint: [theEvent locationInWindow] fromView: nil];
6521   NSTRACE ("[EmacsView mouseDown:]");
6523   [self deleteWorkingText];
6525   if (!emacs_event)
6526     return;
6528   dpyinfo->last_mouse_frame = emacsframe;
6529   /* appears to be needed to prevent spurious movement events generated on
6530      button clicks */
6531   emacsframe->mouse_moved = 0;
6533   if ([theEvent type] == NSEventTypeScrollWheel)
6534     {
6535 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
6536 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6537       if ([theEvent respondsToSelector:@selector(hasPreciseScrollingDeltas)])
6538         {
6539 #endif
6540           /* If the input device is a touchpad or similar, use precise
6541            * scrolling deltas.  These are measured in pixels, so we
6542            * have to add them up until they exceed one line height,
6543            * then we can send a scroll wheel event.
6544            *
6545            * If the device only has coarse scrolling deltas, like a
6546            * real mousewheel, the deltas represent a ratio of whole
6547            * lines, so round up the number of lines.  This means we
6548            * always send one scroll event per click, but can still
6549            * scroll more than one line if the OS tells us to.
6550            */
6551           bool horizontal;
6552           int lines = 0;
6553           int scrollUp = NO;
6555           /* FIXME: At the top or bottom of the buffer we should
6556            * ignore momentum-phase events.  */
6557           if (! ns_use_mwheel_momentum
6558               && [theEvent momentumPhase] != NSEventPhaseNone)
6559             return;
6561           if ([theEvent hasPreciseScrollingDeltas])
6562             {
6563               static int totalDeltaX, totalDeltaY;
6564               int lineHeight;
6566               if (NUMBERP (ns_mwheel_line_height))
6567                 lineHeight = XINT (ns_mwheel_line_height);
6568               else
6569                 {
6570                   /* FIXME: Use actual line height instead of the default.  */
6571                   lineHeight = default_line_pixel_height
6572                     (XWINDOW (FRAME_SELECTED_WINDOW (emacsframe)));
6573                 }
6575               if ([theEvent phase] == NSEventPhaseBegan)
6576                 {
6577                   totalDeltaX = 0;
6578                   totalDeltaY = 0;
6579                 }
6581               totalDeltaX += [theEvent scrollingDeltaX];
6582               totalDeltaY += [theEvent scrollingDeltaY];
6584               /* Calculate the number of lines, if any, to scroll, and
6585                * reset the total delta for the direction we're NOT
6586                * scrolling so that small movements don't add up.  */
6587               if (abs (totalDeltaX) > abs (totalDeltaY)
6588                   && abs (totalDeltaX) > lineHeight)
6589                 {
6590                   horizontal = YES;
6591                   scrollUp = totalDeltaX > 0;
6593                   lines = abs (totalDeltaX / lineHeight);
6594                   totalDeltaX = totalDeltaX % lineHeight;
6595                   totalDeltaY = 0;
6596                 }
6597               else if (abs (totalDeltaY) >= abs (totalDeltaX)
6598                        && abs (totalDeltaY) > lineHeight)
6599                 {
6600                   horizontal = NO;
6601                   scrollUp = totalDeltaY > 0;
6603                   lines = abs (totalDeltaY / lineHeight);
6604                   totalDeltaY = totalDeltaY % lineHeight;
6605                   totalDeltaX = 0;
6606                 }
6608               if (lines > 1 && ! ns_use_mwheel_acceleration)
6609                 lines = 1;
6610             }
6611           else
6612             {
6613               CGFloat delta;
6615               if ([theEvent scrollingDeltaY] == 0)
6616                 {
6617                   horizontal = YES;
6618                   delta = [theEvent scrollingDeltaX];
6619                 }
6620               else
6621                 {
6622                   horizontal = NO;
6623                   delta = [theEvent scrollingDeltaY];
6624                 }
6626               lines = (ns_use_mwheel_acceleration)
6627                 ? ceil (fabs (delta)) : 1;
6629               scrollUp = delta > 0;
6630             }
6632           if (lines == 0)
6633             return;
6635           emacs_event->kind = horizontal ? HORIZ_WHEEL_EVENT : WHEEL_EVENT;
6636           emacs_event->arg = (make_number (lines));
6638           emacs_event->code = 0;
6639           emacs_event->modifiers = EV_MODIFIERS (theEvent) |
6640             (scrollUp ? up_modifier : down_modifier);
6641 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6642         }
6643       else
6644 #endif
6645 #endif /* defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
6646 #if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6647         {
6648           CGFloat delta = [theEvent deltaY];
6649           /* Mac notebooks send wheel events w/delta =0 when trackpad scrolling */
6650           if (delta == 0)
6651             {
6652               delta = [theEvent deltaX];
6653               if (delta == 0)
6654                 {
6655                   NSTRACE_MSG ("deltaIsZero");
6656                   return;
6657                 }
6658               emacs_event->kind = HORIZ_WHEEL_EVENT;
6659             }
6660           else
6661             emacs_event->kind = WHEEL_EVENT;
6663           emacs_event->code = 0;
6664           emacs_event->modifiers = EV_MODIFIERS (theEvent) |
6665             ((delta > 0) ? up_modifier : down_modifier);
6666         }
6667 #endif
6668     }
6669   else
6670     {
6671       emacs_event->kind = MOUSE_CLICK_EVENT;
6672       emacs_event->code = EV_BUTTON (theEvent);
6673       emacs_event->modifiers = EV_MODIFIERS (theEvent)
6674                              | EV_UDMODIFIERS (theEvent);
6675     }
6677   XSETINT (emacs_event->x, lrint (p.x));
6678   XSETINT (emacs_event->y, lrint (p.y));
6679   EV_TRAILER (theEvent);
6680   return;
6684 - (void)rightMouseDown: (NSEvent *)theEvent
6686   NSTRACE ("[EmacsView rightMouseDown:]");
6687   [self mouseDown: theEvent];
6691 - (void)otherMouseDown: (NSEvent *)theEvent
6693   NSTRACE ("[EmacsView otherMouseDown:]");
6694   [self mouseDown: theEvent];
6698 - (void)mouseUp: (NSEvent *)theEvent
6700   NSTRACE ("[EmacsView mouseUp:]");
6701   [self mouseDown: theEvent];
6705 - (void)rightMouseUp: (NSEvent *)theEvent
6707   NSTRACE ("[EmacsView rightMouseUp:]");
6708   [self mouseDown: theEvent];
6712 - (void)otherMouseUp: (NSEvent *)theEvent
6714   NSTRACE ("[EmacsView otherMouseUp:]");
6715   [self mouseDown: theEvent];
6719 - (void) scrollWheel: (NSEvent *)theEvent
6721   NSTRACE ("[EmacsView scrollWheel:]");
6722   [self mouseDown: theEvent];
6726 /* Tell emacs the mouse has moved. */
6727 - (void)mouseMoved: (NSEvent *)e
6729   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
6730   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6731   Lisp_Object frame;
6732   NSPoint pt;
6734   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsView mouseMoved:]");
6736   dpyinfo->last_mouse_movement_time = EV_TIMESTAMP (e);
6737   pt = [self convertPoint: [e locationInWindow] fromView: nil];
6738   dpyinfo->last_mouse_motion_x = pt.x;
6739   dpyinfo->last_mouse_motion_y = pt.y;
6741   /* update any mouse face */
6742   if (hlinfo->mouse_face_hidden)
6743     {
6744       hlinfo->mouse_face_hidden = 0;
6745       clear_mouse_face (hlinfo);
6746     }
6748   /* tooltip handling */
6749   previous_help_echo_string = help_echo_string;
6750   help_echo_string = Qnil;
6752   if (!NILP (Vmouse_autoselect_window))
6753     {
6754       NSTRACE_MSG ("mouse_autoselect_window");
6755       static Lisp_Object last_mouse_window;
6756       Lisp_Object window
6757         = window_from_coordinates (emacsframe, pt.x, pt.y, 0, 0);
6759       if (WINDOWP (window)
6760           && !EQ (window, last_mouse_window)
6761           && !EQ (window, selected_window)
6762           && (!NILP (focus_follows_mouse)
6763               || (EQ (XWINDOW (window)->frame,
6764                       XWINDOW (selected_window)->frame))))
6765         {
6766           NSTRACE_MSG ("in_window");
6767           emacs_event->kind = SELECT_WINDOW_EVENT;
6768           emacs_event->frame_or_window = window;
6769           EV_TRAILER2 (e);
6770         }
6771       /* Remember the last window where we saw the mouse.  */
6772       last_mouse_window = window;
6773     }
6775   if (!note_mouse_movement (emacsframe, pt.x, pt.y))
6776     help_echo_string = previous_help_echo_string;
6778   XSETFRAME (frame, emacsframe);
6779   if (!NILP (help_echo_string) || !NILP (previous_help_echo_string))
6780     {
6781       /* NOTE: help_echo_{window,pos,object} are set in xdisp.c
6782          (note_mouse_highlight), which is called through the
6783          note_mouse_movement () call above */
6784       any_help_event_p = YES;
6785       gen_help_event (help_echo_string, frame, help_echo_window,
6786                       help_echo_object, help_echo_pos);
6787     }
6789   if (emacsframe->mouse_moved && send_appdefined)
6790     ns_send_appdefined (-1);
6794 - (void)mouseDragged: (NSEvent *)e
6796   NSTRACE ("[EmacsView mouseDragged:]");
6797   [self mouseMoved: e];
6801 - (void)rightMouseDragged: (NSEvent *)e
6803   NSTRACE ("[EmacsView rightMouseDragged:]");
6804   [self mouseMoved: e];
6808 - (void)otherMouseDragged: (NSEvent *)e
6810   NSTRACE ("[EmacsView otherMouseDragged:]");
6811   [self mouseMoved: e];
6815 - (BOOL)windowShouldClose: (id)sender
6817   NSEvent *e =[[self window] currentEvent];
6819   NSTRACE ("[EmacsView windowShouldClose:]");
6820   windowClosing = YES;
6821   if (!emacs_event)
6822     return NO;
6823   emacs_event->kind = DELETE_WINDOW_EVENT;
6824   emacs_event->modifiers = 0;
6825   emacs_event->code = 0;
6826   EV_TRAILER (e);
6827   /* Don't close this window, let this be done from lisp code.  */
6828   return NO;
6831 - (void) updateFrameSize: (BOOL) delay
6833   NSWindow *window = [self window];
6834   NSRect wr = [window frame];
6835   int extra = 0;
6836   int oldc = cols, oldr = rows;
6837   int oldw = FRAME_PIXEL_WIDTH (emacsframe);
6838   int oldh = FRAME_PIXEL_HEIGHT (emacsframe);
6839   int neww, newh;
6841   NSTRACE ("[EmacsView updateFrameSize:]");
6842   NSTRACE_SIZE ("Original size", NSMakeSize (oldw, oldh));
6843   NSTRACE_RECT ("Original frame", wr);
6844   NSTRACE_MSG  ("Original columns: %d", cols);
6845   NSTRACE_MSG  ("Original rows: %d", rows);
6847   if (! [self isFullscreen])
6848     {
6849       int toolbar_height;
6850 #ifdef NS_IMPL_GNUSTEP
6851       // GNUstep does not always update the tool bar height.  Force it.
6852       if (toolbar && [toolbar isVisible])
6853           update_frame_tool_bar (emacsframe);
6854 #endif
6856       toolbar_height = FRAME_TOOLBAR_HEIGHT (emacsframe);
6857       if (toolbar_height < 0)
6858         toolbar_height = 35;
6860       extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
6861         + toolbar_height;
6862     }
6864   if (wait_for_tool_bar)
6865     {
6866       /* The toolbar height is always 0 in fullscreen and undecorated
6867          frames, so don't wait for it to become available. */
6868       if (FRAME_TOOLBAR_HEIGHT (emacsframe) == 0
6869           && FRAME_UNDECORATED (emacsframe) == false
6870           && ! [self isFullscreen])
6871         {
6872           NSTRACE_MSG ("Waiting for toolbar");
6873           return;
6874         }
6875       wait_for_tool_bar = NO;
6876     }
6878   neww = (int)wr.size.width - emacsframe->border_width;
6879   newh = (int)wr.size.height - extra;
6881   NSTRACE_SIZE ("New size", NSMakeSize (neww, newh));
6882   NSTRACE_MSG ("FRAME_TOOLBAR_HEIGHT: %d", FRAME_TOOLBAR_HEIGHT (emacsframe));
6883   NSTRACE_MSG ("FRAME_NS_TITLEBAR_HEIGHT: %d", FRAME_NS_TITLEBAR_HEIGHT (emacsframe));
6885   cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, neww);
6886   rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe, newh);
6888   if (cols < MINWIDTH)
6889     cols = MINWIDTH;
6891   if (rows < MINHEIGHT)
6892     rows = MINHEIGHT;
6894   NSTRACE_MSG ("New columns: %d", cols);
6895   NSTRACE_MSG ("New rows: %d", rows);
6897   if (oldr != rows || oldc != cols || neww != oldw || newh != oldh)
6898     {
6899       NSView *view = FRAME_NS_VIEW (emacsframe);
6901       change_frame_size (emacsframe,
6902                          FRAME_PIXEL_TO_TEXT_WIDTH (emacsframe, neww),
6903                          FRAME_PIXEL_TO_TEXT_HEIGHT (emacsframe, newh),
6904                          0, delay, 0, 1);
6905       SET_FRAME_GARBAGED (emacsframe);
6906       cancel_mouse_face (emacsframe);
6908       /* The next two lines set the frame to the same size as we've
6909          already set above.  We need to do this when we switch back
6910          from non-native fullscreen, in other circumstances it appears
6911          to be a noop.  (bug#28872) */
6912       wr = NSMakeRect (0, 0, neww, newh);
6913       [view setFrame: wr];
6915       // to do: consider using [NSNotificationCenter postNotificationName:].
6916       [self windowDidMove: // Update top/left.
6917               [NSNotification notificationWithName:NSWindowDidMoveNotification
6918                                             object:[view window]]];
6919     }
6920   else
6921     {
6922       NSTRACE_MSG ("No change");
6923     }
6926 - (NSSize)windowWillResize: (NSWindow *)sender toSize: (NSSize)frameSize
6927 /* normalize frame to gridded text size */
6929   int extra = 0;
6931   NSTRACE ("[EmacsView windowWillResize:toSize: " NSTRACE_FMT_SIZE "]",
6932            NSTRACE_ARG_SIZE (frameSize));
6933   NSTRACE_RECT   ("[sender frame]", [sender frame]);
6934   NSTRACE_FSTYPE ("fs_state", fs_state);
6936   if (!FRAME_LIVE_P (emacsframe))
6937     return frameSize;
6939   if (fs_state == FULLSCREEN_MAXIMIZED
6940       && (maximized_width != (int)frameSize.width
6941           || maximized_height != (int)frameSize.height))
6942     [self setFSValue: FULLSCREEN_NONE];
6943   else if (fs_state == FULLSCREEN_WIDTH
6944            && maximized_width != (int)frameSize.width)
6945     [self setFSValue: FULLSCREEN_NONE];
6946   else if (fs_state == FULLSCREEN_HEIGHT
6947            && maximized_height != (int)frameSize.height)
6948     [self setFSValue: FULLSCREEN_NONE];
6950   if (fs_state == FULLSCREEN_NONE)
6951     maximized_width = maximized_height = -1;
6953   if (! [self isFullscreen])
6954     {
6955       extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
6956         + FRAME_TOOLBAR_HEIGHT (emacsframe);
6957     }
6959   cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, frameSize.width);
6960   if (cols < MINWIDTH)
6961     cols = MINWIDTH;
6963   rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe,
6964                                            frameSize.height - extra);
6965   if (rows < MINHEIGHT)
6966     rows = MINHEIGHT;
6967 #ifdef NS_IMPL_COCOA
6968   {
6969     /* this sets window title to have size in it; the wm does this under GS */
6970     NSRect r = [[self window] frame];
6971     if (r.size.height == frameSize.height && r.size.width == frameSize.width)
6972       {
6973         if (old_title != 0)
6974           {
6975             xfree (old_title);
6976             old_title = 0;
6977           }
6978       }
6979     else if (fs_state == FULLSCREEN_NONE && ! maximizing_resize
6980              && [[self window] title] != NULL)
6981       {
6982         char *size_title;
6983         NSWindow *window = [self window];
6984         if (old_title == 0)
6985           {
6986             char *t = strdup ([[[self window] title] UTF8String]);
6987             char *pos = strstr (t, "  â€”  ");
6988             if (pos)
6989               *pos = '\0';
6990             old_title = t;
6991           }
6992         size_title = xmalloc (strlen (old_title) + 40);
6993         esprintf (size_title, "%s  â€”  (%d x %d)", old_title, cols, rows);
6994         [window setTitle: [NSString stringWithUTF8String: size_title]];
6995         [window display];
6996         xfree (size_title);
6997       }
6998   }
6999 #endif /* NS_IMPL_COCOA */
7001   NSTRACE_MSG ("cols: %d  rows: %d", cols, rows);
7003   /* Restrict the new size to the text gird.
7005      Don't restrict the width if the user only adjusted the height, and
7006      vice versa.  (Without this, the frame would shrink, and move
7007      slightly, if the window was resized by dragging one of its
7008      borders.) */
7009   if (!frame_resize_pixelwise)
7010     {
7011       NSRect r = [[self window] frame];
7013       if (r.size.width != frameSize.width)
7014         {
7015           frameSize.width =
7016             FRAME_TEXT_COLS_TO_PIXEL_WIDTH  (emacsframe, cols);
7017         }
7019       if (r.size.height != frameSize.height)
7020         {
7021           frameSize.height =
7022             FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (emacsframe, rows) + extra;
7023         }
7024     }
7026   NSTRACE_RETURN_SIZE (frameSize);
7028   return frameSize;
7032 - (void)windowDidResize: (NSNotification *)notification
7034   NSTRACE ("[EmacsView windowDidResize:]");
7035   if (!FRAME_LIVE_P (emacsframe))
7036     {
7037       NSTRACE_MSG ("Ignored (frame dead)");
7038       return;
7039     }
7040   if (emacsframe->output_data.ns->in_animation)
7041     {
7042       NSTRACE_MSG ("Ignored (in animation)");
7043       return;
7044     }
7046   if (! [self fsIsNative])
7047     {
7048       NSWindow *theWindow = [notification object];
7049       /* We can get notification on the non-FS window when in
7050          fullscreen mode.  */
7051       if ([self window] != theWindow) return;
7052     }
7054   NSTRACE_RECT ("frame", [[notification object] frame]);
7056 #ifdef NS_IMPL_GNUSTEP
7057   NSWindow *theWindow = [notification object];
7059    /* In GNUstep, at least currently, it's possible to get a didResize
7060       without getting a willResize.. therefore we need to act as if we got
7061       the willResize now */
7062   NSSize sz = [theWindow frame].size;
7063   sz = [self windowWillResize: theWindow toSize: sz];
7064 #endif /* NS_IMPL_GNUSTEP */
7066   if (cols > 0 && rows > 0)
7067     {
7068       [self updateFrameSize: YES];
7069     }
7071   ns_send_appdefined (-1);
7074 #ifdef NS_IMPL_COCOA
7075 - (void)viewDidEndLiveResize
7077   NSTRACE ("[EmacsView viewDidEndLiveResize]");
7079   [super viewDidEndLiveResize];
7080   if (old_title != 0)
7081     {
7082       [[self window] setTitle: [NSString stringWithUTF8String: old_title]];
7083       xfree (old_title);
7084       old_title = 0;
7085     }
7086   maximizing_resize = NO;
7088 #endif /* NS_IMPL_COCOA */
7091 - (void)windowDidBecomeKey: (NSNotification *)notification
7092 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
7094   [self windowDidBecomeKey];
7098 - (void)windowDidBecomeKey      /* for direct calls */
7100   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
7101   struct frame *old_focus = dpyinfo->x_focus_frame;
7103   NSTRACE ("[EmacsView windowDidBecomeKey]");
7105   if (emacsframe != old_focus)
7106     dpyinfo->x_focus_frame = emacsframe;
7108   ns_frame_rehighlight (emacsframe);
7110   if (emacs_event)
7111     {
7112       emacs_event->kind = FOCUS_IN_EVENT;
7113       EV_TRAILER ((id)nil);
7114     }
7118 - (void)windowDidResignKey: (NSNotification *)notification
7119 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
7121   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
7122   BOOL is_focus_frame = dpyinfo->x_focus_frame == emacsframe;
7123   NSTRACE ("[EmacsView windowDidResignKey:]");
7125   if (is_focus_frame)
7126     dpyinfo->x_focus_frame = 0;
7128   emacsframe->mouse_moved = 0;
7129   ns_frame_rehighlight (emacsframe);
7131   /* FIXME: for some reason needed on second and subsequent clicks away
7132             from sole-frame Emacs to get hollow box to show */
7133   if (!windowClosing && [[self window] isVisible] == YES)
7134     {
7135       x_update_cursor (emacsframe, 1);
7136       x_set_frame_alpha (emacsframe);
7137     }
7139   if (any_help_event_p)
7140     {
7141       Lisp_Object frame;
7142       XSETFRAME (frame, emacsframe);
7143       help_echo_string = Qnil;
7144       gen_help_event (Qnil, frame, Qnil, Qnil, 0);
7145     }
7147   if (emacs_event && is_focus_frame)
7148     {
7149       [self deleteWorkingText];
7150       emacs_event->kind = FOCUS_OUT_EVENT;
7151       EV_TRAILER ((id)nil);
7152     }
7156 - (void)windowWillMiniaturize: sender
7158   NSTRACE ("[EmacsView windowWillMiniaturize:]");
7162 - (void)setFrame:(NSRect)frameRect
7164   NSTRACE ("[EmacsView setFrame:" NSTRACE_FMT_RECT "]",
7165            NSTRACE_ARG_RECT (frameRect));
7167   [super setFrame:(NSRect)frameRect];
7171 - (BOOL)isFlipped
7173   return YES;
7177 - (BOOL)isOpaque
7179   return NO;
7183 - (void)createToolbar: (struct frame *)f
7185   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
7186   NSWindow *window = [view window];
7188   toolbar = [[EmacsToolbar alloc] initForView: self withIdentifier:
7189                    [NSString stringWithFormat: @"Emacs Frame %d",
7190                              ns_window_num]];
7191   [toolbar setVisible: NO];
7192   [window setToolbar: toolbar];
7194   /* Don't set frame garbaged until tool bar is up to date?
7195      This avoids an extra clear and redraw (flicker) at frame creation.  */
7196   if (FRAME_EXTERNAL_TOOL_BAR (f)) wait_for_tool_bar = YES;
7197   else wait_for_tool_bar = NO;
7200 #ifdef NS_IMPL_COCOA
7201   {
7202     NSButton *toggleButton;
7203     toggleButton = [window standardWindowButton: NSWindowToolbarButton];
7204     [toggleButton setTarget: self];
7205     [toggleButton setAction: @selector (toggleToolbar: )];
7206   }
7207 #endif
7211 - (instancetype) initFrameFromEmacs: (struct frame *)f
7213   NSRect r, wr;
7214   Lisp_Object tem;
7215   NSWindow *win;
7216   NSColor *col;
7217   NSString *name;
7219   NSTRACE ("[EmacsView initFrameFromEmacs:]");
7220   NSTRACE_MSG ("cols:%d lines:%d", f->text_cols, f->text_lines);
7222   windowClosing = NO;
7223   processingCompose = NO;
7224   scrollbarsNeedingUpdate = 0;
7225   fs_state = FULLSCREEN_NONE;
7226   fs_before_fs = next_maximized = -1;
7228   fs_is_native = NO;
7229 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7230 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7231   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7232 #endif
7233     fs_is_native = ns_use_native_fullscreen;
7234 #endif
7236   maximized_width = maximized_height = -1;
7237   nonfs_window = nil;
7239   ns_userRect = NSMakeRect (0, 0, 0, 0);
7240   r = NSMakeRect (0, 0, FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, f->text_cols),
7241                  FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, f->text_lines));
7242   [self initWithFrame: r];
7243   [self setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
7245   FRAME_NS_VIEW (f) = self;
7246   emacsframe = f;
7247 #ifdef NS_IMPL_COCOA
7248   old_title = 0;
7249   maximizing_resize = NO;
7250 #endif
7252   win = [[EmacsWindow alloc]
7253             initWithContentRect: r
7254                       styleMask: (FRAME_UNDECORATED (f)
7255                                   ? FRAME_UNDECORATED_FLAGS
7256                                   : FRAME_DECORATED_FLAGS)
7257                         backing: NSBackingStoreBuffered
7258                           defer: YES];
7260 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7261 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7262   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7263 #endif
7264     [win setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
7265 #endif
7267   wr = [win frame];
7268   bwidth = f->border_width = wr.size.width - r.size.width;
7270   [win setAcceptsMouseMovedEvents: YES];
7271   [win setDelegate: self];
7272 #if !defined (NS_IMPL_COCOA) || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7273 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7274   if ([win respondsToSelector: @selector(useOptimizedDrawing:)])
7275 #endif
7276     [win useOptimizedDrawing: YES];
7277 #endif
7279   [[win contentView] addSubview: self];
7281   if (ns_drag_types)
7282     [self registerForDraggedTypes: ns_drag_types];
7284   tem = f->name;
7285   name = [NSString stringWithUTF8String:
7286                    NILP (tem) ? "Emacs" : SSDATA (tem)];
7287   [win setTitle: name];
7289   /* toolbar support */
7290   if (! FRAME_UNDECORATED (f))
7291     [self createToolbar: f];
7293 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
7294 #ifndef NSAppKitVersionNumber10_10
7295 #define NSAppKitVersionNumber10_10 1343
7296 #endif
7298   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_10
7299       && FRAME_NS_APPEARANCE (f) != ns_appearance_aqua)
7300     win.appearance = [NSAppearance
7301                           appearanceNamed: NSAppearanceNameVibrantDark];
7302 #endif
7304 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
7305   if ([win respondsToSelector: @selector(titlebarAppearsTransparent)])
7306     win.titlebarAppearsTransparent = FRAME_NS_TRANSPARENT_TITLEBAR (f);
7307 #endif
7309   tem = f->icon_name;
7310   if (!NILP (tem))
7311     [win setMiniwindowTitle:
7312            [NSString stringWithUTF8String: SSDATA (tem)]];
7314   if (FRAME_PARENT_FRAME (f) != NULL)
7315     {
7316       NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
7317       [parent addChildWindow: win
7318                      ordered: NSWindowAbove];
7319     }
7321   if (FRAME_Z_GROUP (f) != z_group_none)
7322       win.level = NSNormalWindowLevel
7323         + (FRAME_Z_GROUP_BELOW (f) ? -1 : 1);
7325   {
7326     NSScreen *screen = [win screen];
7328     if (screen != 0)
7329       {
7330         NSPoint pt = NSMakePoint
7331           (IN_BOUND (-SCREENMAX, f->left_pos
7332                      + NS_PARENT_WINDOW_LEFT_POS (f), SCREENMAX),
7333            IN_BOUND (-SCREENMAX,
7334                      NS_PARENT_WINDOW_TOP_POS (f) - f->top_pos,
7335                      SCREENMAX));
7337         [win setFrameTopLeftPoint: pt];
7339         NSTRACE_RECT ("new frame", [win frame]);
7340       }
7341   }
7343   [win makeFirstResponder: self];
7345   col = ns_lookup_indexed_color (NS_FACE_BACKGROUND
7346                                  (FACE_FROM_ID (emacsframe, DEFAULT_FACE_ID)),
7347                                  emacsframe);
7348   [win setBackgroundColor: col];
7349   if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7350     [win setOpaque: NO];
7352 #if !defined (NS_IMPL_COCOA) \
7353   || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7354 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7355   if ([self respondsToSelector: @selector(allocateGState)])
7356 #endif
7357     [self allocateGState];
7358 #endif
7359   [NSApp registerServicesMenuSendTypes: ns_send_types
7360                            returnTypes: [NSArray array]];
7362   /* macOS Sierra automatically enables tabbed windows.  We can't
7363      allow this to be enabled until it's available on a Free system.
7364      Currently it only happens by accident and is buggy anyway. */
7365 #if defined (NS_IMPL_COCOA) \
7366   && MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
7367 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101200
7368   if ([win respondsToSelector: @selector(setTabbingMode:)])
7369 #endif
7370     [win setTabbingMode: NSWindowTabbingModeDisallowed];
7371 #endif
7373   ns_window_num++;
7374   return self;
7378 - (void)windowDidMove: sender
7380   NSWindow *win = [self window];
7381   NSRect r = [win frame];
7382   NSArray *screens = [NSScreen screens];
7383   NSScreen *screen = [screens objectAtIndex: 0];
7385   NSTRACE ("[EmacsView windowDidMove:]");
7387   if (!emacsframe->output_data.ns)
7388     return;
7389   if (screen != nil)
7390     {
7391       emacsframe->left_pos = r.origin.x - NS_PARENT_WINDOW_LEFT_POS (emacsframe);
7392       emacsframe->top_pos =
7393         NS_PARENT_WINDOW_TOP_POS (emacsframe) - (r.origin.y + r.size.height);
7395       if (emacs_event)
7396         {
7397           emacs_event->kind = MOVE_FRAME_EVENT;
7398           EV_TRAILER ((id)nil);
7399         }
7400     }
7404 /* Called AFTER method below, but before our windowWillResize call there leads
7405    to windowDidResize -> x_set_window_size.  Update emacs' notion of frame
7406    location so set_window_size moves the frame. */
7407 - (BOOL)windowShouldZoom: (NSWindow *)sender toFrame: (NSRect)newFrame
7409   NSTRACE (("[EmacsView windowShouldZoom:toFrame:" NSTRACE_FMT_RECT "]"
7410             NSTRACE_FMT_RETURN "YES"),
7411            NSTRACE_ARG_RECT (newFrame));
7413   emacsframe->output_data.ns->zooming = 1;
7414   return YES;
7418 /* Override to do something slightly nonstandard, but nice.  First click on
7419    zoom button will zoom vertically.  Second will zoom completely.  Third
7420    returns to original. */
7421 - (NSRect)windowWillUseStandardFrame:(NSWindow *)sender
7422                         defaultFrame:(NSRect)defaultFrame
7424   // TODO: Rename to "currentFrame" and assign "result" properly in
7425   // all paths.
7426   NSRect result = [sender frame];
7428   NSTRACE (("[EmacsView windowWillUseStandardFrame:defaultFrame:"
7429             NSTRACE_FMT_RECT "]"),
7430            NSTRACE_ARG_RECT (defaultFrame));
7431   NSTRACE_FSTYPE ("fs_state", fs_state);
7432   NSTRACE_FSTYPE ("fs_before_fs", fs_before_fs);
7433   NSTRACE_FSTYPE ("next_maximized", next_maximized);
7434   NSTRACE_RECT   ("ns_userRect", ns_userRect);
7435   NSTRACE_RECT   ("[sender frame]", [sender frame]);
7437   if (fs_before_fs != -1) /* Entering fullscreen */
7438     {
7439       NSTRACE_MSG ("Entering fullscreen");
7440       result = defaultFrame;
7441     }
7442   else
7443     {
7444       // Save the window size and position (frame) before the resize.
7445       if (fs_state != FULLSCREEN_MAXIMIZED
7446           && fs_state != FULLSCREEN_WIDTH)
7447         {
7448           ns_userRect.size.width = result.size.width;
7449           ns_userRect.origin.x   = result.origin.x;
7450         }
7452       if (fs_state != FULLSCREEN_MAXIMIZED
7453           && fs_state != FULLSCREEN_HEIGHT)
7454         {
7455           ns_userRect.size.height = result.size.height;
7456           ns_userRect.origin.y    = result.origin.y;
7457         }
7459       NSTRACE_RECT ("ns_userRect (2)", ns_userRect);
7461       if (next_maximized == FULLSCREEN_HEIGHT
7462           || (next_maximized == -1
7463               && abs ((int)(defaultFrame.size.height - result.size.height))
7464               > FRAME_LINE_HEIGHT (emacsframe)))
7465         {
7466           /* first click */
7467           NSTRACE_MSG ("FULLSCREEN_HEIGHT");
7468           maximized_height = result.size.height = defaultFrame.size.height;
7469           maximized_width = -1;
7470           result.origin.y = defaultFrame.origin.y;
7471           if (ns_userRect.size.height != 0)
7472             {
7473               result.origin.x = ns_userRect.origin.x;
7474               result.size.width = ns_userRect.size.width;
7475             }
7476           [self setFSValue: FULLSCREEN_HEIGHT];
7477 #ifdef NS_IMPL_COCOA
7478           maximizing_resize = YES;
7479 #endif
7480         }
7481       else if (next_maximized == FULLSCREEN_WIDTH)
7482         {
7483           NSTRACE_MSG ("FULLSCREEN_WIDTH");
7484           maximized_width = result.size.width = defaultFrame.size.width;
7485           maximized_height = -1;
7486           result.origin.x = defaultFrame.origin.x;
7487           if (ns_userRect.size.width != 0)
7488             {
7489               result.origin.y = ns_userRect.origin.y;
7490               result.size.height = ns_userRect.size.height;
7491             }
7492           [self setFSValue: FULLSCREEN_WIDTH];
7493         }
7494       else if (next_maximized == FULLSCREEN_MAXIMIZED
7495                || (next_maximized == -1
7496                    && abs ((int)(defaultFrame.size.width - result.size.width))
7497                    > FRAME_COLUMN_WIDTH (emacsframe)))
7498         {
7499           NSTRACE_MSG ("FULLSCREEN_MAXIMIZED");
7501           result = defaultFrame;  /* second click */
7502           maximized_width = result.size.width;
7503           maximized_height = result.size.height;
7504           [self setFSValue: FULLSCREEN_MAXIMIZED];
7505 #ifdef NS_IMPL_COCOA
7506           maximizing_resize = YES;
7507 #endif
7508         }
7509       else
7510         {
7511           /* restore */
7512           NSTRACE_MSG ("Restore");
7513           result = ns_userRect.size.height ? ns_userRect : result;
7514           NSTRACE_RECT ("restore (2)", result);
7515           ns_userRect = NSMakeRect (0, 0, 0, 0);
7516 #ifdef NS_IMPL_COCOA
7517           maximizing_resize = fs_state != FULLSCREEN_NONE;
7518 #endif
7519           [self setFSValue: FULLSCREEN_NONE];
7520           maximized_width = maximized_height = -1;
7521         }
7522     }
7524   if (fs_before_fs == -1) next_maximized = -1;
7526   NSTRACE_RECT   ("Final ns_userRect", ns_userRect);
7527   NSTRACE_MSG    ("Final maximized_width: %d", maximized_width);
7528   NSTRACE_MSG    ("Final maximized_height: %d", maximized_height);
7529   NSTRACE_FSTYPE ("Final next_maximized", next_maximized);
7531   [self windowWillResize: sender toSize: result.size];
7533   NSTRACE_RETURN_RECT (result);
7535   return result;
7539 - (void)windowDidDeminiaturize: sender
7541   NSTRACE ("[EmacsView windowDidDeminiaturize:]");
7542   if (!emacsframe->output_data.ns)
7543     return;
7545   SET_FRAME_ICONIFIED (emacsframe, 0);
7546   SET_FRAME_VISIBLE (emacsframe, 1);
7547   windows_or_buffers_changed = 63;
7549   if (emacs_event)
7550     {
7551       emacs_event->kind = DEICONIFY_EVENT;
7552       EV_TRAILER ((id)nil);
7553     }
7557 - (void)windowDidExpose: sender
7559   NSTRACE ("[EmacsView windowDidExpose:]");
7560   if (!emacsframe->output_data.ns)
7561     return;
7563   SET_FRAME_VISIBLE (emacsframe, 1);
7564   SET_FRAME_GARBAGED (emacsframe);
7566   if (send_appdefined)
7567     ns_send_appdefined (-1);
7571 - (void)windowDidMiniaturize: sender
7573   NSTRACE ("[EmacsView windowDidMiniaturize:]");
7574   if (!emacsframe->output_data.ns)
7575     return;
7577   SET_FRAME_ICONIFIED (emacsframe, 1);
7578   SET_FRAME_VISIBLE (emacsframe, 0);
7580   if (emacs_event)
7581     {
7582       emacs_event->kind = ICONIFY_EVENT;
7583       EV_TRAILER ((id)nil);
7584     }
7587 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7588 - (NSApplicationPresentationOptions)window:(NSWindow *)window
7589       willUseFullScreenPresentationOptions:
7590   (NSApplicationPresentationOptions)proposedOptions
7592   return proposedOptions|NSApplicationPresentationAutoHideToolbar;
7594 #endif
7596 - (void)windowWillEnterFullScreen:(NSNotification *)notification
7598   NSTRACE ("[EmacsView windowWillEnterFullScreen:]");
7599   [self windowWillEnterFullScreen];
7601 - (void)windowWillEnterFullScreen /* provided for direct calls */
7603   NSTRACE ("[EmacsView windowWillEnterFullScreen]");
7604   fs_before_fs = fs_state;
7607 - (void)windowDidEnterFullScreen:(NSNotification *)notification
7609   NSTRACE ("[EmacsView windowDidEnterFullScreen:]");
7610   [self windowDidEnterFullScreen];
7613 - (void)windowDidEnterFullScreen /* provided for direct calls */
7615   NSTRACE ("[EmacsView windowDidEnterFullScreen]");
7616   [self setFSValue: FULLSCREEN_BOTH];
7617   if (! [self fsIsNative])
7618     {
7619       [self windowDidBecomeKey];
7620       [nonfs_window orderOut:self];
7621     }
7622   else
7623     {
7624       BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (emacsframe) ? YES : NO;
7625 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 \
7626   && MAC_OS_X_VERSION_MIN_REQUIRED <= 1070
7627       unsigned val = (unsigned)[NSApp presentationOptions];
7629       // Mac OS X 10.7 bug fix, the menu won't appear without this.
7630       // val is non-zero on other macOS versions.
7631       if (val == 0)
7632         {
7633           NSApplicationPresentationOptions options
7634             = NSApplicationPresentationAutoHideDock
7635             | NSApplicationPresentationAutoHideMenuBar
7636             | NSApplicationPresentationFullScreen
7637             | NSApplicationPresentationAutoHideToolbar;
7639           [NSApp setPresentationOptions: options];
7640         }
7641 #endif
7642       [toolbar setVisible:tbar_visible];
7643     }
7646 - (void)windowWillExitFullScreen:(NSNotification *)notification
7648   NSTRACE ("[EmacsView windowWillExitFullScreen:]");
7649   [self windowWillExitFullScreen];
7652 - (void)windowWillExitFullScreen /* provided for direct calls */
7654   NSTRACE ("[EmacsView windowWillExitFullScreen]");
7655   if (!FRAME_LIVE_P (emacsframe))
7656     {
7657       NSTRACE_MSG ("Ignored (frame dead)");
7658       return;
7659     }
7660   if (next_maximized != -1)
7661     fs_before_fs = next_maximized;
7664 - (void)windowDidExitFullScreen:(NSNotification *)notification
7666   NSTRACE ("[EmacsView windowDidExitFullScreen:]");
7667   [self windowDidExitFullScreen];
7670 - (void)windowDidExitFullScreen /* provided for direct calls */
7672   NSTRACE ("[EmacsView windowDidExitFullScreen]");
7673   if (!FRAME_LIVE_P (emacsframe))
7674     {
7675       NSTRACE_MSG ("Ignored (frame dead)");
7676       return;
7677     }
7678   [self setFSValue: fs_before_fs];
7679   fs_before_fs = -1;
7680 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7681   [self updateCollectionBehavior];
7682 #endif
7683   if (FRAME_EXTERNAL_TOOL_BAR (emacsframe))
7684     {
7685       [toolbar setVisible:YES];
7686       update_frame_tool_bar (emacsframe);
7687       [self updateFrameSize:YES];
7688       [[self window] display];
7689     }
7690   else
7691     [toolbar setVisible:NO];
7693   if (next_maximized != -1)
7694     [[self window] performZoom:self];
7697 - (BOOL)fsIsNative
7699   return fs_is_native;
7702 - (BOOL)isFullscreen
7704   BOOL res;
7706   if (! fs_is_native)
7707     {
7708       res = (nonfs_window != nil);
7709     }
7710   else
7711     {
7712 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7713       res = (([[self window] styleMask] & NSWindowStyleMaskFullScreen) != 0);
7714 #else
7715       res = NO;
7716 #endif
7717     }
7719   NSTRACE ("[EmacsView isFullscreen] " NSTRACE_FMT_RETURN " %d",
7720            (int) res);
7722   return res;
7725 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7726 - (void)updateCollectionBehavior
7728   NSTRACE ("[EmacsView updateCollectionBehavior]");
7730   if (! [self isFullscreen])
7731     {
7732       NSWindow *win = [self window];
7733       NSWindowCollectionBehavior b = [win collectionBehavior];
7734       if (ns_use_native_fullscreen)
7735         b |= NSWindowCollectionBehaviorFullScreenPrimary;
7736       else
7737         b &= ~NSWindowCollectionBehaviorFullScreenPrimary;
7739       [win setCollectionBehavior: b];
7740 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7741       if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7742 #endif
7743         fs_is_native = ns_use_native_fullscreen;
7744     }
7746 #endif
7748 - (void)toggleFullScreen: (id)sender
7750   NSWindow *w, *fw;
7751   BOOL onFirstScreen;
7752   struct frame *f;
7753   NSRect r, wr;
7754   NSColor *col;
7756   NSTRACE ("[EmacsView toggleFullScreen:]");
7758   if (fs_is_native)
7759     {
7760 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7761 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7762       if ([[self window] respondsToSelector: @selector(toggleFullScreen:)])
7763 #endif
7764         [[self window] toggleFullScreen:sender];
7765 #endif
7766       return;
7767     }
7769   w = [self window];
7770   onFirstScreen = [[w screen] isEqual:[[NSScreen screens] objectAtIndex:0]];
7771   f = emacsframe;
7772   wr = [w frame];
7773   col = ns_lookup_indexed_color (NS_FACE_BACKGROUND
7774                                  (FACE_FROM_ID (f, DEFAULT_FACE_ID)),
7775                                  f);
7777   if (fs_state != FULLSCREEN_BOTH)
7778     {
7779       NSScreen *screen = [w screen];
7781 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
7782       /* Hide ghost menu bar on secondary monitor? */
7783       if (! onFirstScreen
7784 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
7785           && [NSScreen respondsToSelector: @selector(screensHaveSeparateSpaces)]
7786 #endif
7787           )
7788         onFirstScreen = [NSScreen screensHaveSeparateSpaces];
7789 #endif
7790       /* Hide dock and menubar if we are on the primary screen.  */
7791       if (onFirstScreen)
7792         {
7793 #ifdef NS_IMPL_COCOA
7794           NSApplicationPresentationOptions options
7795             = NSApplicationPresentationAutoHideDock
7796             | NSApplicationPresentationAutoHideMenuBar;
7798           [NSApp setPresentationOptions: options];
7799 #else
7800           [NSMenu setMenuBarVisible:NO];
7801 #endif
7802         }
7804       fw = [[EmacsFSWindow alloc]
7805                        initWithContentRect:[w contentRectForFrameRect:wr]
7806                                  styleMask:NSWindowStyleMaskBorderless
7807                                    backing:NSBackingStoreBuffered
7808                                      defer:YES
7809                                     screen:screen];
7811       [fw setContentView:[w contentView]];
7812       [fw setTitle:[w title]];
7813       [fw setDelegate:self];
7814       [fw setAcceptsMouseMovedEvents: YES];
7815 #if !defined (NS_IMPL_COCOA) \
7816   || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7817 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7818       if ([fw respondsToSelector: @selector(useOptimizedDrawing:)])
7819 #endif
7820         [fw useOptimizedDrawing: YES];
7821 #endif
7822       [fw setBackgroundColor: col];
7823       if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7824         [fw setOpaque: NO];
7826       f->border_width = 0;
7828       nonfs_window = w;
7830       [self windowWillEnterFullScreen];
7831       [fw makeKeyAndOrderFront:NSApp];
7832       [fw makeFirstResponder:self];
7833       [w orderOut:self];
7834       r = [fw frameRectForContentRect:[screen frame]];
7835       [fw setFrame: r display:YES animate:ns_use_fullscreen_animation];
7836       [self windowDidEnterFullScreen];
7837       [fw display];
7838     }
7839   else
7840     {
7841       fw = w;
7842       w = nonfs_window;
7843       nonfs_window = nil;
7845       if (onFirstScreen)
7846         {
7847 #ifdef NS_IMPL_COCOA
7848           [NSApp setPresentationOptions: NSApplicationPresentationDefault];
7849 #else
7850           [NSMenu setMenuBarVisible:YES];
7851 #endif
7852         }
7854       [w setContentView:[fw contentView]];
7855       [w setBackgroundColor: col];
7856       if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7857         [w setOpaque: NO];
7859       f->border_width = bwidth;
7861       // to do: consider using [NSNotificationCenter postNotificationName:] to send notifications.
7863       [self windowWillExitFullScreen];
7864       [fw setFrame: [w frame] display:YES animate:ns_use_fullscreen_animation];
7865       [fw close];
7866       [w makeKeyAndOrderFront:NSApp];
7867       [self windowDidExitFullScreen];
7868       [self updateFrameSize:YES];
7869     }
7872 - (void)handleFS
7874   NSTRACE ("[EmacsView handleFS]");
7876   if (fs_state != emacsframe->want_fullscreen)
7877     {
7878       if (fs_state == FULLSCREEN_BOTH)
7879         {
7880           NSTRACE_MSG ("fs_state == FULLSCREEN_BOTH");
7881           [self toggleFullScreen:self];
7882         }
7884       switch (emacsframe->want_fullscreen)
7885         {
7886         case FULLSCREEN_BOTH:
7887           NSTRACE_MSG ("FULLSCREEN_BOTH");
7888           [self toggleFullScreen:self];
7889           break;
7890         case FULLSCREEN_WIDTH:
7891           NSTRACE_MSG ("FULLSCREEN_WIDTH");
7892           next_maximized = FULLSCREEN_WIDTH;
7893           if (fs_state != FULLSCREEN_BOTH)
7894             [[self window] performZoom:self];
7895           break;
7896         case FULLSCREEN_HEIGHT:
7897           NSTRACE_MSG ("FULLSCREEN_HEIGHT");
7898           next_maximized = FULLSCREEN_HEIGHT;
7899           if (fs_state != FULLSCREEN_BOTH)
7900             [[self window] performZoom:self];
7901           break;
7902         case FULLSCREEN_MAXIMIZED:
7903           NSTRACE_MSG ("FULLSCREEN_MAXIMIZED");
7904           next_maximized = FULLSCREEN_MAXIMIZED;
7905           if (fs_state != FULLSCREEN_BOTH)
7906             [[self window] performZoom:self];
7907           break;
7908         case FULLSCREEN_NONE:
7909           NSTRACE_MSG ("FULLSCREEN_NONE");
7910           if (fs_state != FULLSCREEN_BOTH)
7911             {
7912               next_maximized = FULLSCREEN_NONE;
7913               [[self window] performZoom:self];
7914             }
7915           break;
7916         }
7918       emacsframe->want_fullscreen = FULLSCREEN_NONE;
7919     }
7923 - (void) setFSValue: (int)value
7925   NSTRACE ("[EmacsView setFSValue:" NSTRACE_FMT_FSTYPE "]",
7926            NSTRACE_ARG_FSTYPE(value));
7928   Lisp_Object lval = Qnil;
7929   switch (value)
7930     {
7931     case FULLSCREEN_BOTH:
7932       lval = Qfullboth;
7933       break;
7934     case FULLSCREEN_WIDTH:
7935       lval = Qfullwidth;
7936       break;
7937     case FULLSCREEN_HEIGHT:
7938       lval = Qfullheight;
7939       break;
7940     case FULLSCREEN_MAXIMIZED:
7941       lval = Qmaximized;
7942       break;
7943     }
7944   store_frame_param (emacsframe, Qfullscreen, lval);
7945   fs_state = value;
7948 - (void)mouseEntered: (NSEvent *)theEvent
7950   NSTRACE ("[EmacsView mouseEntered:]");
7951   if (emacsframe)
7952     FRAME_DISPLAY_INFO (emacsframe)->last_mouse_movement_time
7953       = EV_TIMESTAMP (theEvent);
7957 - (void)mouseExited: (NSEvent *)theEvent
7959   Mouse_HLInfo *hlinfo = emacsframe ? MOUSE_HL_INFO (emacsframe) : NULL;
7961   NSTRACE ("[EmacsView mouseExited:]");
7963   if (!hlinfo)
7964     return;
7966   FRAME_DISPLAY_INFO (emacsframe)->last_mouse_movement_time
7967     = EV_TIMESTAMP (theEvent);
7969   if (emacsframe == hlinfo->mouse_face_mouse_frame)
7970     {
7971       clear_mouse_face (hlinfo);
7972       hlinfo->mouse_face_mouse_frame = 0;
7973     }
7977 - (instancetype)menuDown: sender
7979   NSTRACE ("[EmacsView menuDown:]");
7980   if (context_menu_value == -1)
7981     context_menu_value = [sender tag];
7982   else
7983     {
7984       NSInteger tag = [sender tag];
7985       find_and_call_menu_selection (emacsframe, emacsframe->menu_bar_items_used,
7986                                     emacsframe->menu_bar_vector,
7987                                     (void *)tag);
7988     }
7990   ns_send_appdefined (-1);
7991   return self;
7995 - (EmacsToolbar *)toolbar
7997   return toolbar;
8001 /* this gets called on toolbar button click */
8002 - (instancetype)toolbarClicked: (id)item
8004   NSEvent *theEvent;
8005   int idx = [item tag] * TOOL_BAR_ITEM_NSLOTS;
8007   NSTRACE ("[EmacsView toolbarClicked:]");
8009   if (!emacs_event)
8010     return self;
8012   /* send first event (for some reason two needed) */
8013   theEvent = [[self window] currentEvent];
8014   emacs_event->kind = TOOL_BAR_EVENT;
8015   XSETFRAME (emacs_event->arg, emacsframe);
8016   EV_TRAILER (theEvent);
8018   emacs_event->kind = TOOL_BAR_EVENT;
8019 /*   XSETINT (emacs_event->code, 0); */
8020   emacs_event->arg = AREF (emacsframe->tool_bar_items,
8021                            idx + TOOL_BAR_ITEM_KEY);
8022   emacs_event->modifiers = EV_MODIFIERS (theEvent);
8023   EV_TRAILER (theEvent);
8024   return self;
8028 - (instancetype)toggleToolbar: (id)sender
8030   NSTRACE ("[EmacsView toggleToolbar:]");
8032   if (!emacs_event)
8033     return self;
8035   emacs_event->kind = NS_NONKEY_EVENT;
8036   emacs_event->code = KEY_NS_TOGGLE_TOOLBAR;
8037   EV_TRAILER ((id)nil);
8038   return self;
8042 - (void)drawRect: (NSRect)rect
8044   int x = NSMinX (rect), y = NSMinY (rect);
8045   int width = NSWidth (rect), height = NSHeight (rect);
8047   NSTRACE ("[EmacsView drawRect:" NSTRACE_FMT_RECT "]",
8048            NSTRACE_ARG_RECT(rect));
8050   if (!emacsframe || !emacsframe->output_data.ns)
8051     return;
8053   ns_clear_frame_area (emacsframe, x, y, width, height);
8054   block_input ();
8055   expose_frame (emacsframe, x, y, width, height);
8056   unblock_input ();
8058   /*
8059     drawRect: may be called (at least in Mac OS X 10.5) for invisible
8060     views as well for some reason.  Thus, do not infer visibility
8061     here.
8063     emacsframe->async_visible = 1;
8064     emacsframe->async_iconified = 0;
8065   */
8069 /* NSDraggingDestination protocol methods.  Actually this is not really a
8070    protocol, but a category of Object.  O well...  */
8072 -(NSDragOperation) draggingEntered: (id <NSDraggingInfo>) sender
8074   NSTRACE ("[EmacsView draggingEntered:]");
8075   return NSDragOperationGeneric;
8079 -(BOOL)prepareForDragOperation: (id <NSDraggingInfo>) sender
8081   return YES;
8085 -(BOOL)performDragOperation: (id <NSDraggingInfo>) sender
8087   id pb;
8088   int x, y;
8089   NSString *type;
8090   NSEvent *theEvent = [[self window] currentEvent];
8091   NSPoint position;
8092   NSDragOperation op = [sender draggingSourceOperationMask];
8093   int modifiers = 0;
8095   NSTRACE ("[EmacsView performDragOperation:]");
8097   if (!emacs_event)
8098     return NO;
8100   position = [self convertPoint: [sender draggingLocation] fromView: nil];
8101   x = lrint (position.x);  y = lrint (position.y);
8103   pb = [sender draggingPasteboard];
8104   type = [pb availableTypeFromArray: ns_drag_types];
8106   if (! (op & (NSDragOperationMove|NSDragOperationDelete)) &&
8107       // URL drags contain all operations (0xf), don't allow all to be set.
8108       (op & 0xf) != 0xf)
8109     {
8110       if (op & NSDragOperationLink)
8111         modifiers |= NSEventModifierFlagControl;
8112       if (op & NSDragOperationCopy)
8113         modifiers |= NSEventModifierFlagOption;
8114       if (op & NSDragOperationGeneric)
8115         modifiers |= NSEventModifierFlagCommand;
8116     }
8118   modifiers = EV_MODIFIERS2 (modifiers);
8119   if (type == 0)
8120     {
8121       return NO;
8122     }
8123   else if ([type isEqualToString: NSFilenamesPboardType])
8124     {
8125       NSArray *files;
8126       NSEnumerator *fenum;
8127       NSString *file;
8129       if (!(files = [pb propertyListForType: type]))
8130         return NO;
8132       fenum = [files objectEnumerator];
8133       while ( (file = [fenum nextObject]) )
8134         {
8135           emacs_event->kind = DRAG_N_DROP_EVENT;
8136           XSETINT (emacs_event->x, x);
8137           XSETINT (emacs_event->y, y);
8138           emacs_event->modifiers = modifiers;
8139           emacs_event->arg =  list2 (Qfile, build_string ([file UTF8String]));
8140           EV_TRAILER (theEvent);
8141         }
8142       return YES;
8143     }
8144   else if ([type isEqualToString: NSURLPboardType])
8145     {
8146       NSURL *url = [NSURL URLFromPasteboard: pb];
8147       if (url == nil) return NO;
8149       emacs_event->kind = DRAG_N_DROP_EVENT;
8150       XSETINT (emacs_event->x, x);
8151       XSETINT (emacs_event->y, y);
8152       emacs_event->modifiers = modifiers;
8153       emacs_event->arg =  list2 (Qurl,
8154                                  build_string ([[url absoluteString]
8155                                                  UTF8String]));
8156       EV_TRAILER (theEvent);
8158       if ([url isFileURL] != NO)
8159         {
8160           NSString *file = [url path];
8161           ns_input_file = append2 (ns_input_file,
8162                                    build_string ([file UTF8String]));
8163         }
8164       return YES;
8165     }
8166   else if ([type isEqualToString: NSStringPboardType]
8167            || [type isEqualToString: NSTabularTextPboardType])
8168     {
8169       NSString *data;
8171       if (! (data = [pb stringForType: type]))
8172         return NO;
8174       emacs_event->kind = DRAG_N_DROP_EVENT;
8175       XSETINT (emacs_event->x, x);
8176       XSETINT (emacs_event->y, y);
8177       emacs_event->modifiers = modifiers;
8178       emacs_event->arg =  list2 (Qnil, build_string ([data UTF8String]));
8179       EV_TRAILER (theEvent);
8180       return YES;
8181     }
8182   else
8183     {
8184       fprintf (stderr, "Invalid data type in dragging pasteboard");
8185       return NO;
8186     }
8190 - (id) validRequestorForSendType: (NSString *)typeSent
8191                       returnType: (NSString *)typeReturned
8193   NSTRACE ("[EmacsView validRequestorForSendType:returnType:]");
8194   if (typeSent != nil && [ns_send_types indexOfObject: typeSent] != NSNotFound
8195       && typeReturned == nil)
8196     {
8197       if (! NILP (ns_get_local_selection (QPRIMARY, QUTF8_STRING)))
8198         return self;
8199     }
8201   return [super validRequestorForSendType: typeSent
8202                                returnType: typeReturned];
8206 /* The next two methods are part of NSServicesRequests informal protocol,
8207    supposedly called when a services menu item is chosen from this app.
8208    But this should not happen because we override the services menu with our
8209    own entries which call ns-perform-service.
8210    Nonetheless, it appeared to happen (under strange circumstances): bug#1435.
8211    So let's at least stub them out until further investigation can be done. */
8213 - (BOOL) readSelectionFromPasteboard: (NSPasteboard *)pb
8215   /* we could call ns_string_from_pasteboard(pboard) here but then it should
8216      be written into the buffer in place of the existing selection..
8217      ordinary service calls go through functions defined in ns-win.el */
8218   return NO;
8221 - (BOOL) writeSelectionToPasteboard: (NSPasteboard *)pb types: (NSArray *)types
8223   NSArray *typesDeclared;
8224   Lisp_Object val;
8226   NSTRACE ("[EmacsView writeSelectionToPasteboard:types:]");
8228   /* We only support NSStringPboardType */
8229   if ([types containsObject:NSStringPboardType] == NO) {
8230     return NO;
8231   }
8233   val = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8234   if (CONSP (val) && SYMBOLP (XCAR (val)))
8235     {
8236       val = XCDR (val);
8237       if (CONSP (val) && NILP (XCDR (val)))
8238         val = XCAR (val);
8239     }
8240   if (! STRINGP (val))
8241     return NO;
8243   typesDeclared = [NSArray arrayWithObject:NSStringPboardType];
8244   [pb declareTypes:typesDeclared owner:nil];
8245   ns_string_to_pasteboard (pb, val);
8246   return YES;
8250 /* setMini =YES means set from internal (gives a finder icon), NO means set nil
8251    (gives a miniaturized version of the window); currently we use the latter for
8252    frames whose active buffer doesn't correspond to any file
8253    (e.g., '*scratch*') */
8254 - (instancetype)setMiniwindowImage: (BOOL) setMini
8256   id image = [[self window] miniwindowImage];
8257   NSTRACE ("[EmacsView setMiniwindowImage:%d]", setMini);
8259   /* NOTE: under Cocoa miniwindowImage always returns nil, documentation
8260      about "AppleDockIconEnabled" notwithstanding, however the set message
8261      below has its effect nonetheless. */
8262   if (image != emacsframe->output_data.ns->miniimage)
8263     {
8264       if (image && [image isKindOfClass: [EmacsImage class]])
8265         [image release];
8266       [[self window] setMiniwindowImage:
8267                        setMini ? emacsframe->output_data.ns->miniimage : nil];
8268     }
8270   return self;
8274 - (void) setRows: (int) r andColumns: (int) c
8276   NSTRACE ("[EmacsView setRows:%d andColumns:%d]", r, c);
8277   rows = r;
8278   cols = c;
8281 - (int) fullscreenState
8283   return fs_state;
8286 @end  /* EmacsView */
8290 /* ==========================================================================
8292     EmacsWindow implementation
8294    ========================================================================== */
8296 @implementation EmacsWindow
8298 #ifdef NS_IMPL_COCOA
8299 - (id)accessibilityAttributeValue:(NSString *)attribute
8301   Lisp_Object str = Qnil;
8302   struct frame *f = SELECTED_FRAME ();
8303   struct buffer *curbuf = XBUFFER (XWINDOW (f->selected_window)->contents);
8305   NSTRACE ("[EmacsWindow accessibilityAttributeValue:]");
8307   if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
8308     return NSAccessibilityTextFieldRole;
8310   if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]
8311       && curbuf && ! NILP (BVAR (curbuf, mark_active)))
8312     {
8313       str = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8314     }
8315   else if (curbuf && [attribute isEqualToString:NSAccessibilityValueAttribute])
8316     {
8317       if (! NILP (BVAR (curbuf, mark_active)))
8318           str = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8320       if (NILP (str))
8321         {
8322           ptrdiff_t start_byte = BUF_BEGV_BYTE (curbuf);
8323           ptrdiff_t byte_range = BUF_ZV_BYTE (curbuf) - start_byte;
8324           ptrdiff_t range = BUF_ZV (curbuf) - BUF_BEGV (curbuf);
8326           if (! NILP (BVAR (curbuf, enable_multibyte_characters)))
8327             str = make_uninit_multibyte_string (range, byte_range);
8328           else
8329             str = make_uninit_string (range);
8330           /* To check: This returns emacs-utf-8, which is a superset of utf-8.
8331              Is this a problem?  */
8332           memcpy (SDATA (str), BYTE_POS_ADDR (start_byte), byte_range);
8333         }
8334     }
8337   if (! NILP (str))
8338     {
8339       if (CONSP (str) && SYMBOLP (XCAR (str)))
8340         {
8341           str = XCDR (str);
8342           if (CONSP (str) && NILP (XCDR (str)))
8343             str = XCAR (str);
8344         }
8345       if (STRINGP (str))
8346         {
8347           const char *utfStr = SSDATA (str);
8348           NSString *nsStr = [NSString stringWithUTF8String: utfStr];
8349           return nsStr;
8350         }
8351     }
8353   return [super accessibilityAttributeValue:attribute];
8355 #endif /* NS_IMPL_COCOA */
8357 /* Constrain size and placement of a frame.
8359    By returning the original "frameRect", the frame is not
8360    constrained. This can lead to unwanted situations where, for
8361    example, the menu bar covers the frame.
8363    The default implementation (accessed using "super") constrains the
8364    frame to the visible area of SCREEN, minus the menu bar (if
8365    present) and the Dock.  Note that default implementation also calls
8366    windowWillResize, with the frame it thinks should have.  (This can
8367    make the frame exit maximized mode.)
8369    Note that this should work in situations where multiple monitors
8370    are present.  Common configurations are side-by-side monitors and a
8371    monitor on top of another (e.g. when a laptop is placed under a
8372    large screen). */
8373 - (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
8375   NSTRACE ("[EmacsWindow constrainFrameRect:" NSTRACE_FMT_RECT " toScreen:]",
8376              NSTRACE_ARG_RECT (frameRect));
8378 #ifdef NS_IMPL_COCOA
8379 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
8380   // If separate spaces is on, it is like each screen is independent.  There is
8381   // no spanning of frames across screens.
8382   if (
8383 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
8384       [NSScreen respondsToSelector: @selector(screensHaveSeparateSpaces)] &&
8385 #endif
8386       [NSScreen screensHaveSeparateSpaces])
8387     {
8388       NSTRACE_MSG ("Screens have separate spaces");
8389       frameRect = [super constrainFrameRect:frameRect toScreen:screen];
8390       NSTRACE_RETURN_RECT (frameRect);
8391       return frameRect;
8392     }
8393   else
8394 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 */
8396     // Check that the proposed frameRect is visible in at least one
8397     // screen.  If it is not, ask the system to reposition it (only
8398     // for non-child windows).
8400     if (!FRAME_PARENT_FRAME (((EmacsView *)[self delegate])->emacsframe))
8401     {
8402       NSArray *screens = [NSScreen screens];
8403       NSUInteger nr_screens = [screens count];
8405       int i;
8406       BOOL frame_on_screen = NO;
8408       for (i = 0; i < nr_screens; ++i)
8409         {
8410           NSScreen *s = [screens objectAtIndex: i];
8411           NSRect scrRect = [s frame];
8413           if (NSIntersectsRect(frameRect, scrRect))
8414             {
8415               frame_on_screen = YES;
8416               break;
8417             }
8418         }
8420       if (!frame_on_screen)
8421         {
8422           NSTRACE_MSG ("Frame outside screens; constraining");
8423           frameRect = [super constrainFrameRect:frameRect toScreen:screen];
8424           NSTRACE_RETURN_RECT (frameRect);
8425           return frameRect;
8426         }
8427     }
8428 #endif
8430   return constrain_frame_rect(frameRect,
8431                               [(EmacsView *)[self delegate] isFullscreen]);
8435 - (void)performZoom:(id)sender
8437   NSTRACE ("[EmacsWindow performZoom:]");
8439   return [super performZoom:sender];
8442 - (void)zoom:(id)sender
8444   NSTRACE ("[EmacsWindow zoom:]");
8446   ns_update_auto_hide_menu_bar();
8448   // Below are three zoom implementations.  In the final commit, the
8449   // idea is that the last should be included.
8451 #if 0
8452   // Native zoom done using the standard zoom animation.  Size of the
8453   // resulting frame reduced to accommodate the Dock and, if present,
8454   // the menu-bar.
8455   [super zoom:sender];
8457 #elif 0
8458   // Native zoom done using the standard zoom animation, plus an
8459   // explicit resize to cover the full screen, except the menu-bar and
8460   // dock, if present.
8461   [super zoom:sender];
8463   // After the native zoom, resize the resulting frame to fill the
8464   // entire screen, except the menu-bar.
8465   //
8466   // This works for all practical purposes.  (The only minor oddity is
8467   // when transiting from full-height frame to a maximized, the
8468   // animation reduces the height of the frame slightly (to the 4
8469   // pixels needed to accommodate the Doc) before it snaps back into
8470   // full height.  The user would need a very trained eye to spot
8471   // this.)
8472   NSScreen * screen = [self screen];
8473   if (screen != nil)
8474     {
8475       int fs_state = [(EmacsView *)[self delegate] fullscreenState];
8477       NSTRACE_FSTYPE ("fullscreenState", fs_state);
8479       NSRect sr = [screen frame];
8480       struct EmacsMargins margins
8481         = ns_screen_margins_ignoring_hidden_dock(screen);
8483       NSRect wr = [self frame];
8484       NSTRACE_RECT ("Rect after zoom", wr);
8486       NSRect newWr = wr;
8488       if (fs_state == FULLSCREEN_MAXIMIZED
8489           || fs_state == FULLSCREEN_HEIGHT)
8490         {
8491           newWr.origin.y = sr.origin.y + margins.bottom;
8492           newWr.size.height = sr.size.height - margins.top - margins.bottom;
8493         }
8495       if (fs_state == FULLSCREEN_MAXIMIZED
8496           || fs_state == FULLSCREEN_WIDTH)
8497         {
8498           newWr.origin.x = sr.origin.x + margins.left;
8499           newWr.size.width = sr.size.width - margins.right - margins.left;
8500         }
8502       if (newWr.size.width     != wr.size.width
8503           || newWr.size.height != wr.size.height
8504           || newWr.origin.x    != wr.origin.x
8505           || newWr.origin.y    != wr.origin.y)
8506         {
8507           NSTRACE_MSG ("New frame different");
8508           [self setFrame: newWr display: NO];
8509         }
8510     }
8511 #else
8512   // Non-native zoom which is done instantaneously.  The resulting
8513   // frame covers the entire screen, except the menu-bar and dock, if
8514   // present.
8515   NSScreen * screen = [self screen];
8516   if (screen != nil)
8517     {
8518       NSRect sr = [screen frame];
8519       struct EmacsMargins margins
8520         = ns_screen_margins_ignoring_hidden_dock(screen);
8522       sr.size.height -= (margins.top + margins.bottom);
8523       sr.size.width  -= (margins.left + margins.right);
8524       sr.origin.x += margins.left;
8525       sr.origin.y += margins.bottom;
8527       sr = [[self delegate] windowWillUseStandardFrame:self
8528                                           defaultFrame:sr];
8529       [self setFrame: sr display: NO];
8530     }
8531 #endif
8534 - (void)setFrame:(NSRect)windowFrame
8535          display:(BOOL)displayViews
8537   NSTRACE ("[EmacsWindow setFrame:" NSTRACE_FMT_RECT " display:%d]",
8538            NSTRACE_ARG_RECT (windowFrame), displayViews);
8540   [super setFrame:windowFrame display:displayViews];
8543 - (void)setFrame:(NSRect)windowFrame
8544          display:(BOOL)displayViews
8545          animate:(BOOL)performAnimation
8547   NSTRACE ("[EmacsWindow setFrame:" NSTRACE_FMT_RECT
8548            " display:%d performAnimation:%d]",
8549            NSTRACE_ARG_RECT (windowFrame), displayViews, performAnimation);
8551   [super setFrame:windowFrame display:displayViews animate:performAnimation];
8554 - (void)setFrameTopLeftPoint:(NSPoint)point
8556   NSTRACE ("[EmacsWindow setFrameTopLeftPoint:" NSTRACE_FMT_POINT "]",
8557            NSTRACE_ARG_POINT (point));
8559   [super setFrameTopLeftPoint:point];
8562 - (BOOL)canBecomeKeyWindow
8564   return !FRAME_NO_ACCEPT_FOCUS (((EmacsView *)[self delegate])->emacsframe);
8566 @end /* EmacsWindow */
8569 @implementation EmacsFSWindow
8571 - (BOOL)canBecomeKeyWindow
8573   return YES;
8576 - (BOOL)canBecomeMainWindow
8578   return YES;
8581 @end
8583 /* ==========================================================================
8585     EmacsScroller implementation
8587    ========================================================================== */
8590 @implementation EmacsScroller
8592 /* for repeat button push */
8593 #define SCROLL_BAR_FIRST_DELAY 0.5
8594 #define SCROLL_BAR_CONTINUOUS_DELAY (1.0 / 15)
8596 + (CGFloat) scrollerWidth
8598   /* TODO: if we want to allow variable widths, this is the place to do it,
8599            however neither GNUstep nor Cocoa support it very well */
8600   CGFloat r;
8601 #if defined (NS_IMPL_COCOA) \
8602   && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
8603 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
8604   if ([NSScroller respondsToSelector:
8605                     @selector(scrollerWidthForControlSize:scrollerStyle:)])
8606 #endif
8607     r = [NSScroller scrollerWidthForControlSize: NSControlSizeRegular
8608                                   scrollerStyle: NSScrollerStyleLegacy];
8609 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
8610   else
8611 #endif
8612 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
8613 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 \
8614   || defined (NS_IMPL_GNUSTEP)
8615     r = [NSScroller scrollerWidth];
8616 #endif
8617   return r;
8620 - (instancetype)initFrame: (NSRect )r window: (Lisp_Object)nwin
8622   NSTRACE ("[EmacsScroller initFrame: window:]");
8624   if (r.size.width > r.size.height)
8625       horizontal = YES;
8626   else
8627       horizontal = NO;
8629   [super initWithFrame: r/*NSMakeRect (0, 0, 0, 0)*/];
8630   [self setContinuous: YES];
8631   [self setEnabled: YES];
8633   /* Ensure auto resizing of scrollbars occurs within the emacs frame's view
8634      locked against the top and bottom edges, and right edge on macOS, where
8635      scrollers are on right. */
8636 #ifdef NS_IMPL_GNUSTEP
8637   [self setAutoresizingMask: NSViewMaxXMargin | NSViewHeightSizable];
8638 #else
8639   [self setAutoresizingMask: NSViewMinXMargin | NSViewHeightSizable];
8640 #endif
8642   window = XWINDOW (nwin);
8643   condemned = NO;
8644   if (horizontal)
8645     pixel_length = NSWidth (r);
8646   else
8647     pixel_length = NSHeight (r);
8648   if (pixel_length == 0) pixel_length = 1;
8649   min_portion = 20 / pixel_length;
8651   frame = XFRAME (window->frame);
8652   if (FRAME_LIVE_P (frame))
8653     {
8654       int i;
8655       EmacsView *view = FRAME_NS_VIEW (frame);
8656       NSView *sview = [[view window] contentView];
8657       NSArray *subs = [sview subviews];
8659       /* disable optimization stopping redraw of other scrollbars */
8660       view->scrollbarsNeedingUpdate = 0;
8661       for (i =[subs count]-1; i >= 0; i--)
8662         if ([[subs objectAtIndex: i] isKindOfClass: [EmacsScroller class]])
8663           view->scrollbarsNeedingUpdate++;
8664       [sview addSubview: self];
8665     }
8667 /*  [self setFrame: r]; */
8669   return self;
8673 - (void)setFrame: (NSRect)newRect
8675   NSTRACE ("[EmacsScroller setFrame:]");
8677 /*  block_input (); */
8678   if (horizontal)
8679     pixel_length = NSWidth (newRect);
8680   else
8681     pixel_length = NSHeight (newRect);
8682   if (pixel_length == 0) pixel_length = 1;
8683   min_portion = 20 / pixel_length;
8684   [super setFrame: newRect];
8685 /*  unblock_input (); */
8689 - (void)dealloc
8691   NSTRACE ("[EmacsScroller dealloc]");
8692   if (window)
8693     {
8694       if (horizontal)
8695         wset_horizontal_scroll_bar (window, Qnil);
8696       else
8697         wset_vertical_scroll_bar (window, Qnil);
8698     }
8699   window = 0;
8700   [super dealloc];
8704 - (instancetype)condemn
8706   NSTRACE ("[EmacsScroller condemn]");
8707   condemned =YES;
8708   return self;
8712 - (instancetype)reprieve
8714   NSTRACE ("[EmacsScroller reprieve]");
8715   condemned =NO;
8716   return self;
8720 -(bool)judge
8722   NSTRACE ("[EmacsScroller judge]");
8723   bool ret = condemned;
8724   if (condemned)
8725     {
8726       EmacsView *view;
8727       block_input ();
8728       /* ensure other scrollbar updates after deletion */
8729       view = (EmacsView *)FRAME_NS_VIEW (frame);
8730       if (view != nil)
8731         view->scrollbarsNeedingUpdate++;
8732       if (window)
8733         {
8734           if (horizontal)
8735             wset_horizontal_scroll_bar (window, Qnil);
8736           else
8737             wset_vertical_scroll_bar (window, Qnil);
8738         }
8739       window = 0;
8740       [self removeFromSuperview];
8741       [self release];
8742       unblock_input ();
8743     }
8744   return ret;
8748 - (void)resetCursorRects
8750   NSRect visible = [self visibleRect];
8751   NSTRACE ("[EmacsScroller resetCursorRects]");
8753   if (!NSIsEmptyRect (visible))
8754     [self addCursorRect: visible cursor: [NSCursor arrowCursor]];
8756 #if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 101300
8757 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
8758   if ([[NSCursor arrowCursor] respondsToSelector:
8759                                 @selector(setOnMouseEntered)])
8760 #endif
8761     [[NSCursor arrowCursor] setOnMouseEntered: YES];
8762 #endif
8766 - (int) checkSamePosition: (int) position portion: (int) portion
8767                     whole: (int) whole
8769   return em_position ==position && em_portion ==portion && em_whole ==whole
8770     && portion != whole; /* needed for resize empty buf */
8774 - (instancetype)setPosition: (int)position portion: (int)portion whole: (int)whole
8776   NSTRACE ("[EmacsScroller setPosition:portion:whole:]");
8778   em_position = position;
8779   em_portion = portion;
8780   em_whole = whole;
8782   if (portion >= whole)
8783     {
8784 #ifdef NS_IMPL_COCOA
8785       [self setKnobProportion: 1.0];
8786       [self setDoubleValue: 1.0];
8787 #else
8788       [self setFloatValue: 0.0 knobProportion: 1.0];
8789 #endif
8790     }
8791   else
8792     {
8793       float pos;
8794       CGFloat por;
8795       portion = max ((float)whole*min_portion/pixel_length, portion);
8796       pos = (float)position / (whole - portion);
8797       por = (CGFloat)portion/whole;
8798 #ifdef NS_IMPL_COCOA
8799       [self setKnobProportion: por];
8800       [self setDoubleValue: pos];
8801 #else
8802       [self setFloatValue: pos knobProportion: por];
8803 #endif
8804     }
8806   return self;
8809 /* set up emacs_event */
8810 - (void) sendScrollEventAtLoc: (float)loc fromEvent: (NSEvent *)e
8812   Lisp_Object win;
8814   NSTRACE ("[EmacsScroller sendScrollEventAtLoc:fromEvent:]");
8816   if (!emacs_event)
8817     return;
8819   emacs_event->part = last_hit_part;
8820   emacs_event->code = 0;
8821   emacs_event->modifiers = EV_MODIFIERS (e) | down_modifier;
8822   XSETWINDOW (win, window);
8823   emacs_event->frame_or_window = win;
8824   emacs_event->timestamp = EV_TIMESTAMP (e);
8825   emacs_event->arg = Qnil;
8827   if (horizontal)
8828     {
8829       emacs_event->kind = HORIZONTAL_SCROLL_BAR_CLICK_EVENT;
8830       XSETINT (emacs_event->x, em_whole * loc / pixel_length);
8831       XSETINT (emacs_event->y, em_whole);
8832     }
8833   else
8834     {
8835       emacs_event->kind = SCROLL_BAR_CLICK_EVENT;
8836       XSETINT (emacs_event->x, loc);
8837       XSETINT (emacs_event->y, pixel_length-20);
8838     }
8840   if (q_event_ptr)
8841     {
8842       n_emacs_events_pending++;
8843       kbd_buffer_store_event_hold (emacs_event, q_event_ptr);
8844     }
8845   else
8846     hold_event (emacs_event);
8847   EVENT_INIT (*emacs_event);
8848   ns_send_appdefined (-1);
8852 /* called manually thru timer to implement repeated button action w/hold-down */
8853 - (instancetype)repeatScroll: (NSTimer *)scrollEntry
8855   NSEvent *e = [[self window] currentEvent];
8856   NSPoint p =  [[self window] mouseLocationOutsideOfEventStream];
8857   BOOL inKnob = [self testPart: p] == NSScrollerKnob;
8859   NSTRACE ("[EmacsScroller repeatScroll:]");
8861   /* clear timer if need be */
8862   if (inKnob || [scroll_repeat_entry timeInterval] == SCROLL_BAR_FIRST_DELAY)
8863     {
8864         [scroll_repeat_entry invalidate];
8865         [scroll_repeat_entry release];
8866         scroll_repeat_entry = nil;
8868         if (inKnob)
8869           return self;
8871         scroll_repeat_entry
8872           = [[NSTimer scheduledTimerWithTimeInterval:
8873                         SCROLL_BAR_CONTINUOUS_DELAY
8874                                             target: self
8875                                           selector: @selector (repeatScroll:)
8876                                           userInfo: 0
8877                                            repeats: YES]
8878               retain];
8879     }
8881   [self sendScrollEventAtLoc: 0 fromEvent: e];
8882   return self;
8886 /* Asynchronous mouse tracking for scroller.  This allows us to dispatch
8887    mouseDragged events without going into a modal loop. */
8888 - (void)mouseDown: (NSEvent *)e
8890   NSRect sr, kr;
8891   /* hitPart is only updated AFTER event is passed on */
8892   NSScrollerPart part = [self testPart: [e locationInWindow]];
8893   CGFloat loc, kloc, pos UNINIT;
8894   int edge = 0;
8896   NSTRACE ("[EmacsScroller mouseDown:]");
8898   switch (part)
8899     {
8900     case NSScrollerDecrementPage:
8901       last_hit_part = horizontal ? scroll_bar_before_handle : scroll_bar_above_handle; break;
8902     case NSScrollerIncrementPage:
8903       last_hit_part = horizontal ? scroll_bar_after_handle : scroll_bar_below_handle; break;
8904     case NSScrollerDecrementLine:
8905       last_hit_part = horizontal ? scroll_bar_left_arrow : scroll_bar_up_arrow; break;
8906     case NSScrollerIncrementLine:
8907       last_hit_part = horizontal ? scroll_bar_right_arrow : scroll_bar_down_arrow; break;
8908     case NSScrollerKnob:
8909       last_hit_part = horizontal ? scroll_bar_horizontal_handle : scroll_bar_handle; break;
8910     case NSScrollerKnobSlot:  /* GNUstep-only */
8911       last_hit_part = scroll_bar_move_ratio; break;
8912     default:  /* NSScrollerNoPart? */
8913       fprintf (stderr, "EmacsScroller-mouseDown: unexpected part %ld\n",
8914                (long) part);
8915       return;
8916     }
8918   if (part == NSScrollerKnob || part == NSScrollerKnobSlot)
8919     {
8920       /* handle, or on GNUstep possibly slot */
8921       NSEvent *fake_event;
8922       int length;
8924       /* compute float loc in slot and mouse offset on knob */
8925       sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot]
8926                       toView: nil];
8927       if (horizontal)
8928         {
8929           length = NSWidth (sr);
8930           loc = ([e locationInWindow].x - NSMinX (sr));
8931         }
8932       else
8933         {
8934           length = NSHeight (sr);
8935           loc = length - ([e locationInWindow].y - NSMinY (sr));
8936         }
8938       if (loc <= 0.0)
8939         {
8940           loc = 0.0;
8941           edge = -1;
8942         }
8943       else if (loc >= length)
8944         {
8945           loc = length;
8946           edge = 1;
8947         }
8949       if (edge)
8950         kloc = 0.5 * edge;
8951       else
8952         {
8953           kr = [self convertRect: [self rectForPart: NSScrollerKnob]
8954                           toView: nil];
8955           if (horizontal)
8956             kloc = ([e locationInWindow].x - NSMinX (kr));
8957           else
8958             kloc = NSHeight (kr) - ([e locationInWindow].y - NSMinY (kr));
8959         }
8960       last_mouse_offset = kloc;
8962       /* if knob, tell emacs a location offset by knob pos
8963          (to indicate top of handle) */
8964       if (part == NSScrollerKnob)
8965         pos = (loc - last_mouse_offset);
8966       else
8967         /* else this is a slot click on GNUstep: go straight there */
8968         pos = loc;
8970       /* If there are buttons in the scroller area, we need to
8971          recalculate pos as emacs expects the scroller slot to take up
8972          the entire available length.  */
8973       if (length != pixel_length)
8974         pos = pos * pixel_length / length;
8976       /* send a fake mouse-up to super to preempt modal -trackKnob: mode */
8977       fake_event = [NSEvent mouseEventWithType: NSEventTypeLeftMouseUp
8978                                       location: [e locationInWindow]
8979                                  modifierFlags: [e modifierFlags]
8980                                      timestamp: [e timestamp]
8981                                   windowNumber: [e windowNumber]
8982                                        context: nil
8983                                    eventNumber: [e eventNumber]
8984                                     clickCount: [e clickCount]
8985                                       pressure: [e pressure]];
8986       [super mouseUp: fake_event];
8987     }
8988   else
8989     {
8990       pos = 0;      /* ignored */
8992       /* set a timer to repeat, as we can't let superclass do this modally */
8993       scroll_repeat_entry
8994         = [[NSTimer scheduledTimerWithTimeInterval: SCROLL_BAR_FIRST_DELAY
8995                                             target: self
8996                                           selector: @selector (repeatScroll:)
8997                                           userInfo: 0
8998                                            repeats: YES]
8999             retain];
9000     }
9002   if (part != NSScrollerKnob)
9003     [self sendScrollEventAtLoc: pos fromEvent: e];
9007 /* Called as we manually track scroller drags, rather than superclass. */
9008 - (void)mouseDragged: (NSEvent *)e
9010     NSRect sr;
9011     double loc, pos;
9012     int length;
9014     NSTRACE ("[EmacsScroller mouseDragged:]");
9016       sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot]
9017                       toView: nil];
9019       if (horizontal)
9020         {
9021           length = NSWidth (sr);
9022           loc = ([e locationInWindow].x - NSMinX (sr));
9023         }
9024       else
9025         {
9026           length = NSHeight (sr);
9027           loc = length - ([e locationInWindow].y - NSMinY (sr));
9028         }
9030       if (loc <= 0.0)
9031         {
9032           loc = 0.0;
9033         }
9034       else if (loc >= length + last_mouse_offset)
9035         {
9036           loc = length + last_mouse_offset;
9037         }
9039       pos = (loc - last_mouse_offset);
9041       /* If there are buttons in the scroller area, we need to
9042          recalculate pos as emacs expects the scroller slot to take up
9043          the entire available length.  */
9044       if (length != pixel_length)
9045         pos = pos * pixel_length / length;
9047       [self sendScrollEventAtLoc: pos fromEvent: e];
9051 - (void)mouseUp: (NSEvent *)e
9053   NSTRACE ("[EmacsScroller mouseUp:]");
9055   if (scroll_repeat_entry)
9056     {
9057       [scroll_repeat_entry invalidate];
9058       [scroll_repeat_entry release];
9059       scroll_repeat_entry = nil;
9060     }
9061   last_hit_part = scroll_bar_above_handle;
9065 /* treat scrollwheel events in the bar as though they were in the main window */
9066 - (void) scrollWheel: (NSEvent *)theEvent
9068   NSTRACE ("[EmacsScroller scrollWheel:]");
9070   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (frame);
9071   [view mouseDown: theEvent];
9074 @end  /* EmacsScroller */
9077 #ifdef NS_IMPL_GNUSTEP
9078 /* Dummy class to get rid of startup warnings.  */
9079 @implementation EmacsDocument
9081 @end
9082 #endif
9085 /* ==========================================================================
9087    Font-related functions; these used to be in nsfaces.m
9089    ========================================================================== */
9092 Lisp_Object
9093 x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
9095   struct font *font = XFONT_OBJECT (font_object);
9096   EmacsView *view = FRAME_NS_VIEW (f);
9097   int font_ascent, font_descent;
9099   if (fontset < 0)
9100     fontset = fontset_from_font (font_object);
9101   FRAME_FONTSET (f) = fontset;
9103   if (FRAME_FONT (f) == font)
9104     /* This font is already set in frame F.  There's nothing more to
9105        do.  */
9106     return font_object;
9108   FRAME_FONT (f) = font;
9110   FRAME_BASELINE_OFFSET (f) = font->baseline_offset;
9111   FRAME_COLUMN_WIDTH (f) = font->average_width;
9112   get_font_ascent_descent (font, &font_ascent, &font_descent);
9113   FRAME_LINE_HEIGHT (f) = font_ascent + font_descent;
9115   /* Compute the scroll bar width in character columns.  */
9116   if (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) > 0)
9117     {
9118       int wid = FRAME_COLUMN_WIDTH (f);
9119       FRAME_CONFIG_SCROLL_BAR_COLS (f)
9120         = (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) + wid - 1) / wid;
9121     }
9122   else
9123     {
9124       int wid = FRAME_COLUMN_WIDTH (f);
9125       FRAME_CONFIG_SCROLL_BAR_COLS (f) = (14 + wid - 1) / wid;
9126     }
9128   /* Compute the scroll bar height in character lines.  */
9129   if (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) > 0)
9130     {
9131       int height = FRAME_LINE_HEIGHT (f);
9132       FRAME_CONFIG_SCROLL_BAR_LINES (f)
9133         = (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) + height - 1) / height;
9134     }
9135   else
9136     {
9137       int height = FRAME_LINE_HEIGHT (f);
9138       FRAME_CONFIG_SCROLL_BAR_LINES (f) = (14 + height - 1) / height;
9139     }
9141   /* Now make the frame display the given font.  */
9142   if (FRAME_NS_WINDOW (f) != 0 && ! [view isFullscreen])
9143     adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
9144                        FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3,
9145                        false, Qfont);
9147   return font_object;
9151 /* XLFD: -foundry-family-weight-slant-swidth-adstyle-pxlsz-ptSz-resx-resy-spc-avgWidth-rgstry-encoding */
9152 /* Note: ns_font_to_xlfd and ns_fontname_to_xlfd no longer needed, removed
9153          in 1.43. */
9155 const char *
9156 ns_xlfd_to_fontname (const char *xlfd)
9157 /* --------------------------------------------------------------------------
9158     Convert an X font name (XLFD) to an NS font name.
9159     Only family is used.
9160     The string returned is temporarily allocated.
9161    -------------------------------------------------------------------------- */
9163   char *name = xmalloc (180);
9164   int i, len;
9165   const char *ret;
9167   if (!strncmp (xlfd, "--", 2))
9168     sscanf (xlfd, "--%*[^-]-%179[^-]-", name);
9169   else
9170     sscanf (xlfd, "-%*[^-]-%179[^-]-", name);
9172   /* stopgap for malformed XLFD input */
9173   if (strlen (name) == 0)
9174     strcpy (name, "Monaco");
9176   /* undo hack in ns_fontname_to_xlfd, converting '$' to '-', '_' to ' '
9177      also uppercase after '-' or ' ' */
9178   name[0] = c_toupper (name[0]);
9179   for (len =strlen (name), i =0; i<len; i++)
9180     {
9181       if (name[i] == '$')
9182         {
9183           name[i] = '-';
9184           if (i+1<len)
9185             name[i+1] = c_toupper (name[i+1]);
9186         }
9187       else if (name[i] == '_')
9188         {
9189           name[i] = ' ';
9190           if (i+1<len)
9191             name[i+1] = c_toupper (name[i+1]);
9192         }
9193     }
9194 /*fprintf (stderr, "converted '%s' to '%s'\n",xlfd,name);  */
9195   ret = [[NSString stringWithUTF8String: name] UTF8String];
9196   xfree (name);
9197   return ret;
9201 void
9202 syms_of_nsterm (void)
9204   NSTRACE ("syms_of_nsterm");
9206   ns_antialias_threshold = 10.0;
9208   /* from 23+ we need to tell emacs what modifiers there are.. */
9209   DEFSYM (Qmodifier_value, "modifier-value");
9210   DEFSYM (Qalt, "alt");
9211   DEFSYM (Qhyper, "hyper");
9212   DEFSYM (Qmeta, "meta");
9213   DEFSYM (Qsuper, "super");
9214   DEFSYM (Qcontrol, "control");
9215   DEFSYM (QUTF8_STRING, "UTF8_STRING");
9217   DEFSYM (Qfile, "file");
9218   DEFSYM (Qurl, "url");
9220   Fput (Qalt, Qmodifier_value, make_number (alt_modifier));
9221   Fput (Qhyper, Qmodifier_value, make_number (hyper_modifier));
9222   Fput (Qmeta, Qmodifier_value, make_number (meta_modifier));
9223   Fput (Qsuper, Qmodifier_value, make_number (super_modifier));
9224   Fput (Qcontrol, Qmodifier_value, make_number (ctrl_modifier));
9226   DEFVAR_LISP ("ns-input-file", ns_input_file,
9227               "The file specified in the last NS event.");
9228   ns_input_file =Qnil;
9230   DEFVAR_LISP ("ns-working-text", ns_working_text,
9231               "String for visualizing working composition sequence.");
9232   ns_working_text =Qnil;
9234   DEFVAR_LISP ("ns-input-font", ns_input_font,
9235               "The font specified in the last NS event.");
9236   ns_input_font =Qnil;
9238   DEFVAR_LISP ("ns-input-fontsize", ns_input_fontsize,
9239               "The fontsize specified in the last NS event.");
9240   ns_input_fontsize =Qnil;
9242   DEFVAR_LISP ("ns-input-line", ns_input_line,
9243                "The line specified in the last NS event.");
9244   ns_input_line =Qnil;
9246   DEFVAR_LISP ("ns-input-spi-name", ns_input_spi_name,
9247                "The service name specified in the last NS event.");
9248   ns_input_spi_name =Qnil;
9250   DEFVAR_LISP ("ns-input-spi-arg", ns_input_spi_arg,
9251                "The service argument specified in the last NS event.");
9252   ns_input_spi_arg =Qnil;
9254   DEFVAR_LISP ("ns-alternate-modifier", ns_alternate_modifier,
9255                "This variable describes the behavior of the alternate or option key.\n\
9256 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9257 that key.\n\
9258 Set to none means that the alternate / option key is not interpreted by Emacs\n\
9259 at all, allowing it to be used at a lower level for accented character entry.");
9260   ns_alternate_modifier = Qmeta;
9262   DEFVAR_LISP ("ns-right-alternate-modifier", ns_right_alternate_modifier,
9263                "This variable describes the behavior of the right alternate or option key.\n\
9264 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9265 that key.\n\
9266 Set to left means be the same key as `ns-alternate-modifier'.\n\
9267 Set to none means that the alternate / option key is not interpreted by Emacs\n\
9268 at all, allowing it to be used at a lower level for accented character entry.");
9269   ns_right_alternate_modifier = Qleft;
9271   DEFVAR_LISP ("ns-command-modifier", ns_command_modifier,
9272                "This variable describes the behavior of the command key.\n\
9273 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9274 that key.");
9275   ns_command_modifier = Qsuper;
9277   DEFVAR_LISP ("ns-right-command-modifier", ns_right_command_modifier,
9278                "This variable describes the behavior of the right command key.\n\
9279 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9280 that key.\n\
9281 Set to left means be the same key as `ns-command-modifier'.\n\
9282 Set to none means that the command / option key is not interpreted by Emacs\n\
9283 at all, allowing it to be used at a lower level for accented character entry.");
9284   ns_right_command_modifier = Qleft;
9286   DEFVAR_LISP ("ns-control-modifier", ns_control_modifier,
9287                "This variable describes the behavior of the control key.\n\
9288 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9289 that key.");
9290   ns_control_modifier = Qcontrol;
9292   DEFVAR_LISP ("ns-right-control-modifier", ns_right_control_modifier,
9293                "This variable describes the behavior of the right control key.\n\
9294 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9295 that key.\n\
9296 Set to left means be the same key as `ns-control-modifier'.\n\
9297 Set to none means that the control / option key is not interpreted by Emacs\n\
9298 at all, allowing it to be used at a lower level for accented character entry.");
9299   ns_right_control_modifier = Qleft;
9301   DEFVAR_LISP ("ns-function-modifier", ns_function_modifier,
9302                "This variable describes the behavior of the function key (on laptops).\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 function key is not interpreted by Emacs at all,\n\
9306 allowing it to be used at a lower level for accented character entry.");
9307   ns_function_modifier = Qnone;
9309   DEFVAR_LISP ("ns-antialias-text", ns_antialias_text,
9310                "Non-nil (the default) means to render text antialiased.");
9311   ns_antialias_text = Qt;
9313   DEFVAR_LISP ("ns-use-thin-smoothing", ns_use_thin_smoothing,
9314                "Non-nil turns on a font smoothing method that produces thinner strokes.");
9315   ns_use_thin_smoothing = Qnil;
9317   DEFVAR_LISP ("ns-confirm-quit", ns_confirm_quit,
9318                "Whether to confirm application quit using dialog.");
9319   ns_confirm_quit = Qnil;
9321   DEFVAR_LISP ("ns-auto-hide-menu-bar", ns_auto_hide_menu_bar,
9322                doc: /* Non-nil means that the menu bar is hidden, but appears when the mouse is near.
9323 Only works on Mac OS X 10.6 or later.  */);
9324   ns_auto_hide_menu_bar = Qnil;
9326   DEFVAR_BOOL ("ns-use-native-fullscreen", ns_use_native_fullscreen,
9327      doc: /*Non-nil means to use native fullscreen on Mac OS X 10.7 and later.
9328 Nil means use fullscreen the old (< 10.7) way.  The old way works better with
9329 multiple monitors, but lacks tool bar.  This variable is ignored on
9330 Mac OS X < 10.7.  Default is t.  */);
9331   ns_use_native_fullscreen = YES;
9332   ns_last_use_native_fullscreen = ns_use_native_fullscreen;
9334   DEFVAR_BOOL ("ns-use-fullscreen-animation", ns_use_fullscreen_animation,
9335      doc: /*Non-nil means use animation on non-native fullscreen.
9336 For native fullscreen, this does nothing.
9337 Default is nil.  */);
9338   ns_use_fullscreen_animation = NO;
9340   DEFVAR_BOOL ("ns-use-srgb-colorspace", ns_use_srgb_colorspace,
9341      doc: /*Non-nil means to use sRGB colorspace on Mac OS X 10.7 and later.
9342 Note that this does not apply to images.
9343 This variable is ignored on Mac OS X < 10.7 and GNUstep.  */);
9344   ns_use_srgb_colorspace = YES;
9346   DEFVAR_BOOL ("ns-use-mwheel-acceleration",
9347                ns_use_mwheel_acceleration,
9348      doc: /*Non-nil means use macOS's standard mouse wheel acceleration.
9349 This variable is ignored on macOS < 10.7 and GNUstep.  Default is t.  */);
9350   ns_use_mwheel_acceleration = YES;
9352   DEFVAR_LISP ("ns-mwheel-line-height", ns_mwheel_line_height,
9353                doc: /*The number of pixels touchpad scrolling considers one line.
9354 Nil or a non-number means use the default frame line height.
9355 This variable is ignored on macOS < 10.7 and GNUstep.  Default is nil.  */);
9356   ns_mwheel_line_height = Qnil;
9358   DEFVAR_BOOL ("ns-use-mwheel-momentum", ns_use_mwheel_momentum,
9359                doc: /*Non-nil means mouse wheel scrolling uses momentum.
9360 This variable is ignored on macOS < 10.7 and GNUstep.  Default is t.  */);
9361   ns_use_mwheel_momentum = YES;
9363   /* TODO: move to common code */
9364   DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars,
9365                doc: /* Which toolkit scroll bars Emacs uses, if any.
9366 A value of nil means Emacs doesn't use toolkit scroll bars.
9367 With the X Window system, the value is a symbol describing the
9368 X toolkit.  Possible values are: gtk, motif, xaw, or xaw3d.
9369 With MS Windows or Nextstep, the value is t.  */);
9370   Vx_toolkit_scroll_bars = Qt;
9372   DEFVAR_BOOL ("x-use-underline-position-properties",
9373                x_use_underline_position_properties,
9374      doc: /*Non-nil means make use of UNDERLINE_POSITION font properties.
9375 A value of nil means ignore them.  If you encounter fonts with bogus
9376 UNDERLINE_POSITION font properties, for example 7x13 on XFree prior
9377 to 4.1, set this to nil. */);
9378   x_use_underline_position_properties = 0;
9380   DEFVAR_BOOL ("x-underline-at-descent-line",
9381                x_underline_at_descent_line,
9382      doc: /* Non-nil means to draw the underline at the same place as the descent line.
9383 A value of nil means to draw the underline according to the value of the
9384 variable `x-use-underline-position-properties', which is usually at the
9385 baseline level.  The default value is nil.  */);
9386   x_underline_at_descent_line = 0;
9388   /* Tell Emacs about this window system.  */
9389   Fprovide (Qns, Qnil);
9391   DEFSYM (Qcocoa, "cocoa");
9392   DEFSYM (Qgnustep, "gnustep");
9394 #ifdef NS_IMPL_COCOA
9395   Fprovide (Qcocoa, Qnil);
9396   syms_of_macfont ();
9397 #else
9398   Fprovide (Qgnustep, Qnil);
9399   syms_of_nsfont ();
9400 #endif