Document reserved keys
[emacs.git] / src / nsterm.m
blob3d58cd5ec649dee9f0fdb2e1cef2cca08c0b466c
1 /* NeXT/Open/GNUstep / macOS communication module.      -*- coding: utf-8 -*-
3 Copyright (C) 1989, 1993-1994, 2005-2006, 2008-2018 Free Software
4 Foundation, Inc.
6 This file is part of GNU Emacs.
8 GNU Emacs is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or (at
11 your option) any later version.
13 GNU Emacs is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
22 Originally by Carl Edman
23 Updated by Christian Limpach (chris@nice.ch)
24 OpenStep/Rhapsody port by Scott Bender (sbender@harmony-ds.com)
25 macOS/Aqua port by Christophe de Dinechin (descubes@earthlink.net)
26 GNUstep port and post-20 update by Adrian Robert (arobert@cogsci.ucsd.edu)
29 /* This should be the first include, as it may set up #defines affecting
30    interpretation of even the system includes. */
31 #include <config.h>
33 #include <fcntl.h>
34 #include <math.h>
35 #include <pthread.h>
36 #include <sys/types.h>
37 #include <time.h>
38 #include <signal.h>
39 #include <unistd.h>
41 #include <c-ctype.h>
42 #include <c-strcase.h>
43 #include <ftoastr.h>
45 #include "lisp.h"
46 #include "blockinput.h"
47 #include "sysselect.h"
48 #include "nsterm.h"
49 #include "systime.h"
50 #include "character.h"
51 #include "fontset.h"
52 #include "composite.h"
53 #include "ccl.h"
55 #include "termhooks.h"
56 #include "termchar.h"
57 #include "menu.h"
58 #include "window.h"
59 #include "keyboard.h"
60 #include "buffer.h"
61 #include "font.h"
63 #ifdef NS_IMPL_GNUSTEP
64 #include "process.h"
65 #endif
67 #ifdef NS_IMPL_COCOA
68 #include "macfont.h"
69 #endif
71 static EmacsMenu *dockMenu;
72 #ifdef NS_IMPL_COCOA
73 static EmacsMenu *mainMenu;
74 #endif
76 /* ==========================================================================
78    NSTRACE, Trace support.
80    ========================================================================== */
82 #if NSTRACE_ENABLED
84 /* The following use "volatile" since they can be accessed from
85    parallel threads. */
86 volatile int nstrace_num = 0;
87 volatile int nstrace_depth = 0;
89 /* When 0, no trace is emitted.  This is used by NSTRACE_WHEN and
90    NSTRACE_UNLESS to silence functions called.
92    TODO: This should really be a thread-local variable, to avoid that
93    a function with disabled trace thread silence trace output in
94    another.  However, in practice this seldom is a problem. */
95 volatile int nstrace_enabled_global = 1;
97 /* Called when nstrace_enabled goes out of scope. */
98 void nstrace_leave(int * pointer_to_nstrace_enabled)
100   if (*pointer_to_nstrace_enabled)
101     {
102       --nstrace_depth;
103     }
107 /* Called when nstrace_saved_enabled_global goes out of scope. */
108 void nstrace_restore_global_trace_state(int * pointer_to_saved_enabled_global)
110   nstrace_enabled_global = *pointer_to_saved_enabled_global;
114 char const * nstrace_fullscreen_type_name (int fs_type)
116   switch (fs_type)
117     {
118     case -1:                   return "-1";
119     case FULLSCREEN_NONE:      return "FULLSCREEN_NONE";
120     case FULLSCREEN_WIDTH:     return "FULLSCREEN_WIDTH";
121     case FULLSCREEN_HEIGHT:    return "FULLSCREEN_HEIGHT";
122     case FULLSCREEN_BOTH:      return "FULLSCREEN_BOTH";
123     case FULLSCREEN_MAXIMIZED: return "FULLSCREEN_MAXIMIZED";
124     default:                   return "FULLSCREEN_?????";
125     }
127 #endif
130 /* ==========================================================================
132    NSColor, EmacsColor category.
134    ========================================================================== */
135 @implementation NSColor (EmacsColor)
136 + (NSColor *)colorForEmacsRed:(CGFloat)red green:(CGFloat)green
137                          blue:(CGFloat)blue alpha:(CGFloat)alpha
139 #if defined (NS_IMPL_COCOA) \
140   && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
141   if (ns_use_srgb_colorspace
142 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
143       && [NSColor respondsToSelector:
144                     @selector(colorWithSRGBRed:green:blue:alpha:)]
145 #endif
146       )
147     return [NSColor colorWithSRGBRed: red
148                                green: green
149                                 blue: blue
150                                alpha: alpha];
151 #endif
152   return [NSColor colorWithCalibratedRed: red
153                                    green: green
154                                     blue: blue
155                                    alpha: alpha];
158 - (NSColor *)colorUsingDefaultColorSpace
160   /* FIXMES: We're checking for colorWithSRGBRed here so this will
161      only work in the same place as in the method above.  It should
162      really be a check whether we're on macOS 10.7 or above. */
163 #if defined (NS_IMPL_COCOA) \
164   && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
165   if (ns_use_srgb_colorspace
166 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
167       && [NSColor respondsToSelector:
168                     @selector(colorWithSRGBRed:green:blue:alpha:)]
169 #endif
170       )
171     return [self colorUsingColorSpace: [NSColorSpace sRGBColorSpace]];
172 #endif
173   return [self colorUsingColorSpaceName: NSCalibratedRGBColorSpace];
176 @end
178 /* ==========================================================================
180     Local declarations
182    ========================================================================== */
184 /* Convert a symbol indexed with an NSxxx value to a value as defined
185    in keyboard.c (lispy_function_key). I hope this is a correct way
186    of doing things... */
187 static unsigned convert_ns_to_X_keysym[] =
189   NSHomeFunctionKey,            0x50,
190   NSLeftArrowFunctionKey,       0x51,
191   NSUpArrowFunctionKey,         0x52,
192   NSRightArrowFunctionKey,      0x53,
193   NSDownArrowFunctionKey,       0x54,
194   NSPageUpFunctionKey,          0x55,
195   NSPageDownFunctionKey,        0x56,
196   NSEndFunctionKey,             0x57,
197   NSBeginFunctionKey,           0x58,
198   NSSelectFunctionKey,          0x60,
199   NSPrintFunctionKey,           0x61,
200   NSClearLineFunctionKey,       0x0B,
201   NSExecuteFunctionKey,         0x62,
202   NSInsertFunctionKey,          0x63,
203   NSUndoFunctionKey,            0x65,
204   NSRedoFunctionKey,            0x66,
205   NSMenuFunctionKey,            0x67,
206   NSFindFunctionKey,            0x68,
207   NSHelpFunctionKey,            0x6A,
208   NSBreakFunctionKey,           0x6B,
210   NSF1FunctionKey,              0xBE,
211   NSF2FunctionKey,              0xBF,
212   NSF3FunctionKey,              0xC0,
213   NSF4FunctionKey,              0xC1,
214   NSF5FunctionKey,              0xC2,
215   NSF6FunctionKey,              0xC3,
216   NSF7FunctionKey,              0xC4,
217   NSF8FunctionKey,              0xC5,
218   NSF9FunctionKey,              0xC6,
219   NSF10FunctionKey,             0xC7,
220   NSF11FunctionKey,             0xC8,
221   NSF12FunctionKey,             0xC9,
222   NSF13FunctionKey,             0xCA,
223   NSF14FunctionKey,             0xCB,
224   NSF15FunctionKey,             0xCC,
225   NSF16FunctionKey,             0xCD,
226   NSF17FunctionKey,             0xCE,
227   NSF18FunctionKey,             0xCF,
228   NSF19FunctionKey,             0xD0,
229   NSF20FunctionKey,             0xD1,
230   NSF21FunctionKey,             0xD2,
231   NSF22FunctionKey,             0xD3,
232   NSF23FunctionKey,             0xD4,
233   NSF24FunctionKey,             0xD5,
235   NSBackspaceCharacter,         0x08,  /* 8: Not on some KBs. */
236   NSDeleteCharacter,            0xFF,  /* 127: Big 'delete' key upper right. */
237   NSDeleteFunctionKey,          0x9F,  /* 63272: Del forw key off main array. */
239   NSTabCharacter,               0x09,
240   0x19,                         0x09,  /* left tab->regular since pass shift */
241   NSCarriageReturnCharacter,    0x0D,
242   NSNewlineCharacter,           0x0D,
243   NSEnterCharacter,             0x8D,
245   0x41|NSEventModifierFlagNumericPad,   0xAE,  /* KP_Decimal */
246   0x43|NSEventModifierFlagNumericPad,   0xAA,  /* KP_Multiply */
247   0x45|NSEventModifierFlagNumericPad,   0xAB,  /* KP_Add */
248   0x4B|NSEventModifierFlagNumericPad,   0xAF,  /* KP_Divide */
249   0x4E|NSEventModifierFlagNumericPad,   0xAD,  /* KP_Subtract */
250   0x51|NSEventModifierFlagNumericPad,   0xBD,  /* KP_Equal */
251   0x52|NSEventModifierFlagNumericPad,   0xB0,  /* KP_0 */
252   0x53|NSEventModifierFlagNumericPad,   0xB1,  /* KP_1 */
253   0x54|NSEventModifierFlagNumericPad,   0xB2,  /* KP_2 */
254   0x55|NSEventModifierFlagNumericPad,   0xB3,  /* KP_3 */
255   0x56|NSEventModifierFlagNumericPad,   0xB4,  /* KP_4 */
256   0x57|NSEventModifierFlagNumericPad,   0xB5,  /* KP_5 */
257   0x58|NSEventModifierFlagNumericPad,   0xB6,  /* KP_6 */
258   0x59|NSEventModifierFlagNumericPad,   0xB7,  /* KP_7 */
259   0x5B|NSEventModifierFlagNumericPad,   0xB8,  /* KP_8 */
260   0x5C|NSEventModifierFlagNumericPad,   0xB9,  /* KP_9 */
262   0x1B,                         0x1B   /* escape */
265 /* On macOS picks up the default NSGlobalDomain AppleAntiAliasingThreshold,
266    the maximum font size to NOT antialias.  On GNUstep there is currently
267    no way to control this behavior. */
268 float ns_antialias_threshold;
270 NSArray *ns_send_types = 0, *ns_return_types = 0;
271 static NSArray *ns_drag_types = 0;
272 NSString *ns_app_name = @"Emacs";  /* default changed later */
274 /* Display variables */
275 struct ns_display_info *x_display_list; /* Chain of existing displays */
276 long context_menu_value = 0;
278 /* display update */
279 static struct frame *ns_updating_frame;
280 static NSView *focus_view = NULL;
281 static int ns_window_num = 0;
282 #ifdef NS_IMPL_GNUSTEP
283 static NSRect uRect;            // TODO: This is dead, remove it?
284 #endif
285 static BOOL gsaved = NO;
286 static BOOL ns_fake_keydown = NO;
287 #ifdef NS_IMPL_COCOA
288 static BOOL ns_menu_bar_is_hidden = NO;
289 #endif
290 /*static int debug_lock = 0; */
292 /* event loop */
293 static BOOL send_appdefined = YES;
294 #define NO_APPDEFINED_DATA (-8)
295 static int last_appdefined_event_data = NO_APPDEFINED_DATA;
296 static NSTimer *timed_entry = 0;
297 static NSTimer *scroll_repeat_entry = nil;
298 static fd_set select_readfds, select_writefds;
299 enum { SELECT_HAVE_READ = 1, SELECT_HAVE_WRITE = 2, SELECT_HAVE_TMO = 4 };
300 static int select_nfds = 0, select_valid = 0;
301 static struct timespec select_timeout = { 0, 0 };
302 static int selfds[2] = { -1, -1 };
303 static pthread_mutex_t select_mutex;
304 static NSAutoreleasePool *outerpool;
305 static struct input_event *emacs_event = NULL;
306 static struct input_event *q_event_ptr = NULL;
307 static int n_emacs_events_pending = 0;
308 static NSMutableArray *ns_pending_files, *ns_pending_service_names,
309   *ns_pending_service_args;
310 static BOOL ns_do_open_file = NO;
311 static BOOL ns_last_use_native_fullscreen;
313 /* Non-zero means that a HELP_EVENT has been generated since Emacs
314    start.  */
316 static BOOL any_help_event_p = NO;
318 static struct {
319   struct input_event *q;
320   int nr, cap;
321 } hold_event_q = {
322   NULL, 0, 0
325 static NSString *represented_filename = nil;
326 static struct frame *represented_frame = 0;
328 #ifdef NS_IMPL_COCOA
330  * State for pending menu activation:
331  * MENU_NONE     Normal state
332  * MENU_PENDING  A menu has been clicked on, but has been canceled so we can
333  *               run lisp to update the menu.
334  * MENU_OPENING  Menu is up to date, and the click event is redone so the menu
335  *               will open.
336  */
337 #define MENU_NONE 0
338 #define MENU_PENDING 1
339 #define MENU_OPENING 2
340 static int menu_will_open_state = MENU_NONE;
342 /* Saved position for menu click.  */
343 static CGPoint menu_mouse_point;
344 #endif
346 /* Convert modifiers in a NeXTstep event to emacs style modifiers.  */
347 #define NS_FUNCTION_KEY_MASK 0x800000
348 #define NSLeftControlKeyMask    (0x000001 | NSEventModifierFlagControl)
349 #define NSRightControlKeyMask   (0x002000 | NSEventModifierFlagControl)
350 #define NSLeftCommandKeyMask    (0x000008 | NSEventModifierFlagCommand)
351 #define NSRightCommandKeyMask   (0x000010 | NSEventModifierFlagCommand)
352 #define NSLeftAlternateKeyMask  (0x000020 | NSEventModifierFlagOption)
353 #define NSRightAlternateKeyMask (0x000040 | NSEventModifierFlagOption)
354 #define EV_MODIFIERS2(flags)                          \
355     (((flags & NSEventModifierFlagHelp) ?           \
356            hyper_modifier : 0)                        \
357      | (!EQ (ns_right_alternate_modifier, Qleft) && \
358         ((flags & NSRightAlternateKeyMask) \
359          == NSRightAlternateKeyMask) ? \
360            parse_solitary_modifier (ns_right_alternate_modifier) : 0) \
361      | ((flags & NSEventModifierFlagOption) ?                 \
362            parse_solitary_modifier (ns_alternate_modifier) : 0)   \
363      | ((flags & NSEventModifierFlagShift) ?     \
364            shift_modifier : 0)                        \
365      | (!EQ (ns_right_control_modifier, Qleft) && \
366         ((flags & NSRightControlKeyMask) \
367          == NSRightControlKeyMask) ? \
368            parse_solitary_modifier (ns_right_control_modifier) : 0) \
369      | ((flags & NSEventModifierFlagControl) ?      \
370            parse_solitary_modifier (ns_control_modifier) : 0)     \
371      | ((flags & NS_FUNCTION_KEY_MASK) ?  \
372            parse_solitary_modifier (ns_function_modifier) : 0)    \
373      | (!EQ (ns_right_command_modifier, Qleft) && \
374         ((flags & NSRightCommandKeyMask) \
375          == NSRightCommandKeyMask) ? \
376            parse_solitary_modifier (ns_right_command_modifier) : 0) \
377      | ((flags & NSEventModifierFlagCommand) ?      \
378            parse_solitary_modifier (ns_command_modifier):0))
379 #define EV_MODIFIERS(e) EV_MODIFIERS2 ([e modifierFlags])
381 #define EV_UDMODIFIERS(e)                                      \
382     ((([e type] == NSEventTypeLeftMouseDown) ? down_modifier : 0)       \
383      | (([e type] == NSEventTypeRightMouseDown) ? down_modifier : 0)    \
384      | (([e type] == NSEventTypeOtherMouseDown) ? down_modifier : 0)    \
385      | (([e type] == NSEventTypeLeftMouseDragged) ? down_modifier : 0)  \
386      | (([e type] == NSEventTypeRightMouseDragged) ? down_modifier : 0) \
387      | (([e type] == NSEventTypeOtherMouseDragged) ? down_modifier : 0) \
388      | (([e type] == NSEventTypeLeftMouseUp)   ? up_modifier   : 0)     \
389      | (([e type] == NSEventTypeRightMouseUp)   ? up_modifier   : 0)    \
390      | (([e type] == NSEventTypeOtherMouseUp)   ? up_modifier   : 0))
392 #define EV_BUTTON(e)                                                         \
393     ((([e type] == NSEventTypeLeftMouseDown) || ([e type] == NSEventTypeLeftMouseUp)) ? 0 :    \
394       (([e type] == NSEventTypeRightMouseDown) || ([e type] == NSEventTypeRightMouseUp)) ? 2 : \
395      [e buttonNumber] - 1)
397 /* Convert the time field to a timestamp in milliseconds. */
398 #define EV_TIMESTAMP(e) ([e timestamp] * 1000)
400 /* This is a piece of code which is common to all the event handling
401    methods.  Maybe it should even be a function.  */
402 #define EV_TRAILER(e)                                                   \
403   {                                                                     \
404     XSETFRAME (emacs_event->frame_or_window, emacsframe);               \
405     EV_TRAILER2 (e);                                                    \
406   }
408 #define EV_TRAILER2(e)                                                  \
409   {                                                                     \
410       if (e) emacs_event->timestamp = EV_TIMESTAMP (e);                 \
411       if (q_event_ptr)                                                  \
412         {                                                               \
413           Lisp_Object tem = Vinhibit_quit;                              \
414           Vinhibit_quit = Qt;                                           \
415           n_emacs_events_pending++;                                     \
416           kbd_buffer_store_event_hold (emacs_event, q_event_ptr);       \
417           Vinhibit_quit = tem;                                          \
418         }                                                               \
419       else                                                              \
420         hold_event (emacs_event);                                       \
421       EVENT_INIT (*emacs_event);                                        \
422       ns_send_appdefined (-1);                                          \
423     }
426 /* These flags will be OR'd or XOR'd with the NSWindow's styleMask
427    property depending on what we're doing. */
428 #define FRAME_DECORATED_FLAGS (NSWindowStyleMaskTitled              \
429                                | NSWindowStyleMaskResizable         \
430                                | NSWindowStyleMaskMiniaturizable    \
431                                | NSWindowStyleMaskClosable)
432 #define FRAME_UNDECORATED_FLAGS NSWindowStyleMaskBorderless
434 /* TODO: get rid of need for these forward declarations */
435 static void ns_condemn_scroll_bars (struct frame *f);
436 static void ns_judge_scroll_bars (struct frame *f);
439 /* ==========================================================================
441     Utilities
443    ========================================================================== */
445 void
446 ns_set_represented_filename (NSString *fstr, struct frame *f)
448   represented_filename = [fstr retain];
449   represented_frame = f;
452 void
453 ns_init_events (struct input_event *ev)
455   EVENT_INIT (*ev);
456   emacs_event = ev;
459 void
460 ns_finish_events (void)
462   emacs_event = NULL;
465 static void
466 hold_event (struct input_event *event)
468   if (hold_event_q.nr == hold_event_q.cap)
469     {
470       if (hold_event_q.cap == 0) hold_event_q.cap = 10;
471       else hold_event_q.cap *= 2;
472       hold_event_q.q =
473         xrealloc (hold_event_q.q, hold_event_q.cap * sizeof *hold_event_q.q);
474     }
476   hold_event_q.q[hold_event_q.nr++] = *event;
477   /* Make sure ns_read_socket is called, i.e. we have input.  */
478   raise (SIGIO);
479   send_appdefined = YES;
482 static Lisp_Object
483 append2 (Lisp_Object list, Lisp_Object item)
484 /* --------------------------------------------------------------------------
485    Utility to append to a list
486    -------------------------------------------------------------------------- */
488   return CALLN (Fnconc, list, list1 (item));
492 const char *
493 ns_etc_directory (void)
494 /* If running as a self-contained app bundle, return as a string the
495    filename of the etc directory, if present; else nil.  */
497   NSBundle *bundle = [NSBundle mainBundle];
498   NSString *resourceDir = [bundle resourcePath];
499   NSString *resourcePath;
500   NSFileManager *fileManager = [NSFileManager defaultManager];
501   BOOL isDir;
503   resourcePath = [resourceDir stringByAppendingPathComponent: @"etc"];
504   if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
505     {
506       if (isDir) return [resourcePath UTF8String];
507     }
508   return NULL;
512 const char *
513 ns_exec_path (void)
514 /* If running as a self-contained app bundle, return as a path string
515    the filenames of the libexec and bin directories, ie libexec:bin.
516    Otherwise, return nil.
517    Normally, Emacs does not add its own bin/ directory to the PATH.
518    However, a self-contained NS build has a different layout, with
519    bin/ and libexec/ subdirectories in the directory that contains
520    Emacs.app itself.
521    We put libexec first, because init_callproc_1 uses the first
522    element to initialize exec-directory.  An alternative would be
523    for init_callproc to check for invocation-directory/libexec.
526   NSBundle *bundle = [NSBundle mainBundle];
527   NSString *resourceDir = [bundle resourcePath];
528   NSString *binDir = [bundle bundlePath];
529   NSString *resourcePath, *resourcePaths;
530   NSRange range;
531   NSString *pathSeparator = [NSString stringWithFormat: @"%c", SEPCHAR];
532   NSFileManager *fileManager = [NSFileManager defaultManager];
533   NSArray *paths;
534   NSEnumerator *pathEnum;
535   BOOL isDir;
537   range = [resourceDir rangeOfString: @"Contents"];
538   if (range.location != NSNotFound)
539     {
540       binDir = [binDir stringByAppendingPathComponent: @"Contents"];
541 #ifdef NS_IMPL_COCOA
542       binDir = [binDir stringByAppendingPathComponent: @"MacOS"];
543 #endif
544     }
546   paths = [binDir stringsByAppendingPaths:
547                 [NSArray arrayWithObjects: @"libexec", @"bin", nil]];
548   pathEnum = [paths objectEnumerator];
549   resourcePaths = @"";
551   while ((resourcePath = [pathEnum nextObject]))
552     {
553       if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
554         if (isDir)
555           {
556             if ([resourcePaths length] > 0)
557               resourcePaths
558                 = [resourcePaths stringByAppendingString: pathSeparator];
559             resourcePaths
560               = [resourcePaths stringByAppendingString: resourcePath];
561           }
562     }
563   if ([resourcePaths length] > 0) return [resourcePaths UTF8String];
565   return NULL;
569 const char *
570 ns_load_path (void)
571 /* If running as a self-contained app bundle, return as a path string
572    the filenames of the site-lisp and lisp directories.
573    Ie, site-lisp:lisp.  Otherwise, return nil.  */
575   NSBundle *bundle = [NSBundle mainBundle];
576   NSString *resourceDir = [bundle resourcePath];
577   NSString *resourcePath, *resourcePaths;
578   NSString *pathSeparator = [NSString stringWithFormat: @"%c", SEPCHAR];
579   NSFileManager *fileManager = [NSFileManager defaultManager];
580   BOOL isDir;
581   NSArray *paths = [resourceDir stringsByAppendingPaths:
582                               [NSArray arrayWithObjects:
583                                          @"site-lisp", @"lisp", nil]];
584   NSEnumerator *pathEnum = [paths objectEnumerator];
585   resourcePaths = @"";
587   /* Hack to skip site-lisp.  */
588   if (no_site_lisp) resourcePath = [pathEnum nextObject];
590   while ((resourcePath = [pathEnum nextObject]))
591     {
592       if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
593         if (isDir)
594           {
595             if ([resourcePaths length] > 0)
596               resourcePaths
597                 = [resourcePaths stringByAppendingString: pathSeparator];
598             resourcePaths
599               = [resourcePaths stringByAppendingString: resourcePath];
600           }
601     }
602   if ([resourcePaths length] > 0) return [resourcePaths UTF8String];
604   return NULL;
608 void
609 ns_init_locale (void)
610 /* macOS doesn't set any environment variables for the locale when run
611    from the GUI. Get the locale from the OS and set LANG. */
613   NSLocale *locale = [NSLocale currentLocale];
615   NSTRACE ("ns_init_locale");
617   @try
618     {
619       /* It seems macOS should probably use UTF-8 everywhere.
620          'localeIdentifier' does not specify the encoding, and I can't
621          find any way to get the OS to tell us which encoding to use,
622          so hard-code '.UTF-8'. */
623       NSString *localeID = [NSString stringWithFormat:@"%@.UTF-8",
624                                      [locale localeIdentifier]];
626       /* Set LANG to locale, but not if LANG is already set. */
627       setenv("LANG", [localeID UTF8String], 0);
628     }
629   @catch (NSException *e)
630     {
631       NSLog (@"Locale detection failed: %@: %@", [e name], [e reason]);
632     }
636 void
637 ns_release_object (void *obj)
638 /* --------------------------------------------------------------------------
639     Release an object (callable from C)
640    -------------------------------------------------------------------------- */
642     [(id)obj release];
646 void
647 ns_retain_object (void *obj)
648 /* --------------------------------------------------------------------------
649     Retain an object (callable from C)
650    -------------------------------------------------------------------------- */
652     [(id)obj retain];
656 void *
657 ns_alloc_autorelease_pool (void)
658 /* --------------------------------------------------------------------------
659      Allocate a pool for temporary objects (callable from C)
660    -------------------------------------------------------------------------- */
662   return [[NSAutoreleasePool alloc] init];
666 void
667 ns_release_autorelease_pool (void *pool)
668 /* --------------------------------------------------------------------------
669      Free a pool and temporary objects it refers to (callable from C)
670    -------------------------------------------------------------------------- */
672   ns_release_object (pool);
676 static BOOL
677 ns_menu_bar_should_be_hidden (void)
678 /* True, if the menu bar should be hidden.  */
680   return !NILP (ns_auto_hide_menu_bar)
681     && [NSApp respondsToSelector:@selector(setPresentationOptions:)];
685 struct EmacsMargins
687   CGFloat top;
688   CGFloat bottom;
689   CGFloat left;
690   CGFloat right;
694 static struct EmacsMargins
695 ns_screen_margins (NSScreen *screen)
696 /* The parts of SCREEN used by the operating system.  */
698   NSTRACE ("ns_screen_margins");
700   struct EmacsMargins margins;
702   NSRect screenFrame = [screen frame];
703   NSRect screenVisibleFrame = [screen visibleFrame];
705   /* Sometimes, visibleFrame isn't up-to-date with respect to a hidden
706      menu bar, check this explicitly.  */
707   if (ns_menu_bar_should_be_hidden())
708     {
709       margins.top = 0;
710     }
711   else
712     {
713       CGFloat frameTop = screenFrame.origin.y + screenFrame.size.height;
714       CGFloat visibleFrameTop = (screenVisibleFrame.origin.y
715                                  + screenVisibleFrame.size.height);
717       margins.top = frameTop - visibleFrameTop;
718     }
720   {
721     CGFloat frameRight = screenFrame.origin.x + screenFrame.size.width;
722     CGFloat visibleFrameRight = (screenVisibleFrame.origin.x
723                                  + screenVisibleFrame.size.width);
724     margins.right = frameRight - visibleFrameRight;
725   }
727   margins.bottom = screenVisibleFrame.origin.y - screenFrame.origin.y;
728   margins.left   = screenVisibleFrame.origin.x - screenFrame.origin.x;
730   NSTRACE_MSG ("left:%g right:%g top:%g bottom:%g",
731                margins.left,
732                margins.right,
733                margins.top,
734                margins.bottom);
736   return margins;
740 /* A screen margin between 1 and DOCK_IGNORE_LIMIT (inclusive) is
741    assumed to contain a hidden dock.  macOS currently use 4 pixels for
742    this, however, to be future compatible, a larger value is used.  */
743 #define DOCK_IGNORE_LIMIT 6
745 static struct EmacsMargins
746 ns_screen_margins_ignoring_hidden_dock (NSScreen *screen)
747 /* The parts of SCREEN used by the operating system, excluding the parts
748 reserved for an hidden dock.  */
750   NSTRACE ("ns_screen_margins_ignoring_hidden_dock");
752   struct EmacsMargins margins = ns_screen_margins(screen);
754   /* macOS (currently) reserved 4 pixels along the edge where a hidden
755      dock is located.  Unfortunately, it's not possible to find the
756      location and information about if the dock is hidden.  Instead,
757      it is assumed that if the margin of an edge is less than
758      DOCK_IGNORE_LIMIT, it contains a hidden dock.  */
759   if (margins.left <= DOCK_IGNORE_LIMIT)
760     {
761       margins.left = 0;
762     }
763   if (margins.right <= DOCK_IGNORE_LIMIT)
764     {
765       margins.right = 0;
766     }
767   if (margins.top <= DOCK_IGNORE_LIMIT)
768     {
769       margins.top = 0;
770     }
771   /* Note: This doesn't occur in current versions of macOS, but
772      included for completeness and future compatibility.  */
773   if (margins.bottom <= DOCK_IGNORE_LIMIT)
774     {
775       margins.bottom = 0;
776     }
778   NSTRACE_MSG ("left:%g right:%g top:%g bottom:%g",
779                margins.left,
780                margins.right,
781                margins.top,
782                margins.bottom);
784   return margins;
788 static CGFloat
789 ns_menu_bar_height (NSScreen *screen)
790 /* The height of the menu bar, if visible.
792    Note: Don't use this when fullscreen is enabled -- the screen
793    sometimes includes, sometimes excludes the menu bar area.  */
795   struct EmacsMargins margins = ns_screen_margins(screen);
797   CGFloat res = margins.top;
799   NSTRACE ("ns_menu_bar_height " NSTRACE_FMT_RETURN " %.0f", res);
801   return res;
805 /* ==========================================================================
807     Focus (clipping) and screen update
809    ========================================================================== */
812 // Window constraining
813 // -------------------
815 // To ensure that the windows are not placed under the menu bar, they
816 // are typically moved by the call-back constrainFrameRect. However,
817 // by overriding it, it's possible to inhibit this, leaving the window
818 // in it's original position.
820 // It's possible to hide the menu bar. However, technically, it's only
821 // possible to hide it when the application is active. To ensure that
822 // this work properly, the menu bar and window constraining are
823 // deferred until the application becomes active.
825 // Even though it's not possible to manually move a window above the
826 // top of the screen, it is allowed if it's done programmatically,
827 // when the menu is hidden. This allows the editable area to cover the
828 // full screen height.
830 // Test cases
831 // ----------
833 // Use the following extra files:
835 //    init.el:
836 //       ;; Hide menu and place frame slightly above the top of the screen.
837 //       (setq ns-auto-hide-menu-bar t)
838 //       (set-frame-position (selected-frame) 0 -20)
840 // Test 1:
842 //    emacs -Q -l init.el
844 //    Result: No menu bar, and the title bar should be above the screen.
846 // Test 2:
848 //    emacs -Q
850 //    Result: Menu bar visible, frame placed immediately below the menu.
853 static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
855   NSTRACE ("constrain_frame_rect(" NSTRACE_FMT_RECT ")",
856              NSTRACE_ARG_RECT (frameRect));
858   // --------------------
859   // Collect information about the screen the frame is covering.
860   //
862   NSArray *screens = [NSScreen screens];
863   NSUInteger nr_screens = [screens count];
865   int i;
867   // The height of the menu bar, if present in any screen the frame is
868   // displayed in.
869   int menu_bar_height = 0;
871   // A rectangle covering all the screen the frame is displayed in.
872   NSRect multiscreenRect = NSMakeRect(0, 0, 0, 0);
873   for (i = 0; i < nr_screens; ++i )
874     {
875       NSScreen *s = [screens objectAtIndex: i];
876       NSRect scrRect = [s frame];
878       NSTRACE_MSG ("Screen %d: " NSTRACE_FMT_RECT,
879                    i, NSTRACE_ARG_RECT (scrRect));
881       if (NSIntersectionRect (frameRect, scrRect).size.height != 0)
882         {
883           multiscreenRect = NSUnionRect (multiscreenRect, scrRect);
885           if (!isFullscreen)
886             {
887               CGFloat screen_menu_bar_height = ns_menu_bar_height (s);
888               menu_bar_height = max(menu_bar_height, screen_menu_bar_height);
889             }
890         }
891     }
893   NSTRACE_RECT ("multiscreenRect", multiscreenRect);
895   NSTRACE_MSG ("menu_bar_height: %d", menu_bar_height);
897   if (multiscreenRect.size.width == 0
898       || multiscreenRect.size.height == 0)
899     {
900       // Failed to find any monitor, give up.
901       NSTRACE_MSG ("multiscreenRect empty");
902       NSTRACE_RETURN_RECT (frameRect);
903       return frameRect;
904     }
907   // --------------------
908   // Find a suitable placement.
909   //
911   if (ns_menu_bar_should_be_hidden())
912     {
913       // When the menu bar is hidden, the user may place part of the
914       // frame above the top of the screen, for example to hide the
915       // title bar.
916       //
917       // Hence, keep the original position.
918     }
919   else
920     {
921       // Ensure that the frame is below the menu bar, or below the top
922       // of the screen.
923       //
924       // This assume that the menu bar is placed at the top in the
925       // rectangle that covers the monitors.  (It doesn't have to be,
926       // but if it's not it's hard to do anything useful.)
927       CGFloat topOfWorkArea = (multiscreenRect.origin.y
928                                + multiscreenRect.size.height
929                                - menu_bar_height);
931       CGFloat topOfFrame = frameRect.origin.y + frameRect.size.height;
932       if (topOfFrame > topOfWorkArea)
933         {
934           frameRect.origin.y -= topOfFrame - topOfWorkArea;
935           NSTRACE_RECT ("After placement adjust", frameRect);
936         }
937     }
939   // Include the following section to restrict frame to the screens.
940   // (If so, update it to allow the frame to stretch down below the
941   // screen.)
942 #if 0
943   // --------------------
944   // Ensure frame doesn't stretch below the screens.
945   //
947   CGFloat diff = multiscreenRect.origin.y - frameRect.origin.y;
949   if (diff > 0)
950     {
951       frameRect.origin.y = multiscreenRect.origin.y;
952       frameRect.size.height -= diff;
953     }
954 #endif
956   NSTRACE_RETURN_RECT (frameRect);
957   return frameRect;
961 static void
962 ns_constrain_all_frames (void)
963 /* --------------------------------------------------------------------------
964      Ensure that the menu bar doesn't cover any frames.
965    -------------------------------------------------------------------------- */
967   Lisp_Object tail, frame;
969   NSTRACE ("ns_constrain_all_frames");
971   block_input ();
973   FOR_EACH_FRAME (tail, frame)
974     {
975       struct frame *f = XFRAME (frame);
976       if (FRAME_NS_P (f))
977         {
978           EmacsView *view = FRAME_NS_VIEW (f);
980           if (![view isFullscreen])
981             {
982               [[view window]
983                 setFrame:constrain_frame_rect([[view window] frame], false)
984                  display:NO];
985             }
986         }
987     }
989   unblock_input ();
993 static void
994 ns_update_auto_hide_menu_bar (void)
995 /* --------------------------------------------------------------------------
996      Show or hide the menu bar, based on user setting.
997    -------------------------------------------------------------------------- */
999 #ifdef NS_IMPL_COCOA
1000   NSTRACE ("ns_update_auto_hide_menu_bar");
1002   block_input ();
1004   if (NSApp != nil && [NSApp isActive])
1005     {
1006       // Note, "setPresentationOptions" triggers an error unless the
1007       // application is active.
1008       BOOL menu_bar_should_be_hidden = ns_menu_bar_should_be_hidden ();
1010       if (menu_bar_should_be_hidden != ns_menu_bar_is_hidden)
1011         {
1012           NSApplicationPresentationOptions options
1013             = NSApplicationPresentationDefault;
1015           if (menu_bar_should_be_hidden)
1016             options |= NSApplicationPresentationAutoHideMenuBar
1017               | NSApplicationPresentationAutoHideDock;
1019           [NSApp setPresentationOptions: options];
1021           ns_menu_bar_is_hidden = menu_bar_should_be_hidden;
1023           if (!ns_menu_bar_is_hidden)
1024             {
1025               ns_constrain_all_frames ();
1026             }
1027         }
1028     }
1030   unblock_input ();
1031 #endif
1035 static void
1036 ns_update_begin (struct frame *f)
1037 /* --------------------------------------------------------------------------
1038    Prepare for a grouped sequence of drawing calls
1039    external (RIF) call; whole frame, called before update_window_begin
1040    -------------------------------------------------------------------------- */
1042   EmacsView *view = FRAME_NS_VIEW (f);
1043   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_begin");
1045   ns_update_auto_hide_menu_bar ();
1047 #ifdef NS_IMPL_COCOA
1048   if ([view isFullscreen] && [view fsIsNative])
1049   {
1050     // Fix reappearing tool bar in fullscreen for Mac OS X 10.7
1051     BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (f) ? YES : NO;
1052     NSToolbar *toolbar = [FRAME_NS_VIEW (f) toolbar];
1053     if (! tbar_visible != ! [toolbar isVisible])
1054       [toolbar setVisible: tbar_visible];
1055   }
1056 #endif
1058   ns_updating_frame = f;
1059   [view lockFocus];
1061   /* drawRect may have been called for say the minibuffer, and then clip path
1062      is for the minibuffer.  But the display engine may draw more because
1063      we have set the frame as garbaged.  So reset clip path to the whole
1064      view.  */
1065 #ifdef NS_IMPL_COCOA
1066   {
1067     NSBezierPath *bp;
1068     NSRect r = [view frame];
1069     NSRect cr = [[view window] frame];
1070     /* If a large frame size is set, r may be larger than the window frame
1071        before constrained.  In that case don't change the clip path, as we
1072        will clear in to the tool bar and title bar.  */
1073     if (r.size.height
1074         + FRAME_NS_TITLEBAR_HEIGHT (f)
1075         + FRAME_TOOLBAR_HEIGHT (f) <= cr.size.height)
1076       {
1077         bp = [[NSBezierPath bezierPathWithRect: r] retain];
1078         [bp setClip];
1079         [bp release];
1080       }
1081   }
1082 #endif
1084 #ifdef NS_IMPL_GNUSTEP
1085   uRect = NSMakeRect (0, 0, 0, 0);
1086 #endif
1090 static void
1091 ns_update_window_begin (struct window *w)
1092 /* --------------------------------------------------------------------------
1093    Prepare for a grouped sequence of drawing calls
1094    external (RIF) call; for one window, called after update_begin
1095    -------------------------------------------------------------------------- */
1097   struct frame *f = XFRAME (WINDOW_FRAME (w));
1098   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
1100   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_window_begin");
1101   w->output_cursor = w->cursor;
1103   block_input ();
1105   if (f == hlinfo->mouse_face_mouse_frame)
1106     {
1107       /* Don't do highlighting for mouse motion during the update.  */
1108       hlinfo->mouse_face_defer = 1;
1110         /* If the frame needs to be redrawn,
1111            simply forget about any prior mouse highlighting.  */
1112       if (FRAME_GARBAGED_P (f))
1113         hlinfo->mouse_face_window = Qnil;
1115       /* (further code for mouse faces ifdef'd out in other terms elided) */
1116     }
1118   unblock_input ();
1122 static void
1123 ns_update_window_end (struct window *w, bool cursor_on_p,
1124                       bool mouse_face_overwritten_p)
1125 /* --------------------------------------------------------------------------
1126    Finished a grouped sequence of drawing calls
1127    external (RIF) call; for one window called before update_end
1128    -------------------------------------------------------------------------- */
1130   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_window_end");
1132   /* note: this fn is nearly identical in all terms */
1133   if (!w->pseudo_window_p)
1134     {
1135       block_input ();
1137       if (cursor_on_p)
1138         display_and_set_cursor (w, 1,
1139                                 w->output_cursor.hpos, w->output_cursor.vpos,
1140                                 w->output_cursor.x, w->output_cursor.y);
1142       if (draw_window_fringes (w, 1))
1143         {
1144           if (WINDOW_RIGHT_DIVIDER_WIDTH (w))
1145             x_draw_right_divider (w);
1146           else
1147             x_draw_vertical_border (w);
1148         }
1150       unblock_input ();
1151     }
1153   /* If a row with mouse-face was overwritten, arrange for
1154      frame_up_to_date to redisplay the mouse highlight.  */
1155   if (mouse_face_overwritten_p)
1156     reset_mouse_highlight (MOUSE_HL_INFO (XFRAME (w->frame)));
1160 static void
1161 ns_update_end (struct frame *f)
1162 /* --------------------------------------------------------------------------
1163    Finished a grouped sequence of drawing calls
1164    external (RIF) call; for whole frame, called after update_window_end
1165    -------------------------------------------------------------------------- */
1167   EmacsView *view = FRAME_NS_VIEW (f);
1169   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_end");
1171 /*   if (f == MOUSE_HL_INFO (f)->mouse_face_mouse_frame) */
1172   MOUSE_HL_INFO (f)->mouse_face_defer = 0;
1174   block_input ();
1176   [view unlockFocus];
1177   [[view window] flushWindow];
1179   unblock_input ();
1180   ns_updating_frame = NULL;
1183 static void
1184 ns_focus (struct frame *f, NSRect *r, int n)
1185 /* --------------------------------------------------------------------------
1186    Internal: Focus on given frame.  During small local updates this is used to
1187      draw, however during large updates, ns_update_begin and ns_update_end are
1188      called to wrap the whole thing, in which case these calls are stubbed out.
1189      Except, on GNUstep, we accumulate the rectangle being drawn into, because
1190      the back end won't do this automatically, and will just end up flushing
1191      the entire window.
1192    -------------------------------------------------------------------------- */
1194   NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_focus");
1195   if (r != NULL)
1196     {
1197       NSTRACE_RECT ("r", *r);
1198     }
1200   if (f != ns_updating_frame)
1201     {
1202       NSView *view = FRAME_NS_VIEW (f);
1203       if (view != focus_view)
1204         {
1205           if (focus_view != NULL)
1206             {
1207               [focus_view unlockFocus];
1208               [[focus_view window] flushWindow];
1209 /*debug_lock--; */
1210             }
1212           if (view)
1213             [view lockFocus];
1214           focus_view = view;
1215 /*if (view) debug_lock++; */
1216         }
1217     }
1219   /* clipping */
1220   if (r)
1221     {
1222       [[NSGraphicsContext currentContext] saveGraphicsState];
1223       if (n == 2)
1224         NSRectClipList (r, 2);
1225       else
1226         NSRectClip (*r);
1227       gsaved = YES;
1228     }
1232 static void
1233 ns_unfocus (struct frame *f)
1234 /* --------------------------------------------------------------------------
1235      Internal: Remove focus on given frame
1236    -------------------------------------------------------------------------- */
1238   NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_unfocus");
1240   if (gsaved)
1241     {
1242       [[NSGraphicsContext currentContext] restoreGraphicsState];
1243       gsaved = NO;
1244     }
1246   if (f != ns_updating_frame)
1247     {
1248       if (focus_view != NULL)
1249         {
1250           [focus_view unlockFocus];
1251           [[focus_view window] flushWindow];
1252           focus_view = NULL;
1253 /*debug_lock--; */
1254         }
1255     }
1259 static void
1260 ns_clip_to_row (struct window *w, struct glyph_row *row,
1261                 enum glyph_row_area area, BOOL gc)
1262 /* --------------------------------------------------------------------------
1263      Internal (but parallels other terms): Focus drawing on given row
1264    -------------------------------------------------------------------------- */
1266   struct frame *f = XFRAME (WINDOW_FRAME (w));
1267   NSRect clip_rect;
1268   int window_x, window_y, window_width;
1270   window_box (w, area, &window_x, &window_y, &window_width, 0);
1272   clip_rect.origin.x = window_x;
1273   clip_rect.origin.y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, row->y));
1274   clip_rect.origin.y = max (clip_rect.origin.y, window_y);
1275   clip_rect.size.width = window_width;
1276   clip_rect.size.height = row->visible_height;
1278   ns_focus (f, &clip_rect, 1);
1282 /* ==========================================================================
1284     Visible bell and beep.
1286    ========================================================================== */
1289 // This bell implementation shows the visual bell image asynchronously
1290 // from the rest of Emacs. This is done by adding a NSView to the
1291 // superview of the Emacs window and removing it using a timer.
1293 // Unfortunately, some Emacs operations, like scrolling, is done using
1294 // low-level primitives that copy the content of the window, including
1295 // the bell image. To some extent, this is handled by removing the
1296 // image prior to scrolling and marking that the window is in need for
1297 // redisplay.
1299 // To test this code, make sure that there is no artifacts of the bell
1300 // image in the following situations. Use a non-empty buffer (like the
1301 // tutorial) to ensure that a scroll is performed:
1303 // * Single-window: C-g C-v
1305 // * Side-by-windows: C-x 3 C-g C-v
1307 // * Windows above each other: C-x 2 C-g C-v
1309 @interface EmacsBell : NSImageView
1311   // Number of currently active bell:s.
1312   unsigned int nestCount;
1313   NSView * mView;
1314   bool isAttached;
1316 - (void)show:(NSView *)view;
1317 - (void)hide;
1318 - (void)remove;
1319 @end
1321 @implementation EmacsBell
1323 - (id)init
1325   NSTRACE ("[EmacsBell init]");
1326   if ((self = [super init]))
1327     {
1328       nestCount = 0;
1329       isAttached = false;
1330 #ifdef NS_IMPL_GNUSTEP
1331       // GNUstep doesn't provide named images.  This was reported in
1332       // 2011, see https://savannah.gnu.org/bugs/?33396
1333       //
1334       // As a drop in replacement, a semitransparent gray square is used.
1335       self.image = [[NSImage alloc] initWithSize:NSMakeSize(32 * 5, 32 * 5)];
1336       [self.image lockFocus];
1337       [[NSColor colorForEmacsRed:0.5 green:0.5 blue:0.5 alpha:0.5] set];
1338       NSRectFill(NSMakeRect(0, 0, 32, 32));
1339       [self.image unlockFocus];
1340 #else
1341       self.image = [NSImage imageNamed:NSImageNameCaution];
1342       [self.image setSize:NSMakeSize(self.image.size.width * 5,
1343                                      self.image.size.height * 5)];
1344 #endif
1345     }
1346   return self;
1349 - (void)show:(NSView *)view
1351   NSTRACE ("[EmacsBell show:]");
1352   NSTRACE_MSG ("nestCount: %u", nestCount);
1354   // Show the image, unless it's already shown.
1355   if (nestCount == 0)
1356     {
1357       NSRect rect = [view bounds];
1358       NSPoint pos;
1359       pos.x = rect.origin.x + (rect.size.width  - self.image.size.width )/2;
1360       pos.y = rect.origin.y + (rect.size.height - self.image.size.height)/2;
1362       [self setFrameOrigin:pos];
1363       [self setFrameSize:self.image.size];
1365       isAttached = true;
1366       mView = view;
1367       [[[view window] contentView] addSubview:self
1368                                    positioned:NSWindowAbove
1369                                    relativeTo:nil];
1370     }
1372   ++nestCount;
1374   [self performSelector:@selector(hide) withObject:self afterDelay:0.5];
1378 - (void)hide
1380   // Note: Trace output from this method isn't shown, reason unknown.
1381   // NSTRACE ("[EmacsBell hide]");
1383   if (nestCount > 0)
1384     --nestCount;
1386   // Remove the image once the last bell became inactive.
1387   if (nestCount == 0)
1388     {
1389       [self remove];
1390     }
1394 -(void)remove
1396   NSTRACE ("[EmacsBell remove]");
1397   if (isAttached)
1398     {
1399       NSTRACE_MSG ("removeFromSuperview");
1400       [self removeFromSuperview];
1401       mView.needsDisplay = YES;
1402       isAttached = false;
1403     }
1406 @end
1409 static EmacsBell * bell_view = nil;
1411 static void
1412 ns_ring_bell (struct frame *f)
1413 /* --------------------------------------------------------------------------
1414      "Beep" routine
1415    -------------------------------------------------------------------------- */
1417   NSTRACE ("ns_ring_bell");
1418   if (visible_bell)
1419     {
1420       struct frame *frame = SELECTED_FRAME ();
1421       NSView *view;
1423       if (bell_view == nil)
1424         {
1425           bell_view = [[EmacsBell alloc] init];
1426           [bell_view retain];
1427         }
1429       block_input ();
1431       view = FRAME_NS_VIEW (frame);
1432       if (view != nil)
1433         {
1434           [bell_view show:view];
1435         }
1437       unblock_input ();
1438     }
1439   else
1440     {
1441       NSBeep ();
1442     }
1446 static void
1447 hide_bell (void)
1448 /* --------------------------------------------------------------------------
1449      Ensure the bell is hidden.
1450    -------------------------------------------------------------------------- */
1452   NSTRACE ("hide_bell");
1454   if (bell_view != nil)
1455     {
1456       [bell_view remove];
1457     }
1461 /* ==========================================================================
1463     Frame / window manager related functions
1465    ========================================================================== */
1468 static void
1469 ns_raise_frame (struct frame *f, BOOL make_key)
1470 /* --------------------------------------------------------------------------
1471      Bring window to foreground and if make_key is YES, give it focus.
1472    -------------------------------------------------------------------------- */
1474   NSView *view;
1476   check_window_system (f);
1477   view = FRAME_NS_VIEW (f);
1478   block_input ();
1479   if (FRAME_VISIBLE_P (f))
1480     {
1481       if (make_key)
1482         [[view window] makeKeyAndOrderFront: NSApp];
1483       else
1484         [[view window] orderFront: NSApp];
1485     }
1486   unblock_input ();
1490 static void
1491 ns_lower_frame (struct frame *f)
1492 /* --------------------------------------------------------------------------
1493      Send window to back
1494    -------------------------------------------------------------------------- */
1496   NSView *view;
1498   check_window_system (f);
1499   view = FRAME_NS_VIEW (f);
1500   block_input ();
1501   [[view window] orderBack: NSApp];
1502   unblock_input ();
1506 static void
1507 ns_frame_raise_lower (struct frame *f, bool raise)
1508 /* --------------------------------------------------------------------------
1509      External (hook)
1510    -------------------------------------------------------------------------- */
1512   NSTRACE ("ns_frame_raise_lower");
1514   if (raise)
1515     ns_raise_frame (f, YES);
1516   else
1517     ns_lower_frame (f);
1521 static void
1522 ns_frame_rehighlight (struct frame *frame)
1523 /* --------------------------------------------------------------------------
1524      External (hook): called on things like window switching within frame
1525    -------------------------------------------------------------------------- */
1527   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
1528   struct frame *old_highlight = dpyinfo->x_highlight_frame;
1530   NSTRACE ("ns_frame_rehighlight");
1531   if (dpyinfo->x_focus_frame)
1532     {
1533       dpyinfo->x_highlight_frame
1534         = (FRAMEP (FRAME_FOCUS_FRAME (dpyinfo->x_focus_frame))
1535            ? XFRAME (FRAME_FOCUS_FRAME (dpyinfo->x_focus_frame))
1536            : dpyinfo->x_focus_frame);
1537       if (!FRAME_LIVE_P (dpyinfo->x_highlight_frame))
1538         {
1539           fset_focus_frame (dpyinfo->x_focus_frame, Qnil);
1540           dpyinfo->x_highlight_frame = dpyinfo->x_focus_frame;
1541         }
1542     }
1543   else
1544       dpyinfo->x_highlight_frame = 0;
1546   if (dpyinfo->x_highlight_frame &&
1547          dpyinfo->x_highlight_frame != old_highlight)
1548     {
1549       if (old_highlight)
1550         {
1551           x_update_cursor (old_highlight, 1);
1552           x_set_frame_alpha (old_highlight);
1553         }
1554       if (dpyinfo->x_highlight_frame)
1555         {
1556           x_update_cursor (dpyinfo->x_highlight_frame, 1);
1557           x_set_frame_alpha (dpyinfo->x_highlight_frame);
1558         }
1559     }
1563 void
1564 x_make_frame_visible (struct frame *f)
1565 /* --------------------------------------------------------------------------
1566      External: Show the window (X11 semantics)
1567    -------------------------------------------------------------------------- */
1569   NSTRACE ("x_make_frame_visible");
1570   /* XXX: at some points in past this was not needed, as the only place that
1571      called this (frame.c:Fraise_frame ()) also called raise_lower;
1572      if this ends up the case again, comment this out again. */
1573   if (!FRAME_VISIBLE_P (f))
1574     {
1575       EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
1576       NSWindow *window = [view window];
1578       SET_FRAME_VISIBLE (f, 1);
1579       ns_raise_frame (f, ! FRAME_NO_FOCUS_ON_MAP (f));
1581       /* Making a new frame from a fullscreen frame will make the new frame
1582          fullscreen also.  So skip handleFS as this will print an error.  */
1583       if ([view fsIsNative] && f->want_fullscreen == FULLSCREEN_BOTH
1584           && [view isFullscreen])
1585         return;
1587       if (f->want_fullscreen != FULLSCREEN_NONE)
1588         {
1589           block_input ();
1590           [view handleFS];
1591           unblock_input ();
1592         }
1594       /* Making a frame invisible seems to break the parent->child
1595          relationship, so reinstate it. */
1596       if ([window parentWindow] == nil && FRAME_PARENT_FRAME (f) != NULL)
1597         {
1598           NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
1600           block_input ();
1601           [parent addChildWindow: window
1602                          ordered: NSWindowAbove];
1603           unblock_input ();
1605           /* If the parent frame moved while the child frame was
1606              invisible, the child frame's position won't have been
1607              updated.  Make sure it's in the right place now. */
1608           x_set_offset(f, f->left_pos, f->top_pos, 0);
1609         }
1610     }
1614 void
1615 x_make_frame_invisible (struct frame *f)
1616 /* --------------------------------------------------------------------------
1617      External: Hide the window (X11 semantics)
1618    -------------------------------------------------------------------------- */
1620   NSView *view;
1621   NSTRACE ("x_make_frame_invisible");
1622   check_window_system (f);
1623   view = FRAME_NS_VIEW (f);
1624   [[view window] orderOut: NSApp];
1625   SET_FRAME_VISIBLE (f, 0);
1626   SET_FRAME_ICONIFIED (f, 0);
1630 void
1631 x_iconify_frame (struct frame *f)
1632 /* --------------------------------------------------------------------------
1633      External: Iconify window
1634    -------------------------------------------------------------------------- */
1636   NSView *view;
1637   struct ns_display_info *dpyinfo;
1639   NSTRACE ("x_iconify_frame");
1640   check_window_system (f);
1641   view = FRAME_NS_VIEW (f);
1642   dpyinfo = FRAME_DISPLAY_INFO (f);
1644   if (dpyinfo->x_highlight_frame == f)
1645     dpyinfo->x_highlight_frame = 0;
1647   if ([[view window] windowNumber] <= 0)
1648     {
1649       /* the window is still deferred.  Make it very small, bring it
1650          on screen and order it out. */
1651       NSRect s = { { 100, 100}, {0, 0} };
1652       NSRect t;
1653       t = [[view window] frame];
1654       [[view window] setFrame: s display: NO];
1655       [[view window] orderBack: NSApp];
1656       [[view window] orderOut: NSApp];
1657       [[view window] setFrame: t display: NO];
1658     }
1660   /* Processing input while Emacs is being minimized can cause a
1661      crash, so block it for the duration. */
1662   block_input();
1663   [[view window] miniaturize: NSApp];
1664   unblock_input();
1667 /* Free X resources of frame F.  */
1669 void
1670 x_free_frame_resources (struct frame *f)
1672   NSView *view;
1673   struct ns_display_info *dpyinfo;
1674   Mouse_HLInfo *hlinfo;
1676   NSTRACE ("x_free_frame_resources");
1677   check_window_system (f);
1678   view = FRAME_NS_VIEW (f);
1679   dpyinfo = FRAME_DISPLAY_INFO (f);
1680   hlinfo = MOUSE_HL_INFO (f);
1682   [(EmacsView *)view setWindowClosing: YES]; /* may not have been informed */
1684   block_input ();
1686   free_frame_menubar (f);
1687   free_frame_faces (f);
1689   if (f == dpyinfo->x_focus_frame)
1690     dpyinfo->x_focus_frame = 0;
1691   if (f == dpyinfo->x_highlight_frame)
1692     dpyinfo->x_highlight_frame = 0;
1693   if (f == hlinfo->mouse_face_mouse_frame)
1694     reset_mouse_highlight (hlinfo);
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 *screen = [[view window] screen];
1741   NSTRACE ("x_set_offset");
1743   block_input ();
1745   f->left_pos = xoff;
1746   f->top_pos = yoff;
1748   if (view != nil)
1749     {
1750       if (FRAME_PARENT_FRAME (f) == NULL && screen)
1751         {
1752           f->left_pos = f->size_hint_flags & XNegative
1753             ? [screen visibleFrame].size.width + f->left_pos - FRAME_PIXEL_WIDTH (f)
1754             : f->left_pos;
1755           /* We use visibleFrame here to take menu bar into account.
1756              Ideally we should also adjust left/top with visibleFrame.origin.  */
1758           f->top_pos = f->size_hint_flags & YNegative
1759             ? ([screen visibleFrame].size.height + f->top_pos
1760                - FRAME_PIXEL_HEIGHT (f) - FRAME_NS_TITLEBAR_HEIGHT (f)
1761                - FRAME_TOOLBAR_HEIGHT (f))
1762             : f->top_pos;
1763 #ifdef NS_IMPL_GNUSTEP
1764           if (f->left_pos < 100)
1765             f->left_pos = 100;  /* don't overlap menu */
1766 #endif
1767         }
1768       else if (FRAME_PARENT_FRAME (f) != NULL)
1769         {
1770           struct frame *parent = FRAME_PARENT_FRAME (f);
1772           /* On X negative values for child frames always result in
1773              positioning relative to the bottom right corner of the
1774              parent frame.  */
1775           if (f->left_pos < 0)
1776             f->left_pos = FRAME_PIXEL_WIDTH (parent) - FRAME_PIXEL_WIDTH (f) + f->left_pos;
1778           if (f->top_pos < 0)
1779             f->top_pos = FRAME_PIXEL_HEIGHT (parent) + FRAME_TOOLBAR_HEIGHT (parent)
1780               - FRAME_PIXEL_HEIGHT (f) + f->top_pos;
1781         }
1783       /* Constrain the setFrameTopLeftPoint so we don't move behind the
1784          menu bar.  */
1785       NSPoint pt = NSMakePoint (SCREENMAXBOUND (f->left_pos
1786                                                 + NS_PARENT_WINDOW_LEFT_POS (f)),
1787                                 SCREENMAXBOUND (NS_PARENT_WINDOW_TOP_POS (f)
1788                                                 - f->top_pos));
1789       NSTRACE_POINT ("setFrameTopLeftPoint", pt);
1790       [[view window] setFrameTopLeftPoint: pt];
1791       f->size_hint_flags &= ~(XNegative|YNegative);
1792     }
1794   unblock_input ();
1798 void
1799 x_set_window_size (struct frame *f,
1800                    bool change_gravity,
1801                    int width,
1802                    int height,
1803                    bool pixelwise)
1804 /* --------------------------------------------------------------------------
1805      Adjust window pixel size based on given character grid size
1806      Impl is a bit more complex than other terms, need to do some
1807      internal clipping.
1808    -------------------------------------------------------------------------- */
1810   EmacsView *view = FRAME_NS_VIEW (f);
1811   NSWindow *window = [view window];
1812   NSRect wr = [window frame];
1813   int pixelwidth, pixelheight;
1814   int orig_height = wr.size.height;
1816   NSTRACE ("x_set_window_size");
1818   if (view == nil)
1819     return;
1821   NSTRACE_RECT ("current", wr);
1822   NSTRACE_MSG ("Width:%d Height:%d Pixelwise:%d", width, height, pixelwise);
1823   NSTRACE_MSG ("Font %d x %d", FRAME_COLUMN_WIDTH (f), FRAME_LINE_HEIGHT (f));
1825   block_input ();
1827   if (pixelwise)
1828     {
1829       pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
1830       pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
1831     }
1832   else
1833     {
1834       pixelwidth =  FRAME_TEXT_COLS_TO_PIXEL_WIDTH   (f, width);
1835       pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, height);
1836     }
1838   wr.size.width = pixelwidth + f->border_width;
1839   wr.size.height = pixelheight;
1840   if (! [view isFullscreen])
1841     wr.size.height += FRAME_NS_TITLEBAR_HEIGHT (f)
1842       + FRAME_TOOLBAR_HEIGHT (f);
1844   /* Do not try to constrain to this screen.  We may have multiple
1845      screens, and want Emacs to span those.  Constraining to screen
1846      prevents that, and that is not nice to the user.  */
1847  if (f->output_data.ns->zooming)
1848    f->output_data.ns->zooming = 0;
1849  else
1850    wr.origin.y += orig_height - wr.size.height;
1852  frame_size_history_add
1853    (f, Qx_set_window_size_1, width, height,
1854     list5 (Fcons (make_number (pixelwidth), make_number (pixelheight)),
1855            Fcons (make_number (wr.size.width), make_number (wr.size.height)),
1856            make_number (f->border_width),
1857            make_number (FRAME_NS_TITLEBAR_HEIGHT (f)),
1858            make_number (FRAME_TOOLBAR_HEIGHT (f))));
1860   [window setFrame: wr display: YES];
1862   [view updateFrameSize: NO];
1863   unblock_input ();
1866 #ifdef NS_IMPL_COCOA
1867 void
1868 x_set_undecorated (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1869 /* --------------------------------------------------------------------------
1870      Set frame F's `undecorated' parameter.  If non-nil, F's window-system
1871      window is drawn without decorations, title, minimize/maximize boxes
1872      and external borders.  This usually means that the window cannot be
1873      dragged, resized, iconified, maximized or deleted with the mouse.  If
1874      nil, draw the frame with all the elements listed above unless these
1875      have been suspended via window manager settings.
1877      GNUStep cannot change an existing window's style.
1878    -------------------------------------------------------------------------- */
1880   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
1881   NSWindow *window = [view window];
1883   NSTRACE ("x_set_undecorated");
1885   if (!EQ (new_value, old_value))
1886     {
1887       block_input ();
1889       if (NILP (new_value))
1890         {
1891           FRAME_UNDECORATED (f) = false;
1892           [window setStyleMask: ((window.styleMask | FRAME_DECORATED_FLAGS)
1893                                   ^ FRAME_UNDECORATED_FLAGS)];
1895           [view createToolbar: f];
1896         }
1897       else
1898         {
1899           [window setToolbar: nil];
1900           /* Do I need to release the toolbar here? */
1902           FRAME_UNDECORATED (f) = true;
1903           [window setStyleMask: ((window.styleMask | FRAME_UNDECORATED_FLAGS)
1904                                  ^ FRAME_DECORATED_FLAGS)];
1905         }
1907       /* At this point it seems we don't have an active NSResponder,
1908          so some key presses (TAB) are swallowed by the system. */
1909       [window makeFirstResponder: view];
1911       [view updateFrameSize: NO];
1912       unblock_input ();
1913     }
1915 #endif /* NS_IMPL_COCOA */
1917 void
1918 x_set_parent_frame (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1919 /* --------------------------------------------------------------------------
1920      Set frame F's `parent-frame' parameter.  If non-nil, make F a child
1921      frame of the frame specified by that parameter.  Technically, this
1922      makes F's window-system window a child window of the parent frame's
1923      window-system window.  If nil, make F's window-system window a
1924      top-level window--a child of its display's root window.
1926      A child frame's `left' and `top' parameters specify positions
1927      relative to the top-left corner of its parent frame's native
1928      rectangle.  On macOS moving a parent frame moves all its child
1929      frames too, keeping their position relative to the parent
1930      unaltered.  When a parent frame is iconified or made invisible, its
1931      child frames are made invisible.  When a parent frame is deleted,
1932      its child frames are deleted too.
1934      Whether a child frame has a tool bar may be window-system or window
1935      manager dependent.  It's advisable to disable it via the frame
1936      parameter settings.
1938      Some window managers may not honor this parameter.
1939    -------------------------------------------------------------------------- */
1941   struct frame *p = NULL;
1942   NSWindow *parent, *child;
1944   NSTRACE ("x_set_parent_frame");
1946   if (!NILP (new_value)
1947       && (!FRAMEP (new_value)
1948           || !FRAME_LIVE_P (p = XFRAME (new_value))
1949           || !FRAME_NS_P (p)))
1950     {
1951       store_frame_param (f, Qparent_frame, old_value);
1952       error ("Invalid specification of `parent-frame'");
1953     }
1955   if (p != FRAME_PARENT_FRAME (f))
1956     {
1957       parent = [FRAME_NS_VIEW (p) window];
1958       child = [FRAME_NS_VIEW (f) window];
1960       block_input ();
1961       [parent addChildWindow: child
1962                      ordered: NSWindowAbove];
1963       unblock_input ();
1965       fset_parent_frame (f, new_value);
1966     }
1969 void
1970 x_set_no_focus_on_map (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1971 /* Set frame F's `no-focus-on-map' parameter which, if non-nil, means
1972  * that F's window-system window does not want to receive input focus
1973  * when it is mapped.  (A frame's window is mapped when the frame is
1974  * displayed for the first time and when the frame changes its state
1975  * from `iconified' or `invisible' to `visible'.)
1977  * Some window managers may not honor this parameter. */
1979   NSTRACE ("x_set_no_focus_on_map");
1981   if (!EQ (new_value, old_value))
1982     {
1983       FRAME_NO_FOCUS_ON_MAP (f) = !NILP (new_value);
1984     }
1987 void
1988 x_set_no_accept_focus (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
1989 /*  Set frame F's `no-accept-focus' parameter which, if non-nil, hints
1990  * that F's window-system window does not want to receive input focus
1991  * via mouse clicks or by moving the mouse into it.
1993  * If non-nil, this may have the unwanted side-effect that a user cannot
1994  * scroll a non-selected frame with the mouse.
1996  * Some window managers may not honor this parameter. */
1998   NSTRACE ("x_set_no_accept_focus");
2000   if (!EQ (new_value, old_value))
2001     FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value);
2004 void
2005 x_set_z_group (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
2006 /* Set frame F's `z-group' parameter.  If `above', F's window-system
2007    window is displayed above all windows that do not have the `above'
2008    property set.  If nil, F's window is shown below all windows that
2009    have the `above' property set and above all windows that have the
2010    `below' property set.  If `below', F's window is displayed below
2011    all windows that do.
2013    Some window managers may not honor this parameter. */
2015   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2016   NSWindow *window = [view window];
2018   NSTRACE ("x_set_z_group");
2020   if (NILP (new_value))
2021     {
2022       window.level = NSNormalWindowLevel;
2023       FRAME_Z_GROUP (f) = z_group_none;
2024     }
2025   else if (EQ (new_value, Qabove))
2026     {
2027       window.level = NSNormalWindowLevel + 1;
2028       FRAME_Z_GROUP (f) = z_group_above;
2029     }
2030   else if (EQ (new_value, Qabove_suspended))
2031     {
2032       /* Not sure what level this should be. */
2033       window.level = NSNormalWindowLevel + 1;
2034       FRAME_Z_GROUP (f) = z_group_above_suspended;
2035     }
2036   else if (EQ (new_value, Qbelow))
2037     {
2038       window.level = NSNormalWindowLevel - 1;
2039       FRAME_Z_GROUP (f) = z_group_below;
2040     }
2041   else
2042     error ("Invalid z-group specification");
2045 #ifdef NS_IMPL_COCOA
2046 void
2047 ns_set_appearance (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
2049 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
2050   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2051   NSWindow *window = [view window];
2053   NSTRACE ("ns_set_appearance");
2055 #ifndef NSAppKitVersionNumber10_10
2056 #define NSAppKitVersionNumber10_10 1343
2057 #endif
2059   if (NSAppKitVersionNumber < NSAppKitVersionNumber10_10)
2060     return;
2062   if (EQ (new_value, Qdark))
2063     {
2064       window.appearance = [NSAppearance
2065                             appearanceNamed: NSAppearanceNameVibrantDark];
2066       FRAME_NS_APPEARANCE (f) = ns_appearance_vibrant_dark;
2067     }
2068   else
2069     {
2070       window.appearance = [NSAppearance
2071                             appearanceNamed: NSAppearanceNameAqua];
2072       FRAME_NS_APPEARANCE (f) = ns_appearance_aqua;
2073     }
2074 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 */
2077 void
2078 ns_set_transparent_titlebar (struct frame *f, Lisp_Object new_value,
2079                              Lisp_Object old_value)
2081 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
2082   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2083   NSWindow *window = [view window];
2085   NSTRACE ("ns_set_transparent_titlebar");
2087   if ([window respondsToSelector: @selector(titlebarAppearsTransparent)]
2088       && !EQ (new_value, old_value))
2089     {
2090       window.titlebarAppearsTransparent = !NILP (new_value);
2091       FRAME_NS_TRANSPARENT_TITLEBAR (f) = !NILP (new_value);
2092     }
2093 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 */
2095 #endif /* NS_IMPL_COCOA */
2097 static void
2098 ns_fullscreen_hook (struct frame *f)
2100   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
2102   NSTRACE ("ns_fullscreen_hook");
2104   if (!FRAME_VISIBLE_P (f))
2105     return;
2107    if (! [view fsIsNative] && f->want_fullscreen == FULLSCREEN_BOTH)
2108     {
2109       /* Old style fs don't initiate correctly if created from
2110          init/default-frame alist, so use a timer (not nice...).
2111       */
2112       [NSTimer scheduledTimerWithTimeInterval: 0.5 target: view
2113                                      selector: @selector (handleFS)
2114                                      userInfo: nil repeats: NO];
2115       return;
2116     }
2118   block_input ();
2119   [view handleFS];
2120   unblock_input ();
2123 /* ==========================================================================
2125     Color management
2127    ========================================================================== */
2130 NSColor *
2131 ns_lookup_indexed_color (unsigned long idx, struct frame *f)
2133   struct ns_color_table *color_table = FRAME_DISPLAY_INFO (f)->color_table;
2134   if (idx < 1 || idx >= color_table->avail)
2135     return nil;
2136   return color_table->colors[idx];
2140 unsigned long
2141 ns_index_color (NSColor *color, struct frame *f)
2143   struct ns_color_table *color_table = FRAME_DISPLAY_INFO (f)->color_table;
2144   ptrdiff_t idx;
2145   ptrdiff_t i;
2147   if (!color_table->colors)
2148     {
2149       color_table->size = NS_COLOR_CAPACITY;
2150       color_table->avail = 1; /* skip idx=0 as marker */
2151       color_table->colors = xmalloc (color_table->size * sizeof (NSColor *));
2152       color_table->colors[0] = nil;
2153       color_table->empty_indices = [[NSMutableSet alloc] init];
2154     }
2156   /* Do we already have this color?  */
2157   for (i = 1; i < color_table->avail; i++)
2158     if (color_table->colors[i] && [color_table->colors[i] isEqual: color])
2159       return i;
2161   if ([color_table->empty_indices count] > 0)
2162     {
2163       NSNumber *index = [color_table->empty_indices anyObject];
2164       [color_table->empty_indices removeObject: index];
2165       idx = [index unsignedLongValue];
2166     }
2167   else
2168     {
2169       if (color_table->avail == color_table->size)
2170         color_table->colors =
2171           xpalloc (color_table->colors, &color_table->size, 1,
2172                    min (ULONG_MAX, PTRDIFF_MAX), sizeof *color_table->colors);
2173       idx = color_table->avail++;
2174     }
2176   color_table->colors[idx] = color;
2177   [color retain];
2178 /*fprintf(stderr, "color_table: allocated %d\n",idx);*/
2179   return idx;
2183 static int
2184 ns_get_color (const char *name, NSColor **col)
2185 /* --------------------------------------------------------------------------
2186      Parse a color name
2187    -------------------------------------------------------------------------- */
2188 /* On *Step, we attempt to mimic the X11 platform here, down to installing an
2189    X11 rgb.txt-compatible color list in Emacs.clr (see ns_term_init()).
2190    See: http://thread.gmane.org/gmane.emacs.devel/113050/focus=113272). */
2192   NSColor *new = nil;
2193   static char hex[20];
2194   int scaling = 0;
2195   float r = -1.0, g, b;
2196   NSString *nsname = [NSString stringWithUTF8String: name];
2198   NSTRACE ("ns_get_color(%s, **)", name);
2200   block_input ();
2202   if ([nsname isEqualToString: @"ns_selection_bg_color"])
2203     {
2204 #ifdef NS_IMPL_COCOA
2205       NSString *defname = [[NSUserDefaults standardUserDefaults]
2206                             stringForKey: @"AppleHighlightColor"];
2207       if (defname != nil)
2208         nsname = defname;
2209       else
2210 #endif
2211       if ((new = [NSColor selectedTextBackgroundColor]) != nil)
2212         {
2213           *col = [new colorUsingDefaultColorSpace];
2214           unblock_input ();
2215           return 0;
2216         }
2217       else
2218         nsname = NS_SELECTION_BG_COLOR_DEFAULT;
2220       name = [nsname UTF8String];
2221     }
2222   else if ([nsname isEqualToString: @"ns_selection_fg_color"])
2223     {
2224       /* NOTE: macOS applications normally don't set foreground
2225          selection, but text may be unreadable if we don't.
2226       */
2227       if ((new = [NSColor selectedTextColor]) != nil)
2228         {
2229           *col = [new colorUsingDefaultColorSpace];
2230           unblock_input ();
2231           return 0;
2232         }
2234       nsname = NS_SELECTION_FG_COLOR_DEFAULT;
2235       name = [nsname UTF8String];
2236     }
2238   /* First, check for some sort of numeric specification. */
2239   hex[0] = '\0';
2241   if (name[0] == '0' || name[0] == '1' || name[0] == '.')  /* RGB decimal */
2242     {
2243       NSScanner *scanner = [NSScanner scannerWithString: nsname];
2244       [scanner scanFloat: &r];
2245       [scanner scanFloat: &g];
2246       [scanner scanFloat: &b];
2247     }
2248   else if (!strncmp(name, "rgb:", 4))  /* A newer X11 format -- rgb:r/g/b */
2249     scaling = (snprintf (hex, sizeof hex, "%s", name + 4) - 2) / 3;
2250   else if (name[0] == '#')        /* An old X11 format; convert to newer */
2251     {
2252       int len = (strlen(name) - 1);
2253       int start = (len % 3 == 0) ? 1 : len / 4 + 1;
2254       int i;
2255       scaling = strlen(name+start) / 3;
2256       for (i = 0; i < 3; i++)
2257         sprintf (hex + i * (scaling + 1), "%.*s/", scaling,
2258                  name + start + i * scaling);
2259       hex[3 * (scaling + 1) - 1] = '\0';
2260     }
2262   if (hex[0])
2263     {
2264       unsigned int rr, gg, bb;
2265       float fscale = scaling == 4 ? 65535.0 : (scaling == 2 ? 255.0 : 15.0);
2266       if (sscanf (hex, "%x/%x/%x", &rr, &gg, &bb))
2267         {
2268           r = rr / fscale;
2269           g = gg / fscale;
2270           b = bb / fscale;
2271         }
2272     }
2274   if (r >= 0.0F)
2275     {
2276       *col = [NSColor colorForEmacsRed: r green: g blue: b alpha: 1.0];
2277       unblock_input ();
2278       return 0;
2279     }
2281   /* Otherwise, color is expected to be from a list */
2282   {
2283     NSEnumerator *lenum, *cenum;
2284     NSString *name;
2285     NSColorList *clist;
2287 #ifdef NS_IMPL_GNUSTEP
2288     /* XXX: who is wrong, the requestor or the implementation? */
2289     if ([nsname compare: @"Highlight" options: NSCaseInsensitiveSearch]
2290         == NSOrderedSame)
2291       nsname = @"highlightColor";
2292 #endif
2294     lenum = [[NSColorList availableColorLists] objectEnumerator];
2295     while ( (clist = [lenum nextObject]) && new == nil)
2296       {
2297         cenum = [[clist allKeys] objectEnumerator];
2298         while ( (name = [cenum nextObject]) && new == nil )
2299           {
2300             if ([name compare: nsname
2301                       options: NSCaseInsensitiveSearch] == NSOrderedSame )
2302               new = [clist colorWithKey: name];
2303           }
2304       }
2305   }
2307   if (new)
2308     *col = [new colorUsingDefaultColorSpace];
2309   unblock_input ();
2310   return new ? 0 : 1;
2315 ns_lisp_to_color (Lisp_Object color, NSColor **col)
2316 /* --------------------------------------------------------------------------
2317      Convert a Lisp string object to a NS color
2318    -------------------------------------------------------------------------- */
2320   NSTRACE ("ns_lisp_to_color");
2321   if (STRINGP (color))
2322     return ns_get_color (SSDATA (color), col);
2323   else if (SYMBOLP (color))
2324     return ns_get_color (SSDATA (SYMBOL_NAME (color)), col);
2325   return 1;
2329 void
2330 ns_query_color(void *col, XColor *color_def, int setPixel)
2331 /* --------------------------------------------------------------------------
2332          Get ARGB values out of NSColor col and put them into color_def.
2333          If setPixel, set the pixel to a concatenated version.
2334          and set color_def pixel to the resulting index.
2335    -------------------------------------------------------------------------- */
2337   EmacsCGFloat r, g, b, a;
2339   [((NSColor *)col) getRed: &r green: &g blue: &b alpha: &a];
2340   color_def->red   = r * 65535;
2341   color_def->green = g * 65535;
2342   color_def->blue  = b * 65535;
2344   if (setPixel == YES)
2345     color_def->pixel
2346       = ARGB_TO_ULONG((int)(a*255),
2347                       (int)(r*255), (int)(g*255), (int)(b*255));
2351 bool
2352 ns_defined_color (struct frame *f,
2353                   const char *name,
2354                   XColor *color_def,
2355                   bool alloc,
2356                   bool makeIndex)
2357 /* --------------------------------------------------------------------------
2358          Return true if named color found, and set color_def rgb accordingly.
2359          If makeIndex and alloc are nonzero put the color in the color_table,
2360          and set color_def pixel to the resulting index.
2361          If makeIndex is zero, set color_def pixel to ARGB.
2362          Return false if not found
2363    -------------------------------------------------------------------------- */
2365   NSColor *col;
2366   NSTRACE_WHEN (NSTRACE_GROUP_COLOR, "ns_defined_color");
2368   block_input ();
2369   if (ns_get_color (name, &col) != 0) /* Color not found  */
2370     {
2371       unblock_input ();
2372       return 0;
2373     }
2374   if (makeIndex && alloc)
2375     color_def->pixel = ns_index_color (col, f);
2376   ns_query_color (col, color_def, !makeIndex);
2377   unblock_input ();
2378   return 1;
2382 void
2383 x_set_frame_alpha (struct frame *f)
2384 /* --------------------------------------------------------------------------
2385      change the entire-frame transparency
2386    -------------------------------------------------------------------------- */
2388   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
2389   double alpha = 1.0;
2390   double alpha_min = 1.0;
2392   NSTRACE ("x_set_frame_alpha");
2394   if (dpyinfo->x_highlight_frame == f)
2395     alpha = f->alpha[0];
2396   else
2397     alpha = f->alpha[1];
2399   if (FLOATP (Vframe_alpha_lower_limit))
2400     alpha_min = XFLOAT_DATA (Vframe_alpha_lower_limit);
2401   else if (INTEGERP (Vframe_alpha_lower_limit))
2402     alpha_min = (XINT (Vframe_alpha_lower_limit)) / 100.0;
2404   if (alpha < 0.0)
2405     return;
2406   else if (1.0 < alpha)
2407     alpha = 1.0;
2408   else if (0.0 <= alpha && alpha < alpha_min && alpha_min <= 1.0)
2409     alpha = alpha_min;
2411 #ifdef NS_IMPL_COCOA
2412   {
2413     EmacsView *view = FRAME_NS_VIEW (f);
2414   [[view window] setAlphaValue: alpha];
2415   }
2416 #endif
2420 /* ==========================================================================
2422     Mouse handling
2424    ========================================================================== */
2427 void
2428 frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y)
2429 /* --------------------------------------------------------------------------
2430      Programmatically reposition mouse pointer in pixel coordinates
2431    -------------------------------------------------------------------------- */
2433   NSTRACE ("frame_set_mouse_pixel_position");
2435   /* FIXME: what about GNUstep? */
2436 #ifdef NS_IMPL_COCOA
2437   CGPoint mouse_pos =
2438     CGPointMake(f->left_pos + pix_x,
2439                 f->top_pos + pix_y +
2440                 FRAME_NS_TITLEBAR_HEIGHT(f) + FRAME_TOOLBAR_HEIGHT(f));
2441   CGWarpMouseCursorPosition (mouse_pos);
2442 #endif
2445 static int
2446 note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y)
2447 /*   ------------------------------------------------------------------------
2448      Called by EmacsView on mouseMovement events.  Passes on
2449      to emacs mainstream code if we moved off of a rect of interest
2450      known as last_mouse_glyph.
2451      ------------------------------------------------------------------------ */
2453   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
2454   NSRect *r;
2456 //  NSTRACE ("note_mouse_movement");
2458   dpyinfo->last_mouse_motion_frame = frame;
2459   r = &dpyinfo->last_mouse_glyph;
2461   /* Note, this doesn't get called for enter/leave, since we don't have a
2462      position.  Those are taken care of in the corresponding NSView methods. */
2464   /* has movement gone beyond last rect we were tracking? */
2465   if (x < r->origin.x || x >= r->origin.x + r->size.width
2466       || y < r->origin.y || y >= r->origin.y + r->size.height)
2467     {
2468       ns_update_begin (frame);
2469       frame->mouse_moved = 1;
2470       note_mouse_highlight (frame, x, y);
2471       remember_mouse_glyph (frame, x, y, r);
2472       ns_update_end (frame);
2473       return 1;
2474     }
2476   return 0;
2480 static void
2481 ns_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window,
2482                    enum scroll_bar_part *part, Lisp_Object *x, Lisp_Object *y,
2483                    Time *time)
2484 /* --------------------------------------------------------------------------
2485     External (hook): inform emacs about mouse position and hit parts.
2486     If a scrollbar is being dragged, set bar_window, part, x, y, time.
2487     x & y should be position in the scrollbar (the whole bar, not the handle)
2488     and length of scrollbar respectively
2489    -------------------------------------------------------------------------- */
2491   id view;
2492   NSPoint position;
2493   Lisp_Object frame, tail;
2494   struct frame *f;
2495   struct ns_display_info *dpyinfo;
2497   NSTRACE ("ns_mouse_position");
2499   if (*fp == NULL)
2500     {
2501       fprintf (stderr, "Warning: ns_mouse_position () called with null *fp.\n");
2502       return;
2503     }
2505   dpyinfo = FRAME_DISPLAY_INFO (*fp);
2507   block_input ();
2509   /* Clear the mouse-moved flag for every frame on this display.  */
2510   FOR_EACH_FRAME (tail, frame)
2511     if (FRAME_NS_P (XFRAME (frame))
2512         && FRAME_NS_DISPLAY (XFRAME (frame)) == FRAME_NS_DISPLAY (*fp))
2513       XFRAME (frame)->mouse_moved = 0;
2515   dpyinfo->last_mouse_scroll_bar = nil;
2516   if (dpyinfo->last_mouse_frame
2517       && FRAME_LIVE_P (dpyinfo->last_mouse_frame))
2518     f = dpyinfo->last_mouse_frame;
2519   else
2520     f = dpyinfo->x_focus_frame ? dpyinfo->x_focus_frame : SELECTED_FRAME ();
2522   if (f && FRAME_NS_P (f))
2523     {
2524       view = FRAME_NS_VIEW (*fp);
2526       position = [[view window] mouseLocationOutsideOfEventStream];
2527       position = [view convertPoint: position fromView: nil];
2528       remember_mouse_glyph (f, position.x, position.y,
2529                             &dpyinfo->last_mouse_glyph);
2530       NSTRACE_POINT ("position", position);
2532       if (bar_window) *bar_window = Qnil;
2533       if (part) *part = scroll_bar_above_handle;
2535       if (x) XSETINT (*x, lrint (position.x));
2536       if (y) XSETINT (*y, lrint (position.y));
2537       if (time)
2538         *time = dpyinfo->last_mouse_movement_time;
2539       *fp = f;
2540     }
2542   unblock_input ();
2546 static void
2547 ns_frame_up_to_date (struct frame *f)
2548 /* --------------------------------------------------------------------------
2549     External (hook): Fix up mouse highlighting right after a full update.
2550     Can't use FRAME_MOUSE_UPDATE due to ns_frame_begin and ns_frame_end calls.
2551    -------------------------------------------------------------------------- */
2553   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_frame_up_to_date");
2555   if (FRAME_NS_P (f))
2556     {
2557       Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
2558       if (f == hlinfo->mouse_face_mouse_frame)
2559         {
2560           block_input ();
2561           ns_update_begin(f);
2562           note_mouse_highlight (hlinfo->mouse_face_mouse_frame,
2563                                 hlinfo->mouse_face_mouse_x,
2564                                 hlinfo->mouse_face_mouse_y);
2565           ns_update_end(f);
2566           unblock_input ();
2567         }
2568     }
2572 static void
2573 ns_define_frame_cursor (struct frame *f, Cursor cursor)
2574 /* --------------------------------------------------------------------------
2575     External (RIF): set frame mouse pointer type.
2576    -------------------------------------------------------------------------- */
2578   NSTRACE ("ns_define_frame_cursor");
2579   if (FRAME_POINTER_TYPE (f) != cursor)
2580     {
2581       EmacsView *view = FRAME_NS_VIEW (f);
2582       FRAME_POINTER_TYPE (f) = cursor;
2583       [[view window] invalidateCursorRectsForView: view];
2584       /* Redisplay assumes this function also draws the changed frame
2585          cursor, but this function doesn't, so do it explicitly.  */
2586       x_update_cursor (f, 1);
2587     }
2592 /* ==========================================================================
2594     Keyboard handling
2596    ========================================================================== */
2599 static unsigned
2600 ns_convert_key (unsigned code)
2601 /* --------------------------------------------------------------------------
2602     Internal call used by NSView-keyDown.
2603    -------------------------------------------------------------------------- */
2605   const unsigned last_keysym = ARRAYELTS (convert_ns_to_X_keysym);
2606   unsigned keysym;
2607   /* An array would be faster, but less easy to read. */
2608   for (keysym = 0; keysym < last_keysym; keysym += 2)
2609     if (code == convert_ns_to_X_keysym[keysym])
2610       return 0xFF00 | convert_ns_to_X_keysym[keysym+1];
2611   return 0;
2612 /* if decide to use keyCode and Carbon table, use this line:
2613      return code > 0xff ? 0 : 0xFF00 | ns_keycode_to_xkeysym_table[code]; */
2617 char *
2618 x_get_keysym_name (int keysym)
2619 /* --------------------------------------------------------------------------
2620     Called by keyboard.c.  Not sure if the return val is important, except
2621     that it be unique.
2622    -------------------------------------------------------------------------- */
2624   static char value[16];
2625   NSTRACE ("x_get_keysym_name");
2626   sprintf (value, "%d", keysym);
2627   return value;
2632 /* ==========================================================================
2634     Block drawing operations
2636    ========================================================================== */
2639 static void
2640 ns_redraw_scroll_bars (struct frame *f)
2642   int i;
2643   id view;
2644   NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews];
2645   NSTRACE ("ns_redraw_scroll_bars");
2646   for (i =[subviews count]-1; i >= 0; i--)
2647     {
2648       view = [subviews objectAtIndex: i];
2649       if (![view isKindOfClass: [EmacsScroller class]]) continue;
2650       [view display];
2651     }
2655 void
2656 ns_clear_frame (struct frame *f)
2657 /* --------------------------------------------------------------------------
2658       External (hook): Erase the entire frame
2659    -------------------------------------------------------------------------- */
2661   NSView *view = FRAME_NS_VIEW (f);
2662   NSRect r;
2664   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_clear_frame");
2666  /* comes on initial frame because we have
2667     after-make-frame-functions = select-frame */
2668  if (!FRAME_DEFAULT_FACE (f))
2669    return;
2671   mark_window_cursors_off (XWINDOW (FRAME_ROOT_WINDOW (f)));
2673   r = [view bounds];
2675   block_input ();
2676   ns_focus (f, &r, 1);
2677   [ns_lookup_indexed_color (NS_FACE_BACKGROUND
2678                             (FACE_FROM_ID (f, DEFAULT_FACE_ID)), f) set];
2679   NSRectFill (r);
2680   ns_unfocus (f);
2682   /* as of 2006/11 or so this is now needed */
2683   ns_redraw_scroll_bars (f);
2684   unblock_input ();
2688 static void
2689 ns_clear_frame_area (struct frame *f, int x, int y, int width, int height)
2690 /* --------------------------------------------------------------------------
2691     External (RIF):  Clear section of frame
2692    -------------------------------------------------------------------------- */
2694   NSRect r = NSMakeRect (x, y, width, height);
2695   NSView *view = FRAME_NS_VIEW (f);
2696   struct face *face = FRAME_DEFAULT_FACE (f);
2698   if (!view || !face)
2699     return;
2701   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_clear_frame_area");
2703   r = NSIntersectionRect (r, [view frame]);
2704   ns_focus (f, &r, 1);
2705   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), f) set];
2707   NSRectFill (r);
2709   ns_unfocus (f);
2710   return;
2713 static void
2714 ns_copy_bits (struct frame *f, NSRect src, NSRect dest)
2716   NSTRACE ("ns_copy_bits");
2718   if (FRAME_NS_VIEW (f))
2719     {
2720       hide_bell();              // Ensure the bell image isn't scrolled.
2722       ns_focus (f, &dest, 1);
2723       [FRAME_NS_VIEW (f) scrollRect: src
2724                                  by: NSMakeSize (dest.origin.x - src.origin.x,
2725                                                  dest.origin.y - src.origin.y)];
2726       ns_unfocus (f);
2727     }
2730 static void
2731 ns_scroll_run (struct window *w, struct run *run)
2732 /* --------------------------------------------------------------------------
2733     External (RIF):  Insert or delete n lines at line vpos
2734    -------------------------------------------------------------------------- */
2736   struct frame *f = XFRAME (w->frame);
2737   int x, y, width, height, from_y, to_y, bottom_y;
2739   NSTRACE ("ns_scroll_run");
2741   /* begin copy from other terms */
2742   /* Get frame-relative bounding box of the text display area of W,
2743      without mode lines.  Include in this box the left and right
2744      fringe of W.  */
2745   window_box (w, ANY_AREA, &x, &y, &width, &height);
2747   from_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->current_y);
2748   to_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->desired_y);
2749   bottom_y = y + height;
2751   if (to_y < from_y)
2752     {
2753       /* Scrolling up.  Make sure we don't copy part of the mode
2754          line at the bottom.  */
2755       if (from_y + run->height > bottom_y)
2756         height = bottom_y - from_y;
2757       else
2758         height = run->height;
2759     }
2760   else
2761     {
2762       /* Scrolling down.  Make sure we don't copy over the mode line.
2763          at the bottom.  */
2764       if (to_y + run->height > bottom_y)
2765         height = bottom_y - to_y;
2766       else
2767         height = run->height;
2768     }
2769   /* end copy from other terms */
2771   if (height == 0)
2772       return;
2774   block_input ();
2776   x_clear_cursor (w);
2778   {
2779     NSRect srcRect = NSMakeRect (x, from_y, width, height);
2780     NSRect dstRect = NSMakeRect (x, to_y, width, height);
2782     ns_copy_bits (f, srcRect , dstRect);
2783   }
2785   unblock_input ();
2789 static void
2790 ns_after_update_window_line (struct window *w, struct glyph_row *desired_row)
2791 /* --------------------------------------------------------------------------
2792     External (RIF): preparatory to fringe update after text was updated
2793    -------------------------------------------------------------------------- */
2795   struct frame *f;
2796   int width, height;
2798   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_after_update_window_line");
2800   /* begin copy from other terms */
2801   eassert (w);
2803   if (!desired_row->mode_line_p && !w->pseudo_window_p)
2804     desired_row->redraw_fringe_bitmaps_p = 1;
2806   /* When a window has disappeared, make sure that no rest of
2807      full-width rows stays visible in the internal border.  */
2808   if (windows_or_buffers_changed
2809       && desired_row->full_width_p
2810       && (f = XFRAME (w->frame),
2811           width = FRAME_INTERNAL_BORDER_WIDTH (f),
2812           width != 0)
2813       && (height = desired_row->visible_height,
2814           height > 0))
2815     {
2816       int y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, desired_row->y));
2818       block_input ();
2819       ns_clear_frame_area (f, 0, y, width, height);
2820       ns_clear_frame_area (f,
2821                            FRAME_PIXEL_WIDTH (f) - width,
2822                            y, width, height);
2823       unblock_input ();
2824     }
2828 static void
2829 ns_shift_glyphs_for_insert (struct frame *f,
2830                            int x, int y, int width, int height,
2831                            int shift_by)
2832 /* --------------------------------------------------------------------------
2833     External (RIF): copy an area horizontally, don't worry about clearing src
2834    -------------------------------------------------------------------------- */
2836   NSRect srcRect = NSMakeRect (x, y, width, height);
2837   NSRect dstRect = NSMakeRect (x+shift_by, y, width, height);
2839   NSTRACE ("ns_shift_glyphs_for_insert");
2841   ns_copy_bits (f, srcRect, dstRect);
2846 /* ==========================================================================
2848     Character encoding and metrics
2850    ========================================================================== */
2853 static void
2854 ns_compute_glyph_string_overhangs (struct glyph_string *s)
2855 /* --------------------------------------------------------------------------
2856      External (RIF); compute left/right overhang of whole string and set in s
2857    -------------------------------------------------------------------------- */
2859   struct font *font = s->font;
2861   if (s->char2b)
2862     {
2863       struct font_metrics metrics;
2864       unsigned int codes[2];
2865       codes[0] = *(s->char2b);
2866       codes[1] = *(s->char2b + s->nchars - 1);
2868       font->driver->text_extents (font, codes, 2, &metrics);
2869       s->left_overhang = -metrics.lbearing;
2870       s->right_overhang
2871         = metrics.rbearing > metrics.width
2872         ? metrics.rbearing - metrics.width : 0;
2873     }
2874   else
2875     {
2876       s->left_overhang = 0;
2877       if (EQ (font->driver->type, Qns))
2878         s->right_overhang = ((struct nsfont_info *)font)->ital ?
2879           FONT_HEIGHT (font) * 0.2 : 0;
2880       else
2881         s->right_overhang = 0;
2882     }
2887 /* ==========================================================================
2889     Fringe and cursor drawing
2891    ========================================================================== */
2894 extern int max_used_fringe_bitmap;
2895 static void
2896 ns_draw_fringe_bitmap (struct window *w, struct glyph_row *row,
2897                       struct draw_fringe_bitmap_params *p)
2898 /* --------------------------------------------------------------------------
2899     External (RIF); fringe-related
2900    -------------------------------------------------------------------------- */
2902   /* Fringe bitmaps comes in two variants, normal and periodic.  A
2903      periodic bitmap is used to create a continuous pattern.  Since a
2904      bitmap is rendered one text line at a time, the start offset (dh)
2905      of the bitmap varies.  Concretely, this is used for the empty
2906      line indicator.
2908      For a bitmap, "h + dh" is the full height and is always
2909      invariant.  For a normal bitmap "dh" is zero.
2911      For example, when the period is three and the full height is 72
2912      the following combinations exists:
2914        h=72 dh=0
2915        h=71 dh=1
2916        h=70 dh=2 */
2918   struct frame *f = XFRAME (WINDOW_FRAME (w));
2919   struct face *face = p->face;
2920   static EmacsImage **bimgs = NULL;
2921   static int nBimgs = 0;
2923   NSTRACE_WHEN (NSTRACE_GROUP_FRINGE, "ns_draw_fringe_bitmap");
2924   NSTRACE_MSG ("which:%d cursor:%d overlay:%d width:%d height:%d period:%d",
2925                p->which, p->cursor_p, p->overlay_p, p->wd, p->h, p->dh);
2927   /* grow bimgs if needed */
2928   if (nBimgs < max_used_fringe_bitmap)
2929     {
2930       bimgs = xrealloc (bimgs, max_used_fringe_bitmap * sizeof *bimgs);
2931       memset (bimgs + nBimgs, 0,
2932               (max_used_fringe_bitmap - nBimgs) * sizeof *bimgs);
2933       nBimgs = max_used_fringe_bitmap;
2934     }
2936   /* Must clip because of partially visible lines.  */
2937   ns_clip_to_row (w, row, ANY_AREA, YES);
2939   if (!p->overlay_p)
2940     {
2941       int bx = p->bx, by = p->by, nx = p->nx, ny = p->ny;
2943       if (bx >= 0 && nx > 0)
2944         {
2945           NSRect r = NSMakeRect (bx, by, nx, ny);
2946           NSRectClip (r);
2947           [ns_lookup_indexed_color (face->background, f) set];
2948           NSRectFill (r);
2949         }
2950     }
2952   if (p->which)
2953     {
2954       NSRect r = NSMakeRect (p->x, p->y, p->wd, p->h);
2955       EmacsImage *img = bimgs[p->which - 1];
2957       if (!img)
2958         {
2959           // Note: For "periodic" images, allocate one EmacsImage for
2960           // the base image, and use it for all dh:s.
2961           unsigned short *bits = p->bits;
2962           int full_height = p->h + p->dh;
2963           int i;
2964           unsigned char *cbits = xmalloc (full_height);
2966           for (i = 0; i < full_height; i++)
2967             cbits[i] = bits[i];
2968           img = [[EmacsImage alloc] initFromXBM: cbits width: 8
2969                                          height: full_height
2970                                              fg: 0 bg: 0];
2971           bimgs[p->which - 1] = img;
2972           xfree (cbits);
2973         }
2975       NSTRACE_RECT ("r", r);
2977       NSRectClip (r);
2978       /* Since we composite the bitmap instead of just blitting it, we need
2979          to erase the whole background. */
2980       [ns_lookup_indexed_color(face->background, f) set];
2981       NSRectFill (r);
2983       {
2984         NSColor *bm_color;
2985         if (!p->cursor_p)
2986           bm_color = ns_lookup_indexed_color(face->foreground, f);
2987         else if (p->overlay_p)
2988           bm_color = ns_lookup_indexed_color(face->background, f);
2989         else
2990           bm_color = f->output_data.ns->cursor_color;
2991         [img setXBMColor: bm_color];
2992       }
2994 #ifdef NS_IMPL_COCOA
2995       // Note: For periodic images, the full image height is "h + hd".
2996       // By using the height h, a suitable part of the image is used.
2997       NSRect fromRect = NSMakeRect(0, 0, p->wd, p->h);
2999       NSTRACE_RECT ("fromRect", fromRect);
3001       [img drawInRect: r
3002               fromRect: fromRect
3003              operation: NSCompositingOperationSourceOver
3004               fraction: 1.0
3005            respectFlipped: YES
3006                 hints: nil];
3007 #else
3008       {
3009         NSPoint pt = r.origin;
3010         pt.y += p->h;
3011         [img compositeToPoint: pt operation: NSCompositingOperationSourceOver];
3012       }
3013 #endif
3014     }
3015   ns_unfocus (f);
3019 static void
3020 ns_draw_window_cursor (struct window *w, struct glyph_row *glyph_row,
3021                        int x, int y, enum text_cursor_kinds cursor_type,
3022                        int cursor_width, bool on_p, bool active_p)
3023 /* --------------------------------------------------------------------------
3024      External call (RIF): draw cursor.
3025      Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
3026    -------------------------------------------------------------------------- */
3028   NSRect r, s;
3029   int fx, fy, h, cursor_height;
3030   struct frame *f = WINDOW_XFRAME (w);
3031   struct glyph *phys_cursor_glyph;
3032   struct glyph *cursor_glyph;
3033   struct face *face;
3034   NSColor *hollow_color = FRAME_BACKGROUND_COLOR (f);
3036   /* If cursor is out of bounds, don't draw garbage.  This can happen
3037      in mini-buffer windows when switching between echo area glyphs
3038      and mini-buffer.  */
3040   NSTRACE ("ns_draw_window_cursor");
3042   if (!on_p)
3043     return;
3045   w->phys_cursor_type = cursor_type;
3046   w->phys_cursor_on_p = on_p;
3048   if (cursor_type == NO_CURSOR)
3049     {
3050       w->phys_cursor_width = 0;
3051       return;
3052     }
3054   if ((phys_cursor_glyph = get_phys_cursor_glyph (w)) == NULL)
3055     {
3056       if (glyph_row->exact_window_width_line_p
3057           && w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])
3058         {
3059           glyph_row->cursor_in_fringe_p = 1;
3060           draw_fringe_bitmap (w, glyph_row, 0);
3061         }
3062       return;
3063     }
3065   /* We draw the cursor (with NSRectFill), then draw the glyph on top
3066      (other terminals do it the other way round).  We must set
3067      w->phys_cursor_width to the cursor width.  For bar cursors, that
3068      is CURSOR_WIDTH; for box cursors, it is the glyph width.  */
3069   get_phys_cursor_geometry (w, glyph_row, phys_cursor_glyph, &fx, &fy, &h);
3071   /* The above get_phys_cursor_geometry call set w->phys_cursor_width
3072      to the glyph width; replace with CURSOR_WIDTH for (V)BAR cursors. */
3073   if (cursor_type == BAR_CURSOR)
3074     {
3075       if (cursor_width < 1)
3076         cursor_width = max (FRAME_CURSOR_WIDTH (f), 1);
3078       /* The bar cursor should never be wider than the glyph. */
3079       if (cursor_width < w->phys_cursor_width)
3080         w->phys_cursor_width = cursor_width;
3081     }
3082   /* If we have an HBAR, "cursor_width" MAY specify height. */
3083   else if (cursor_type == HBAR_CURSOR)
3084     {
3085       cursor_height = (cursor_width < 1) ? lrint (0.25 * h) : cursor_width;
3086       if (cursor_height > glyph_row->height)
3087         cursor_height = glyph_row->height;
3088       if (h > cursor_height) // Cursor smaller than line height, move down
3089         fy += h - cursor_height;
3090       h = cursor_height;
3091     }
3093   r.origin.x = fx, r.origin.y = fy;
3094   r.size.height = h;
3095   r.size.width = w->phys_cursor_width;
3097   /* Prevent the cursor from being drawn outside the text area. */
3098   ns_clip_to_row (w, glyph_row, TEXT_AREA, NO); /* do ns_focus(f, &r, 1); if remove */
3101   face = FACE_FROM_ID_OR_NULL (f, phys_cursor_glyph->face_id);
3102   if (face && NS_FACE_BACKGROUND (face)
3103       == ns_index_color (FRAME_CURSOR_COLOR (f), f))
3104     {
3105       [ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), f) set];
3106       hollow_color = FRAME_CURSOR_COLOR (f);
3107     }
3108   else
3109     [FRAME_CURSOR_COLOR (f) set];
3111 #ifdef NS_IMPL_COCOA
3112   /* TODO: This makes drawing of cursor plus that of phys_cursor_glyph
3113            atomic.  Cleaner ways of doing this should be investigated.
3114            One way would be to set a global variable DRAWING_CURSOR
3115            when making the call to draw_phys..(), don't focus in that
3116            case, then move the ns_unfocus() here after that call. */
3117   NSDisableScreenUpdates ();
3118 #endif
3120   switch (cursor_type)
3121     {
3122     case DEFAULT_CURSOR:
3123     case NO_CURSOR:
3124       break;
3125     case FILLED_BOX_CURSOR:
3126       NSRectFill (r);
3127       break;
3128     case HOLLOW_BOX_CURSOR:
3129       NSRectFill (r);
3130       [hollow_color set];
3131       NSRectFill (NSInsetRect (r, 1, 1));
3132       [FRAME_CURSOR_COLOR (f) set];
3133       break;
3134     case HBAR_CURSOR:
3135       NSRectFill (r);
3136       break;
3137     case BAR_CURSOR:
3138       s = r;
3139       /* If the character under cursor is R2L, draw the bar cursor
3140          on the right of its glyph, rather than on the left.  */
3141       cursor_glyph = get_phys_cursor_glyph (w);
3142       if ((cursor_glyph->resolved_level & 1) != 0)
3143         s.origin.x += cursor_glyph->pixel_width - s.size.width;
3145       NSRectFill (s);
3146       break;
3147     }
3148   ns_unfocus (f);
3150   /* draw the character under the cursor */
3151   if (cursor_type != NO_CURSOR)
3152     draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
3154 #ifdef NS_IMPL_COCOA
3155   NSEnableScreenUpdates ();
3156 #endif
3161 static void
3162 ns_draw_vertical_window_border (struct window *w, int x, int y0, int y1)
3163 /* --------------------------------------------------------------------------
3164      External (RIF): Draw a vertical line.
3165    -------------------------------------------------------------------------- */
3167   struct frame *f = XFRAME (WINDOW_FRAME (w));
3168   struct face *face;
3169   NSRect r = NSMakeRect (x, y0, 1, y1-y0);
3171   NSTRACE ("ns_draw_vertical_window_border");
3173   face = FACE_FROM_ID_OR_NULL (f, VERTICAL_BORDER_FACE_ID);
3175   ns_focus (f, &r, 1);
3176   if (face)
3177     [ns_lookup_indexed_color(face->foreground, f) set];
3179   NSRectFill(r);
3180   ns_unfocus (f);
3184 static void
3185 ns_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1)
3186 /* --------------------------------------------------------------------------
3187      External (RIF): Draw a window divider.
3188    -------------------------------------------------------------------------- */
3190   struct frame *f = XFRAME (WINDOW_FRAME (w));
3191   struct face *face = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FACE_ID);
3192   struct face *face_first
3193     = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FIRST_PIXEL_FACE_ID);
3194   struct face *face_last
3195     = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_LAST_PIXEL_FACE_ID);
3196   unsigned long color = face ? face->foreground : FRAME_FOREGROUND_PIXEL (f);
3197   unsigned long color_first = (face_first
3198                                ? face_first->foreground
3199                                : FRAME_FOREGROUND_PIXEL (f));
3200   unsigned long color_last = (face_last
3201                               ? face_last->foreground
3202                               : FRAME_FOREGROUND_PIXEL (f));
3203   NSRect divider = NSMakeRect (x0, y0, x1-x0, y1-y0);
3205   NSTRACE ("ns_draw_window_divider");
3207   ns_focus (f, &divider, 1);
3209   if ((y1 - y0 > x1 - x0) && (x1 - x0 >= 3))
3210     /* A vertical divider, at least three pixels wide: Draw first and
3211        last pixels differently.  */
3212     {
3213       [ns_lookup_indexed_color(color_first, f) set];
3214       NSRectFill(NSMakeRect (x0, y0, 1, y1 - y0));
3215       [ns_lookup_indexed_color(color, f) set];
3216       NSRectFill(NSMakeRect (x0 + 1, y0, x1 - x0 - 2, y1 - y0));
3217       [ns_lookup_indexed_color(color_last, f) set];
3218       NSRectFill(NSMakeRect (x1 - 1, y0, 1, y1 - y0));
3219     }
3220   else if ((x1 - x0 > y1 - y0) && (y1 - y0 >= 3))
3221     /* A horizontal divider, at least three pixels high: Draw first and
3222        last pixels differently.  */
3223     {
3224       [ns_lookup_indexed_color(color_first, f) set];
3225       NSRectFill(NSMakeRect (x0, y0, x1 - x0, 1));
3226       [ns_lookup_indexed_color(color, f) set];
3227       NSRectFill(NSMakeRect (x0, y0 + 1, x1 - x0, y1 - y0 - 2));
3228       [ns_lookup_indexed_color(color_last, f) set];
3229       NSRectFill(NSMakeRect (x0, y1 - 1, x1 - x0, 1));
3230     }
3231   else
3232     {
3233       /* In any other case do not draw the first and last pixels
3234          differently.  */
3235       [ns_lookup_indexed_color(color, f) set];
3236       NSRectFill(divider);
3237     }
3239   ns_unfocus (f);
3242 static void
3243 ns_show_hourglass (struct frame *f)
3245   /* TODO: add NSProgressIndicator to all frames.  */
3248 static void
3249 ns_hide_hourglass (struct frame *f)
3251   /* TODO: remove NSProgressIndicator from all frames.  */
3254 /* ==========================================================================
3256     Glyph drawing operations
3258    ========================================================================== */
3260 static int
3261 ns_get_glyph_string_clip_rect (struct glyph_string *s, NativeRectangle *nr)
3262 /* --------------------------------------------------------------------------
3263     Wrapper utility to account for internal border width on full-width lines,
3264     and allow top full-width rows to hit the frame top.  nr should be pointer
3265     to two successive NSRects.  Number of rects actually used is returned.
3266    -------------------------------------------------------------------------- */
3268   int n = get_glyph_string_clip_rects (s, nr, 2);
3269   return n;
3272 /* --------------------------------------------------------------------
3273    Draw a wavy line under glyph string s. The wave fills wave_height
3274    pixels from y.
3276                     x          wave_length = 2
3277                                  --
3278                 y    *   *   *   *   *
3279                      |* * * * * * * * *
3280     wave_height = 3  | *   *   *   *
3281   --------------------------------------------------------------------- */
3283 static void
3284 ns_draw_underwave (struct glyph_string *s, EmacsCGFloat width, EmacsCGFloat x)
3286   int wave_height = 3, wave_length = 2;
3287   int y, dx, dy, odd, xmax;
3288   NSPoint a, b;
3289   NSRect waveClip;
3291   dx = wave_length;
3292   dy = wave_height - 1;
3293   y =  s->ybase - wave_height + 3;
3294   xmax = x + width;
3296   /* Find and set clipping rectangle */
3297   waveClip = NSMakeRect (x, y, width, wave_height);
3298   [[NSGraphicsContext currentContext] saveGraphicsState];
3299   NSRectClip (waveClip);
3301   /* Draw the waves */
3302   a.x = x - ((int)(x) % dx) + (EmacsCGFloat) 0.5;
3303   b.x = a.x + dx;
3304   odd = (int)(a.x/dx) % 2;
3305   a.y = b.y = y + 0.5;
3307   if (odd)
3308     a.y += dy;
3309   else
3310     b.y += dy;
3312   while (a.x <= xmax)
3313     {
3314       [NSBezierPath strokeLineFromPoint:a toPoint:b];
3315       a.x = b.x, a.y = b.y;
3316       b.x += dx, b.y = y + 0.5 + odd*dy;
3317       odd = !odd;
3318     }
3320   /* Restore previous clipping rectangle(s) */
3321   [[NSGraphicsContext currentContext] restoreGraphicsState];
3326 static void
3327 ns_draw_text_decoration (struct glyph_string *s, struct face *face,
3328                          NSColor *defaultCol, CGFloat width, CGFloat x)
3329 /* --------------------------------------------------------------------------
3330    Draw underline, overline, and strike-through on glyph string s.
3331    -------------------------------------------------------------------------- */
3333   if (s->for_overlaps)
3334     return;
3336   /* Do underline. */
3337   if (face->underline_p)
3338     {
3339       if (s->face->underline_type == FACE_UNDER_WAVE)
3340         {
3341           if (face->underline_defaulted_p)
3342             [defaultCol set];
3343           else
3344             [ns_lookup_indexed_color (face->underline_color, s->f) set];
3346           ns_draw_underwave (s, width, x);
3347         }
3348       else if (s->face->underline_type == FACE_UNDER_LINE)
3349         {
3351           NSRect r;
3352           unsigned long thickness, position;
3354           /* If the prev was underlined, match its appearance. */
3355           if (s->prev && s->prev->face->underline_p
3356               && s->prev->face->underline_type == FACE_UNDER_LINE
3357               && s->prev->underline_thickness > 0)
3358             {
3359               thickness = s->prev->underline_thickness;
3360               position = s->prev->underline_position;
3361             }
3362           else
3363             {
3364               struct font *font = font_for_underline_metrics (s);
3365               unsigned long descent = s->y + s->height - s->ybase;
3367               /* Use underline thickness of font, defaulting to 1. */
3368               thickness = (font && font->underline_thickness > 0)
3369                 ? font->underline_thickness : 1;
3371               /* Determine the offset of underlining from the baseline. */
3372               if (x_underline_at_descent_line)
3373                 position = descent - thickness;
3374               else if (x_use_underline_position_properties
3375                        && font && font->underline_position >= 0)
3376                 position = font->underline_position;
3377               else if (font)
3378                 position = lround (font->descent / 2);
3379               else
3380                 position = underline_minimum_offset;
3382               position = max (position, underline_minimum_offset);
3384               /* Ensure underlining is not cropped. */
3385               if (descent <= position)
3386                 {
3387                   position = descent - 1;
3388                   thickness = 1;
3389                 }
3390               else if (descent < position + thickness)
3391                 thickness = 1;
3392             }
3394           s->underline_thickness = thickness;
3395           s->underline_position = position;
3397           r = NSMakeRect (x, s->ybase + position, width, thickness);
3399           if (face->underline_defaulted_p)
3400             [defaultCol set];
3401           else
3402             [ns_lookup_indexed_color (face->underline_color, s->f) set];
3403           NSRectFill (r);
3404         }
3405     }
3406   /* Do overline. We follow other terms in using a thickness of 1
3407      and ignoring overline_margin. */
3408   if (face->overline_p)
3409     {
3410       NSRect r;
3411       r = NSMakeRect (x, s->y, width, 1);
3413       if (face->overline_color_defaulted_p)
3414         [defaultCol set];
3415       else
3416         [ns_lookup_indexed_color (face->overline_color, s->f) set];
3417       NSRectFill (r);
3418     }
3420   /* Do strike-through.  We follow other terms for thickness and
3421      vertical position.*/
3422   if (face->strike_through_p)
3423     {
3424       NSRect r;
3425       /* Y-coordinate and height of the glyph string's first glyph.
3426          We cannot use s->y and s->height because those could be
3427          larger if there are taller display elements (e.g., characters
3428          displayed with a larger font) in the same glyph row.  */
3429       int glyph_y = s->ybase - s->first_glyph->ascent;
3430       int glyph_height = s->first_glyph->ascent + s->first_glyph->descent;
3431       /* Strike-through width and offset from the glyph string's
3432          top edge.  */
3433       unsigned long h = 1;
3434       unsigned long dy;
3436       dy = lrint ((glyph_height - h) / 2);
3437       r = NSMakeRect (x, glyph_y + dy, width, 1);
3439       if (face->strike_through_color_defaulted_p)
3440         [defaultCol set];
3441       else
3442         [ns_lookup_indexed_color (face->strike_through_color, s->f) set];
3443       NSRectFill (r);
3444     }
3447 static void
3448 ns_draw_box (NSRect r, CGFloat thickness, NSColor *col,
3449              char left_p, char right_p)
3450 /* --------------------------------------------------------------------------
3451     Draw an unfilled rect inside r, optionally leaving left and/or right open.
3452     Note we can't just use an NSDrawRect command, because of the possibility
3453     of some sides not being drawn, and because the rect will be filled.
3454    -------------------------------------------------------------------------- */
3456   NSRect s = r;
3457   [col set];
3459   /* top, bottom */
3460   s.size.height = thickness;
3461   NSRectFill (s);
3462   s.origin.y += r.size.height - thickness;
3463   NSRectFill (s);
3465   s.size.height = r.size.height;
3466   s.origin.y = r.origin.y;
3468   /* left, right (optional) */
3469   s.size.width = thickness;
3470   if (left_p)
3471     NSRectFill (s);
3472   if (right_p)
3473     {
3474       s.origin.x += r.size.width - thickness;
3475       NSRectFill (s);
3476     }
3480 static void
3481 ns_draw_relief (NSRect r, int thickness, char raised_p,
3482                char top_p, char bottom_p, char left_p, char right_p,
3483                struct glyph_string *s)
3484 /* --------------------------------------------------------------------------
3485     Draw a relief rect inside r, optionally leaving some sides open.
3486     Note we can't just use an NSDrawBezel command, because of the possibility
3487     of some sides not being drawn, and because the rect will be filled.
3488    -------------------------------------------------------------------------- */
3490   static NSColor *baseCol = nil, *lightCol = nil, *darkCol = nil;
3491   NSColor *newBaseCol = nil;
3492   NSRect sr = r;
3494   NSTRACE ("ns_draw_relief");
3496   /* set up colors */
3498   if (s->face->use_box_color_for_shadows_p)
3499     {
3500       newBaseCol = ns_lookup_indexed_color (s->face->box_color, s->f);
3501     }
3502 /*     else if (s->first_glyph->type == IMAGE_GLYPH
3503            && s->img->pixmap
3504            && !IMAGE_BACKGROUND_TRANSPARENT (s->img, s->f, 0))
3505        {
3506          newBaseCol = IMAGE_BACKGROUND  (s->img, s->f, 0);
3507        } */
3508   else
3509     {
3510       newBaseCol = ns_lookup_indexed_color (s->face->background, s->f);
3511     }
3513   if (newBaseCol == nil)
3514     newBaseCol = [NSColor grayColor];
3516   if (newBaseCol != baseCol)  /* TODO: better check */
3517     {
3518       [baseCol release];
3519       baseCol = [newBaseCol retain];
3520       [lightCol release];
3521       lightCol = [[baseCol highlightWithLevel: 0.2] retain];
3522       [darkCol release];
3523       darkCol = [[baseCol shadowWithLevel: 0.3] retain];
3524     }
3526   [(raised_p ? lightCol : darkCol) set];
3528   /* TODO: mitering. Using NSBezierPath doesn't work because of color switch. */
3530   /* top */
3531   sr.size.height = thickness;
3532   if (top_p) NSRectFill (sr);
3534   /* left */
3535   sr.size.height = r.size.height;
3536   sr.size.width = thickness;
3537   if (left_p) NSRectFill (sr);
3539   [(raised_p ? darkCol : lightCol) set];
3541   /* bottom */
3542   sr.size.width = r.size.width;
3543   sr.size.height = thickness;
3544   sr.origin.y += r.size.height - thickness;
3545   if (bottom_p) NSRectFill (sr);
3547   /* right */
3548   sr.size.height = r.size.height;
3549   sr.origin.y = r.origin.y;
3550   sr.size.width = thickness;
3551   sr.origin.x += r.size.width - thickness;
3552   if (right_p) NSRectFill (sr);
3556 static void
3557 ns_dumpglyphs_box_or_relief (struct glyph_string *s)
3558 /* --------------------------------------------------------------------------
3559       Function modeled after x_draw_glyph_string_box ().
3560       Sets up parameters for drawing.
3561    -------------------------------------------------------------------------- */
3563   int right_x, last_x;
3564   char left_p, right_p;
3565   struct glyph *last_glyph;
3566   NSRect r;
3567   int thickness;
3568   struct face *face;
3570   if (s->hl == DRAW_MOUSE_FACE)
3571     {
3572       face = FACE_FROM_ID_OR_NULL (s->f,
3573                                    MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3574       if (!face)
3575         face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3576     }
3577   else
3578     face = s->face;
3580   thickness = face->box_line_width;
3582   NSTRACE ("ns_dumpglyphs_box_or_relief");
3584   last_x = ((s->row->full_width_p && !s->w->pseudo_window_p)
3585             ? WINDOW_RIGHT_EDGE_X (s->w)
3586             : window_box_right (s->w, s->area));
3587   last_glyph = (s->cmp || s->img
3588                 ? s->first_glyph : s->first_glyph + s->nchars-1);
3590   right_x = ((s->row->full_width_p && s->extends_to_end_of_line_p
3591               ? last_x - 1 : min (last_x, s->x + s->background_width) - 1));
3593   left_p = (s->first_glyph->left_box_line_p
3594             || (s->hl == DRAW_MOUSE_FACE
3595                 && (s->prev == NULL || s->prev->hl != s->hl)));
3596   right_p = (last_glyph->right_box_line_p
3597              || (s->hl == DRAW_MOUSE_FACE
3598                  && (s->next == NULL || s->next->hl != s->hl)));
3600   r = NSMakeRect (s->x, s->y, right_x - s->x + 1, s->height);
3602   /* TODO: Sometimes box_color is 0 and this seems wrong; should investigate. */
3603   if (s->face->box == FACE_SIMPLE_BOX && s->face->box_color)
3604     {
3605       ns_draw_box (r, abs (thickness),
3606                    ns_lookup_indexed_color (face->box_color, s->f),
3607                   left_p, right_p);
3608     }
3609   else
3610     {
3611       ns_draw_relief (r, abs (thickness), s->face->box == FACE_RAISED_BOX,
3612                      1, 1, left_p, right_p, s);
3613     }
3617 static void
3618 ns_maybe_dumpglyphs_background (struct glyph_string *s, char force_p)
3619 /* --------------------------------------------------------------------------
3620       Modeled after x_draw_glyph_string_background, which draws BG in
3621       certain cases.  Others are left to the text rendering routine.
3622    -------------------------------------------------------------------------- */
3624   NSTRACE ("ns_maybe_dumpglyphs_background");
3626   if (!s->background_filled_p/* || s->hl == DRAW_MOUSE_FACE*/)
3627     {
3628       int box_line_width = max (s->face->box_line_width, 0);
3629       if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width
3630           /* When xdisp.c ignores FONT_HEIGHT, we cannot trust font
3631              dimensions, since the actual glyphs might be much
3632              smaller.  So in that case we always clear the rectangle
3633              with background color.  */
3634           || FONT_TOO_HIGH (s->font)
3635           || s->font_not_found_p || s->extends_to_end_of_line_p || force_p)
3636         {
3637           struct face *face;
3638           if (s->hl == DRAW_MOUSE_FACE)
3639             {
3640               face
3641                 = FACE_FROM_ID_OR_NULL (s->f,
3642                                         MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3643               if (!face)
3644                 face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3645             }
3646           else
3647             face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3648           if (!face->stipple)
3649             [(NS_FACE_BACKGROUND (face) != 0
3650               ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
3651               : FRAME_BACKGROUND_COLOR (s->f)) set];
3652           else
3653             {
3654               struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
3655               [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set];
3656             }
3658           if (s->hl != DRAW_CURSOR)
3659             {
3660               NSRect r = NSMakeRect (s->x, s->y + box_line_width,
3661                                     s->background_width,
3662                                     s->height-2*box_line_width);
3663               NSRectFill (r);
3664             }
3666           s->background_filled_p = 1;
3667         }
3668     }
3672 static void
3673 ns_dumpglyphs_image (struct glyph_string *s, NSRect r)
3674 /* --------------------------------------------------------------------------
3675       Renders an image and associated borders.
3676    -------------------------------------------------------------------------- */
3678   EmacsImage *img = s->img->pixmap;
3679   int box_line_vwidth = max (s->face->box_line_width, 0);
3680   int x = s->x, y = s->ybase - image_ascent (s->img, s->face, &s->slice);
3681   int bg_x, bg_y, bg_height;
3682   int th;
3683   char raised_p;
3684   NSRect br;
3685   struct face *face;
3686   NSColor *tdCol;
3688   NSTRACE ("ns_dumpglyphs_image");
3690   if (s->face->box != FACE_NO_BOX
3691       && s->first_glyph->left_box_line_p && s->slice.x == 0)
3692     x += abs (s->face->box_line_width);
3694   bg_x = x;
3695   bg_y =  s->slice.y == 0 ? s->y : s->y + box_line_vwidth;
3696   bg_height = s->height;
3697   /* other terms have this, but was causing problems w/tabbar mode */
3698   /* - 2 * box_line_vwidth; */
3700   if (s->slice.x == 0) x += s->img->hmargin;
3701   if (s->slice.y == 0) y += s->img->vmargin;
3703   /* Draw BG: if we need larger area than image itself cleared, do that,
3704      otherwise, since we composite the image under NS (instead of mucking
3705      with its background color), we must clear just the image area. */
3706   if (s->hl == DRAW_MOUSE_FACE)
3707     {
3708       face = FACE_FROM_ID_OR_NULL (s->f,
3709                                    MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3710       if (!face)
3711        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3712     }
3713   else
3714     face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3716   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f) set];
3718   if (bg_height > s->slice.height || s->img->hmargin || s->img->vmargin
3719       || s->img->mask || s->img->pixmap == 0 || s->width != s->background_width)
3720     {
3721       br = NSMakeRect (bg_x, bg_y, s->background_width, bg_height);
3722       s->background_filled_p = 1;
3723     }
3724   else
3725     {
3726       br = NSMakeRect (x, y, s->slice.width, s->slice.height);
3727     }
3729   NSRectFill (br);
3731   /* Draw the image.. do we need to draw placeholder if img ==nil? */
3732   if (img != nil)
3733     {
3734 #ifdef NS_IMPL_COCOA
3735       NSRect dr = NSMakeRect (x, y, s->slice.width, s->slice.height);
3736       NSRect ir = NSMakeRect (s->slice.x,
3737                               s->img->height - s->slice.y - s->slice.height,
3738                               s->slice.width, s->slice.height);
3739       [img drawInRect: dr
3740              fromRect: ir
3741              operation: NSCompositingOperationSourceOver
3742               fraction: 1.0
3743            respectFlipped: YES
3744                 hints: nil];
3745 #else
3746       [img compositeToPoint: NSMakePoint (x, y + s->slice.height)
3747                   operation: NSCompositingOperationSourceOver];
3748 #endif
3749     }
3751   if (s->hl == DRAW_CURSOR)
3752     {
3753     [FRAME_CURSOR_COLOR (s->f) set];
3754     if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
3755       tdCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
3756     else
3757       /* Currently on NS img->mask is always 0. Since
3758          get_window_cursor_type specifies a hollow box cursor when on
3759          a non-masked image we never reach this clause. But we put it
3760          in, in anticipation of better support for image masks on
3761          NS. */
3762       tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3763     }
3764   else
3765     {
3766       tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3767     }
3769   /* Draw underline, overline, strike-through. */
3770   ns_draw_text_decoration (s, face, tdCol, br.size.width, br.origin.x);
3772   /* Draw relief, if requested */
3773   if (s->img->relief || s->hl ==DRAW_IMAGE_RAISED || s->hl ==DRAW_IMAGE_SUNKEN)
3774     {
3775       if (s->hl == DRAW_IMAGE_SUNKEN || s->hl == DRAW_IMAGE_RAISED)
3776         {
3777           th = tool_bar_button_relief >= 0 ?
3778             tool_bar_button_relief : DEFAULT_TOOL_BAR_BUTTON_RELIEF;
3779           raised_p = (s->hl == DRAW_IMAGE_RAISED);
3780         }
3781       else
3782         {
3783           th = abs (s->img->relief);
3784           raised_p = (s->img->relief > 0);
3785         }
3787       r.origin.x = x - th;
3788       r.origin.y = y - th;
3789       r.size.width = s->slice.width + 2*th-1;
3790       r.size.height = s->slice.height + 2*th-1;
3791       ns_draw_relief (r, th, raised_p,
3792                       s->slice.y == 0,
3793                       s->slice.y + s->slice.height == s->img->height,
3794                       s->slice.x == 0,
3795                       s->slice.x + s->slice.width == s->img->width, s);
3796     }
3798   /* If there is no mask, the background won't be seen,
3799      so draw a rectangle on the image for the cursor.
3800      Do this for all images, getting transparency right is not reliable.  */
3801   if (s->hl == DRAW_CURSOR)
3802     {
3803       int thickness = abs (s->img->relief);
3804       if (thickness == 0) thickness = 1;
3805       ns_draw_box (br, thickness, FRAME_CURSOR_COLOR (s->f), 1, 1);
3806     }
3810 static void
3811 ns_dumpglyphs_stretch (struct glyph_string *s)
3813   NSRect r[2];
3814   int n, i;
3815   struct face *face;
3816   NSColor *fgCol, *bgCol;
3818   if (!s->background_filled_p)
3819     {
3820       n = ns_get_glyph_string_clip_rect (s, r);
3821       *r = NSMakeRect (s->x, s->y, s->background_width, s->height);
3823       ns_focus (s->f, r, n);
3825       if (s->hl == DRAW_MOUSE_FACE)
3826        {
3827          face = FACE_FROM_ID_OR_NULL (s->f,
3828                                       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3829          if (!face)
3830            face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3831        }
3832       else
3833        face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3835       bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
3836       fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3838       for (i = 0; i < n; ++i)
3839         {
3840           if (!s->row->full_width_p)
3841             {
3842               int overrun, leftoverrun;
3844               /* truncate to avoid overwriting fringe and/or scrollbar */
3845               overrun = max (0, (s->x + s->background_width)
3846                              - (WINDOW_BOX_RIGHT_EDGE_X (s->w)
3847                                 - WINDOW_RIGHT_FRINGE_WIDTH (s->w)));
3848               r[i].size.width -= overrun;
3850               /* truncate to avoid overwriting to left of the window box */
3851               leftoverrun = (WINDOW_BOX_LEFT_EDGE_X (s->w)
3852                              + WINDOW_LEFT_FRINGE_WIDTH (s->w)) - s->x;
3854               if (leftoverrun > 0)
3855                 {
3856                   r[i].origin.x += leftoverrun;
3857                   r[i].size.width -= leftoverrun;
3858                 }
3860               /* XXX: Try to work between problem where a stretch glyph on
3861                  a partially-visible bottom row will clear part of the
3862                  modeline, and another where list-buffers headers and similar
3863                  rows erroneously have visible_height set to 0.  Not sure
3864                  where this is coming from as other terms seem not to show. */
3865               r[i].size.height = min (s->height, s->row->visible_height);
3866             }
3868           [bgCol set];
3870           /* NOTE: under NS this is NOT used to draw cursors, but we must avoid
3871              overwriting cursor (usually when cursor on a tab) */
3872           if (s->hl == DRAW_CURSOR)
3873             {
3874               CGFloat x, width;
3876               x = r[i].origin.x;
3877               width = s->w->phys_cursor_width;
3878               r[i].size.width -= width;
3879               r[i].origin.x += width;
3881               NSRectFill (r[i]);
3883               /* Draw overlining, etc. on the cursor. */
3884               if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
3885                 ns_draw_text_decoration (s, face, bgCol, width, x);
3886               else
3887                 ns_draw_text_decoration (s, face, fgCol, width, x);
3888             }
3889           else
3890             {
3891               NSRectFill (r[i]);
3892             }
3894           /* Draw overlining, etc. on the stretch glyph (or the part
3895              of the stretch glyph after the cursor). */
3896           ns_draw_text_decoration (s, face, fgCol, r[i].size.width,
3897                                    r[i].origin.x);
3898         }
3899       ns_unfocus (s->f);
3900       s->background_filled_p = 1;
3901     }
3905 static void
3906 ns_draw_glyph_string_foreground (struct glyph_string *s)
3908   int x, flags;
3909   struct font *font = s->font;
3911   /* If first glyph of S has a left box line, start drawing the text
3912      of S to the right of that box line.  */
3913   if (s->face && s->face->box != FACE_NO_BOX
3914       && s->first_glyph->left_box_line_p)
3915     x = s->x + eabs (s->face->box_line_width);
3916   else
3917     x = s->x;
3919   flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
3920     (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
3921      (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
3922       NS_DUMPGLYPH_NORMAL));
3924   font->driver->draw
3925     (s, s->cmp_from, s->nchars, x, s->ybase,
3926      (flags == NS_DUMPGLYPH_NORMAL && !s->background_filled_p)
3927      || flags == NS_DUMPGLYPH_MOUSEFACE);
3931 static void
3932 ns_draw_composite_glyph_string_foreground (struct glyph_string *s)
3934   int i, j, x;
3935   struct font *font = s->font;
3937   /* If first glyph of S has a left box line, start drawing the text
3938      of S to the right of that box line.  */
3939   if (s->face && s->face->box != FACE_NO_BOX
3940       && s->first_glyph->left_box_line_p)
3941     x = s->x + eabs (s->face->box_line_width);
3942   else
3943     x = s->x;
3945   /* S is a glyph string for a composition.  S->cmp_from is the index
3946      of the first character drawn for glyphs of this composition.
3947      S->cmp_from == 0 means we are drawing the very first character of
3948      this composition.  */
3950   /* Draw a rectangle for the composition if the font for the very
3951      first character of the composition could not be loaded.  */
3952   if (s->font_not_found_p)
3953     {
3954       if (s->cmp_from == 0)
3955         {
3956           NSRect r = NSMakeRect (s->x, s->y, s->width-1, s->height -1);
3957           ns_draw_box (r, 1, FRAME_CURSOR_COLOR (s->f), 1, 1);
3958         }
3959     }
3960   else if (! s->first_glyph->u.cmp.automatic)
3961     {
3962       int y = s->ybase;
3964       for (i = 0, j = s->cmp_from; i < s->nchars; i++, j++)
3965         /* TAB in a composition means display glyphs with padding
3966            space on the left or right.  */
3967         if (COMPOSITION_GLYPH (s->cmp, j) != '\t')
3968           {
3969             int xx = x + s->cmp->offsets[j * 2];
3970             int yy = y - s->cmp->offsets[j * 2 + 1];
3972             font->driver->draw (s, j, j + 1, xx, yy, false);
3973             if (s->face->overstrike)
3974               font->driver->draw (s, j, j + 1, xx + 1, yy, false);
3975           }
3976     }
3977   else
3978     {
3979       Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
3980       Lisp_Object glyph;
3981       int y = s->ybase;
3982       int width = 0;
3984       for (i = j = s->cmp_from; i < s->cmp_to; i++)
3985         {
3986           glyph = LGSTRING_GLYPH (gstring, i);
3987           if (NILP (LGLYPH_ADJUSTMENT (glyph)))
3988             width += LGLYPH_WIDTH (glyph);
3989           else
3990             {
3991               int xoff, yoff, wadjust;
3993               if (j < i)
3994                 {
3995                   font->driver->draw (s, j, i, x, y, false);
3996                   if (s->face->overstrike)
3997                     font->driver->draw (s, j, i, x + 1, y, false);
3998                   x += width;
3999                 }
4000               xoff = LGLYPH_XOFF (glyph);
4001               yoff = LGLYPH_YOFF (glyph);
4002               wadjust = LGLYPH_WADJUST (glyph);
4003               font->driver->draw (s, i, i + 1, x + xoff, y + yoff, false);
4004               if (s->face->overstrike)
4005                 font->driver->draw (s, i, i + 1, x + xoff + 1, y + yoff,
4006                                     false);
4007               x += wadjust;
4008               j = i + 1;
4009               width = 0;
4010             }
4011         }
4012       if (j < i)
4013         {
4014           font->driver->draw (s, j, i, x, y, false);
4015           if (s->face->overstrike)
4016             font->driver->draw (s, j, i, x + 1, y, false);
4017         }
4018     }
4021 static void
4022 ns_draw_glyph_string (struct glyph_string *s)
4023 /* --------------------------------------------------------------------------
4024       External (RIF): Main draw-text call.
4025    -------------------------------------------------------------------------- */
4027   /* TODO (optimize): focus for box and contents draw */
4028   NSRect r[2];
4029   int n;
4030   char box_drawn_p = 0;
4031   struct font *font = s->face->font;
4032   if (! font) font = FRAME_FONT (s->f);
4034   NSTRACE_WHEN (NSTRACE_GROUP_GLYPHS, "ns_draw_glyph_string");
4036   if (s->next && s->right_overhang && !s->for_overlaps/*&&s->hl!=DRAW_CURSOR*/)
4037     {
4038       int width;
4039       struct glyph_string *next;
4041       for (width = 0, next = s->next;
4042            next && width < s->right_overhang;
4043            width += next->width, next = next->next)
4044         if (next->first_glyph->type != IMAGE_GLYPH)
4045           {
4046             if (next->first_glyph->type != STRETCH_GLYPH)
4047               {
4048                 n = ns_get_glyph_string_clip_rect (s->next, r);
4049                 ns_focus (s->f, r, n);
4050                 ns_maybe_dumpglyphs_background (s->next, 1);
4051                 ns_unfocus (s->f);
4052               }
4053             else
4054               {
4055                 ns_dumpglyphs_stretch (s->next);
4056               }
4057             next->num_clips = 0;
4058           }
4059     }
4061   if (!s->for_overlaps && s->face->box != FACE_NO_BOX
4062         && (s->first_glyph->type == CHAR_GLYPH
4063             || s->first_glyph->type == COMPOSITE_GLYPH))
4064     {
4065       n = ns_get_glyph_string_clip_rect (s, r);
4066       ns_focus (s->f, r, n);
4067       ns_maybe_dumpglyphs_background (s, 1);
4068       ns_dumpglyphs_box_or_relief (s);
4069       ns_unfocus (s->f);
4070       box_drawn_p = 1;
4071     }
4073   switch (s->first_glyph->type)
4074     {
4076     case IMAGE_GLYPH:
4077       n = ns_get_glyph_string_clip_rect (s, r);
4078       ns_focus (s->f, r, n);
4079       ns_dumpglyphs_image (s, r[0]);
4080       ns_unfocus (s->f);
4081       break;
4083     case STRETCH_GLYPH:
4084       ns_dumpglyphs_stretch (s);
4085       break;
4087     case CHAR_GLYPH:
4088     case COMPOSITE_GLYPH:
4089       n = ns_get_glyph_string_clip_rect (s, r);
4090       ns_focus (s->f, r, n);
4092       if (s->for_overlaps || (s->cmp_from > 0
4093                               && ! s->first_glyph->u.cmp.automatic))
4094         s->background_filled_p = 1;
4095       else
4096         ns_maybe_dumpglyphs_background
4097           (s, s->first_glyph->type == COMPOSITE_GLYPH);
4099       if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
4100         {
4101           unsigned long tmp = NS_FACE_BACKGROUND (s->face);
4102           NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
4103           NS_FACE_FOREGROUND (s->face) = tmp;
4104         }
4106       {
4107         BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
4109         if (isComposite)
4110           ns_draw_composite_glyph_string_foreground (s);
4111         else
4112           ns_draw_glyph_string_foreground (s);
4113       }
4115       {
4116         NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
4117                         ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face),
4118                                                    s->f)
4119                         : FRAME_FOREGROUND_COLOR (s->f));
4120         [col set];
4122         /* Draw underline, overline, strike-through. */
4123         ns_draw_text_decoration (s, s->face, col, s->width, s->x);
4124       }
4126       if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
4127         {
4128           unsigned long tmp = NS_FACE_BACKGROUND (s->face);
4129           NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
4130           NS_FACE_FOREGROUND (s->face) = tmp;
4131         }
4133       ns_unfocus (s->f);
4134       break;
4136     case GLYPHLESS_GLYPH:
4137       n = ns_get_glyph_string_clip_rect (s, r);
4138       ns_focus (s->f, r, n);
4140       if (s->for_overlaps || (s->cmp_from > 0
4141                               && ! s->first_glyph->u.cmp.automatic))
4142         s->background_filled_p = 1;
4143       else
4144         ns_maybe_dumpglyphs_background
4145           (s, s->first_glyph->type == COMPOSITE_GLYPH);
4146       /* ... */
4147       /* Not yet implemented.  */
4148       /* ... */
4149       ns_unfocus (s->f);
4150       break;
4152     default:
4153       emacs_abort ();
4154     }
4156   /* Draw box if not done already. */
4157   if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX)
4158     {
4159       n = ns_get_glyph_string_clip_rect (s, r);
4160       ns_focus (s->f, r, n);
4161       ns_dumpglyphs_box_or_relief (s);
4162       ns_unfocus (s->f);
4163     }
4165   s->num_clips = 0;
4170 /* ==========================================================================
4172     Event loop
4174    ========================================================================== */
4177 static void
4178 ns_send_appdefined (int value)
4179 /* --------------------------------------------------------------------------
4180     Internal: post an appdefined event which EmacsApp-sendEvent will
4181               recognize and take as a command to halt the event loop.
4182    -------------------------------------------------------------------------- */
4184   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_send_appdefined(%d)", value);
4186   // GNUstep needs postEvent to happen on the main thread.
4187   // Cocoa needs nextEventMatchingMask to happen on the main thread too.
4188   if (! [[NSThread currentThread] isMainThread])
4189     {
4190       EmacsApp *app = (EmacsApp *)NSApp;
4191       app->nextappdefined = value;
4192       [app performSelectorOnMainThread:@selector (sendFromMainThread:)
4193                             withObject:nil
4194                          waitUntilDone:NO];
4195       return;
4196     }
4198   /* Only post this event if we haven't already posted one.  This will end
4199        the [NXApp run] main loop after having processed all events queued at
4200        this moment.  */
4202 #ifdef NS_IMPL_COCOA
4203   if (! send_appdefined)
4204     {
4205       /* OS X 10.10.1 swallows the AppDefined event we are sending ourselves
4206          in certain situations (rapid incoming events).
4207          So check if we have one, if not add one.  */
4208       NSEvent *appev = [NSApp nextEventMatchingMask:NSEventMaskApplicationDefined
4209                                           untilDate:[NSDate distantPast]
4210                                              inMode:NSDefaultRunLoopMode
4211                                             dequeue:NO];
4212       if (! appev) send_appdefined = YES;
4213     }
4214 #endif
4216   if (send_appdefined)
4217     {
4218       NSEvent *nxev;
4220       /* We only need one NX_APPDEFINED event to stop NXApp from running.  */
4221       send_appdefined = NO;
4223       /* Don't need wakeup timer any more */
4224       if (timed_entry)
4225         {
4226           [timed_entry invalidate];
4227           [timed_entry release];
4228           timed_entry = nil;
4229         }
4231       nxev = [NSEvent otherEventWithType: NSEventTypeApplicationDefined
4232                                 location: NSMakePoint (0, 0)
4233                            modifierFlags: 0
4234                                timestamp: 0
4235                             windowNumber: [[NSApp mainWindow] windowNumber]
4236                                  context: [NSApp context]
4237                                  subtype: 0
4238                                    data1: value
4239                                    data2: 0];
4241       /* Post an application defined event on the event queue.  When this is
4242          received the [NXApp run] will return, thus having processed all
4243          events which are currently queued.  */
4244       [NSApp postEvent: nxev atStart: NO];
4245     }
4248 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4249 static void
4250 check_native_fs ()
4252   Lisp_Object frame, tail;
4254   if (ns_last_use_native_fullscreen == ns_use_native_fullscreen)
4255     return;
4257   ns_last_use_native_fullscreen = ns_use_native_fullscreen;
4259   FOR_EACH_FRAME (tail, frame)
4260     {
4261       struct frame *f = XFRAME (frame);
4262       if (FRAME_NS_P (f))
4263         {
4264           EmacsView *view = FRAME_NS_VIEW (f);
4265           [view updateCollectionBehavior];
4266         }
4267     }
4269 #endif
4271 /* GNUstep does not have cancelTracking.  */
4272 #ifdef NS_IMPL_COCOA
4273 /* Check if menu open should be canceled or continued as normal.  */
4274 void
4275 ns_check_menu_open (NSMenu *menu)
4277   /* Click in menu bar? */
4278   NSArray *a = [[NSApp mainMenu] itemArray];
4279   int i;
4280   BOOL found = NO;
4282   if (menu == nil) // Menu tracking ended.
4283     {
4284       if (menu_will_open_state == MENU_OPENING)
4285         menu_will_open_state = MENU_NONE;
4286       return;
4287     }
4289   for (i = 0; ! found && i < [a count]; i++)
4290     found = menu == [[a objectAtIndex:i] submenu];
4291   if (found)
4292     {
4293       if (menu_will_open_state == MENU_NONE && emacs_event)
4294         {
4295           NSEvent *theEvent = [NSApp currentEvent];
4296           struct frame *emacsframe = SELECTED_FRAME ();
4298           [menu cancelTracking];
4299           menu_will_open_state = MENU_PENDING;
4300           emacs_event->kind = MENU_BAR_ACTIVATE_EVENT;
4301           EV_TRAILER (theEvent);
4303           CGEventRef ourEvent = CGEventCreate (NULL);
4304           menu_mouse_point = CGEventGetLocation (ourEvent);
4305           CFRelease (ourEvent);
4306         }
4307       else if (menu_will_open_state == MENU_OPENING)
4308         {
4309           menu_will_open_state = MENU_NONE;
4310         }
4311     }
4314 /* Redo saved menu click if state is MENU_PENDING.  */
4315 void
4316 ns_check_pending_open_menu ()
4318   if (menu_will_open_state == MENU_PENDING)
4319     {
4320       CGEventSourceRef source
4321         = CGEventSourceCreate (kCGEventSourceStateHIDSystemState);
4323       CGEventRef event = CGEventCreateMouseEvent (source,
4324                                                   kCGEventLeftMouseDown,
4325                                                   menu_mouse_point,
4326                                                   kCGMouseButtonLeft);
4327       CGEventSetType (event, kCGEventLeftMouseDown);
4328       CGEventPost (kCGHIDEventTap, event);
4329       CFRelease (event);
4330       CFRelease (source);
4332       menu_will_open_state = MENU_OPENING;
4333     }
4335 #endif /* NS_IMPL_COCOA */
4337 static int
4338 ns_read_socket (struct terminal *terminal, struct input_event *hold_quit)
4339 /* --------------------------------------------------------------------------
4340      External (hook): Post an event to ourself and keep reading events until
4341      we read it back again.  In effect process all events which were waiting.
4342      From 21+ we have to manage the event buffer ourselves.
4343    -------------------------------------------------------------------------- */
4345   struct input_event ev;
4346   int nevents;
4348   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_read_socket");
4350 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4351   check_native_fs ();
4352 #endif
4354   if ([NSApp modalWindow] != nil)
4355     return -1;
4357   if (hold_event_q.nr > 0)
4358     {
4359       int i;
4360       for (i = 0; i < hold_event_q.nr; ++i)
4361         kbd_buffer_store_event_hold (&hold_event_q.q[i], hold_quit);
4362       hold_event_q.nr = 0;
4363       return i;
4364     }
4366   if ([NSThread isMainThread])
4367     {
4368       block_input ();
4369       n_emacs_events_pending = 0;
4370       ns_init_events (&ev);
4371       q_event_ptr = hold_quit;
4373       /* we manage autorelease pools by allocate/reallocate each time around
4374          the loop; strict nesting is occasionally violated but seems not to
4375          matter.. earlier methods using full nesting caused major memory leaks */
4376       [outerpool release];
4377       outerpool = [[NSAutoreleasePool alloc] init];
4379       /* If have pending open-file requests, attend to the next one of those. */
4380       if (ns_pending_files && [ns_pending_files count] != 0
4381           && [(EmacsApp *)NSApp openFile: [ns_pending_files objectAtIndex: 0]])
4382         {
4383           [ns_pending_files removeObjectAtIndex: 0];
4384         }
4385       /* Deal with pending service requests. */
4386       else if (ns_pending_service_names && [ns_pending_service_names count] != 0
4387                && [(EmacsApp *)
4388                     NSApp fulfillService: [ns_pending_service_names objectAtIndex: 0]
4389                                  withArg: [ns_pending_service_args objectAtIndex: 0]])
4390         {
4391           [ns_pending_service_names removeObjectAtIndex: 0];
4392           [ns_pending_service_args removeObjectAtIndex: 0];
4393         }
4394       else
4395         {
4396           /* Run and wait for events.  We must always send one NX_APPDEFINED event
4397              to ourself, otherwise [NXApp run] will never exit.  */
4398           send_appdefined = YES;
4399           ns_send_appdefined (-1);
4401           [NSApp run];
4402         }
4404       nevents = n_emacs_events_pending;
4405       n_emacs_events_pending = 0;
4406       ns_finish_events ();
4407       q_event_ptr = NULL;
4408       unblock_input ();
4409     }
4410   else
4411     return -1;
4413   return nevents;
4418 ns_select (int nfds, fd_set *readfds, fd_set *writefds,
4419            fd_set *exceptfds, struct timespec *timeout,
4420            sigset_t *sigmask)
4421 /* --------------------------------------------------------------------------
4422      Replacement for select, checking for events
4423    -------------------------------------------------------------------------- */
4425   int result;
4426   int t, k, nr = 0;
4427   struct input_event event;
4428   char c;
4430   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_select");
4432 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
4433   check_native_fs ();
4434 #endif
4436   if (hold_event_q.nr > 0)
4437     {
4438       /* We already have events pending. */
4439       raise (SIGIO);
4440       errno = EINTR;
4441       return -1;
4442     }
4444   for (k = 0; k < nfds+1; k++)
4445     {
4446       if (readfds && FD_ISSET(k, readfds)) ++nr;
4447       if (writefds && FD_ISSET(k, writefds)) ++nr;
4448     }
4450   if (NSApp == nil
4451       || ![NSThread isMainThread]
4452       || (timeout && timeout->tv_sec == 0 && timeout->tv_nsec == 0))
4453     return thread_select(pselect, nfds, readfds, writefds,
4454                          exceptfds, timeout, sigmask);
4455   else
4456     {
4457       struct timespec t = {0, 0};
4458       thread_select(pselect, 0, NULL, NULL, NULL, &t, sigmask);
4459     }
4461   [outerpool release];
4462   outerpool = [[NSAutoreleasePool alloc] init];
4465   send_appdefined = YES;
4466   if (nr > 0)
4467     {
4468       pthread_mutex_lock (&select_mutex);
4469       select_nfds = nfds;
4470       select_valid = 0;
4471       if (readfds)
4472         {
4473           select_readfds = *readfds;
4474           select_valid += SELECT_HAVE_READ;
4475         }
4476       if (writefds)
4477         {
4478           select_writefds = *writefds;
4479           select_valid += SELECT_HAVE_WRITE;
4480         }
4482       if (timeout)
4483         {
4484           select_timeout = *timeout;
4485           select_valid += SELECT_HAVE_TMO;
4486         }
4488       pthread_mutex_unlock (&select_mutex);
4490       /* Inform fd_handler that select should be called */
4491       c = 'g';
4492       emacs_write_sig (selfds[1], &c, 1);
4493     }
4494   else if (nr == 0 && timeout)
4495     {
4496       /* No file descriptor, just a timeout, no need to wake fd_handler  */
4497       double time = timespectod (*timeout);
4498       timed_entry = [[NSTimer scheduledTimerWithTimeInterval: time
4499                                                       target: NSApp
4500                                                     selector:
4501                                   @selector (timeout_handler:)
4502                                                     userInfo: 0
4503                                                      repeats: NO]
4504                       retain];
4505     }
4506   else /* No timeout and no file descriptors, can this happen?  */
4507     {
4508       /* Send appdefined so we exit from the loop */
4509       ns_send_appdefined (-1);
4510     }
4512   block_input ();
4513   ns_init_events (&event);
4515   [NSApp run];
4517   ns_finish_events ();
4518   if (nr > 0 && readfds)
4519     {
4520       c = 's';
4521       emacs_write_sig (selfds[1], &c, 1);
4522     }
4523   unblock_input ();
4525   t = last_appdefined_event_data;
4527   if (t != NO_APPDEFINED_DATA)
4528     {
4529       last_appdefined_event_data = NO_APPDEFINED_DATA;
4531       if (t == -2)
4532         {
4533           /* The NX_APPDEFINED event we received was a timeout. */
4534           result = 0;
4535         }
4536       else if (t == -1)
4537         {
4538           /* The NX_APPDEFINED event we received was the result of
4539              at least one real input event arriving.  */
4540           errno = EINTR;
4541           result = -1;
4542         }
4543       else
4544         {
4545           /* Received back from select () in fd_handler; copy the results */
4546           pthread_mutex_lock (&select_mutex);
4547           if (readfds) *readfds = select_readfds;
4548           if (writefds) *writefds = select_writefds;
4549           pthread_mutex_unlock (&select_mutex);
4550           result = t;
4551         }
4552     }
4553   else
4554     {
4555       errno = EINTR;
4556       result = -1;
4557     }
4559   return result;
4562 #ifdef HAVE_PTHREAD
4563 void
4564 ns_run_loop_break ()
4565 /* Break out of the NS run loop in ns_select or ns_read_socket. */
4567   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_run_loop_break");
4569   /* If we don't have a GUI, don't send the event. */
4570   if (NSApp != NULL)
4571     ns_send_appdefined(-1);
4573 #endif
4576 /* ==========================================================================
4578     Scrollbar handling
4580    ========================================================================== */
4583 static void
4584 ns_set_vertical_scroll_bar (struct window *window,
4585                            int portion, int whole, int position)
4586 /* --------------------------------------------------------------------------
4587       External (hook): Update or add scrollbar
4588    -------------------------------------------------------------------------- */
4590   Lisp_Object win;
4591   NSRect r, v;
4592   struct frame *f = XFRAME (WINDOW_FRAME (window));
4593   EmacsView *view = FRAME_NS_VIEW (f);
4594   EmacsScroller *bar;
4595   int window_y, window_height;
4596   int top, left, height, width;
4597   BOOL update_p = YES;
4599   /* optimization; display engine sends WAY too many of these.. */
4600   if (!NILP (window->vertical_scroll_bar))
4601     {
4602       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4603       if ([bar checkSamePosition: position portion: portion whole: whole])
4604         {
4605           if (view->scrollbarsNeedingUpdate == 0)
4606             {
4607               if (!windows_or_buffers_changed)
4608                   return;
4609             }
4610           else
4611             view->scrollbarsNeedingUpdate--;
4612           update_p = NO;
4613         }
4614     }
4616   NSTRACE ("ns_set_vertical_scroll_bar");
4618   /* Get dimensions.  */
4619   window_box (window, ANY_AREA, 0, &window_y, 0, &window_height);
4620   top = window_y;
4621   height = window_height;
4622   width = NS_SCROLL_BAR_WIDTH (f);
4623   left = WINDOW_SCROLL_BAR_AREA_X (window);
4625   r = NSMakeRect (left, top, width, height);
4626   /* the parent view is flipped, so we need to flip y value */
4627   v = [view frame];
4628   r.origin.y = (v.size.height - r.size.height - r.origin.y);
4630   XSETWINDOW (win, window);
4631   block_input ();
4633   /* we want at least 5 lines to display a scrollbar */
4634   if (WINDOW_TOTAL_LINES (window) < 5)
4635     {
4636       if (!NILP (window->vertical_scroll_bar))
4637         {
4638           bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4639           [bar removeFromSuperview];
4640           wset_vertical_scroll_bar (window, Qnil);
4641           [bar release];
4642         }
4643       ns_clear_frame_area (f, left, top, width, height);
4644       unblock_input ();
4645       return;
4646     }
4648   if (NILP (window->vertical_scroll_bar))
4649     {
4650       if (width > 0 && height > 0)
4651         ns_clear_frame_area (f, left, top, width, height);
4653       bar = [[EmacsScroller alloc] initFrame: r window: win];
4654       wset_vertical_scroll_bar (window, make_save_ptr (bar));
4655       update_p = YES;
4656     }
4657   else
4658     {
4659       NSRect oldRect;
4660       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4661       oldRect = [bar frame];
4662       r.size.width = oldRect.size.width;
4663       if (FRAME_LIVE_P (f) && !NSEqualRects (oldRect, r))
4664         {
4665           if (oldRect.origin.x != r.origin.x)
4666               ns_clear_frame_area (f, left, top, width, height);
4667           [bar setFrame: r];
4668         }
4669     }
4671   if (update_p)
4672     [bar setPosition: position portion: portion whole: whole];
4673   unblock_input ();
4677 static void
4678 ns_set_horizontal_scroll_bar (struct window *window,
4679                               int portion, int whole, int position)
4680 /* --------------------------------------------------------------------------
4681       External (hook): Update or add scrollbar
4682    -------------------------------------------------------------------------- */
4684   Lisp_Object win;
4685   NSRect r, v;
4686   struct frame *f = XFRAME (WINDOW_FRAME (window));
4687   EmacsView *view = FRAME_NS_VIEW (f);
4688   EmacsScroller *bar;
4689   int top, height, left, width;
4690   int window_x, window_width;
4691   BOOL update_p = YES;
4693   /* optimization; display engine sends WAY too many of these.. */
4694   if (!NILP (window->horizontal_scroll_bar))
4695     {
4696       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4697       if ([bar checkSamePosition: position portion: portion whole: whole])
4698         {
4699           if (view->scrollbarsNeedingUpdate == 0)
4700             {
4701               if (!windows_or_buffers_changed)
4702                   return;
4703             }
4704           else
4705             view->scrollbarsNeedingUpdate--;
4706           update_p = NO;
4707         }
4708     }
4710   NSTRACE ("ns_set_horizontal_scroll_bar");
4712   /* Get dimensions.  */
4713   window_box (window, ANY_AREA, &window_x, 0, &window_width, 0);
4714   left = window_x;
4715   width = window_width;
4716   height = NS_SCROLL_BAR_HEIGHT (f);
4717   top = WINDOW_SCROLL_BAR_AREA_Y (window);
4719   r = NSMakeRect (left, top, width, height);
4720   /* the parent view is flipped, so we need to flip y value */
4721   v = [view frame];
4722   r.origin.y = (v.size.height - r.size.height - r.origin.y);
4724   XSETWINDOW (win, window);
4725   block_input ();
4727   if (NILP (window->horizontal_scroll_bar))
4728     {
4729       if (width > 0 && height > 0)
4730         ns_clear_frame_area (f, left, top, width, height);
4732       bar = [[EmacsScroller alloc] initFrame: r window: win];
4733       wset_horizontal_scroll_bar (window, make_save_ptr (bar));
4734       update_p = YES;
4735     }
4736   else
4737     {
4738       NSRect oldRect;
4739       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4740       oldRect = [bar frame];
4741       if (FRAME_LIVE_P (f) && !NSEqualRects (oldRect, r))
4742         {
4743           if (oldRect.origin.y != r.origin.y)
4744             ns_clear_frame_area (f, left, top, width, height);
4745           [bar setFrame: r];
4746           update_p = YES;
4747         }
4748     }
4750   /* If there are both horizontal and vertical scroll-bars they leave
4751      a square that belongs to neither. We need to clear it otherwise
4752      it fills with junk. */
4753   if (!NILP (window->vertical_scroll_bar))
4754     ns_clear_frame_area (f, WINDOW_SCROLL_BAR_AREA_X (window), top,
4755                          NS_SCROLL_BAR_HEIGHT (f), height);
4757   if (update_p)
4758     [bar setPosition: position portion: portion whole: whole];
4759   unblock_input ();
4763 static void
4764 ns_condemn_scroll_bars (struct frame *f)
4765 /* --------------------------------------------------------------------------
4766      External (hook): arrange for all frame's scrollbars to be removed
4767      at next call to judge_scroll_bars, except for those redeemed.
4768    -------------------------------------------------------------------------- */
4770   int i;
4771   id view;
4772   NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews];
4774   NSTRACE ("ns_condemn_scroll_bars");
4776   for (i =[subviews count]-1; i >= 0; i--)
4777     {
4778       view = [subviews objectAtIndex: i];
4779       if ([view isKindOfClass: [EmacsScroller class]])
4780         [view condemn];
4781     }
4785 static void
4786 ns_redeem_scroll_bar (struct window *window)
4787 /* --------------------------------------------------------------------------
4788      External (hook): arrange to spare this window's scrollbar
4789      at next call to judge_scroll_bars.
4790    -------------------------------------------------------------------------- */
4792   id bar;
4793   NSTRACE ("ns_redeem_scroll_bar");
4794   if (!NILP (window->vertical_scroll_bar)
4795       && WINDOW_HAS_VERTICAL_SCROLL_BAR (window))
4796     {
4797       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4798       [bar reprieve];
4799     }
4801   if (!NILP (window->horizontal_scroll_bar)
4802       && WINDOW_HAS_HORIZONTAL_SCROLL_BAR (window))
4803     {
4804       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4805       [bar reprieve];
4806     }
4810 static void
4811 ns_judge_scroll_bars (struct frame *f)
4812 /* --------------------------------------------------------------------------
4813      External (hook): destroy all scrollbars on frame that weren't
4814      redeemed after call to condemn_scroll_bars.
4815    -------------------------------------------------------------------------- */
4817   int i;
4818   id view;
4819   EmacsView *eview = FRAME_NS_VIEW (f);
4820   NSArray *subviews = [[eview superview] subviews];
4821   BOOL removed = NO;
4823   NSTRACE ("ns_judge_scroll_bars");
4824   for (i = [subviews count]-1; i >= 0; --i)
4825     {
4826       view = [subviews objectAtIndex: i];
4827       if (![view isKindOfClass: [EmacsScroller class]]) continue;
4828       if ([view judge])
4829         removed = YES;
4830     }
4832   if (removed)
4833     [eview updateFrameSize: NO];
4836 /* ==========================================================================
4838     Initialization
4840    ========================================================================== */
4843 x_display_pixel_height (struct ns_display_info *dpyinfo)
4845   NSArray *screens = [NSScreen screens];
4846   NSEnumerator *enumerator = [screens objectEnumerator];
4847   NSScreen *screen;
4848   NSRect frame;
4850   frame = NSZeroRect;
4851   while ((screen = [enumerator nextObject]) != nil)
4852     frame = NSUnionRect (frame, [screen frame]);
4854   return NSHeight (frame);
4858 x_display_pixel_width (struct ns_display_info *dpyinfo)
4860   NSArray *screens = [NSScreen screens];
4861   NSEnumerator *enumerator = [screens objectEnumerator];
4862   NSScreen *screen;
4863   NSRect frame;
4865   frame = NSZeroRect;
4866   while ((screen = [enumerator nextObject]) != nil)
4867     frame = NSUnionRect (frame, [screen frame]);
4869   return NSWidth (frame);
4873 static Lisp_Object ns_string_to_lispmod (const char *s)
4874 /* --------------------------------------------------------------------------
4875      Convert modifier name to lisp symbol
4876    -------------------------------------------------------------------------- */
4878   if (!strncmp (SSDATA (SYMBOL_NAME (Qmeta)), s, 10))
4879     return Qmeta;
4880   else if (!strncmp (SSDATA (SYMBOL_NAME (Qsuper)), s, 10))
4881     return Qsuper;
4882   else if (!strncmp (SSDATA (SYMBOL_NAME (Qcontrol)), s, 10))
4883     return Qcontrol;
4884   else if (!strncmp (SSDATA (SYMBOL_NAME (Qalt)), s, 10))
4885     return Qalt;
4886   else if (!strncmp (SSDATA (SYMBOL_NAME (Qhyper)), s, 10))
4887     return Qhyper;
4888   else if (!strncmp (SSDATA (SYMBOL_NAME (Qnone)), s, 10))
4889     return Qnone;
4890   else
4891     return Qnil;
4895 static void
4896 ns_default (const char *parameter, Lisp_Object *result,
4897            Lisp_Object yesval, Lisp_Object noval,
4898            BOOL is_float, BOOL is_modstring)
4899 /* --------------------------------------------------------------------------
4900       Check a parameter value in user's preferences
4901    -------------------------------------------------------------------------- */
4903   const char *value = ns_get_defaults_value (parameter);
4905   if (value)
4906     {
4907       double f;
4908       char *pos;
4909       if (c_strcasecmp (value, "YES") == 0)
4910         *result = yesval;
4911       else if (c_strcasecmp (value, "NO") == 0)
4912         *result = noval;
4913       else if (is_float && (f = strtod (value, &pos), pos != value))
4914         *result = make_float (f);
4915       else if (is_modstring && value)
4916         *result = ns_string_to_lispmod (value);
4917       else fprintf (stderr,
4918                    "Bad value for default \"%s\": \"%s\"\n", parameter, value);
4919     }
4923 static void
4924 ns_initialize_display_info (struct ns_display_info *dpyinfo)
4925 /* --------------------------------------------------------------------------
4926       Initialize global info and storage for display.
4927    -------------------------------------------------------------------------- */
4929     NSScreen *screen = [NSScreen mainScreen];
4930     NSWindowDepth depth = [screen depth];
4932     dpyinfo->resx = 72.27; /* used 75.0, but this makes pt == pixel, expected */
4933     dpyinfo->resy = 72.27;
4934     dpyinfo->color_p = ![NSDeviceWhiteColorSpace isEqualToString:
4935                                                   NSColorSpaceFromDepth (depth)]
4936                 && ![NSCalibratedWhiteColorSpace isEqualToString:
4937                                                  NSColorSpaceFromDepth (depth)];
4938     dpyinfo->n_planes = NSBitsPerPixelFromDepth (depth);
4939     dpyinfo->color_table = xmalloc (sizeof *dpyinfo->color_table);
4940     dpyinfo->color_table->colors = NULL;
4941     dpyinfo->root_window = 42; /* a placeholder.. */
4942     dpyinfo->x_highlight_frame = dpyinfo->x_focus_frame = NULL;
4943     dpyinfo->n_fonts = 0;
4944     dpyinfo->smallest_font_height = 1;
4945     dpyinfo->smallest_char_width = 1;
4947     reset_mouse_highlight (&dpyinfo->mouse_highlight);
4951 /* This and next define (many of the) public functions in this file. */
4952 /* x_... are generic versions in xdisp.c that we, and other terms, get away
4953          with using despite presence in the "system dependent" redisplay
4954          interface.  In addition, many of the ns_ methods have code that is
4955          shared with all terms, indicating need for further refactoring. */
4956 extern frame_parm_handler ns_frame_parm_handlers[];
4957 static struct redisplay_interface ns_redisplay_interface =
4959   ns_frame_parm_handlers,
4960   x_produce_glyphs,
4961   x_write_glyphs,
4962   x_insert_glyphs,
4963   x_clear_end_of_line,
4964   ns_scroll_run,
4965   ns_after_update_window_line,
4966   ns_update_window_begin,
4967   ns_update_window_end,
4968   0, /* flush_display */
4969   x_clear_window_mouse_face,
4970   x_get_glyph_overhangs,
4971   x_fix_overlapping_area,
4972   ns_draw_fringe_bitmap,
4973   0, /* define_fringe_bitmap */ /* FIXME: simplify ns_draw_fringe_bitmap */
4974   0, /* destroy_fringe_bitmap */
4975   ns_compute_glyph_string_overhangs,
4976   ns_draw_glyph_string,
4977   ns_define_frame_cursor,
4978   ns_clear_frame_area,
4979   ns_draw_window_cursor,
4980   ns_draw_vertical_window_border,
4981   ns_draw_window_divider,
4982   ns_shift_glyphs_for_insert,
4983   ns_show_hourglass,
4984   ns_hide_hourglass
4988 static void
4989 ns_delete_display (struct ns_display_info *dpyinfo)
4991   /* TODO... */
4995 /* This function is called when the last frame on a display is deleted. */
4996 static void
4997 ns_delete_terminal (struct terminal *terminal)
4999   struct ns_display_info *dpyinfo = terminal->display_info.ns;
5001   NSTRACE ("ns_delete_terminal");
5003   /* Protect against recursive calls.  delete_frame in
5004      delete_terminal calls us back when it deletes our last frame.  */
5005   if (!terminal->name)
5006     return;
5008   block_input ();
5010   x_destroy_all_bitmaps (dpyinfo);
5011   ns_delete_display (dpyinfo);
5012   unblock_input ();
5016 static struct terminal *
5017 ns_create_terminal (struct ns_display_info *dpyinfo)
5018 /* --------------------------------------------------------------------------
5019       Set up use of NS before we make the first connection.
5020    -------------------------------------------------------------------------- */
5022   struct terminal *terminal;
5024   NSTRACE ("ns_create_terminal");
5026   terminal = create_terminal (output_ns, &ns_redisplay_interface);
5028   terminal->display_info.ns = dpyinfo;
5029   dpyinfo->terminal = terminal;
5031   terminal->clear_frame_hook = ns_clear_frame;
5032   terminal->ring_bell_hook = ns_ring_bell;
5033   terminal->update_begin_hook = ns_update_begin;
5034   terminal->update_end_hook = ns_update_end;
5035   terminal->read_socket_hook = ns_read_socket;
5036   terminal->frame_up_to_date_hook = ns_frame_up_to_date;
5037   terminal->mouse_position_hook = ns_mouse_position;
5038   terminal->frame_rehighlight_hook = ns_frame_rehighlight;
5039   terminal->frame_raise_lower_hook = ns_frame_raise_lower;
5040   terminal->fullscreen_hook = ns_fullscreen_hook;
5041   terminal->menu_show_hook = ns_menu_show;
5042   terminal->popup_dialog_hook = ns_popup_dialog;
5043   terminal->set_vertical_scroll_bar_hook = ns_set_vertical_scroll_bar;
5044   terminal->set_horizontal_scroll_bar_hook = ns_set_horizontal_scroll_bar;
5045   terminal->condemn_scroll_bars_hook = ns_condemn_scroll_bars;
5046   terminal->redeem_scroll_bar_hook = ns_redeem_scroll_bar;
5047   terminal->judge_scroll_bars_hook = ns_judge_scroll_bars;
5048   terminal->delete_frame_hook = x_destroy_window;
5049   terminal->delete_terminal_hook = ns_delete_terminal;
5050   /* Other hooks are NULL by default.  */
5052   return terminal;
5056 struct ns_display_info *
5057 ns_term_init (Lisp_Object display_name)
5058 /* --------------------------------------------------------------------------
5059      Start the Application and get things rolling.
5060    -------------------------------------------------------------------------- */
5062   struct terminal *terminal;
5063   struct ns_display_info *dpyinfo;
5064   static int ns_initialized = 0;
5065   Lisp_Object tmp;
5067   if (ns_initialized) return x_display_list;
5068   ns_initialized = 1;
5070   block_input ();
5072   NSTRACE ("ns_term_init");
5074   [outerpool release];
5075   outerpool = [[NSAutoreleasePool alloc] init];
5077   /* count object allocs (About, click icon); on macOS use ObjectAlloc tool */
5078   /*GSDebugAllocationActive (YES); */
5079   block_input ();
5081   baud_rate = 38400;
5082   Fset_input_interrupt_mode (Qnil);
5084   if (selfds[0] == -1)
5085     {
5086       if (emacs_pipe (selfds) != 0)
5087         {
5088           fprintf (stderr, "Failed to create pipe: %s\n",
5089                    emacs_strerror (errno));
5090           emacs_abort ();
5091         }
5093       fcntl (selfds[0], F_SETFL, O_NONBLOCK|fcntl (selfds[0], F_GETFL));
5094       FD_ZERO (&select_readfds);
5095       FD_ZERO (&select_writefds);
5096       pthread_mutex_init (&select_mutex, NULL);
5097     }
5099   ns_pending_files = [[NSMutableArray alloc] init];
5100   ns_pending_service_names = [[NSMutableArray alloc] init];
5101   ns_pending_service_args = [[NSMutableArray alloc] init];
5103 /* Start app and create the main menu, window, view.
5104      Needs to be here because ns_initialize_display_info () uses AppKit classes.
5105      The view will then ask the NSApp to stop and return to Emacs. */
5106   [EmacsApp sharedApplication];
5107   if (NSApp == nil)
5108     return NULL;
5109   [NSApp setDelegate: NSApp];
5111   /* Start the select thread.  */
5112   [NSThread detachNewThreadSelector:@selector (fd_handler:)
5113                            toTarget:NSApp
5114                          withObject:nil];
5116   /* debugging: log all notifications */
5117   /*   [[NSNotificationCenter defaultCenter] addObserver: NSApp
5118                                          selector: @selector (logNotification:)
5119                                              name: nil object: nil]; */
5121   dpyinfo = xzalloc (sizeof *dpyinfo);
5123   ns_initialize_display_info (dpyinfo);
5124   terminal = ns_create_terminal (dpyinfo);
5126   terminal->kboard = allocate_kboard (Qns);
5127   /* Don't let the initial kboard remain current longer than necessary.
5128      That would cause problems if a file loaded on startup tries to
5129      prompt in the mini-buffer.  */
5130   if (current_kboard == initial_kboard)
5131     current_kboard = terminal->kboard;
5132   terminal->kboard->reference_count++;
5134   dpyinfo->next = x_display_list;
5135   x_display_list = dpyinfo;
5137   dpyinfo->name_list_element = Fcons (display_name, Qnil);
5139   terminal->name = xlispstrdup (display_name);
5141   unblock_input ();
5143   if (!inhibit_x_resources)
5144     {
5145       ns_default ("GSFontAntiAlias", &ns_antialias_text,
5146                  Qt, Qnil, NO, NO);
5147       tmp = Qnil;
5148       /* this is a standard variable */
5149       ns_default ("AppleAntiAliasingThreshold", &tmp,
5150                  make_float (10.0), make_float (6.0), YES, NO);
5151       ns_antialias_threshold = NILP (tmp) ? 10.0 : extract_float (tmp);
5152     }
5154   NSTRACE_MSG ("Colors");
5156   {
5157     NSColorList *cl = [NSColorList colorListNamed: @"Emacs"];
5159     if ( cl == nil )
5160       {
5161         Lisp_Object color_file, color_map, color;
5162         unsigned long c;
5163         char *name;
5165         color_file = Fexpand_file_name (build_string ("rgb.txt"),
5166                          Fsymbol_value (intern ("data-directory")));
5168         color_map = Fx_load_color_file (color_file);
5169         if (NILP (color_map))
5170           fatal ("Could not read %s.\n", SDATA (color_file));
5172         cl = [[NSColorList alloc] initWithName: @"Emacs"];
5173         for ( ; CONSP (color_map); color_map = XCDR (color_map))
5174           {
5175             color = XCAR (color_map);
5176             name = SSDATA (XCAR (color));
5177             c = XINT (XCDR (color));
5178             [cl setColor:
5179                   [NSColor colorForEmacsRed: RED_FROM_ULONG (c) / 255.0
5180                                       green: GREEN_FROM_ULONG (c) / 255.0
5181                                        blue: BLUE_FROM_ULONG (c) / 255.0
5182                                       alpha: 1.0]
5183                   forKey: [NSString stringWithUTF8String: name]];
5184           }
5185         [cl writeToFile: nil];
5186       }
5187   }
5189   NSTRACE_MSG ("Versions");
5191   {
5192 #ifdef NS_IMPL_GNUSTEP
5193     Vwindow_system_version = build_string (gnustep_base_version);
5194 #else
5195     /*PSnextrelease (128, c); */
5196     char c[DBL_BUFSIZE_BOUND];
5197     int len = dtoastr (c, sizeof c, 0, 0, NSAppKitVersionNumber);
5198     Vwindow_system_version = make_unibyte_string (c, len);
5199 #endif
5200   }
5202   delete_keyboard_wait_descriptor (0);
5204   ns_app_name = [[NSProcessInfo processInfo] processName];
5206   /* Set up macOS app menu */
5208   NSTRACE_MSG ("Menu init");
5210 #ifdef NS_IMPL_COCOA
5211   {
5212     NSMenu *appMenu;
5213     NSMenuItem *item;
5214     /* set up the application menu */
5215     svcsMenu = [[EmacsMenu alloc] initWithTitle: @"Services"];
5216     [svcsMenu setAutoenablesItems: NO];
5217     appMenu = [[EmacsMenu alloc] initWithTitle: @"Emacs"];
5218     [appMenu setAutoenablesItems: NO];
5219     mainMenu = [[EmacsMenu alloc] initWithTitle: @""];
5220     dockMenu = [[EmacsMenu alloc] initWithTitle: @""];
5222     [appMenu insertItemWithTitle: @"About Emacs"
5223                           action: @selector (orderFrontStandardAboutPanel:)
5224                    keyEquivalent: @""
5225                          atIndex: 0];
5226     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 1];
5227     [appMenu insertItemWithTitle: @"Preferences..."
5228                           action: @selector (showPreferencesWindow:)
5229                    keyEquivalent: @","
5230                          atIndex: 2];
5231     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 3];
5232     item = [appMenu insertItemWithTitle: @"Services"
5233                                  action: @selector (menuDown:)
5234                           keyEquivalent: @""
5235                                 atIndex: 4];
5236     [appMenu setSubmenu: svcsMenu forItem: item];
5237     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 5];
5238     [appMenu insertItemWithTitle: @"Hide Emacs"
5239                           action: @selector (hide:)
5240                    keyEquivalent: @"h"
5241                          atIndex: 6];
5242     item =  [appMenu insertItemWithTitle: @"Hide Others"
5243                           action: @selector (hideOtherApplications:)
5244                    keyEquivalent: @"h"
5245                          atIndex: 7];
5246     [item setKeyEquivalentModifierMask: NSEventModifierFlagCommand | NSEventModifierFlagOption];
5247     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 8];
5248     [appMenu insertItemWithTitle: @"Quit Emacs"
5249                           action: @selector (terminate:)
5250                    keyEquivalent: @"q"
5251                          atIndex: 9];
5253     item = [mainMenu insertItemWithTitle: ns_app_name
5254                                   action: @selector (menuDown:)
5255                            keyEquivalent: @""
5256                                  atIndex: 0];
5257     [mainMenu setSubmenu: appMenu forItem: item];
5258     [dockMenu insertItemWithTitle: @"New Frame"
5259                            action: @selector (newFrame:)
5260                     keyEquivalent: @""
5261                           atIndex: 0];
5263     [NSApp setMainMenu: mainMenu];
5264     [NSApp setAppleMenu: appMenu];
5265     [NSApp setServicesMenu: svcsMenu];
5266     /* Needed at least on Cocoa, to get dock menu to show windows */
5267     [NSApp setWindowsMenu: [[NSMenu alloc] init]];
5269     [[NSNotificationCenter defaultCenter]
5270       addObserver: mainMenu
5271          selector: @selector (trackingNotification:)
5272              name: NSMenuDidBeginTrackingNotification object: mainMenu];
5273     [[NSNotificationCenter defaultCenter]
5274       addObserver: mainMenu
5275          selector: @selector (trackingNotification:)
5276              name: NSMenuDidEndTrackingNotification object: mainMenu];
5277   }
5278 #endif /* macOS menu setup */
5280   /* Register our external input/output types, used for determining
5281      applicable services and also drag/drop eligibility. */
5283   NSTRACE_MSG ("Input/output types");
5285   ns_send_types = [[NSArray arrayWithObjects: NSStringPboardType, nil] retain];
5286   ns_return_types = [[NSArray arrayWithObjects: NSStringPboardType, nil]
5287                       retain];
5288   ns_drag_types = [[NSArray arrayWithObjects:
5289                             NSStringPboardType,
5290                             NSTabularTextPboardType,
5291                             NSFilenamesPboardType,
5292                             NSURLPboardType, nil] retain];
5294   /* If fullscreen is in init/default-frame-alist, focus isn't set
5295      right for fullscreen windows, so set this.  */
5296   [NSApp activateIgnoringOtherApps:YES];
5298   NSTRACE_MSG ("Call NSApp run");
5300   [NSApp run];
5301   ns_do_open_file = YES;
5303 #ifdef NS_IMPL_GNUSTEP
5304   /* GNUstep steals SIGCHLD for use in NSTask, but we don't use NSTask.
5305      We must re-catch it so subprocess works.  */
5306   catch_child_signal ();
5307 #endif
5309   NSTRACE_MSG ("ns_term_init done");
5311   unblock_input ();
5313   return dpyinfo;
5317 void
5318 ns_term_shutdown (int sig)
5320   [[NSUserDefaults standardUserDefaults] synchronize];
5322   /* code not reached in emacs.c after this is called by shut_down_emacs: */
5323   if (STRINGP (Vauto_save_list_file_name))
5324     unlink (SSDATA (Vauto_save_list_file_name));
5326   if (sig == 0 || sig == SIGTERM)
5327     {
5328       [NSApp terminate: NSApp];
5329     }
5330   else // force a stack trace to happen
5331     {
5332       emacs_abort ();
5333     }
5337 /* ==========================================================================
5339     EmacsApp implementation
5341    ========================================================================== */
5344 @implementation EmacsApp
5346 - (id)init
5348   NSTRACE ("[EmacsApp init]");
5350   if ((self = [super init]))
5351     {
5352 #ifdef NS_IMPL_COCOA
5353       self->isFirst = YES;
5354 #endif
5355 #ifdef NS_IMPL_GNUSTEP
5356       self->applicationDidFinishLaunchingCalled = NO;
5357 #endif
5358     }
5360   return self;
5363 #ifdef NS_IMPL_COCOA
5364 - (void)run
5366   NSTRACE ("[EmacsApp run]");
5368 #ifndef NSAppKitVersionNumber10_9
5369 #define NSAppKitVersionNumber10_9 1265
5370 #endif
5372     if ((int)NSAppKitVersionNumber != NSAppKitVersionNumber10_9)
5373       {
5374         [super run];
5375         return;
5376       }
5378   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5380   if (isFirst) [self finishLaunching];
5381   isFirst = NO;
5383   shouldKeepRunning = YES;
5384   do
5385     {
5386       [pool release];
5387       pool = [[NSAutoreleasePool alloc] init];
5389       NSEvent *event =
5390         [self nextEventMatchingMask:NSEventMaskAny
5391                           untilDate:[NSDate distantFuture]
5392                              inMode:NSDefaultRunLoopMode
5393                             dequeue:YES];
5395       [self sendEvent:event];
5396       [self updateWindows];
5397     } while (shouldKeepRunning);
5399   [pool release];
5402 - (void)stop: (id)sender
5404   NSTRACE ("[EmacsApp stop:]");
5406     shouldKeepRunning = NO;
5407     // Stop possible dialog also.  Noop if no dialog present.
5408     // The file dialog still leaks 7k - 10k on 10.9 though.
5409     [super stop:sender];
5411 #endif /* NS_IMPL_COCOA */
5413 - (void)logNotification: (NSNotification *)notification
5415   NSTRACE ("[EmacsApp logNotification:]");
5417   const char *name = [[notification name] UTF8String];
5418   if (!strstr (name, "Update") && !strstr (name, "NSMenu")
5419       && !strstr (name, "WindowNumber"))
5420     NSLog (@"notification: '%@'", [notification name]);
5424 - (void)sendEvent: (NSEvent *)theEvent
5425 /* --------------------------------------------------------------------------
5426      Called when NSApp is running for each event received.  Used to stop
5427      the loop when we choose, since there's no way to just run one iteration.
5428    -------------------------------------------------------------------------- */
5430   int type = [theEvent type];
5431   NSWindow *window = [theEvent window];
5433   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsApp sendEvent:]");
5434   NSTRACE_MSG ("Type: %d", type);
5436 #ifdef NS_IMPL_GNUSTEP
5437   // Keyboard events aren't propagated to file dialogs for some reason.
5438   if ([NSApp modalWindow] != nil &&
5439       (type == NSEventTypeKeyDown || type == NSEventTypeKeyUp || type == NSEventTypeFlagsChanged))
5440     {
5441       [[NSApp modalWindow] sendEvent: theEvent];
5442       return;
5443     }
5444 #endif
5446   if (represented_filename != nil && represented_frame)
5447     {
5448       NSString *fstr = represented_filename;
5449       NSView *view = FRAME_NS_VIEW (represented_frame);
5450 #ifdef NS_IMPL_COCOA
5451       /* work around a bug observed on 10.3 and later where
5452          setTitleWithRepresentedFilename does not clear out previous state
5453          if given filename does not exist */
5454       if (! [[NSFileManager defaultManager] fileExistsAtPath: fstr])
5455         [[view window] setRepresentedFilename: @""];
5456 #endif
5457       [[view window] setRepresentedFilename: fstr];
5458       [represented_filename release];
5459       represented_filename = nil;
5460       represented_frame = NULL;
5461     }
5463   if (type == NSEventTypeApplicationDefined)
5464     {
5465       switch ([theEvent data2])
5466         {
5467 #ifdef NS_IMPL_COCOA
5468         case NSAPP_DATA2_RUNASSCRIPT:
5469           ns_run_ascript ();
5470           [self stop: self];
5471           return;
5472 #endif
5473         case NSAPP_DATA2_RUNFILEDIALOG:
5474           ns_run_file_dialog ();
5475           [self stop: self];
5476           return;
5477         }
5478     }
5480   if (type == NSEventTypeCursorUpdate && window == nil)
5481     {
5482       fprintf (stderr, "Dropping external cursor update event.\n");
5483       return;
5484     }
5486   if (type == NSEventTypeApplicationDefined)
5487     {
5488       /* Events posted by ns_send_appdefined interrupt the run loop here.
5489          But, if a modal window is up, an appdefined can still come through,
5490          (e.g., from a makeKeyWindow event) but stopping self also stops the
5491          modal loop. Just defer it until later. */
5492       if ([NSApp modalWindow] == nil)
5493         {
5494           last_appdefined_event_data = [theEvent data1];
5495           [self stop: self];
5496         }
5497       else
5498         {
5499           send_appdefined = YES;
5500         }
5501     }
5504 #ifdef NS_IMPL_COCOA
5505   /* If no dialog and none of our frames have focus and it is a move, skip it.
5506      It is a mouse move in an auxiliary menu, i.e. on the top right on macOS,
5507      such as Wifi, sound, date or similar.
5508      This prevents "spooky" highlighting in the frame under the menu.  */
5509   if (type == NSEventTypeMouseMoved && [NSApp modalWindow] == nil)
5510     {
5511       struct ns_display_info *di;
5512       BOOL has_focus = NO;
5513       for (di = x_display_list; ! has_focus && di; di = di->next)
5514         has_focus = di->x_focus_frame != 0;
5515       if (! has_focus)
5516         return;
5517     }
5518 #endif
5520   NSTRACE_UNSILENCE();
5522   [super sendEvent: theEvent];
5526 - (void)showPreferencesWindow: (id)sender
5528   struct frame *emacsframe = SELECTED_FRAME ();
5529   NSEvent *theEvent = [NSApp currentEvent];
5531   if (!emacs_event)
5532     return;
5533   emacs_event->kind = NS_NONKEY_EVENT;
5534   emacs_event->code = KEY_NS_SHOW_PREFS;
5535   emacs_event->modifiers = 0;
5536   EV_TRAILER (theEvent);
5540 - (void)newFrame: (id)sender
5542   NSTRACE ("[EmacsApp newFrame:]");
5544   struct frame *emacsframe = SELECTED_FRAME ();
5545   NSEvent *theEvent = [NSApp currentEvent];
5547   if (!emacs_event)
5548     return;
5549   emacs_event->kind = NS_NONKEY_EVENT;
5550   emacs_event->code = KEY_NS_NEW_FRAME;
5551   emacs_event->modifiers = 0;
5552   EV_TRAILER (theEvent);
5556 /* Open a file (used by below, after going into queue read by ns_read_socket) */
5557 - (BOOL) openFile: (NSString *)fileName
5559   NSTRACE ("[EmacsApp openFile:]");
5561   struct frame *emacsframe = SELECTED_FRAME ();
5562   NSEvent *theEvent = [NSApp currentEvent];
5564   if (!emacs_event)
5565     return NO;
5567   emacs_event->kind = NS_NONKEY_EVENT;
5568   emacs_event->code = KEY_NS_OPEN_FILE_LINE;
5569   ns_input_file = append2 (ns_input_file, build_string ([fileName UTF8String]));
5570   ns_input_line = Qnil; /* can be start or cons start,end */
5571   emacs_event->modifiers =0;
5572   EV_TRAILER (theEvent);
5574   return YES;
5578 /* **************************************************************************
5580       EmacsApp delegate implementation
5582    ************************************************************************** */
5584 - (void)applicationDidFinishLaunching: (NSNotification *)notification
5585 /* --------------------------------------------------------------------------
5586      When application is loaded, terminate event loop in ns_term_init
5587    -------------------------------------------------------------------------- */
5589   NSTRACE ("[EmacsApp applicationDidFinishLaunching:]");
5591 #ifdef NS_IMPL_GNUSTEP
5592   ((EmacsApp *)self)->applicationDidFinishLaunchingCalled = YES;
5593 #endif
5594   [NSApp setServicesProvider: NSApp];
5596   [self antialiasThresholdDidChange:nil];
5597 #ifdef NS_IMPL_COCOA
5598   [[NSNotificationCenter defaultCenter]
5599     addObserver:self
5600        selector:@selector(antialiasThresholdDidChange:)
5601            name:NSAntialiasThresholdChangedNotification
5602          object:nil];
5603 #endif
5605 #ifdef NS_IMPL_COCOA
5606   if ([NSApp activationPolicy] == NSApplicationActivationPolicyProhibited) {
5607     /* Set the app's activation policy to regular when we run outside
5608        of a bundle.  This is already done for us by Info.plist when we
5609        run inside a bundle. */
5610     [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
5611     [NSApp setApplicationIconImage:
5612              [EmacsImage
5613                allocInitFromFile:
5614                  build_string("icons/hicolor/128x128/apps/emacs.png")]];
5615   }
5616 #endif
5618   ns_send_appdefined (-2);
5621 - (void)antialiasThresholdDidChange:(NSNotification *)notification
5623 #ifdef NS_IMPL_COCOA
5624   macfont_update_antialias_threshold ();
5625 #endif
5629 /* Termination sequences:
5630     C-x C-c:
5631     Cmd-Q:
5632     MenuBar | File | Exit:
5633     Select Quit from App menubar:
5634         -terminate
5635         KEY_NS_POWER_OFF, (save-buffers-kill-emacs)
5636         ns_term_shutdown()
5638     Select Quit from Dock menu:
5639     Logout attempt:
5640         -appShouldTerminate
5641           Cancel -> Nothing else
5642           Accept ->
5644           -terminate
5645           KEY_NS_POWER_OFF, (save-buffers-kill-emacs)
5646           ns_term_shutdown()
5650 - (void) terminate: (id)sender
5652   NSTRACE ("[EmacsApp terminate:]");
5654   struct frame *emacsframe = SELECTED_FRAME ();
5656   if (!emacs_event)
5657     return;
5659   emacs_event->kind = NS_NONKEY_EVENT;
5660   emacs_event->code = KEY_NS_POWER_OFF;
5661   emacs_event->arg = Qt; /* mark as non-key event */
5662   EV_TRAILER ((id)nil);
5665 static bool
5666 runAlertPanel(NSString *title,
5667               NSString *msgFormat,
5668               NSString *defaultButton,
5669               NSString *alternateButton)
5671 #ifdef NS_IMPL_GNUSTEP
5672   return NSRunAlertPanel(title, msgFormat, defaultButton, alternateButton, nil)
5673     == NSAlertDefaultReturn;
5674 #else
5675   NSAlert *alert = [[NSAlert alloc] init];
5676   [alert setAlertStyle: NSAlertStyleCritical];
5677   [alert setMessageText: msgFormat];
5678   [alert addButtonWithTitle: defaultButton];
5679   [alert addButtonWithTitle: alternateButton];
5680   NSInteger ret = [alert runModal];
5681   [alert release];
5682   return ret == NSAlertFirstButtonReturn;
5683 #endif
5687 - (NSApplicationTerminateReply)applicationShouldTerminate: (id)sender
5689   NSTRACE ("[EmacsApp applicationShouldTerminate:]");
5691   bool ret;
5693   if (NILP (ns_confirm_quit)) //   || ns_shutdown_properly  --> TO DO
5694     return NSTerminateNow;
5696   ret = runAlertPanel(ns_app_name,
5697                       @"Exit requested.  Would you like to Save Buffers and Exit, or Cancel the request?",
5698                       @"Save Buffers and Exit", @"Cancel");
5700   return ret ? NSTerminateNow : NSTerminateCancel;
5703 static int
5704 not_in_argv (NSString *arg)
5706   int k;
5707   const char *a = [arg UTF8String];
5708   for (k = 1; k < initial_argc; ++k)
5709     if (strcmp (a, initial_argv[k]) == 0) return 0;
5710   return 1;
5713 /*   Notification from the Workspace to open a file */
5714 - (BOOL)application: sender openFile: (NSString *)file
5716   if (ns_do_open_file || not_in_argv (file))
5717     [ns_pending_files addObject: file];
5718   return YES;
5722 /*   Open a file as a temporary file */
5723 - (BOOL)application: sender openTempFile: (NSString *)file
5725   if (ns_do_open_file || not_in_argv (file))
5726     [ns_pending_files addObject: file];
5727   return YES;
5731 /*   Notification from the Workspace to open a file noninteractively (?) */
5732 - (BOOL)application: sender openFileWithoutUI: (NSString *)file
5734   if (ns_do_open_file || not_in_argv (file))
5735     [ns_pending_files addObject: file];
5736   return YES;
5739 /*   Notification from the Workspace to open multiple files */
5740 - (void)application: sender openFiles: (NSArray *)fileList
5742   NSEnumerator *files = [fileList objectEnumerator];
5743   NSString *file;
5744   /* Don't open files from the command line unconditionally,
5745      Cocoa parses the command line wrong, --option value tries to open value
5746      if --option is the last option.  */
5747   while ((file = [files nextObject]) != nil)
5748     if (ns_do_open_file || not_in_argv (file))
5749       [ns_pending_files addObject: file];
5751   [self replyToOpenOrPrint: NSApplicationDelegateReplySuccess];
5756 /* Handle dock menu requests.  */
5757 - (NSMenu *)applicationDockMenu: (NSApplication *) sender
5759   return dockMenu;
5763 /* TODO: these may help w/IO switching btwn terminal and NSApp */
5764 - (void)applicationWillBecomeActive: (NSNotification *)notification
5766   NSTRACE ("[EmacsApp applicationWillBecomeActive:]");
5767   //ns_app_active=YES;
5770 - (void)applicationDidBecomeActive: (NSNotification *)notification
5772   NSTRACE ("[EmacsApp applicationDidBecomeActive:]");
5774 #ifdef NS_IMPL_GNUSTEP
5775   if (! applicationDidFinishLaunchingCalled)
5776     [self applicationDidFinishLaunching:notification];
5777 #endif
5778   //ns_app_active=YES;
5780   ns_update_auto_hide_menu_bar ();
5781   // No constraining takes place when the application is not active.
5782   ns_constrain_all_frames ();
5784 - (void)applicationDidResignActive: (NSNotification *)notification
5786   NSTRACE ("[EmacsApp applicationDidResignActive:]");
5788   //ns_app_active=NO;
5789   ns_send_appdefined (-1);
5794 /* ==========================================================================
5796     EmacsApp aux handlers for managing event loop
5798    ========================================================================== */
5801 - (void)timeout_handler: (NSTimer *)timedEntry
5802 /* --------------------------------------------------------------------------
5803      The timeout specified to ns_select has passed.
5804    -------------------------------------------------------------------------- */
5806   /*NSTRACE ("timeout_handler"); */
5807   ns_send_appdefined (-2);
5810 - (void)sendFromMainThread:(id)unused
5812   ns_send_appdefined (nextappdefined);
5815 - (void)fd_handler:(id)unused
5816 /* --------------------------------------------------------------------------
5817      Check data waiting on file descriptors and terminate if so
5818    -------------------------------------------------------------------------- */
5820   int result;
5821   int waiting = 1, nfds;
5822   char c;
5824   fd_set readfds, writefds, *wfds;
5825   struct timespec timeout, *tmo;
5826   NSAutoreleasePool *pool = nil;
5828   /* NSTRACE ("fd_handler"); */
5830   for (;;)
5831     {
5832       [pool release];
5833       pool = [[NSAutoreleasePool alloc] init];
5835       if (waiting)
5836         {
5837           fd_set fds;
5838           FD_ZERO (&fds);
5839           FD_SET (selfds[0], &fds);
5840           result = select (selfds[0]+1, &fds, NULL, NULL, NULL);
5841           if (result > 0 && read (selfds[0], &c, 1) == 1 && c == 'g')
5842             waiting = 0;
5843         }
5844       else
5845         {
5846           pthread_mutex_lock (&select_mutex);
5847           nfds = select_nfds;
5849           if (select_valid & SELECT_HAVE_READ)
5850             readfds = select_readfds;
5851           else
5852             FD_ZERO (&readfds);
5854           if (select_valid & SELECT_HAVE_WRITE)
5855             {
5856               writefds = select_writefds;
5857               wfds = &writefds;
5858             }
5859           else
5860             wfds = NULL;
5861           if (select_valid & SELECT_HAVE_TMO)
5862             {
5863               timeout = select_timeout;
5864               tmo = &timeout;
5865             }
5866           else
5867             tmo = NULL;
5869           pthread_mutex_unlock (&select_mutex);
5871           FD_SET (selfds[0], &readfds);
5872           if (selfds[0] >= nfds) nfds = selfds[0]+1;
5874           result = pselect (nfds, &readfds, wfds, NULL, tmo, NULL);
5876           if (result == 0)
5877             ns_send_appdefined (-2);
5878           else if (result > 0)
5879             {
5880               if (FD_ISSET (selfds[0], &readfds))
5881                 {
5882                   if (read (selfds[0], &c, 1) == 1 && c == 's')
5883                     waiting = 1;
5884                 }
5885               else
5886                 {
5887                   pthread_mutex_lock (&select_mutex);
5888                   if (select_valid & SELECT_HAVE_READ)
5889                     select_readfds = readfds;
5890                   if (select_valid & SELECT_HAVE_WRITE)
5891                     select_writefds = writefds;
5892                   if (select_valid & SELECT_HAVE_TMO)
5893                     select_timeout = timeout;
5894                   pthread_mutex_unlock (&select_mutex);
5896                   ns_send_appdefined (result);
5897                 }
5898             }
5899           waiting = 1;
5900         }
5901     }
5906 /* ==========================================================================
5908     Service provision
5910    ========================================================================== */
5912 /* called from system: queue for next pass through event loop */
5913 - (void)requestService: (NSPasteboard *)pboard
5914               userData: (NSString *)userData
5915                  error: (NSString **)error
5917   [ns_pending_service_names addObject: userData];
5918   [ns_pending_service_args addObject: [NSString stringWithUTF8String:
5919       SSDATA (ns_string_from_pasteboard (pboard))]];
5923 /* called from ns_read_socket to clear queue */
5924 - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
5926   struct frame *emacsframe = SELECTED_FRAME ();
5927   NSEvent *theEvent = [NSApp currentEvent];
5929   NSTRACE ("[EmacsApp fulfillService:withArg:]");
5931   if (!emacs_event)
5932     return NO;
5934   emacs_event->kind = NS_NONKEY_EVENT;
5935   emacs_event->code = KEY_NS_SPI_SERVICE_CALL;
5936   ns_input_spi_name = build_string ([name UTF8String]);
5937   ns_input_spi_arg = build_string ([arg UTF8String]);
5938   emacs_event->modifiers = EV_MODIFIERS (theEvent);
5939   EV_TRAILER (theEvent);
5941   return YES;
5945 @end  /* EmacsApp */
5949 /* ==========================================================================
5951     EmacsView implementation
5953    ========================================================================== */
5956 @implementation EmacsView
5958 /* needed to inform when window closed from LISP */
5959 - (void) setWindowClosing: (BOOL)closing
5961   NSTRACE ("[EmacsView setWindowClosing:%d]", closing);
5963   windowClosing = closing;
5967 - (void)dealloc
5969   NSTRACE ("[EmacsView dealloc]");
5970   [toolbar release];
5971   if (fs_state == FULLSCREEN_BOTH)
5972     [nonfs_window release];
5973   [super dealloc];
5977 /* called on font panel selection */
5978 - (void)changeFont: (id)sender
5980   NSEvent *e = [[self window] currentEvent];
5981   struct face *face = FACE_FROM_ID (emacsframe, DEFAULT_FACE_ID);
5982   struct font *font = face->font;
5983   id newFont;
5984   CGFloat size;
5985   NSFont *nsfont;
5987   NSTRACE ("[EmacsView changeFont:]");
5989   if (!emacs_event)
5990     return;
5992 #ifdef NS_IMPL_GNUSTEP
5993   nsfont = ((struct nsfont_info *)font)->nsfont;
5994 #endif
5995 #ifdef NS_IMPL_COCOA
5996   nsfont = (NSFont *) macfont_get_nsctfont (font);
5997 #endif
5999   if ((newFont = [sender convertFont: nsfont]))
6000     {
6001       SET_FRAME_GARBAGED (emacsframe); /* now needed as of 2008/10 */
6003       emacs_event->kind = NS_NONKEY_EVENT;
6004       emacs_event->modifiers = 0;
6005       emacs_event->code = KEY_NS_CHANGE_FONT;
6007       size = [newFont pointSize];
6008       ns_input_fontsize = make_number (lrint (size));
6009       ns_input_font = build_string ([[newFont familyName] UTF8String]);
6010       EV_TRAILER (e);
6011     }
6015 - (BOOL)acceptsFirstResponder
6017   NSTRACE ("[EmacsView acceptsFirstResponder]");
6018   return YES;
6022 - (void)resetCursorRects
6024   NSRect visible = [self visibleRect];
6025   NSCursor *currentCursor = FRAME_POINTER_TYPE (emacsframe);
6026   NSTRACE ("[EmacsView resetCursorRects]");
6028   if (currentCursor == nil)
6029     currentCursor = [NSCursor arrowCursor];
6031   if (!NSIsEmptyRect (visible))
6032     [self addCursorRect: visible cursor: currentCursor];
6033   [currentCursor setOnMouseEntered: YES];
6038 /*****************************************************************************/
6039 /* Keyboard handling. */
6040 #define NS_KEYLOG 0
6042 - (void)keyDown: (NSEvent *)theEvent
6044   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
6045   int code;
6046   unsigned fnKeysym = 0;
6047   static NSMutableArray *nsEvArray;
6048   int left_is_none;
6049   unsigned int flags = [theEvent modifierFlags];
6051   NSTRACE ("[EmacsView keyDown:]");
6053   /* Rhapsody and macOS give up and down events for the arrow keys */
6054   if (ns_fake_keydown == YES)
6055     ns_fake_keydown = NO;
6056   else if ([theEvent type] != NSEventTypeKeyDown)
6057     return;
6059   if (!emacs_event)
6060     return;
6062  if (![[self window] isKeyWindow]
6063      && [[theEvent window] isKindOfClass: [EmacsWindow class]]
6064      /* we must avoid an infinite loop here. */
6065      && (EmacsView *)[[theEvent window] delegate] != self)
6066    {
6067      /* XXX: There is an occasional condition in which, when Emacs display
6068          updates a different frame from the current one, and temporarily
6069          selects it, then processes some interrupt-driven input
6070          (dispnew.c:3878), OS will send the event to the correct NSWindow, but
6071          for some reason that window has its first responder set to the NSView
6072          most recently updated (I guess), which is not the correct one. */
6073      [(EmacsView *)[[theEvent window] delegate] keyDown: theEvent];
6074      return;
6075    }
6077   if (nsEvArray == nil)
6078     nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1];
6080   [NSCursor setHiddenUntilMouseMoves: YES];
6082   if (hlinfo->mouse_face_hidden && INTEGERP (Vmouse_highlight))
6083     {
6084       clear_mouse_face (hlinfo);
6085       hlinfo->mouse_face_hidden = 1;
6086     }
6088   if (!processingCompose)
6089     {
6090       /* When using screen sharing, no left or right information is sent,
6091          so use Left key in those cases.  */
6092       int is_left_key, is_right_key;
6094       code = ([[theEvent charactersIgnoringModifiers] length] == 0) ?
6095         0 : [[theEvent charactersIgnoringModifiers] characterAtIndex: 0];
6097       /* (Carbon way: [theEvent keyCode]) */
6099       /* is it a "function key"? */
6100       /* Note: Sometimes a plain key will have the NSEventModifierFlagNumericPad
6101          flag set (this is probably a bug in the OS).
6102       */
6103       if (code < 0x00ff && (flags&NSEventModifierFlagNumericPad))
6104         {
6105           fnKeysym = ns_convert_key ([theEvent keyCode] | NSEventModifierFlagNumericPad);
6106         }
6107       if (fnKeysym == 0)
6108         {
6109           fnKeysym = ns_convert_key (code);
6110         }
6112       if (fnKeysym)
6113         {
6114           /* COUNTERHACK: map 'Delete' on upper-right main KB to 'Backspace',
6115              because Emacs treats Delete and KP-Delete same (in simple.el). */
6116           if ((fnKeysym == 0xFFFF && [theEvent keyCode] == 0x33)
6117 #ifdef NS_IMPL_GNUSTEP
6118               /*  GNUstep uses incompatible keycodes, even for those that are
6119                   supposed to be hardware independent.  Just check for delete.
6120                   Keypad delete does not have keysym 0xFFFF.
6121                   See https://savannah.gnu.org/bugs/?25395
6122               */
6123               || (fnKeysym == 0xFFFF && code == 127)
6124 #endif
6125             )
6126             code = 0xFF08; /* backspace */
6127           else
6128             code = fnKeysym;
6129         }
6131       /* are there modifiers? */
6132       emacs_event->modifiers = 0;
6134       if (flags & NSEventModifierFlagHelp)
6135           emacs_event->modifiers |= hyper_modifier;
6137       if (flags & NSEventModifierFlagShift)
6138         emacs_event->modifiers |= shift_modifier;
6140       is_right_key = (flags & NSRightCommandKeyMask) == NSRightCommandKeyMask;
6141       is_left_key = (flags & NSLeftCommandKeyMask) == NSLeftCommandKeyMask
6142         || (! is_right_key && (flags & NSEventModifierFlagCommand) == NSEventModifierFlagCommand);
6144       if (is_right_key)
6145         emacs_event->modifiers |= parse_solitary_modifier
6146           (EQ (ns_right_command_modifier, Qleft)
6147            ? ns_command_modifier
6148            : ns_right_command_modifier);
6150       if (is_left_key)
6151         {
6152           emacs_event->modifiers |= parse_solitary_modifier
6153             (ns_command_modifier);
6155           /* if super (default), take input manager's word so things like
6156              dvorak / qwerty layout work */
6157           if (EQ (ns_command_modifier, Qsuper)
6158               && !fnKeysym
6159               && [[theEvent characters] length] != 0)
6160             {
6161               /* XXX: the code we get will be unshifted, so if we have
6162                  a shift modifier, must convert ourselves */
6163               if (!(flags & NSEventModifierFlagShift))
6164                 code = [[theEvent characters] characterAtIndex: 0];
6165 #if 0
6166               /* this is ugly and also requires linking w/Carbon framework
6167                  (for LMGetKbdType) so for now leave this rare (?) case
6168                  undealt with.. in future look into CGEvent methods */
6169               else
6170                 {
6171                   long smv = GetScriptManagerVariable (smKeyScript);
6172                   Handle uchrHandle = GetResource
6173                     ('uchr', GetScriptVariable (smv, smScriptKeys));
6174                   UInt32 dummy = 0;
6175                   UCKeyTranslate ((UCKeyboardLayout *) *uchrHandle,
6176                                  [[theEvent characters] characterAtIndex: 0],
6177                                  kUCKeyActionDisplay,
6178                                  (flags & ~NSEventModifierFlagCommand) >> 8,
6179                                  LMGetKbdType (), kUCKeyTranslateNoDeadKeysMask,
6180                                  &dummy, 1, &dummy, &code);
6181                   code &= 0xFF;
6182                 }
6183 #endif
6184             }
6185         }
6187       is_right_key = (flags & NSRightControlKeyMask) == NSRightControlKeyMask;
6188       is_left_key = (flags & NSLeftControlKeyMask) == NSLeftControlKeyMask
6189         || (! is_right_key && (flags & NSEventModifierFlagControl) == NSEventModifierFlagControl);
6191       if (is_right_key)
6192           emacs_event->modifiers |= parse_solitary_modifier
6193               (EQ (ns_right_control_modifier, Qleft)
6194                ? ns_control_modifier
6195                : ns_right_control_modifier);
6197       if (is_left_key)
6198         emacs_event->modifiers |= parse_solitary_modifier
6199           (ns_control_modifier);
6201       if (flags & NS_FUNCTION_KEY_MASK && !fnKeysym)
6202           emacs_event->modifiers |=
6203             parse_solitary_modifier (ns_function_modifier);
6205       left_is_none = NILP (ns_alternate_modifier)
6206         || EQ (ns_alternate_modifier, Qnone);
6208       is_right_key = (flags & NSRightAlternateKeyMask)
6209         == NSRightAlternateKeyMask;
6210       is_left_key = (flags & NSLeftAlternateKeyMask) == NSLeftAlternateKeyMask
6211         || (! is_right_key
6212             && (flags & NSEventModifierFlagOption) == NSEventModifierFlagOption);
6214       if (is_right_key)
6215         {
6216           if ((NILP (ns_right_alternate_modifier)
6217                || EQ (ns_right_alternate_modifier, Qnone)
6218                || (EQ (ns_right_alternate_modifier, Qleft) && left_is_none))
6219               && !fnKeysym)
6220             {   /* accept pre-interp alt comb */
6221               if ([[theEvent characters] length] > 0)
6222                 code = [[theEvent characters] characterAtIndex: 0];
6223               /*HACK: clear lone shift modifier to stop next if from firing */
6224               if (emacs_event->modifiers == shift_modifier)
6225                 emacs_event->modifiers = 0;
6226             }
6227           else
6228             emacs_event->modifiers |= parse_solitary_modifier
6229               (EQ (ns_right_alternate_modifier, Qleft)
6230                ? ns_alternate_modifier
6231                : ns_right_alternate_modifier);
6232         }
6234       if (is_left_key) /* default = meta */
6235         {
6236           if (left_is_none && !fnKeysym)
6237             {   /* accept pre-interp alt comb */
6238               if ([[theEvent characters] length] > 0)
6239                 code = [[theEvent characters] characterAtIndex: 0];
6240               /*HACK: clear lone shift modifier to stop next if from firing */
6241               if (emacs_event->modifiers == shift_modifier)
6242                 emacs_event->modifiers = 0;
6243             }
6244           else
6245               emacs_event->modifiers |=
6246                 parse_solitary_modifier (ns_alternate_modifier);
6247         }
6249   if (NS_KEYLOG)
6250     fprintf (stderr, "keyDown: code =%x\tfnKey =%x\tflags = %x\tmods = %x\n",
6251              (unsigned) code, fnKeysym, flags, emacs_event->modifiers);
6253       /* if it was a function key or had modifiers, pass it directly to emacs */
6254       if (fnKeysym || (emacs_event->modifiers
6255                        && (emacs_event->modifiers != shift_modifier)
6256                        && [[theEvent charactersIgnoringModifiers] length] > 0))
6257 /*[[theEvent characters] length] */
6258         {
6259           emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
6260           if (code < 0x20)
6261             code |= (1<<28)|(3<<16);
6262           else if (code == 0x7f)
6263             code |= (1<<28)|(3<<16);
6264           else if (!fnKeysym)
6265             emacs_event->kind = code > 0xFF
6266               ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT;
6268           emacs_event->code = code;
6269           EV_TRAILER (theEvent);
6270           processingCompose = NO;
6271           return;
6272         }
6273     }
6276   if (NS_KEYLOG && !processingCompose)
6277     fprintf (stderr, "keyDown: Begin compose sequence.\n");
6279   processingCompose = YES;
6280   [nsEvArray addObject: theEvent];
6281   [self interpretKeyEvents: nsEvArray];
6282   [nsEvArray removeObject: theEvent];
6286 /* <NSTextInput> implementation (called through super interpretKeyEvents:]). */
6289 /* <NSTextInput>: called when done composing;
6290    NOTE: also called when we delete over working text, followed immed.
6291          by doCommandBySelector: deleteBackward: */
6292 - (void)insertText: (id)aString
6294   int code;
6295   int len = [(NSString *)aString length];
6296   int i;
6298   NSTRACE ("[EmacsView insertText:]");
6300   if (NS_KEYLOG)
6301     NSLog (@"insertText '%@'\tlen = %d", aString, len);
6302   processingCompose = NO;
6304   if (!emacs_event)
6305     return;
6307   /* first, clear any working text */
6308   if (workingText != nil)
6309     [self deleteWorkingText];
6311   /* now insert the string as keystrokes */
6312   for (i =0; i<len; i++)
6313     {
6314       code = [aString characterAtIndex: i];
6315       /* TODO: still need this? */
6316       if (code == 0x2DC)
6317         code = '~'; /* 0x7E */
6318       if (code != 32) /* Space */
6319         emacs_event->modifiers = 0;
6320       emacs_event->kind
6321         = code > 0xFF ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT;
6322       emacs_event->code = code;
6323       EV_TRAILER ((id)nil);
6324     }
6328 /* <NSTextInput>: inserts display of composing characters */
6329 - (void)setMarkedText: (id)aString selectedRange: (NSRange)selRange
6331   NSString *str = [aString respondsToSelector: @selector (string)] ?
6332     [aString string] : aString;
6334   NSTRACE ("[EmacsView setMarkedText:selectedRange:]");
6336   if (NS_KEYLOG)
6337     NSLog (@"setMarkedText '%@' len =%lu range %lu from %lu",
6338            str, (unsigned long)[str length],
6339            (unsigned long)selRange.length,
6340            (unsigned long)selRange.location);
6342   if (workingText != nil)
6343     [self deleteWorkingText];
6344   if ([str length] == 0)
6345     return;
6347   if (!emacs_event)
6348     return;
6350   processingCompose = YES;
6351   workingText = [str copy];
6352   ns_working_text = build_string ([workingText UTF8String]);
6354   emacs_event->kind = NS_TEXT_EVENT;
6355   emacs_event->code = KEY_NS_PUT_WORKING_TEXT;
6356   EV_TRAILER ((id)nil);
6360 /* delete display of composing characters [not in <NSTextInput>] */
6361 - (void)deleteWorkingText
6363   NSTRACE ("[EmacsView deleteWorkingText]");
6365   if (workingText == nil)
6366     return;
6367   if (NS_KEYLOG)
6368     NSLog(@"deleteWorkingText len =%lu\n", (unsigned long)[workingText length]);
6369   [workingText release];
6370   workingText = nil;
6371   processingCompose = NO;
6373   if (!emacs_event)
6374     return;
6376   emacs_event->kind = NS_TEXT_EVENT;
6377   emacs_event->code = KEY_NS_UNPUT_WORKING_TEXT;
6378   EV_TRAILER ((id)nil);
6382 - (BOOL)hasMarkedText
6384   NSTRACE ("[EmacsView hasMarkedText]");
6386   return workingText != nil;
6390 - (NSRange)markedRange
6392   NSTRACE ("[EmacsView markedRange]");
6394   NSRange rng = workingText != nil
6395     ? NSMakeRange (0, [workingText length]) : NSMakeRange (NSNotFound, 0);
6396   if (NS_KEYLOG)
6397     NSLog (@"markedRange request");
6398   return rng;
6402 - (void)unmarkText
6404   NSTRACE ("[EmacsView unmarkText]");
6406   if (NS_KEYLOG)
6407     NSLog (@"unmark (accept) text");
6408   [self deleteWorkingText];
6409   processingCompose = NO;
6413 /* used to position char selection windows, etc. */
6414 - (NSRect)firstRectForCharacterRange: (NSRange)theRange
6416   NSRect rect;
6417   NSPoint pt;
6418   struct window *win = XWINDOW (FRAME_SELECTED_WINDOW (emacsframe));
6420   NSTRACE ("[EmacsView firstRectForCharacterRange:]");
6422   if (NS_KEYLOG)
6423     NSLog (@"firstRectForCharRange request");
6425   rect.size.width = theRange.length * FRAME_COLUMN_WIDTH (emacsframe);
6426   rect.size.height = FRAME_LINE_HEIGHT (emacsframe);
6427   pt.x = WINDOW_TEXT_TO_FRAME_PIXEL_X (win, win->phys_cursor.x);
6428   pt.y = WINDOW_TO_FRAME_PIXEL_Y (win, win->phys_cursor.y
6429                                        +FRAME_LINE_HEIGHT (emacsframe));
6431   pt = [self convertPoint: pt toView: nil];
6433 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
6434 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6435   if ([[self window] respondsToSelector: @selector(convertRectToScreen:)])
6436     {
6437 #endif
6438       rect.origin = pt;
6439       rect = [(EmacsWindow *) [self window] convertRectToScreen: rect];
6440 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6441     }
6442   else
6443 #endif
6444 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
6445 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 \
6446   || defined (NS_IMPL_GNUSTEP)
6447     {
6448       pt = [[self window] convertBaseToScreen: pt];
6449       rect.origin = pt;
6450     }
6451 #endif
6453   return rect;
6457 - (NSInteger)conversationIdentifier
6459   return (NSInteger)self;
6463 - (void)doCommandBySelector: (SEL)aSelector
6465   NSTRACE ("[EmacsView doCommandBySelector:]");
6467   if (NS_KEYLOG)
6468     NSLog (@"doCommandBySelector: %@", NSStringFromSelector (aSelector));
6470   processingCompose = NO;
6471   if (aSelector == @selector (deleteBackward:))
6472     {
6473       /* happens when user backspaces over an ongoing composition:
6474          throw a 'delete' into the event queue */
6475       if (!emacs_event)
6476         return;
6477       emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
6478       emacs_event->code = 0xFF08;
6479       EV_TRAILER ((id)nil);
6480     }
6483 - (NSArray *)validAttributesForMarkedText
6485   static NSArray *arr = nil;
6486   if (arr == nil) arr = [NSArray new];
6487  /* [[NSArray arrayWithObject: NSUnderlineStyleAttributeName] retain]; */
6488   return arr;
6491 - (NSRange)selectedRange
6493   if (NS_KEYLOG)
6494     NSLog (@"selectedRange request");
6495   return NSMakeRange (NSNotFound, 0);
6498 #if defined (NS_IMPL_COCOA) || GNUSTEP_GUI_MAJOR_VERSION > 0 || \
6499     GNUSTEP_GUI_MINOR_VERSION > 22
6500 - (NSUInteger)characterIndexForPoint: (NSPoint)thePoint
6501 #else
6502 - (unsigned int)characterIndexForPoint: (NSPoint)thePoint
6503 #endif
6505   if (NS_KEYLOG)
6506     NSLog (@"characterIndexForPoint request");
6507   return 0;
6510 - (NSAttributedString *)attributedSubstringFromRange: (NSRange)theRange
6512   static NSAttributedString *str = nil;
6513   if (str == nil) str = [NSAttributedString new];
6514   if (NS_KEYLOG)
6515     NSLog (@"attributedSubstringFromRange request");
6516   return str;
6519 /* End <NSTextInput> impl. */
6520 /*****************************************************************************/
6523 /* This is what happens when the user presses a mouse button.  */
6524 - (void)mouseDown: (NSEvent *)theEvent
6526   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6527   NSPoint p = [self convertPoint: [theEvent locationInWindow] fromView: nil];
6529   NSTRACE ("[EmacsView mouseDown:]");
6531   [self deleteWorkingText];
6533   if (!emacs_event)
6534     return;
6536   dpyinfo->last_mouse_frame = emacsframe;
6537   /* appears to be needed to prevent spurious movement events generated on
6538      button clicks */
6539   emacsframe->mouse_moved = 0;
6541   if ([theEvent type] == NSEventTypeScrollWheel)
6542     {
6543 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
6544 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6545       if ([theEvent respondsToSelector:@selector(hasPreciseScrollingDeltas)])
6546         {
6547 #endif
6548           /* If the input device is a touchpad or similar, use precise
6549            * scrolling deltas.  These are measured in pixels, so we
6550            * have to add them up until they exceed one line height,
6551            * then we can send a scroll wheel event.
6552            *
6553            * If the device only has coarse scrolling deltas, like a
6554            * real mousewheel, the deltas represent a ratio of whole
6555            * lines, so round up the number of lines.  This means we
6556            * always send one scroll event per click, but can still
6557            * scroll more than one line if the OS tells us to.
6558            */
6559           bool horizontal;
6560           int lines = 0;
6561           int scrollUp = NO;
6563           /* FIXME: At the top or bottom of the buffer we should
6564            * ignore momentum-phase events.  */
6565           if (! ns_use_mwheel_momentum
6566               && [theEvent momentumPhase] != NSEventPhaseNone)
6567             return;
6569           if ([theEvent hasPreciseScrollingDeltas])
6570             {
6571               static int totalDeltaX, totalDeltaY;
6572               int lineHeight;
6574               if (NUMBERP (ns_mwheel_line_height))
6575                 lineHeight = XINT (ns_mwheel_line_height);
6576               else
6577                 {
6578                   /* FIXME: Use actual line height instead of the default.  */
6579                   lineHeight = default_line_pixel_height
6580                     (XWINDOW (FRAME_SELECTED_WINDOW (emacsframe)));
6581                 }
6583               if ([theEvent phase] == NSEventPhaseBegan)
6584                 {
6585                   totalDeltaX = 0;
6586                   totalDeltaY = 0;
6587                 }
6589               totalDeltaX += [theEvent scrollingDeltaX];
6590               totalDeltaY += [theEvent scrollingDeltaY];
6592               /* Calculate the number of lines, if any, to scroll, and
6593                * reset the total delta for the direction we're NOT
6594                * scrolling so that small movements don't add up.  */
6595               if (abs (totalDeltaX) > abs (totalDeltaY)
6596                   && abs (totalDeltaX) > lineHeight)
6597                 {
6598                   horizontal = YES;
6599                   scrollUp = totalDeltaX > 0;
6601                   lines = abs (totalDeltaX / lineHeight);
6602                   totalDeltaX = totalDeltaX % lineHeight;
6603                   totalDeltaY = 0;
6604                 }
6605               else if (abs (totalDeltaY) >= abs (totalDeltaX)
6606                        && abs (totalDeltaY) > lineHeight)
6607                 {
6608                   horizontal = NO;
6609                   scrollUp = totalDeltaY > 0;
6611                   lines = abs (totalDeltaY / lineHeight);
6612                   totalDeltaY = totalDeltaY % lineHeight;
6613                   totalDeltaX = 0;
6614                 }
6616               if (lines > 1 && ! ns_use_mwheel_acceleration)
6617                 lines = 1;
6618             }
6619           else
6620             {
6621               CGFloat delta;
6623               if ([theEvent scrollingDeltaY] == 0)
6624                 {
6625                   horizontal = YES;
6626                   delta = [theEvent scrollingDeltaX];
6627                 }
6628               else
6629                 {
6630                   horizontal = NO;
6631                   delta = [theEvent scrollingDeltaY];
6632                 }
6634               lines = (ns_use_mwheel_acceleration)
6635                 ? ceil (fabs (delta)) : 1;
6637               scrollUp = delta > 0;
6638             }
6640           if (lines == 0)
6641             return;
6643           emacs_event->kind = horizontal ? HORIZ_WHEEL_EVENT : WHEEL_EVENT;
6644           emacs_event->arg = (make_number (lines));
6646           emacs_event->code = 0;
6647           emacs_event->modifiers = EV_MODIFIERS (theEvent) |
6648             (scrollUp ? up_modifier : down_modifier);
6649 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6650         }
6651       else
6652 #endif
6653 #endif /* defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
6654 #if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 1070
6655         {
6656           CGFloat delta = [theEvent deltaY];
6657           /* Mac notebooks send wheel events w/delta =0 when trackpad scrolling */
6658           if (delta == 0)
6659             {
6660               delta = [theEvent deltaX];
6661               if (delta == 0)
6662                 {
6663                   NSTRACE_MSG ("deltaIsZero");
6664                   return;
6665                 }
6666               emacs_event->kind = HORIZ_WHEEL_EVENT;
6667             }
6668           else
6669             emacs_event->kind = WHEEL_EVENT;
6671           emacs_event->code = 0;
6672           emacs_event->modifiers = EV_MODIFIERS (theEvent) |
6673             ((delta > 0) ? up_modifier : down_modifier);
6674         }
6675 #endif
6676     }
6677   else
6678     {
6679       emacs_event->kind = MOUSE_CLICK_EVENT;
6680       emacs_event->code = EV_BUTTON (theEvent);
6681       emacs_event->modifiers = EV_MODIFIERS (theEvent)
6682                              | EV_UDMODIFIERS (theEvent);
6683     }
6685   XSETINT (emacs_event->x, lrint (p.x));
6686   XSETINT (emacs_event->y, lrint (p.y));
6687   EV_TRAILER (theEvent);
6688   return;
6692 - (void)rightMouseDown: (NSEvent *)theEvent
6694   NSTRACE ("[EmacsView rightMouseDown:]");
6695   [self mouseDown: theEvent];
6699 - (void)otherMouseDown: (NSEvent *)theEvent
6701   NSTRACE ("[EmacsView otherMouseDown:]");
6702   [self mouseDown: theEvent];
6706 - (void)mouseUp: (NSEvent *)theEvent
6708   NSTRACE ("[EmacsView mouseUp:]");
6709   [self mouseDown: theEvent];
6713 - (void)rightMouseUp: (NSEvent *)theEvent
6715   NSTRACE ("[EmacsView rightMouseUp:]");
6716   [self mouseDown: theEvent];
6720 - (void)otherMouseUp: (NSEvent *)theEvent
6722   NSTRACE ("[EmacsView otherMouseUp:]");
6723   [self mouseDown: theEvent];
6727 - (void) scrollWheel: (NSEvent *)theEvent
6729   NSTRACE ("[EmacsView scrollWheel:]");
6730   [self mouseDown: theEvent];
6734 /* Tell emacs the mouse has moved. */
6735 - (void)mouseMoved: (NSEvent *)e
6737   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
6738   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6739   Lisp_Object frame;
6740   NSPoint pt;
6742   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsView mouseMoved:]");
6744   dpyinfo->last_mouse_movement_time = EV_TIMESTAMP (e);
6745   pt = [self convertPoint: [e locationInWindow] fromView: nil];
6746   dpyinfo->last_mouse_motion_x = pt.x;
6747   dpyinfo->last_mouse_motion_y = pt.y;
6749   /* update any mouse face */
6750   if (hlinfo->mouse_face_hidden)
6751     {
6752       hlinfo->mouse_face_hidden = 0;
6753       clear_mouse_face (hlinfo);
6754     }
6756   /* tooltip handling */
6757   previous_help_echo_string = help_echo_string;
6758   help_echo_string = Qnil;
6760   if (!NILP (Vmouse_autoselect_window))
6761     {
6762       NSTRACE_MSG ("mouse_autoselect_window");
6763       static Lisp_Object last_mouse_window;
6764       Lisp_Object window
6765         = window_from_coordinates (emacsframe, pt.x, pt.y, 0, 0);
6767       if (WINDOWP (window)
6768           && !EQ (window, last_mouse_window)
6769           && !EQ (window, selected_window)
6770           && (!NILP (focus_follows_mouse)
6771               || (EQ (XWINDOW (window)->frame,
6772                       XWINDOW (selected_window)->frame))))
6773         {
6774           NSTRACE_MSG ("in_window");
6775           emacs_event->kind = SELECT_WINDOW_EVENT;
6776           emacs_event->frame_or_window = window;
6777           EV_TRAILER2 (e);
6778         }
6779       /* Remember the last window where we saw the mouse.  */
6780       last_mouse_window = window;
6781     }
6783   if (!note_mouse_movement (emacsframe, pt.x, pt.y))
6784     help_echo_string = previous_help_echo_string;
6786   XSETFRAME (frame, emacsframe);
6787   if (!NILP (help_echo_string) || !NILP (previous_help_echo_string))
6788     {
6789       /* NOTE: help_echo_{window,pos,object} are set in xdisp.c
6790          (note_mouse_highlight), which is called through the
6791          note_mouse_movement () call above */
6792       any_help_event_p = YES;
6793       gen_help_event (help_echo_string, frame, help_echo_window,
6794                       help_echo_object, help_echo_pos);
6795     }
6797   if (emacsframe->mouse_moved && send_appdefined)
6798     ns_send_appdefined (-1);
6802 - (void)mouseDragged: (NSEvent *)e
6804   NSTRACE ("[EmacsView mouseDragged:]");
6805   [self mouseMoved: e];
6809 - (void)rightMouseDragged: (NSEvent *)e
6811   NSTRACE ("[EmacsView rightMouseDragged:]");
6812   [self mouseMoved: e];
6816 - (void)otherMouseDragged: (NSEvent *)e
6818   NSTRACE ("[EmacsView otherMouseDragged:]");
6819   [self mouseMoved: e];
6823 - (BOOL)windowShouldClose: (id)sender
6825   NSEvent *e =[[self window] currentEvent];
6827   NSTRACE ("[EmacsView windowShouldClose:]");
6828   windowClosing = YES;
6829   if (!emacs_event)
6830     return NO;
6831   emacs_event->kind = DELETE_WINDOW_EVENT;
6832   emacs_event->modifiers = 0;
6833   emacs_event->code = 0;
6834   EV_TRAILER (e);
6835   /* Don't close this window, let this be done from lisp code.  */
6836   return NO;
6839 - (void) updateFrameSize: (BOOL) delay
6841   NSWindow *window = [self window];
6842   NSRect wr = [window frame];
6843   int extra = 0;
6844   int oldc = cols, oldr = rows;
6845   int oldw = FRAME_PIXEL_WIDTH (emacsframe);
6846   int oldh = FRAME_PIXEL_HEIGHT (emacsframe);
6847   int neww, newh;
6849   NSTRACE ("[EmacsView updateFrameSize:]");
6850   NSTRACE_SIZE ("Original size", NSMakeSize (oldw, oldh));
6851   NSTRACE_RECT ("Original frame", wr);
6852   NSTRACE_MSG  ("Original columns: %d", cols);
6853   NSTRACE_MSG  ("Original rows: %d", rows);
6855   if (! [self isFullscreen])
6856     {
6857       int toolbar_height;
6858 #ifdef NS_IMPL_GNUSTEP
6859       // GNUstep does not always update the tool bar height.  Force it.
6860       if (toolbar && [toolbar isVisible])
6861           update_frame_tool_bar (emacsframe);
6862 #endif
6864       toolbar_height = FRAME_TOOLBAR_HEIGHT (emacsframe);
6865       if (toolbar_height < 0)
6866         toolbar_height = 35;
6868       extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
6869         + toolbar_height;
6870     }
6872   if (wait_for_tool_bar)
6873     {
6874       /* The toolbar height is always 0 in fullscreen and undecorated
6875          frames, so don't wait for it to become available. */
6876       if (FRAME_TOOLBAR_HEIGHT (emacsframe) == 0
6877           && FRAME_UNDECORATED (emacsframe) == false
6878           && ! [self isFullscreen])
6879         {
6880           NSTRACE_MSG ("Waiting for toolbar");
6881           return;
6882         }
6883       wait_for_tool_bar = NO;
6884     }
6886   neww = (int)wr.size.width - emacsframe->border_width;
6887   newh = (int)wr.size.height - extra;
6889   NSTRACE_SIZE ("New size", NSMakeSize (neww, newh));
6890   NSTRACE_MSG ("FRAME_TOOLBAR_HEIGHT: %d", FRAME_TOOLBAR_HEIGHT (emacsframe));
6891   NSTRACE_MSG ("FRAME_NS_TITLEBAR_HEIGHT: %d", FRAME_NS_TITLEBAR_HEIGHT (emacsframe));
6893   cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, neww);
6894   rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe, newh);
6896   if (cols < MINWIDTH)
6897     cols = MINWIDTH;
6899   if (rows < MINHEIGHT)
6900     rows = MINHEIGHT;
6902   NSTRACE_MSG ("New columns: %d", cols);
6903   NSTRACE_MSG ("New rows: %d", rows);
6905   if (oldr != rows || oldc != cols || neww != oldw || newh != oldh)
6906     {
6907       NSView *view = FRAME_NS_VIEW (emacsframe);
6909       change_frame_size (emacsframe,
6910                          FRAME_PIXEL_TO_TEXT_WIDTH (emacsframe, neww),
6911                          FRAME_PIXEL_TO_TEXT_HEIGHT (emacsframe, newh),
6912                          0, delay, 0, 1);
6913       SET_FRAME_GARBAGED (emacsframe);
6914       cancel_mouse_face (emacsframe);
6916       /* The next two lines set the frame to the same size as we've
6917          already set above.  We need to do this when we switch back
6918          from non-native fullscreen, in other circumstances it appears
6919          to be a noop.  (bug#28872) */
6920       wr = NSMakeRect (0, 0, neww, newh);
6921       [view setFrame: wr];
6923       // to do: consider using [NSNotificationCenter postNotificationName:].
6924       [self windowDidMove: // Update top/left.
6925               [NSNotification notificationWithName:NSWindowDidMoveNotification
6926                                             object:[view window]]];
6927     }
6928   else
6929     {
6930       NSTRACE_MSG ("No change");
6931     }
6934 - (NSSize)windowWillResize: (NSWindow *)sender toSize: (NSSize)frameSize
6935 /* normalize frame to gridded text size */
6937   int extra = 0;
6939   NSTRACE ("[EmacsView windowWillResize:toSize: " NSTRACE_FMT_SIZE "]",
6940            NSTRACE_ARG_SIZE (frameSize));
6941   NSTRACE_RECT   ("[sender frame]", [sender frame]);
6942   NSTRACE_FSTYPE ("fs_state", fs_state);
6944   if (!FRAME_LIVE_P (emacsframe))
6945     return frameSize;
6947   if (fs_state == FULLSCREEN_MAXIMIZED
6948       && (maximized_width != (int)frameSize.width
6949           || maximized_height != (int)frameSize.height))
6950     [self setFSValue: FULLSCREEN_NONE];
6951   else if (fs_state == FULLSCREEN_WIDTH
6952            && maximized_width != (int)frameSize.width)
6953     [self setFSValue: FULLSCREEN_NONE];
6954   else if (fs_state == FULLSCREEN_HEIGHT
6955            && maximized_height != (int)frameSize.height)
6956     [self setFSValue: FULLSCREEN_NONE];
6958   if (fs_state == FULLSCREEN_NONE)
6959     maximized_width = maximized_height = -1;
6961   if (! [self isFullscreen])
6962     {
6963       extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
6964         + FRAME_TOOLBAR_HEIGHT (emacsframe);
6965     }
6967   cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, frameSize.width);
6968   if (cols < MINWIDTH)
6969     cols = MINWIDTH;
6971   rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe,
6972                                            frameSize.height - extra);
6973   if (rows < MINHEIGHT)
6974     rows = MINHEIGHT;
6975 #ifdef NS_IMPL_COCOA
6976   {
6977     /* this sets window title to have size in it; the wm does this under GS */
6978     NSRect r = [[self window] frame];
6979     if (r.size.height == frameSize.height && r.size.width == frameSize.width)
6980       {
6981         if (old_title != 0)
6982           {
6983             xfree (old_title);
6984             old_title = 0;
6985           }
6986       }
6987     else if (fs_state == FULLSCREEN_NONE && ! maximizing_resize
6988              && [[self window] title] != NULL)
6989       {
6990         char *size_title;
6991         NSWindow *window = [self window];
6992         if (old_title == 0)
6993           {
6994             char *t = strdup ([[[self window] title] UTF8String]);
6995             char *pos = strstr (t, "  â€”  ");
6996             if (pos)
6997               *pos = '\0';
6998             old_title = t;
6999           }
7000         size_title = xmalloc (strlen (old_title) + 40);
7001         esprintf (size_title, "%s  â€”  (%d x %d)", old_title, cols, rows);
7002         [window setTitle: [NSString stringWithUTF8String: size_title]];
7003         [window display];
7004         xfree (size_title);
7005       }
7006   }
7007 #endif /* NS_IMPL_COCOA */
7009   NSTRACE_MSG ("cols: %d  rows: %d", cols, rows);
7011   /* Restrict the new size to the text gird.
7013      Don't restrict the width if the user only adjusted the height, and
7014      vice versa.  (Without this, the frame would shrink, and move
7015      slightly, if the window was resized by dragging one of its
7016      borders.) */
7017   if (!frame_resize_pixelwise)
7018     {
7019       NSRect r = [[self window] frame];
7021       if (r.size.width != frameSize.width)
7022         {
7023           frameSize.width =
7024             FRAME_TEXT_COLS_TO_PIXEL_WIDTH  (emacsframe, cols);
7025         }
7027       if (r.size.height != frameSize.height)
7028         {
7029           frameSize.height =
7030             FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (emacsframe, rows) + extra;
7031         }
7032     }
7034   NSTRACE_RETURN_SIZE (frameSize);
7036   return frameSize;
7040 - (void)windowDidResize: (NSNotification *)notification
7042   NSTRACE ("[EmacsView windowDidResize:]");
7043   if (!FRAME_LIVE_P (emacsframe))
7044     {
7045       NSTRACE_MSG ("Ignored (frame dead)");
7046       return;
7047     }
7048   if (emacsframe->output_data.ns->in_animation)
7049     {
7050       NSTRACE_MSG ("Ignored (in animation)");
7051       return;
7052     }
7054   if (! [self fsIsNative])
7055     {
7056       NSWindow *theWindow = [notification object];
7057       /* We can get notification on the non-FS window when in
7058          fullscreen mode.  */
7059       if ([self window] != theWindow) return;
7060     }
7062   NSTRACE_RECT ("frame", [[notification object] frame]);
7064 #ifdef NS_IMPL_GNUSTEP
7065   NSWindow *theWindow = [notification object];
7067    /* In GNUstep, at least currently, it's possible to get a didResize
7068       without getting a willResize.. therefore we need to act as if we got
7069       the willResize now */
7070   NSSize sz = [theWindow frame].size;
7071   sz = [self windowWillResize: theWindow toSize: sz];
7072 #endif /* NS_IMPL_GNUSTEP */
7074   if (cols > 0 && rows > 0)
7075     {
7076       [self updateFrameSize: YES];
7077     }
7079   ns_send_appdefined (-1);
7082 #ifdef NS_IMPL_COCOA
7083 - (void)viewDidEndLiveResize
7085   NSTRACE ("[EmacsView viewDidEndLiveResize]");
7087   [super viewDidEndLiveResize];
7088   if (old_title != 0)
7089     {
7090       [[self window] setTitle: [NSString stringWithUTF8String: old_title]];
7091       xfree (old_title);
7092       old_title = 0;
7093     }
7094   maximizing_resize = NO;
7096 #endif /* NS_IMPL_COCOA */
7099 - (void)windowDidBecomeKey: (NSNotification *)notification
7100 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
7102   [self windowDidBecomeKey];
7106 - (void)windowDidBecomeKey      /* for direct calls */
7108   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
7109   struct frame *old_focus = dpyinfo->x_focus_frame;
7111   NSTRACE ("[EmacsView windowDidBecomeKey]");
7113   if (emacsframe != old_focus)
7114     dpyinfo->x_focus_frame = emacsframe;
7116   ns_frame_rehighlight (emacsframe);
7118   if (emacs_event)
7119     {
7120       emacs_event->kind = FOCUS_IN_EVENT;
7121       EV_TRAILER ((id)nil);
7122     }
7126 - (void)windowDidResignKey: (NSNotification *)notification
7127 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
7129   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
7130   BOOL is_focus_frame = dpyinfo->x_focus_frame == emacsframe;
7131   NSTRACE ("[EmacsView windowDidResignKey:]");
7133   if (is_focus_frame)
7134     dpyinfo->x_focus_frame = 0;
7136   emacsframe->mouse_moved = 0;
7137   ns_frame_rehighlight (emacsframe);
7139   /* FIXME: for some reason needed on second and subsequent clicks away
7140             from sole-frame Emacs to get hollow box to show */
7141   if (!windowClosing && [[self window] isVisible] == YES)
7142     {
7143       x_update_cursor (emacsframe, 1);
7144       x_set_frame_alpha (emacsframe);
7145     }
7147   if (any_help_event_p)
7148     {
7149       Lisp_Object frame;
7150       XSETFRAME (frame, emacsframe);
7151       help_echo_string = Qnil;
7152       gen_help_event (Qnil, frame, Qnil, Qnil, 0);
7153     }
7155   if (emacs_event && is_focus_frame)
7156     {
7157       [self deleteWorkingText];
7158       emacs_event->kind = FOCUS_OUT_EVENT;
7159       EV_TRAILER ((id)nil);
7160     }
7164 - (void)windowWillMiniaturize: sender
7166   NSTRACE ("[EmacsView windowWillMiniaturize:]");
7170 - (void)setFrame:(NSRect)frameRect
7172   NSTRACE ("[EmacsView setFrame:" NSTRACE_FMT_RECT "]",
7173            NSTRACE_ARG_RECT (frameRect));
7175   [super setFrame:(NSRect)frameRect];
7179 - (BOOL)isFlipped
7181   return YES;
7185 - (BOOL)isOpaque
7187   return NO;
7191 - (void)createToolbar: (struct frame *)f
7193   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
7194   NSWindow *window = [view window];
7196   toolbar = [[EmacsToolbar alloc] initForView: self withIdentifier:
7197                    [NSString stringWithFormat: @"Emacs Frame %d",
7198                              ns_window_num]];
7199   [toolbar setVisible: NO];
7200   [window setToolbar: toolbar];
7202   /* Don't set frame garbaged until tool bar is up to date?
7203      This avoids an extra clear and redraw (flicker) at frame creation.  */
7204   if (FRAME_EXTERNAL_TOOL_BAR (f)) wait_for_tool_bar = YES;
7205   else wait_for_tool_bar = NO;
7208 #ifdef NS_IMPL_COCOA
7209   {
7210     NSButton *toggleButton;
7211     toggleButton = [window standardWindowButton: NSWindowToolbarButton];
7212     [toggleButton setTarget: self];
7213     [toggleButton setAction: @selector (toggleToolbar: )];
7214   }
7215 #endif
7219 - (instancetype) initFrameFromEmacs: (struct frame *)f
7221   NSRect r, wr;
7222   Lisp_Object tem;
7223   NSWindow *win;
7224   NSColor *col;
7225   NSString *name;
7227   NSTRACE ("[EmacsView initFrameFromEmacs:]");
7228   NSTRACE_MSG ("cols:%d lines:%d", f->text_cols, f->text_lines);
7230   windowClosing = NO;
7231   processingCompose = NO;
7232   scrollbarsNeedingUpdate = 0;
7233   fs_state = FULLSCREEN_NONE;
7234   fs_before_fs = next_maximized = -1;
7236   fs_is_native = NO;
7237 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7238 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7239   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7240 #endif
7241     fs_is_native = ns_use_native_fullscreen;
7242 #endif
7244   maximized_width = maximized_height = -1;
7245   nonfs_window = nil;
7247   ns_userRect = NSMakeRect (0, 0, 0, 0);
7248   r = NSMakeRect (0, 0, FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, f->text_cols),
7249                  FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, f->text_lines));
7250   [self initWithFrame: r];
7251   [self setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
7253   FRAME_NS_VIEW (f) = self;
7254   emacsframe = f;
7255 #ifdef NS_IMPL_COCOA
7256   old_title = 0;
7257   maximizing_resize = NO;
7258 #endif
7260   win = [[EmacsWindow alloc]
7261             initWithContentRect: r
7262                       styleMask: (FRAME_UNDECORATED (f)
7263                                   ? FRAME_UNDECORATED_FLAGS
7264                                   : FRAME_DECORATED_FLAGS)
7265                         backing: NSBackingStoreBuffered
7266                           defer: YES];
7268 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7269 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7270   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7271 #endif
7272     [win setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
7273 #endif
7275   wr = [win frame];
7276   bwidth = f->border_width = wr.size.width - r.size.width;
7278   [win setAcceptsMouseMovedEvents: YES];
7279   [win setDelegate: self];
7280 #if !defined (NS_IMPL_COCOA) || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7281 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7282   if ([win respondsToSelector: @selector(useOptimizedDrawing:)])
7283 #endif
7284     [win useOptimizedDrawing: YES];
7285 #endif
7287   [[win contentView] addSubview: self];
7289   if (ns_drag_types)
7290     [self registerForDraggedTypes: ns_drag_types];
7292   tem = f->name;
7293   name = [NSString stringWithUTF8String:
7294                    NILP (tem) ? "Emacs" : SSDATA (tem)];
7295   [win setTitle: name];
7297   /* toolbar support */
7298   if (! FRAME_UNDECORATED (f))
7299     [self createToolbar: f];
7301 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
7302 #ifndef NSAppKitVersionNumber10_10
7303 #define NSAppKitVersionNumber10_10 1343
7304 #endif
7306   if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_10
7307       && FRAME_NS_APPEARANCE (f) != ns_appearance_aqua)
7308     win.appearance = [NSAppearance
7309                           appearanceNamed: NSAppearanceNameVibrantDark];
7310 #endif
7312 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
7313   if ([win respondsToSelector: @selector(titlebarAppearsTransparent)])
7314     win.titlebarAppearsTransparent = FRAME_NS_TRANSPARENT_TITLEBAR (f);
7315 #endif
7317   tem = f->icon_name;
7318   if (!NILP (tem))
7319     [win setMiniwindowTitle:
7320            [NSString stringWithUTF8String: SSDATA (tem)]];
7322   if (FRAME_PARENT_FRAME (f) != NULL)
7323     {
7324       NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
7325       [parent addChildWindow: win
7326                      ordered: NSWindowAbove];
7327     }
7329   if (FRAME_Z_GROUP (f) != z_group_none)
7330       win.level = NSNormalWindowLevel
7331         + (FRAME_Z_GROUP_BELOW (f) ? -1 : 1);
7333   {
7334     NSScreen *screen = [win screen];
7336     if (screen != 0)
7337       {
7338         NSPoint pt = NSMakePoint
7339           (IN_BOUND (-SCREENMAX, f->left_pos
7340                      + NS_PARENT_WINDOW_LEFT_POS (f), SCREENMAX),
7341            IN_BOUND (-SCREENMAX,
7342                      NS_PARENT_WINDOW_TOP_POS (f) - f->top_pos,
7343                      SCREENMAX));
7345         [win setFrameTopLeftPoint: pt];
7347         NSTRACE_RECT ("new frame", [win frame]);
7348       }
7349   }
7351   [win makeFirstResponder: self];
7353   col = ns_lookup_indexed_color (NS_FACE_BACKGROUND
7354                                  (FACE_FROM_ID (emacsframe, DEFAULT_FACE_ID)),
7355                                  emacsframe);
7356   [win setBackgroundColor: col];
7357   if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7358     [win setOpaque: NO];
7360 #if !defined (NS_IMPL_COCOA) \
7361   || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7362 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7363   if ([self respondsToSelector: @selector(allocateGState)])
7364 #endif
7365     [self allocateGState];
7366 #endif
7367   [NSApp registerServicesMenuSendTypes: ns_send_types
7368                            returnTypes: [NSArray array]];
7370   /* macOS Sierra automatically enables tabbed windows.  We can't
7371      allow this to be enabled until it's available on a Free system.
7372      Currently it only happens by accident and is buggy anyway. */
7373 #if defined (NS_IMPL_COCOA) \
7374   && MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
7375 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101200
7376   if ([win respondsToSelector: @selector(setTabbingMode:)])
7377 #endif
7378     [win setTabbingMode: NSWindowTabbingModeDisallowed];
7379 #endif
7381   ns_window_num++;
7382   return self;
7386 - (void)windowDidMove: sender
7388   NSWindow *win = [self window];
7389   NSRect r = [win frame];
7390   NSArray *screens = [NSScreen screens];
7391   NSScreen *screen = [screens objectAtIndex: 0];
7393   NSTRACE ("[EmacsView windowDidMove:]");
7395   if (!emacsframe->output_data.ns)
7396     return;
7397   if (screen != nil)
7398     {
7399       emacsframe->left_pos = r.origin.x - NS_PARENT_WINDOW_LEFT_POS (emacsframe);
7400       emacsframe->top_pos =
7401         NS_PARENT_WINDOW_TOP_POS (emacsframe) - (r.origin.y + r.size.height);
7403       if (emacs_event)
7404         {
7405           emacs_event->kind = MOVE_FRAME_EVENT;
7406           EV_TRAILER ((id)nil);
7407         }
7408     }
7412 /* Called AFTER method below, but before our windowWillResize call there leads
7413    to windowDidResize -> x_set_window_size.  Update emacs' notion of frame
7414    location so set_window_size moves the frame. */
7415 - (BOOL)windowShouldZoom: (NSWindow *)sender toFrame: (NSRect)newFrame
7417   NSTRACE (("[EmacsView windowShouldZoom:toFrame:" NSTRACE_FMT_RECT "]"
7418             NSTRACE_FMT_RETURN "YES"),
7419            NSTRACE_ARG_RECT (newFrame));
7421   emacsframe->output_data.ns->zooming = 1;
7422   return YES;
7426 /* Override to do something slightly nonstandard, but nice.  First click on
7427    zoom button will zoom vertically.  Second will zoom completely.  Third
7428    returns to original. */
7429 - (NSRect)windowWillUseStandardFrame:(NSWindow *)sender
7430                         defaultFrame:(NSRect)defaultFrame
7432   // TODO: Rename to "currentFrame" and assign "result" properly in
7433   // all paths.
7434   NSRect result = [sender frame];
7436   NSTRACE (("[EmacsView windowWillUseStandardFrame:defaultFrame:"
7437             NSTRACE_FMT_RECT "]"),
7438            NSTRACE_ARG_RECT (defaultFrame));
7439   NSTRACE_FSTYPE ("fs_state", fs_state);
7440   NSTRACE_FSTYPE ("fs_before_fs", fs_before_fs);
7441   NSTRACE_FSTYPE ("next_maximized", next_maximized);
7442   NSTRACE_RECT   ("ns_userRect", ns_userRect);
7443   NSTRACE_RECT   ("[sender frame]", [sender frame]);
7445   if (fs_before_fs != -1) /* Entering fullscreen */
7446     {
7447       NSTRACE_MSG ("Entering fullscreen");
7448       result = defaultFrame;
7449     }
7450   else
7451     {
7452       // Save the window size and position (frame) before the resize.
7453       if (fs_state != FULLSCREEN_MAXIMIZED
7454           && fs_state != FULLSCREEN_WIDTH)
7455         {
7456           ns_userRect.size.width = result.size.width;
7457           ns_userRect.origin.x   = result.origin.x;
7458         }
7460       if (fs_state != FULLSCREEN_MAXIMIZED
7461           && fs_state != FULLSCREEN_HEIGHT)
7462         {
7463           ns_userRect.size.height = result.size.height;
7464           ns_userRect.origin.y    = result.origin.y;
7465         }
7467       NSTRACE_RECT ("ns_userRect (2)", ns_userRect);
7469       if (next_maximized == FULLSCREEN_HEIGHT
7470           || (next_maximized == -1
7471               && abs ((int)(defaultFrame.size.height - result.size.height))
7472               > FRAME_LINE_HEIGHT (emacsframe)))
7473         {
7474           /* first click */
7475           NSTRACE_MSG ("FULLSCREEN_HEIGHT");
7476           maximized_height = result.size.height = defaultFrame.size.height;
7477           maximized_width = -1;
7478           result.origin.y = defaultFrame.origin.y;
7479           if (ns_userRect.size.height != 0)
7480             {
7481               result.origin.x = ns_userRect.origin.x;
7482               result.size.width = ns_userRect.size.width;
7483             }
7484           [self setFSValue: FULLSCREEN_HEIGHT];
7485 #ifdef NS_IMPL_COCOA
7486           maximizing_resize = YES;
7487 #endif
7488         }
7489       else if (next_maximized == FULLSCREEN_WIDTH)
7490         {
7491           NSTRACE_MSG ("FULLSCREEN_WIDTH");
7492           maximized_width = result.size.width = defaultFrame.size.width;
7493           maximized_height = -1;
7494           result.origin.x = defaultFrame.origin.x;
7495           if (ns_userRect.size.width != 0)
7496             {
7497               result.origin.y = ns_userRect.origin.y;
7498               result.size.height = ns_userRect.size.height;
7499             }
7500           [self setFSValue: FULLSCREEN_WIDTH];
7501         }
7502       else if (next_maximized == FULLSCREEN_MAXIMIZED
7503                || (next_maximized == -1
7504                    && abs ((int)(defaultFrame.size.width - result.size.width))
7505                    > FRAME_COLUMN_WIDTH (emacsframe)))
7506         {
7507           NSTRACE_MSG ("FULLSCREEN_MAXIMIZED");
7509           result = defaultFrame;  /* second click */
7510           maximized_width = result.size.width;
7511           maximized_height = result.size.height;
7512           [self setFSValue: FULLSCREEN_MAXIMIZED];
7513 #ifdef NS_IMPL_COCOA
7514           maximizing_resize = YES;
7515 #endif
7516         }
7517       else
7518         {
7519           /* restore */
7520           NSTRACE_MSG ("Restore");
7521           result = ns_userRect.size.height ? ns_userRect : result;
7522           NSTRACE_RECT ("restore (2)", result);
7523           ns_userRect = NSMakeRect (0, 0, 0, 0);
7524 #ifdef NS_IMPL_COCOA
7525           maximizing_resize = fs_state != FULLSCREEN_NONE;
7526 #endif
7527           [self setFSValue: FULLSCREEN_NONE];
7528           maximized_width = maximized_height = -1;
7529         }
7530     }
7532   if (fs_before_fs == -1) next_maximized = -1;
7534   NSTRACE_RECT   ("Final ns_userRect", ns_userRect);
7535   NSTRACE_MSG    ("Final maximized_width: %d", maximized_width);
7536   NSTRACE_MSG    ("Final maximized_height: %d", maximized_height);
7537   NSTRACE_FSTYPE ("Final next_maximized", next_maximized);
7539   [self windowWillResize: sender toSize: result.size];
7541   NSTRACE_RETURN_RECT (result);
7543   return result;
7547 - (void)windowDidDeminiaturize: sender
7549   NSTRACE ("[EmacsView windowDidDeminiaturize:]");
7550   if (!emacsframe->output_data.ns)
7551     return;
7553   SET_FRAME_ICONIFIED (emacsframe, 0);
7554   SET_FRAME_VISIBLE (emacsframe, 1);
7555   windows_or_buffers_changed = 63;
7557   if (emacs_event)
7558     {
7559       emacs_event->kind = DEICONIFY_EVENT;
7560       EV_TRAILER ((id)nil);
7561     }
7565 - (void)windowDidExpose: sender
7567   NSTRACE ("[EmacsView windowDidExpose:]");
7568   if (!emacsframe->output_data.ns)
7569     return;
7571   SET_FRAME_VISIBLE (emacsframe, 1);
7572   SET_FRAME_GARBAGED (emacsframe);
7574   if (send_appdefined)
7575     ns_send_appdefined (-1);
7579 - (void)windowDidMiniaturize: sender
7581   NSTRACE ("[EmacsView windowDidMiniaturize:]");
7582   if (!emacsframe->output_data.ns)
7583     return;
7585   SET_FRAME_ICONIFIED (emacsframe, 1);
7586   SET_FRAME_VISIBLE (emacsframe, 0);
7588   if (emacs_event)
7589     {
7590       emacs_event->kind = ICONIFY_EVENT;
7591       EV_TRAILER ((id)nil);
7592     }
7595 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7596 - (NSApplicationPresentationOptions)window:(NSWindow *)window
7597       willUseFullScreenPresentationOptions:
7598   (NSApplicationPresentationOptions)proposedOptions
7600   return proposedOptions|NSApplicationPresentationAutoHideToolbar;
7602 #endif
7604 - (void)windowWillEnterFullScreen:(NSNotification *)notification
7606   NSTRACE ("[EmacsView windowWillEnterFullScreen:]");
7607   [self windowWillEnterFullScreen];
7609 - (void)windowWillEnterFullScreen /* provided for direct calls */
7611   NSTRACE ("[EmacsView windowWillEnterFullScreen]");
7612   fs_before_fs = fs_state;
7615 - (void)windowDidEnterFullScreen:(NSNotification *)notification
7617   NSTRACE ("[EmacsView windowDidEnterFullScreen:]");
7618   [self windowDidEnterFullScreen];
7621 - (void)windowDidEnterFullScreen /* provided for direct calls */
7623   NSTRACE ("[EmacsView windowDidEnterFullScreen]");
7624   [self setFSValue: FULLSCREEN_BOTH];
7625   if (! [self fsIsNative])
7626     {
7627       [self windowDidBecomeKey];
7628       [nonfs_window orderOut:self];
7629     }
7630   else
7631     {
7632       BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (emacsframe) ? YES : NO;
7633 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 \
7634   && MAC_OS_X_VERSION_MIN_REQUIRED <= 1070
7635       unsigned val = (unsigned)[NSApp presentationOptions];
7637       // Mac OS X 10.7 bug fix, the menu won't appear without this.
7638       // val is non-zero on other macOS versions.
7639       if (val == 0)
7640         {
7641           NSApplicationPresentationOptions options
7642             = NSApplicationPresentationAutoHideDock
7643             | NSApplicationPresentationAutoHideMenuBar
7644             | NSApplicationPresentationFullScreen
7645             | NSApplicationPresentationAutoHideToolbar;
7647           [NSApp setPresentationOptions: options];
7648         }
7649 #endif
7650       [toolbar setVisible:tbar_visible];
7651     }
7654 - (void)windowWillExitFullScreen:(NSNotification *)notification
7656   NSTRACE ("[EmacsView windowWillExitFullScreen:]");
7657   [self windowWillExitFullScreen];
7660 - (void)windowWillExitFullScreen /* provided for direct calls */
7662   NSTRACE ("[EmacsView windowWillExitFullScreen]");
7663   if (!FRAME_LIVE_P (emacsframe))
7664     {
7665       NSTRACE_MSG ("Ignored (frame dead)");
7666       return;
7667     }
7668   if (next_maximized != -1)
7669     fs_before_fs = next_maximized;
7672 - (void)windowDidExitFullScreen:(NSNotification *)notification
7674   NSTRACE ("[EmacsView windowDidExitFullScreen:]");
7675   [self windowDidExitFullScreen];
7678 - (void)windowDidExitFullScreen /* provided for direct calls */
7680   NSTRACE ("[EmacsView windowDidExitFullScreen]");
7681   if (!FRAME_LIVE_P (emacsframe))
7682     {
7683       NSTRACE_MSG ("Ignored (frame dead)");
7684       return;
7685     }
7686   [self setFSValue: fs_before_fs];
7687   fs_before_fs = -1;
7688 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7689   [self updateCollectionBehavior];
7690 #endif
7691   if (FRAME_EXTERNAL_TOOL_BAR (emacsframe))
7692     {
7693       [toolbar setVisible:YES];
7694       update_frame_tool_bar (emacsframe);
7695       [self updateFrameSize:YES];
7696       [[self window] display];
7697     }
7698   else
7699     [toolbar setVisible:NO];
7701   if (next_maximized != -1)
7702     [[self window] performZoom:self];
7705 - (BOOL)fsIsNative
7707   return fs_is_native;
7710 - (BOOL)isFullscreen
7712   BOOL res;
7714   if (! fs_is_native)
7715     {
7716       res = (nonfs_window != nil);
7717     }
7718   else
7719     {
7720 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7721       res = (([[self window] styleMask] & NSWindowStyleMaskFullScreen) != 0);
7722 #else
7723       res = NO;
7724 #endif
7725     }
7727   NSTRACE ("[EmacsView isFullscreen] " NSTRACE_FMT_RETURN " %d",
7728            (int) res);
7730   return res;
7733 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7734 - (void)updateCollectionBehavior
7736   NSTRACE ("[EmacsView updateCollectionBehavior]");
7738   if (! [self isFullscreen])
7739     {
7740       NSWindow *win = [self window];
7741       NSWindowCollectionBehavior b = [win collectionBehavior];
7742       if (ns_use_native_fullscreen)
7743         b |= NSWindowCollectionBehaviorFullScreenPrimary;
7744       else
7745         b &= ~NSWindowCollectionBehaviorFullScreenPrimary;
7747       [win setCollectionBehavior: b];
7748 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7749       if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
7750 #endif
7751         fs_is_native = ns_use_native_fullscreen;
7752     }
7754 #endif
7756 - (void)toggleFullScreen: (id)sender
7758   NSWindow *w, *fw;
7759   BOOL onFirstScreen;
7760   struct frame *f;
7761   NSRect r, wr;
7762   NSColor *col;
7764   NSTRACE ("[EmacsView toggleFullScreen:]");
7766   if (fs_is_native)
7767     {
7768 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
7769 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
7770       if ([[self window] respondsToSelector: @selector(toggleFullScreen:)])
7771 #endif
7772         [[self window] toggleFullScreen:sender];
7773 #endif
7774       return;
7775     }
7777   w = [self window];
7778   onFirstScreen = [[w screen] isEqual:[[NSScreen screens] objectAtIndex:0]];
7779   f = emacsframe;
7780   wr = [w frame];
7781   col = ns_lookup_indexed_color (NS_FACE_BACKGROUND
7782                                  (FACE_FROM_ID (f, DEFAULT_FACE_ID)),
7783                                  f);
7785   if (fs_state != FULLSCREEN_BOTH)
7786     {
7787       NSScreen *screen = [w screen];
7789 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
7790       /* Hide ghost menu bar on secondary monitor? */
7791       if (! onFirstScreen
7792 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
7793           && [NSScreen respondsToSelector: @selector(screensHaveSeparateSpaces)]
7794 #endif
7795           )
7796         onFirstScreen = [NSScreen screensHaveSeparateSpaces];
7797 #endif
7798       /* Hide dock and menubar if we are on the primary screen.  */
7799       if (onFirstScreen)
7800         {
7801 #ifdef NS_IMPL_COCOA
7802           NSApplicationPresentationOptions options
7803             = NSApplicationPresentationAutoHideDock
7804             | NSApplicationPresentationAutoHideMenuBar;
7806           [NSApp setPresentationOptions: options];
7807 #else
7808           [NSMenu setMenuBarVisible:NO];
7809 #endif
7810         }
7812       fw = [[EmacsFSWindow alloc]
7813                        initWithContentRect:[w contentRectForFrameRect:wr]
7814                                  styleMask:NSWindowStyleMaskBorderless
7815                                    backing:NSBackingStoreBuffered
7816                                      defer:YES
7817                                     screen:screen];
7819       [fw setContentView:[w contentView]];
7820       [fw setTitle:[w title]];
7821       [fw setDelegate:self];
7822       [fw setAcceptsMouseMovedEvents: YES];
7823 #if !defined (NS_IMPL_COCOA) \
7824   || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
7825 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090
7826       if ([fw respondsToSelector: @selector(useOptimizedDrawing:)])
7827 #endif
7828         [fw useOptimizedDrawing: YES];
7829 #endif
7830       [fw setBackgroundColor: col];
7831       if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7832         [fw setOpaque: NO];
7834       f->border_width = 0;
7836       nonfs_window = w;
7838       [self windowWillEnterFullScreen];
7839       [fw makeKeyAndOrderFront:NSApp];
7840       [fw makeFirstResponder:self];
7841       [w orderOut:self];
7842       r = [fw frameRectForContentRect:[screen frame]];
7843       [fw setFrame: r display:YES animate:ns_use_fullscreen_animation];
7844       [self windowDidEnterFullScreen];
7845       [fw display];
7846     }
7847   else
7848     {
7849       fw = w;
7850       w = nonfs_window;
7851       nonfs_window = nil;
7853       if (onFirstScreen)
7854         {
7855 #ifdef NS_IMPL_COCOA
7856           [NSApp setPresentationOptions: NSApplicationPresentationDefault];
7857 #else
7858           [NSMenu setMenuBarVisible:YES];
7859 #endif
7860         }
7862       [w setContentView:[fw contentView]];
7863       [w setBackgroundColor: col];
7864       if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7865         [w setOpaque: NO];
7867       f->border_width = bwidth;
7869       // to do: consider using [NSNotificationCenter postNotificationName:] to send notifications.
7871       [self windowWillExitFullScreen];
7872       [fw setFrame: [w frame] display:YES animate:ns_use_fullscreen_animation];
7873       [fw close];
7874       [w makeKeyAndOrderFront:NSApp];
7875       [self windowDidExitFullScreen];
7876       [self updateFrameSize:YES];
7877     }
7880 - (void)handleFS
7882   NSTRACE ("[EmacsView handleFS]");
7884   if (fs_state != emacsframe->want_fullscreen)
7885     {
7886       if (fs_state == FULLSCREEN_BOTH)
7887         {
7888           NSTRACE_MSG ("fs_state == FULLSCREEN_BOTH");
7889           [self toggleFullScreen:self];
7890         }
7892       switch (emacsframe->want_fullscreen)
7893         {
7894         case FULLSCREEN_BOTH:
7895           NSTRACE_MSG ("FULLSCREEN_BOTH");
7896           [self toggleFullScreen:self];
7897           break;
7898         case FULLSCREEN_WIDTH:
7899           NSTRACE_MSG ("FULLSCREEN_WIDTH");
7900           next_maximized = FULLSCREEN_WIDTH;
7901           if (fs_state != FULLSCREEN_BOTH)
7902             [[self window] performZoom:self];
7903           break;
7904         case FULLSCREEN_HEIGHT:
7905           NSTRACE_MSG ("FULLSCREEN_HEIGHT");
7906           next_maximized = FULLSCREEN_HEIGHT;
7907           if (fs_state != FULLSCREEN_BOTH)
7908             [[self window] performZoom:self];
7909           break;
7910         case FULLSCREEN_MAXIMIZED:
7911           NSTRACE_MSG ("FULLSCREEN_MAXIMIZED");
7912           next_maximized = FULLSCREEN_MAXIMIZED;
7913           if (fs_state != FULLSCREEN_BOTH)
7914             [[self window] performZoom:self];
7915           break;
7916         case FULLSCREEN_NONE:
7917           NSTRACE_MSG ("FULLSCREEN_NONE");
7918           if (fs_state != FULLSCREEN_BOTH)
7919             {
7920               next_maximized = FULLSCREEN_NONE;
7921               [[self window] performZoom:self];
7922             }
7923           break;
7924         }
7926       emacsframe->want_fullscreen = FULLSCREEN_NONE;
7927     }
7931 - (void) setFSValue: (int)value
7933   NSTRACE ("[EmacsView setFSValue:" NSTRACE_FMT_FSTYPE "]",
7934            NSTRACE_ARG_FSTYPE(value));
7936   Lisp_Object lval = Qnil;
7937   switch (value)
7938     {
7939     case FULLSCREEN_BOTH:
7940       lval = Qfullboth;
7941       break;
7942     case FULLSCREEN_WIDTH:
7943       lval = Qfullwidth;
7944       break;
7945     case FULLSCREEN_HEIGHT:
7946       lval = Qfullheight;
7947       break;
7948     case FULLSCREEN_MAXIMIZED:
7949       lval = Qmaximized;
7950       break;
7951     }
7952   store_frame_param (emacsframe, Qfullscreen, lval);
7953   fs_state = value;
7956 - (void)mouseEntered: (NSEvent *)theEvent
7958   NSTRACE ("[EmacsView mouseEntered:]");
7959   if (emacsframe)
7960     FRAME_DISPLAY_INFO (emacsframe)->last_mouse_movement_time
7961       = EV_TIMESTAMP (theEvent);
7965 - (void)mouseExited: (NSEvent *)theEvent
7967   Mouse_HLInfo *hlinfo = emacsframe ? MOUSE_HL_INFO (emacsframe) : NULL;
7969   NSTRACE ("[EmacsView mouseExited:]");
7971   if (!hlinfo)
7972     return;
7974   FRAME_DISPLAY_INFO (emacsframe)->last_mouse_movement_time
7975     = EV_TIMESTAMP (theEvent);
7977   if (emacsframe == hlinfo->mouse_face_mouse_frame)
7978     {
7979       clear_mouse_face (hlinfo);
7980       hlinfo->mouse_face_mouse_frame = 0;
7981     }
7985 - (instancetype)menuDown: sender
7987   NSTRACE ("[EmacsView menuDown:]");
7988   if (context_menu_value == -1)
7989     context_menu_value = [sender tag];
7990   else
7991     {
7992       NSInteger tag = [sender tag];
7993       find_and_call_menu_selection (emacsframe, emacsframe->menu_bar_items_used,
7994                                     emacsframe->menu_bar_vector,
7995                                     (void *)tag);
7996     }
7998   ns_send_appdefined (-1);
7999   return self;
8003 - (EmacsToolbar *)toolbar
8005   return toolbar;
8009 /* this gets called on toolbar button click */
8010 - (instancetype)toolbarClicked: (id)item
8012   NSEvent *theEvent;
8013   int idx = [item tag] * TOOL_BAR_ITEM_NSLOTS;
8015   NSTRACE ("[EmacsView toolbarClicked:]");
8017   if (!emacs_event)
8018     return self;
8020   /* send first event (for some reason two needed) */
8021   theEvent = [[self window] currentEvent];
8022   emacs_event->kind = TOOL_BAR_EVENT;
8023   XSETFRAME (emacs_event->arg, emacsframe);
8024   EV_TRAILER (theEvent);
8026   emacs_event->kind = TOOL_BAR_EVENT;
8027 /*   XSETINT (emacs_event->code, 0); */
8028   emacs_event->arg = AREF (emacsframe->tool_bar_items,
8029                            idx + TOOL_BAR_ITEM_KEY);
8030   emacs_event->modifiers = EV_MODIFIERS (theEvent);
8031   EV_TRAILER (theEvent);
8032   return self;
8036 - (instancetype)toggleToolbar: (id)sender
8038   NSTRACE ("[EmacsView toggleToolbar:]");
8040   if (!emacs_event)
8041     return self;
8043   emacs_event->kind = NS_NONKEY_EVENT;
8044   emacs_event->code = KEY_NS_TOGGLE_TOOLBAR;
8045   EV_TRAILER ((id)nil);
8046   return self;
8050 - (void)drawRect: (NSRect)rect
8052   int x = NSMinX (rect), y = NSMinY (rect);
8053   int width = NSWidth (rect), height = NSHeight (rect);
8055   NSTRACE ("[EmacsView drawRect:" NSTRACE_FMT_RECT "]",
8056            NSTRACE_ARG_RECT(rect));
8058   if (!emacsframe || !emacsframe->output_data.ns)
8059     return;
8061   ns_clear_frame_area (emacsframe, x, y, width, height);
8062   block_input ();
8063   expose_frame (emacsframe, x, y, width, height);
8064   unblock_input ();
8066   /*
8067     drawRect: may be called (at least in Mac OS X 10.5) for invisible
8068     views as well for some reason.  Thus, do not infer visibility
8069     here.
8071     emacsframe->async_visible = 1;
8072     emacsframe->async_iconified = 0;
8073   */
8077 /* NSDraggingDestination protocol methods.  Actually this is not really a
8078    protocol, but a category of Object.  O well...  */
8080 -(NSDragOperation) draggingEntered: (id <NSDraggingInfo>) sender
8082   NSTRACE ("[EmacsView draggingEntered:]");
8083   return NSDragOperationGeneric;
8087 -(BOOL)prepareForDragOperation: (id <NSDraggingInfo>) sender
8089   return YES;
8093 -(BOOL)performDragOperation: (id <NSDraggingInfo>) sender
8095   id pb;
8096   int x, y;
8097   NSString *type;
8098   NSEvent *theEvent = [[self window] currentEvent];
8099   NSPoint position;
8100   NSDragOperation op = [sender draggingSourceOperationMask];
8101   int modifiers = 0;
8103   NSTRACE ("[EmacsView performDragOperation:]");
8105   if (!emacs_event)
8106     return NO;
8108   position = [self convertPoint: [sender draggingLocation] fromView: nil];
8109   x = lrint (position.x);  y = lrint (position.y);
8111   pb = [sender draggingPasteboard];
8112   type = [pb availableTypeFromArray: ns_drag_types];
8114   if (! (op & (NSDragOperationMove|NSDragOperationDelete)) &&
8115       // URL drags contain all operations (0xf), don't allow all to be set.
8116       (op & 0xf) != 0xf)
8117     {
8118       if (op & NSDragOperationLink)
8119         modifiers |= NSEventModifierFlagControl;
8120       if (op & NSDragOperationCopy)
8121         modifiers |= NSEventModifierFlagOption;
8122       if (op & NSDragOperationGeneric)
8123         modifiers |= NSEventModifierFlagCommand;
8124     }
8126   modifiers = EV_MODIFIERS2 (modifiers);
8127   if (type == 0)
8128     {
8129       return NO;
8130     }
8131   else if ([type isEqualToString: NSFilenamesPboardType])
8132     {
8133       NSArray *files;
8134       NSEnumerator *fenum;
8135       NSString *file;
8137       if (!(files = [pb propertyListForType: type]))
8138         return NO;
8140       fenum = [files objectEnumerator];
8141       while ( (file = [fenum nextObject]) )
8142         {
8143           emacs_event->kind = DRAG_N_DROP_EVENT;
8144           XSETINT (emacs_event->x, x);
8145           XSETINT (emacs_event->y, y);
8146           emacs_event->modifiers = modifiers;
8147           emacs_event->arg =  list2 (Qfile, build_string ([file UTF8String]));
8148           EV_TRAILER (theEvent);
8149         }
8150       return YES;
8151     }
8152   else if ([type isEqualToString: NSURLPboardType])
8153     {
8154       NSURL *url = [NSURL URLFromPasteboard: pb];
8155       if (url == nil) return NO;
8157       emacs_event->kind = DRAG_N_DROP_EVENT;
8158       XSETINT (emacs_event->x, x);
8159       XSETINT (emacs_event->y, y);
8160       emacs_event->modifiers = modifiers;
8161       emacs_event->arg =  list2 (Qurl,
8162                                  build_string ([[url absoluteString]
8163                                                  UTF8String]));
8164       EV_TRAILER (theEvent);
8166       if ([url isFileURL] != NO)
8167         {
8168           NSString *file = [url path];
8169           ns_input_file = append2 (ns_input_file,
8170                                    build_string ([file UTF8String]));
8171         }
8172       return YES;
8173     }
8174   else if ([type isEqualToString: NSStringPboardType]
8175            || [type isEqualToString: NSTabularTextPboardType])
8176     {
8177       NSString *data;
8179       if (! (data = [pb stringForType: type]))
8180         return NO;
8182       emacs_event->kind = DRAG_N_DROP_EVENT;
8183       XSETINT (emacs_event->x, x);
8184       XSETINT (emacs_event->y, y);
8185       emacs_event->modifiers = modifiers;
8186       emacs_event->arg =  list2 (Qnil, build_string ([data UTF8String]));
8187       EV_TRAILER (theEvent);
8188       return YES;
8189     }
8190   else
8191     {
8192       fprintf (stderr, "Invalid data type in dragging pasteboard");
8193       return NO;
8194     }
8198 - (id) validRequestorForSendType: (NSString *)typeSent
8199                       returnType: (NSString *)typeReturned
8201   NSTRACE ("[EmacsView validRequestorForSendType:returnType:]");
8202   if (typeSent != nil && [ns_send_types indexOfObject: typeSent] != NSNotFound
8203       && typeReturned == nil)
8204     {
8205       if (! NILP (ns_get_local_selection (QPRIMARY, QUTF8_STRING)))
8206         return self;
8207     }
8209   return [super validRequestorForSendType: typeSent
8210                                returnType: typeReturned];
8214 /* The next two methods are part of NSServicesRequests informal protocol,
8215    supposedly called when a services menu item is chosen from this app.
8216    But this should not happen because we override the services menu with our
8217    own entries which call ns-perform-service.
8218    Nonetheless, it appeared to happen (under strange circumstances): bug#1435.
8219    So let's at least stub them out until further investigation can be done. */
8221 - (BOOL) readSelectionFromPasteboard: (NSPasteboard *)pb
8223   /* we could call ns_string_from_pasteboard(pboard) here but then it should
8224      be written into the buffer in place of the existing selection..
8225      ordinary service calls go through functions defined in ns-win.el */
8226   return NO;
8229 - (BOOL) writeSelectionToPasteboard: (NSPasteboard *)pb types: (NSArray *)types
8231   NSArray *typesDeclared;
8232   Lisp_Object val;
8234   NSTRACE ("[EmacsView writeSelectionToPasteboard:types:]");
8236   /* We only support NSStringPboardType */
8237   if ([types containsObject:NSStringPboardType] == NO) {
8238     return NO;
8239   }
8241   val = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8242   if (CONSP (val) && SYMBOLP (XCAR (val)))
8243     {
8244       val = XCDR (val);
8245       if (CONSP (val) && NILP (XCDR (val)))
8246         val = XCAR (val);
8247     }
8248   if (! STRINGP (val))
8249     return NO;
8251   typesDeclared = [NSArray arrayWithObject:NSStringPboardType];
8252   [pb declareTypes:typesDeclared owner:nil];
8253   ns_string_to_pasteboard (pb, val);
8254   return YES;
8258 /* setMini =YES means set from internal (gives a finder icon), NO means set nil
8259    (gives a miniaturized version of the window); currently we use the latter for
8260    frames whose active buffer doesn't correspond to any file
8261    (e.g., '*scratch*') */
8262 - (instancetype)setMiniwindowImage: (BOOL) setMini
8264   id image = [[self window] miniwindowImage];
8265   NSTRACE ("[EmacsView setMiniwindowImage:%d]", setMini);
8267   /* NOTE: under Cocoa miniwindowImage always returns nil, documentation
8268      about "AppleDockIconEnabled" notwithstanding, however the set message
8269      below has its effect nonetheless. */
8270   if (image != emacsframe->output_data.ns->miniimage)
8271     {
8272       if (image && [image isKindOfClass: [EmacsImage class]])
8273         [image release];
8274       [[self window] setMiniwindowImage:
8275                        setMini ? emacsframe->output_data.ns->miniimage : nil];
8276     }
8278   return self;
8282 - (void) setRows: (int) r andColumns: (int) c
8284   NSTRACE ("[EmacsView setRows:%d andColumns:%d]", r, c);
8285   rows = r;
8286   cols = c;
8289 - (int) fullscreenState
8291   return fs_state;
8294 @end  /* EmacsView */
8298 /* ==========================================================================
8300     EmacsWindow implementation
8302    ========================================================================== */
8304 @implementation EmacsWindow
8306 #ifdef NS_IMPL_COCOA
8307 - (id)accessibilityAttributeValue:(NSString *)attribute
8309   Lisp_Object str = Qnil;
8310   struct frame *f = SELECTED_FRAME ();
8311   struct buffer *curbuf = XBUFFER (XWINDOW (f->selected_window)->contents);
8313   NSTRACE ("[EmacsWindow accessibilityAttributeValue:]");
8315   if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
8316     return NSAccessibilityTextFieldRole;
8318   if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]
8319       && curbuf && ! NILP (BVAR (curbuf, mark_active)))
8320     {
8321       str = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8322     }
8323   else if (curbuf && [attribute isEqualToString:NSAccessibilityValueAttribute])
8324     {
8325       if (! NILP (BVAR (curbuf, mark_active)))
8326           str = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
8328       if (NILP (str))
8329         {
8330           ptrdiff_t start_byte = BUF_BEGV_BYTE (curbuf);
8331           ptrdiff_t byte_range = BUF_ZV_BYTE (curbuf) - start_byte;
8332           ptrdiff_t range = BUF_ZV (curbuf) - BUF_BEGV (curbuf);
8334           if (! NILP (BVAR (curbuf, enable_multibyte_characters)))
8335             str = make_uninit_multibyte_string (range, byte_range);
8336           else
8337             str = make_uninit_string (range);
8338           /* To check: This returns emacs-utf-8, which is a superset of utf-8.
8339              Is this a problem?  */
8340           memcpy (SDATA (str), BYTE_POS_ADDR (start_byte), byte_range);
8341         }
8342     }
8345   if (! NILP (str))
8346     {
8347       if (CONSP (str) && SYMBOLP (XCAR (str)))
8348         {
8349           str = XCDR (str);
8350           if (CONSP (str) && NILP (XCDR (str)))
8351             str = XCAR (str);
8352         }
8353       if (STRINGP (str))
8354         {
8355           const char *utfStr = SSDATA (str);
8356           NSString *nsStr = [NSString stringWithUTF8String: utfStr];
8357           return nsStr;
8358         }
8359     }
8361   return [super accessibilityAttributeValue:attribute];
8363 #endif /* NS_IMPL_COCOA */
8365 /* Constrain size and placement of a frame.
8367    By returning the original "frameRect", the frame is not
8368    constrained. This can lead to unwanted situations where, for
8369    example, the menu bar covers the frame.
8371    The default implementation (accessed using "super") constrains the
8372    frame to the visible area of SCREEN, minus the menu bar (if
8373    present) and the Dock.  Note that default implementation also calls
8374    windowWillResize, with the frame it thinks should have.  (This can
8375    make the frame exit maximized mode.)
8377    Note that this should work in situations where multiple monitors
8378    are present.  Common configurations are side-by-side monitors and a
8379    monitor on top of another (e.g. when a laptop is placed under a
8380    large screen). */
8381 - (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
8383   NSTRACE ("[EmacsWindow constrainFrameRect:" NSTRACE_FMT_RECT " toScreen:]",
8384              NSTRACE_ARG_RECT (frameRect));
8386 #ifdef NS_IMPL_COCOA
8387 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
8388   // If separate spaces is on, it is like each screen is independent.  There is
8389   // no spanning of frames across screens.
8390   if (
8391 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
8392       [NSScreen respondsToSelector: @selector(screensHaveSeparateSpaces)] &&
8393 #endif
8394       [NSScreen screensHaveSeparateSpaces])
8395     {
8396       NSTRACE_MSG ("Screens have separate spaces");
8397       frameRect = [super constrainFrameRect:frameRect toScreen:screen];
8398       NSTRACE_RETURN_RECT (frameRect);
8399       return frameRect;
8400     }
8401   else
8402 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 */
8404     // Check that the proposed frameRect is visible in at least one
8405     // screen.  If it is not, ask the system to reposition it (only
8406     // for non-child windows).
8408     if (!FRAME_PARENT_FRAME (((EmacsView *)[self delegate])->emacsframe))
8409     {
8410       NSArray *screens = [NSScreen screens];
8411       NSUInteger nr_screens = [screens count];
8413       int i;
8414       BOOL frame_on_screen = NO;
8416       for (i = 0; i < nr_screens; ++i)
8417         {
8418           NSScreen *s = [screens objectAtIndex: i];
8419           NSRect scrRect = [s frame];
8421           if (NSIntersectsRect(frameRect, scrRect))
8422             {
8423               frame_on_screen = YES;
8424               break;
8425             }
8426         }
8428       if (!frame_on_screen)
8429         {
8430           NSTRACE_MSG ("Frame outside screens; constraining");
8431           frameRect = [super constrainFrameRect:frameRect toScreen:screen];
8432           NSTRACE_RETURN_RECT (frameRect);
8433           return frameRect;
8434         }
8435     }
8436 #endif
8438   return constrain_frame_rect(frameRect,
8439                               [(EmacsView *)[self delegate] isFullscreen]);
8443 - (void)performZoom:(id)sender
8445   NSTRACE ("[EmacsWindow performZoom:]");
8447   return [super performZoom:sender];
8450 - (void)zoom:(id)sender
8452   NSTRACE ("[EmacsWindow zoom:]");
8454   ns_update_auto_hide_menu_bar();
8456   // Below are three zoom implementations.  In the final commit, the
8457   // idea is that the last should be included.
8459 #if 0
8460   // Native zoom done using the standard zoom animation.  Size of the
8461   // resulting frame reduced to accommodate the Dock and, if present,
8462   // the menu-bar.
8463   [super zoom:sender];
8465 #elif 0
8466   // Native zoom done using the standard zoom animation, plus an
8467   // explicit resize to cover the full screen, except the menu-bar and
8468   // dock, if present.
8469   [super zoom:sender];
8471   // After the native zoom, resize the resulting frame to fill the
8472   // entire screen, except the menu-bar.
8473   //
8474   // This works for all practical purposes.  (The only minor oddity is
8475   // when transiting from full-height frame to a maximized, the
8476   // animation reduces the height of the frame slightly (to the 4
8477   // pixels needed to accommodate the Doc) before it snaps back into
8478   // full height.  The user would need a very trained eye to spot
8479   // this.)
8480   NSScreen * screen = [self screen];
8481   if (screen != nil)
8482     {
8483       int fs_state = [(EmacsView *)[self delegate] fullscreenState];
8485       NSTRACE_FSTYPE ("fullscreenState", fs_state);
8487       NSRect sr = [screen frame];
8488       struct EmacsMargins margins
8489         = ns_screen_margins_ignoring_hidden_dock(screen);
8491       NSRect wr = [self frame];
8492       NSTRACE_RECT ("Rect after zoom", wr);
8494       NSRect newWr = wr;
8496       if (fs_state == FULLSCREEN_MAXIMIZED
8497           || fs_state == FULLSCREEN_HEIGHT)
8498         {
8499           newWr.origin.y = sr.origin.y + margins.bottom;
8500           newWr.size.height = sr.size.height - margins.top - margins.bottom;
8501         }
8503       if (fs_state == FULLSCREEN_MAXIMIZED
8504           || fs_state == FULLSCREEN_WIDTH)
8505         {
8506           newWr.origin.x = sr.origin.x + margins.left;
8507           newWr.size.width = sr.size.width - margins.right - margins.left;
8508         }
8510       if (newWr.size.width     != wr.size.width
8511           || newWr.size.height != wr.size.height
8512           || newWr.origin.x    != wr.origin.x
8513           || newWr.origin.y    != wr.origin.y)
8514         {
8515           NSTRACE_MSG ("New frame different");
8516           [self setFrame: newWr display: NO];
8517         }
8518     }
8519 #else
8520   // Non-native zoom which is done instantaneously.  The resulting
8521   // frame covers the entire screen, except the menu-bar and dock, if
8522   // present.
8523   NSScreen * screen = [self screen];
8524   if (screen != nil)
8525     {
8526       NSRect sr = [screen frame];
8527       struct EmacsMargins margins
8528         = ns_screen_margins_ignoring_hidden_dock(screen);
8530       sr.size.height -= (margins.top + margins.bottom);
8531       sr.size.width  -= (margins.left + margins.right);
8532       sr.origin.x += margins.left;
8533       sr.origin.y += margins.bottom;
8535       sr = [[self delegate] windowWillUseStandardFrame:self
8536                                           defaultFrame:sr];
8537       [self setFrame: sr display: NO];
8538     }
8539 #endif
8542 - (void)setFrame:(NSRect)windowFrame
8543          display:(BOOL)displayViews
8545   NSTRACE ("[EmacsWindow setFrame:" NSTRACE_FMT_RECT " display:%d]",
8546            NSTRACE_ARG_RECT (windowFrame), displayViews);
8548   [super setFrame:windowFrame display:displayViews];
8551 - (void)setFrame:(NSRect)windowFrame
8552          display:(BOOL)displayViews
8553          animate:(BOOL)performAnimation
8555   NSTRACE ("[EmacsWindow setFrame:" NSTRACE_FMT_RECT
8556            " display:%d performAnimation:%d]",
8557            NSTRACE_ARG_RECT (windowFrame), displayViews, performAnimation);
8559   [super setFrame:windowFrame display:displayViews animate:performAnimation];
8562 - (void)setFrameTopLeftPoint:(NSPoint)point
8564   NSTRACE ("[EmacsWindow setFrameTopLeftPoint:" NSTRACE_FMT_POINT "]",
8565            NSTRACE_ARG_POINT (point));
8567   [super setFrameTopLeftPoint:point];
8570 - (BOOL)canBecomeKeyWindow
8572   return !FRAME_NO_ACCEPT_FOCUS (((EmacsView *)[self delegate])->emacsframe);
8574 @end /* EmacsWindow */
8577 @implementation EmacsFSWindow
8579 - (BOOL)canBecomeKeyWindow
8581   return YES;
8584 - (BOOL)canBecomeMainWindow
8586   return YES;
8589 @end
8591 /* ==========================================================================
8593     EmacsScroller implementation
8595    ========================================================================== */
8598 @implementation EmacsScroller
8600 /* for repeat button push */
8601 #define SCROLL_BAR_FIRST_DELAY 0.5
8602 #define SCROLL_BAR_CONTINUOUS_DELAY (1.0 / 15)
8604 + (CGFloat) scrollerWidth
8606   /* TODO: if we want to allow variable widths, this is the place to do it,
8607            however neither GNUstep nor Cocoa support it very well */
8608   CGFloat r;
8609 #if defined (NS_IMPL_COCOA) \
8610   && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
8611 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
8612   if ([NSScroller respondsToSelector:
8613                     @selector(scrollerWidthForControlSize:scrollerStyle:)])
8614 #endif
8615     r = [NSScroller scrollerWidthForControlSize: NSControlSizeRegular
8616                                   scrollerStyle: NSScrollerStyleLegacy];
8617 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
8618   else
8619 #endif
8620 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
8621 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 \
8622   || defined (NS_IMPL_GNUSTEP)
8623     r = [NSScroller scrollerWidth];
8624 #endif
8625   return r;
8628 - (instancetype)initFrame: (NSRect )r window: (Lisp_Object)nwin
8630   NSTRACE ("[EmacsScroller initFrame: window:]");
8632   if (r.size.width > r.size.height)
8633       horizontal = YES;
8634   else
8635       horizontal = NO;
8637   [super initWithFrame: r/*NSMakeRect (0, 0, 0, 0)*/];
8638   [self setContinuous: YES];
8639   [self setEnabled: YES];
8641   /* Ensure auto resizing of scrollbars occurs within the emacs frame's view
8642      locked against the top and bottom edges, and right edge on macOS, where
8643      scrollers are on right. */
8644 #ifdef NS_IMPL_GNUSTEP
8645   [self setAutoresizingMask: NSViewMaxXMargin | NSViewHeightSizable];
8646 #else
8647   [self setAutoresizingMask: NSViewMinXMargin | NSViewHeightSizable];
8648 #endif
8650   window = XWINDOW (nwin);
8651   condemned = NO;
8652   if (horizontal)
8653     pixel_length = NSWidth (r);
8654   else
8655     pixel_length = NSHeight (r);
8656   if (pixel_length == 0) pixel_length = 1;
8657   min_portion = 20 / pixel_length;
8659   frame = XFRAME (window->frame);
8660   if (FRAME_LIVE_P (frame))
8661     {
8662       int i;
8663       EmacsView *view = FRAME_NS_VIEW (frame);
8664       NSView *sview = [[view window] contentView];
8665       NSArray *subs = [sview subviews];
8667       /* disable optimization stopping redraw of other scrollbars */
8668       view->scrollbarsNeedingUpdate = 0;
8669       for (i =[subs count]-1; i >= 0; i--)
8670         if ([[subs objectAtIndex: i] isKindOfClass: [EmacsScroller class]])
8671           view->scrollbarsNeedingUpdate++;
8672       [sview addSubview: self];
8673     }
8675 /*  [self setFrame: r]; */
8677   return self;
8681 - (void)setFrame: (NSRect)newRect
8683   NSTRACE ("[EmacsScroller setFrame:]");
8685 /*  block_input (); */
8686   if (horizontal)
8687     pixel_length = NSWidth (newRect);
8688   else
8689     pixel_length = NSHeight (newRect);
8690   if (pixel_length == 0) pixel_length = 1;
8691   min_portion = 20 / pixel_length;
8692   [super setFrame: newRect];
8693 /*  unblock_input (); */
8697 - (void)dealloc
8699   NSTRACE ("[EmacsScroller dealloc]");
8700   if (window)
8701     {
8702       if (horizontal)
8703         wset_horizontal_scroll_bar (window, Qnil);
8704       else
8705         wset_vertical_scroll_bar (window, Qnil);
8706     }
8707   window = 0;
8708   [super dealloc];
8712 - (instancetype)condemn
8714   NSTRACE ("[EmacsScroller condemn]");
8715   condemned =YES;
8716   return self;
8720 - (instancetype)reprieve
8722   NSTRACE ("[EmacsScroller reprieve]");
8723   condemned =NO;
8724   return self;
8728 -(bool)judge
8730   NSTRACE ("[EmacsScroller judge]");
8731   bool ret = condemned;
8732   if (condemned)
8733     {
8734       EmacsView *view;
8735       block_input ();
8736       /* ensure other scrollbar updates after deletion */
8737       view = (EmacsView *)FRAME_NS_VIEW (frame);
8738       if (view != nil)
8739         view->scrollbarsNeedingUpdate++;
8740       if (window)
8741         {
8742           if (horizontal)
8743             wset_horizontal_scroll_bar (window, Qnil);
8744           else
8745             wset_vertical_scroll_bar (window, Qnil);
8746         }
8747       window = 0;
8748       [self removeFromSuperview];
8749       [self release];
8750       unblock_input ();
8751     }
8752   return ret;
8756 - (void)resetCursorRects
8758   NSRect visible = [self visibleRect];
8759   NSTRACE ("[EmacsScroller resetCursorRects]");
8761   if (!NSIsEmptyRect (visible))
8762     [self addCursorRect: visible cursor: [NSCursor arrowCursor]];
8763   [[NSCursor arrowCursor] setOnMouseEntered: YES];
8767 - (int) checkSamePosition: (int) position portion: (int) portion
8768                     whole: (int) whole
8770   return em_position ==position && em_portion ==portion && em_whole ==whole
8771     && portion != whole; /* needed for resize empty buf */
8775 - (instancetype)setPosition: (int)position portion: (int)portion whole: (int)whole
8777   NSTRACE ("[EmacsScroller setPosition:portion:whole:]");
8779   em_position = position;
8780   em_portion = portion;
8781   em_whole = whole;
8783   if (portion >= whole)
8784     {
8785 #ifdef NS_IMPL_COCOA
8786       [self setKnobProportion: 1.0];
8787       [self setDoubleValue: 1.0];
8788 #else
8789       [self setFloatValue: 0.0 knobProportion: 1.0];
8790 #endif
8791     }
8792   else
8793     {
8794       float pos;
8795       CGFloat por;
8796       portion = max ((float)whole*min_portion/pixel_length, portion);
8797       pos = (float)position / (whole - portion);
8798       por = (CGFloat)portion/whole;
8799 #ifdef NS_IMPL_COCOA
8800       [self setKnobProportion: por];
8801       [self setDoubleValue: pos];
8802 #else
8803       [self setFloatValue: pos knobProportion: por];
8804 #endif
8805     }
8807   return self;
8810 /* set up emacs_event */
8811 - (void) sendScrollEventAtLoc: (float)loc fromEvent: (NSEvent *)e
8813   Lisp_Object win;
8815   NSTRACE ("[EmacsScroller sendScrollEventAtLoc:fromEvent:]");
8817   if (!emacs_event)
8818     return;
8820   emacs_event->part = last_hit_part;
8821   emacs_event->code = 0;
8822   emacs_event->modifiers = EV_MODIFIERS (e) | down_modifier;
8823   XSETWINDOW (win, window);
8824   emacs_event->frame_or_window = win;
8825   emacs_event->timestamp = EV_TIMESTAMP (e);
8826   emacs_event->arg = Qnil;
8828   if (horizontal)
8829     {
8830       emacs_event->kind = HORIZONTAL_SCROLL_BAR_CLICK_EVENT;
8831       XSETINT (emacs_event->x, em_whole * loc / pixel_length);
8832       XSETINT (emacs_event->y, em_whole);
8833     }
8834   else
8835     {
8836       emacs_event->kind = SCROLL_BAR_CLICK_EVENT;
8837       XSETINT (emacs_event->x, loc);
8838       XSETINT (emacs_event->y, pixel_length-20);
8839     }
8841   if (q_event_ptr)
8842     {
8843       n_emacs_events_pending++;
8844       kbd_buffer_store_event_hold (emacs_event, q_event_ptr);
8845     }
8846   else
8847     hold_event (emacs_event);
8848   EVENT_INIT (*emacs_event);
8849   ns_send_appdefined (-1);
8853 /* called manually thru timer to implement repeated button action w/hold-down */
8854 - (instancetype)repeatScroll: (NSTimer *)scrollEntry
8856   NSEvent *e = [[self window] currentEvent];
8857   NSPoint p =  [[self window] mouseLocationOutsideOfEventStream];
8858   BOOL inKnob = [self testPart: p] == NSScrollerKnob;
8860   NSTRACE ("[EmacsScroller repeatScroll:]");
8862   /* clear timer if need be */
8863   if (inKnob || [scroll_repeat_entry timeInterval] == SCROLL_BAR_FIRST_DELAY)
8864     {
8865         [scroll_repeat_entry invalidate];
8866         [scroll_repeat_entry release];
8867         scroll_repeat_entry = nil;
8869         if (inKnob)
8870           return self;
8872         scroll_repeat_entry
8873           = [[NSTimer scheduledTimerWithTimeInterval:
8874                         SCROLL_BAR_CONTINUOUS_DELAY
8875                                             target: self
8876                                           selector: @selector (repeatScroll:)
8877                                           userInfo: 0
8878                                            repeats: YES]
8879               retain];
8880     }
8882   [self sendScrollEventAtLoc: 0 fromEvent: e];
8883   return self;
8887 /* Asynchronous mouse tracking for scroller.  This allows us to dispatch
8888    mouseDragged events without going into a modal loop. */
8889 - (void)mouseDown: (NSEvent *)e
8891   NSRect sr, kr;
8892   /* hitPart is only updated AFTER event is passed on */
8893   NSScrollerPart part = [self testPart: [e locationInWindow]];
8894   CGFloat loc, kloc, pos UNINIT;
8895   int edge = 0;
8897   NSTRACE ("[EmacsScroller mouseDown:]");
8899   switch (part)
8900     {
8901     case NSScrollerDecrementPage:
8902       last_hit_part = horizontal ? scroll_bar_before_handle : scroll_bar_above_handle; break;
8903     case NSScrollerIncrementPage:
8904       last_hit_part = horizontal ? scroll_bar_after_handle : scroll_bar_below_handle; break;
8905     case NSScrollerDecrementLine:
8906       last_hit_part = horizontal ? scroll_bar_left_arrow : scroll_bar_up_arrow; break;
8907     case NSScrollerIncrementLine:
8908       last_hit_part = horizontal ? scroll_bar_right_arrow : scroll_bar_down_arrow; break;
8909     case NSScrollerKnob:
8910       last_hit_part = horizontal ? scroll_bar_horizontal_handle : scroll_bar_handle; break;
8911     case NSScrollerKnobSlot:  /* GNUstep-only */
8912       last_hit_part = scroll_bar_move_ratio; break;
8913     default:  /* NSScrollerNoPart? */
8914       fprintf (stderr, "EmacsScroller-mouseDown: unexpected part %ld\n",
8915                (long) part);
8916       return;
8917     }
8919   if (part == NSScrollerKnob || part == NSScrollerKnobSlot)
8920     {
8921       /* handle, or on GNUstep possibly slot */
8922       NSEvent *fake_event;
8923       int length;
8925       /* compute float loc in slot and mouse offset on knob */
8926       sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot]
8927                       toView: nil];
8928       if (horizontal)
8929         {
8930           length = NSWidth (sr);
8931           loc = ([e locationInWindow].x - NSMinX (sr));
8932         }
8933       else
8934         {
8935           length = NSHeight (sr);
8936           loc = length - ([e locationInWindow].y - NSMinY (sr));
8937         }
8939       if (loc <= 0.0)
8940         {
8941           loc = 0.0;
8942           edge = -1;
8943         }
8944       else if (loc >= length)
8945         {
8946           loc = length;
8947           edge = 1;
8948         }
8950       if (edge)
8951         kloc = 0.5 * edge;
8952       else
8953         {
8954           kr = [self convertRect: [self rectForPart: NSScrollerKnob]
8955                           toView: nil];
8956           if (horizontal)
8957             kloc = ([e locationInWindow].x - NSMinX (kr));
8958           else
8959             kloc = NSHeight (kr) - ([e locationInWindow].y - NSMinY (kr));
8960         }
8961       last_mouse_offset = kloc;
8963       /* if knob, tell emacs a location offset by knob pos
8964          (to indicate top of handle) */
8965       if (part == NSScrollerKnob)
8966         pos = (loc - last_mouse_offset);
8967       else
8968         /* else this is a slot click on GNUstep: go straight there */
8969         pos = loc;
8971       /* If there are buttons in the scroller area, we need to
8972          recalculate pos as emacs expects the scroller slot to take up
8973          the entire available length.  */
8974       if (length != pixel_length)
8975         pos = pos * pixel_length / length;
8977       /* send a fake mouse-up to super to preempt modal -trackKnob: mode */
8978       fake_event = [NSEvent mouseEventWithType: NSEventTypeLeftMouseUp
8979                                       location: [e locationInWindow]
8980                                  modifierFlags: [e modifierFlags]
8981                                      timestamp: [e timestamp]
8982                                   windowNumber: [e windowNumber]
8983                                        context: nil
8984                                    eventNumber: [e eventNumber]
8985                                     clickCount: [e clickCount]
8986                                       pressure: [e pressure]];
8987       [super mouseUp: fake_event];
8988     }
8989   else
8990     {
8991       pos = 0;      /* ignored */
8993       /* set a timer to repeat, as we can't let superclass do this modally */
8994       scroll_repeat_entry
8995         = [[NSTimer scheduledTimerWithTimeInterval: SCROLL_BAR_FIRST_DELAY
8996                                             target: self
8997                                           selector: @selector (repeatScroll:)
8998                                           userInfo: 0
8999                                            repeats: YES]
9000             retain];
9001     }
9003   if (part != NSScrollerKnob)
9004     [self sendScrollEventAtLoc: pos fromEvent: e];
9008 /* Called as we manually track scroller drags, rather than superclass. */
9009 - (void)mouseDragged: (NSEvent *)e
9011     NSRect sr;
9012     double loc, pos;
9013     int length;
9015     NSTRACE ("[EmacsScroller mouseDragged:]");
9017       sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot]
9018                       toView: nil];
9020       if (horizontal)
9021         {
9022           length = NSWidth (sr);
9023           loc = ([e locationInWindow].x - NSMinX (sr));
9024         }
9025       else
9026         {
9027           length = NSHeight (sr);
9028           loc = length - ([e locationInWindow].y - NSMinY (sr));
9029         }
9031       if (loc <= 0.0)
9032         {
9033           loc = 0.0;
9034         }
9035       else if (loc >= length + last_mouse_offset)
9036         {
9037           loc = length + last_mouse_offset;
9038         }
9040       pos = (loc - last_mouse_offset);
9042       /* If there are buttons in the scroller area, we need to
9043          recalculate pos as emacs expects the scroller slot to take up
9044          the entire available length.  */
9045       if (length != pixel_length)
9046         pos = pos * pixel_length / length;
9048       [self sendScrollEventAtLoc: pos fromEvent: e];
9052 - (void)mouseUp: (NSEvent *)e
9054   NSTRACE ("[EmacsScroller mouseUp:]");
9056   if (scroll_repeat_entry)
9057     {
9058       [scroll_repeat_entry invalidate];
9059       [scroll_repeat_entry release];
9060       scroll_repeat_entry = nil;
9061     }
9062   last_hit_part = scroll_bar_above_handle;
9066 /* treat scrollwheel events in the bar as though they were in the main window */
9067 - (void) scrollWheel: (NSEvent *)theEvent
9069   NSTRACE ("[EmacsScroller scrollWheel:]");
9071   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (frame);
9072   [view mouseDown: theEvent];
9075 @end  /* EmacsScroller */
9078 #ifdef NS_IMPL_GNUSTEP
9079 /* Dummy class to get rid of startup warnings.  */
9080 @implementation EmacsDocument
9082 @end
9083 #endif
9086 /* ==========================================================================
9088    Font-related functions; these used to be in nsfaces.m
9090    ========================================================================== */
9093 Lisp_Object
9094 x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
9096   struct font *font = XFONT_OBJECT (font_object);
9097   EmacsView *view = FRAME_NS_VIEW (f);
9098   int font_ascent, font_descent;
9100   if (fontset < 0)
9101     fontset = fontset_from_font (font_object);
9102   FRAME_FONTSET (f) = fontset;
9104   if (FRAME_FONT (f) == font)
9105     /* This font is already set in frame F.  There's nothing more to
9106        do.  */
9107     return font_object;
9109   FRAME_FONT (f) = font;
9111   FRAME_BASELINE_OFFSET (f) = font->baseline_offset;
9112   FRAME_COLUMN_WIDTH (f) = font->average_width;
9113   get_font_ascent_descent (font, &font_ascent, &font_descent);
9114   FRAME_LINE_HEIGHT (f) = font_ascent + font_descent;
9116   /* Compute the scroll bar width in character columns.  */
9117   if (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) > 0)
9118     {
9119       int wid = FRAME_COLUMN_WIDTH (f);
9120       FRAME_CONFIG_SCROLL_BAR_COLS (f)
9121         = (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) + wid - 1) / wid;
9122     }
9123   else
9124     {
9125       int wid = FRAME_COLUMN_WIDTH (f);
9126       FRAME_CONFIG_SCROLL_BAR_COLS (f) = (14 + wid - 1) / wid;
9127     }
9129   /* Compute the scroll bar height in character lines.  */
9130   if (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) > 0)
9131     {
9132       int height = FRAME_LINE_HEIGHT (f);
9133       FRAME_CONFIG_SCROLL_BAR_LINES (f)
9134         = (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) + height - 1) / height;
9135     }
9136   else
9137     {
9138       int height = FRAME_LINE_HEIGHT (f);
9139       FRAME_CONFIG_SCROLL_BAR_LINES (f) = (14 + height - 1) / height;
9140     }
9142   /* Now make the frame display the given font.  */
9143   if (FRAME_NS_WINDOW (f) != 0 && ! [view isFullscreen])
9144     adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
9145                        FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3,
9146                        false, Qfont);
9148   return font_object;
9152 /* XLFD: -foundry-family-weight-slant-swidth-adstyle-pxlsz-ptSz-resx-resy-spc-avgWidth-rgstry-encoding */
9153 /* Note: ns_font_to_xlfd and ns_fontname_to_xlfd no longer needed, removed
9154          in 1.43. */
9156 const char *
9157 ns_xlfd_to_fontname (const char *xlfd)
9158 /* --------------------------------------------------------------------------
9159     Convert an X font name (XLFD) to an NS font name.
9160     Only family is used.
9161     The string returned is temporarily allocated.
9162    -------------------------------------------------------------------------- */
9164   char *name = xmalloc (180);
9165   int i, len;
9166   const char *ret;
9168   if (!strncmp (xlfd, "--", 2))
9169     sscanf (xlfd, "--%*[^-]-%179[^-]-", name);
9170   else
9171     sscanf (xlfd, "-%*[^-]-%179[^-]-", name);
9173   /* stopgap for malformed XLFD input */
9174   if (strlen (name) == 0)
9175     strcpy (name, "Monaco");
9177   /* undo hack in ns_fontname_to_xlfd, converting '$' to '-', '_' to ' '
9178      also uppercase after '-' or ' ' */
9179   name[0] = c_toupper (name[0]);
9180   for (len =strlen (name), i =0; i<len; i++)
9181     {
9182       if (name[i] == '$')
9183         {
9184           name[i] = '-';
9185           if (i+1<len)
9186             name[i+1] = c_toupper (name[i+1]);
9187         }
9188       else if (name[i] == '_')
9189         {
9190           name[i] = ' ';
9191           if (i+1<len)
9192             name[i+1] = c_toupper (name[i+1]);
9193         }
9194     }
9195 /*fprintf (stderr, "converted '%s' to '%s'\n",xlfd,name);  */
9196   ret = [[NSString stringWithUTF8String: name] UTF8String];
9197   xfree (name);
9198   return ret;
9202 void
9203 syms_of_nsterm (void)
9205   NSTRACE ("syms_of_nsterm");
9207   ns_antialias_threshold = 10.0;
9209   /* from 23+ we need to tell emacs what modifiers there are.. */
9210   DEFSYM (Qmodifier_value, "modifier-value");
9211   DEFSYM (Qalt, "alt");
9212   DEFSYM (Qhyper, "hyper");
9213   DEFSYM (Qmeta, "meta");
9214   DEFSYM (Qsuper, "super");
9215   DEFSYM (Qcontrol, "control");
9216   DEFSYM (QUTF8_STRING, "UTF8_STRING");
9218   DEFSYM (Qfile, "file");
9219   DEFSYM (Qurl, "url");
9221   Fput (Qalt, Qmodifier_value, make_number (alt_modifier));
9222   Fput (Qhyper, Qmodifier_value, make_number (hyper_modifier));
9223   Fput (Qmeta, Qmodifier_value, make_number (meta_modifier));
9224   Fput (Qsuper, Qmodifier_value, make_number (super_modifier));
9225   Fput (Qcontrol, Qmodifier_value, make_number (ctrl_modifier));
9227   DEFVAR_LISP ("ns-input-file", ns_input_file,
9228               "The file specified in the last NS event.");
9229   ns_input_file =Qnil;
9231   DEFVAR_LISP ("ns-working-text", ns_working_text,
9232               "String for visualizing working composition sequence.");
9233   ns_working_text =Qnil;
9235   DEFVAR_LISP ("ns-input-font", ns_input_font,
9236               "The font specified in the last NS event.");
9237   ns_input_font =Qnil;
9239   DEFVAR_LISP ("ns-input-fontsize", ns_input_fontsize,
9240               "The fontsize specified in the last NS event.");
9241   ns_input_fontsize =Qnil;
9243   DEFVAR_LISP ("ns-input-line", ns_input_line,
9244                "The line specified in the last NS event.");
9245   ns_input_line =Qnil;
9247   DEFVAR_LISP ("ns-input-spi-name", ns_input_spi_name,
9248                "The service name specified in the last NS event.");
9249   ns_input_spi_name =Qnil;
9251   DEFVAR_LISP ("ns-input-spi-arg", ns_input_spi_arg,
9252                "The service argument specified in the last NS event.");
9253   ns_input_spi_arg =Qnil;
9255   DEFVAR_LISP ("ns-alternate-modifier", ns_alternate_modifier,
9256                "This variable describes the behavior of the alternate or option key.\n\
9257 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9258 that key.\n\
9259 Set to none means that the alternate / option key is not interpreted by Emacs\n\
9260 at all, allowing it to be used at a lower level for accented character entry.");
9261   ns_alternate_modifier = Qmeta;
9263   DEFVAR_LISP ("ns-right-alternate-modifier", ns_right_alternate_modifier,
9264                "This variable describes the behavior of the right alternate or option key.\n\
9265 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9266 that key.\n\
9267 Set to left means be the same key as `ns-alternate-modifier'.\n\
9268 Set to none means that the alternate / option key is not interpreted by Emacs\n\
9269 at all, allowing it to be used at a lower level for accented character entry.");
9270   ns_right_alternate_modifier = Qleft;
9272   DEFVAR_LISP ("ns-command-modifier", ns_command_modifier,
9273                "This variable describes the behavior of the command key.\n\
9274 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9275 that key.");
9276   ns_command_modifier = Qsuper;
9278   DEFVAR_LISP ("ns-right-command-modifier", ns_right_command_modifier,
9279                "This variable describes the behavior of the right command key.\n\
9280 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9281 that key.\n\
9282 Set to left means be the same key as `ns-command-modifier'.\n\
9283 Set to none means that the command / option key is not interpreted by Emacs\n\
9284 at all, allowing it to be used at a lower level for accented character entry.");
9285   ns_right_command_modifier = Qleft;
9287   DEFVAR_LISP ("ns-control-modifier", ns_control_modifier,
9288                "This variable describes the behavior of the control key.\n\
9289 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9290 that key.");
9291   ns_control_modifier = Qcontrol;
9293   DEFVAR_LISP ("ns-right-control-modifier", ns_right_control_modifier,
9294                "This variable describes the behavior of the right control key.\n\
9295 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9296 that key.\n\
9297 Set to left means be the same key as `ns-control-modifier'.\n\
9298 Set to none means that the control / option key is not interpreted by Emacs\n\
9299 at all, allowing it to be used at a lower level for accented character entry.");
9300   ns_right_control_modifier = Qleft;
9302   DEFVAR_LISP ("ns-function-modifier", ns_function_modifier,
9303                "This variable describes the behavior of the function key (on laptops).\n\
9304 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
9305 that key.\n\
9306 Set to none means that the function key is not interpreted by Emacs at all,\n\
9307 allowing it to be used at a lower level for accented character entry.");
9308   ns_function_modifier = Qnone;
9310   DEFVAR_LISP ("ns-antialias-text", ns_antialias_text,
9311                "Non-nil (the default) means to render text antialiased.");
9312   ns_antialias_text = Qt;
9314   DEFVAR_LISP ("ns-use-thin-smoothing", ns_use_thin_smoothing,
9315                "Non-nil turns on a font smoothing method that produces thinner strokes.");
9316   ns_use_thin_smoothing = Qnil;
9318   DEFVAR_LISP ("ns-confirm-quit", ns_confirm_quit,
9319                "Whether to confirm application quit using dialog.");
9320   ns_confirm_quit = Qnil;
9322   DEFVAR_LISP ("ns-auto-hide-menu-bar", ns_auto_hide_menu_bar,
9323                doc: /* Non-nil means that the menu bar is hidden, but appears when the mouse is near.
9324 Only works on Mac OS X 10.6 or later.  */);
9325   ns_auto_hide_menu_bar = Qnil;
9327   DEFVAR_BOOL ("ns-use-native-fullscreen", ns_use_native_fullscreen,
9328      doc: /*Non-nil means to use native fullscreen on Mac OS X 10.7 and later.
9329 Nil means use fullscreen the old (< 10.7) way.  The old way works better with
9330 multiple monitors, but lacks tool bar.  This variable is ignored on
9331 Mac OS X < 10.7.  Default is t.  */);
9332   ns_use_native_fullscreen = YES;
9333   ns_last_use_native_fullscreen = ns_use_native_fullscreen;
9335   DEFVAR_BOOL ("ns-use-fullscreen-animation", ns_use_fullscreen_animation,
9336      doc: /*Non-nil means use animation on non-native fullscreen.
9337 For native fullscreen, this does nothing.
9338 Default is nil.  */);
9339   ns_use_fullscreen_animation = NO;
9341   DEFVAR_BOOL ("ns-use-srgb-colorspace", ns_use_srgb_colorspace,
9342      doc: /*Non-nil means to use sRGB colorspace on Mac OS X 10.7 and later.
9343 Note that this does not apply to images.
9344 This variable is ignored on Mac OS X < 10.7 and GNUstep.  */);
9345   ns_use_srgb_colorspace = YES;
9347   DEFVAR_BOOL ("ns-use-mwheel-acceleration",
9348                ns_use_mwheel_acceleration,
9349      doc: /*Non-nil means use macOS's standard mouse wheel acceleration.
9350 This variable is ignored on macOS < 10.7 and GNUstep.  Default is t.  */);
9351   ns_use_mwheel_acceleration = YES;
9353   DEFVAR_LISP ("ns-mwheel-line-height", ns_mwheel_line_height,
9354                doc: /*The number of pixels touchpad scrolling considers one line.
9355 Nil or a non-number means use the default frame line height.
9356 This variable is ignored on macOS < 10.7 and GNUstep.  Default is nil.  */);
9357   ns_mwheel_line_height = Qnil;
9359   DEFVAR_BOOL ("ns-use-mwheel-momentum", ns_use_mwheel_momentum,
9360                doc: /*Non-nil means mouse wheel scrolling uses momentum.
9361 This variable is ignored on macOS < 10.7 and GNUstep.  Default is t.  */);
9362   ns_use_mwheel_momentum = YES;
9364   /* TODO: move to common code */
9365   DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars,
9366                doc: /* Which toolkit scroll bars Emacs uses, if any.
9367 A value of nil means Emacs doesn't use toolkit scroll bars.
9368 With the X Window system, the value is a symbol describing the
9369 X toolkit.  Possible values are: gtk, motif, xaw, or xaw3d.
9370 With MS Windows or Nextstep, the value is t.  */);
9371   Vx_toolkit_scroll_bars = Qt;
9373   DEFVAR_BOOL ("x-use-underline-position-properties",
9374                x_use_underline_position_properties,
9375      doc: /*Non-nil means make use of UNDERLINE_POSITION font properties.
9376 A value of nil means ignore them.  If you encounter fonts with bogus
9377 UNDERLINE_POSITION font properties, for example 7x13 on XFree prior
9378 to 4.1, set this to nil. */);
9379   x_use_underline_position_properties = 0;
9381   DEFVAR_BOOL ("x-underline-at-descent-line",
9382                x_underline_at_descent_line,
9383      doc: /* Non-nil means to draw the underline at the same place as the descent line.
9384 (If `line-spacing' is in effect, that moves the underline lower by
9385 that many pixels.)
9386 A value of nil means to draw the underline according to the value of the
9387 variable `x-use-underline-position-properties', which is usually at the
9388 baseline level.  The default value is nil.  */);
9389   x_underline_at_descent_line = 0;
9391   /* Tell Emacs about this window system.  */
9392   Fprovide (Qns, Qnil);
9394   DEFSYM (Qcocoa, "cocoa");
9395   DEFSYM (Qgnustep, "gnustep");
9397 #ifdef NS_IMPL_COCOA
9398   Fprovide (Qcocoa, Qnil);
9399   syms_of_macfont ();
9400 #else
9401   Fprovide (Qgnustep, Qnil);
9402   syms_of_nsfont ();
9403 #endif