Subject: Restore correct Gnus newsgroup name after sending message
[emacs.git] / src / nsterm.m
blob63f1b15b24ed989096fc65d0c4b0fe9f19bc9271
1 /* NeXT/Open/GNUstep / macOS communication module.      -*- coding: utf-8 -*-
3 Copyright (C) 1989, 1993-1994, 2005-2006, 2008-2017 Free Software
4 Foundation, Inc.
6 This file is part of GNU Emacs.
8 GNU Emacs is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or (at
11 your option) any later version.
13 GNU Emacs is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GNU Emacs.  If not, see <http://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 #ifdef NS_IMPL_COCOA
140 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
141   if (ns_use_srgb_colorspace)
142       return [NSColor colorWithSRGBRed: red
143                                  green: green
144                                   blue: blue
145                                  alpha: alpha];
146 #endif
147 #endif
148   return [NSColor colorWithCalibratedRed: red
149                                    green: green
150                                     blue: blue
151                                    alpha: alpha];
154 - (NSColor *)colorUsingDefaultColorSpace
156 #ifdef NS_IMPL_COCOA
157 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
158   if (ns_use_srgb_colorspace)
159     return [self colorUsingColorSpace: [NSColorSpace sRGBColorSpace]];
160 #endif
161 #endif
162   return [self colorUsingColorSpaceName: NSCalibratedRGBColorSpace];
165 @end
167 /* ==========================================================================
169     Local declarations
171    ========================================================================== */
173 /* Convert a symbol indexed with an NSxxx value to a value as defined
174    in keyboard.c (lispy_function_key). I hope this is a correct way
175    of doing things... */
176 static unsigned convert_ns_to_X_keysym[] =
178   NSHomeFunctionKey,            0x50,
179   NSLeftArrowFunctionKey,       0x51,
180   NSUpArrowFunctionKey,         0x52,
181   NSRightArrowFunctionKey,      0x53,
182   NSDownArrowFunctionKey,       0x54,
183   NSPageUpFunctionKey,          0x55,
184   NSPageDownFunctionKey,        0x56,
185   NSEndFunctionKey,             0x57,
186   NSBeginFunctionKey,           0x58,
187   NSSelectFunctionKey,          0x60,
188   NSPrintFunctionKey,           0x61,
189   NSClearLineFunctionKey,       0x0B,
190   NSExecuteFunctionKey,         0x62,
191   NSInsertFunctionKey,          0x63,
192   NSUndoFunctionKey,            0x65,
193   NSRedoFunctionKey,            0x66,
194   NSMenuFunctionKey,            0x67,
195   NSFindFunctionKey,            0x68,
196   NSHelpFunctionKey,            0x6A,
197   NSBreakFunctionKey,           0x6B,
199   NSF1FunctionKey,              0xBE,
200   NSF2FunctionKey,              0xBF,
201   NSF3FunctionKey,              0xC0,
202   NSF4FunctionKey,              0xC1,
203   NSF5FunctionKey,              0xC2,
204   NSF6FunctionKey,              0xC3,
205   NSF7FunctionKey,              0xC4,
206   NSF8FunctionKey,              0xC5,
207   NSF9FunctionKey,              0xC6,
208   NSF10FunctionKey,             0xC7,
209   NSF11FunctionKey,             0xC8,
210   NSF12FunctionKey,             0xC9,
211   NSF13FunctionKey,             0xCA,
212   NSF14FunctionKey,             0xCB,
213   NSF15FunctionKey,             0xCC,
214   NSF16FunctionKey,             0xCD,
215   NSF17FunctionKey,             0xCE,
216   NSF18FunctionKey,             0xCF,
217   NSF19FunctionKey,             0xD0,
218   NSF20FunctionKey,             0xD1,
219   NSF21FunctionKey,             0xD2,
220   NSF22FunctionKey,             0xD3,
221   NSF23FunctionKey,             0xD4,
222   NSF24FunctionKey,             0xD5,
224   NSBackspaceCharacter,         0x08,  /* 8: Not on some KBs. */
225   NSDeleteCharacter,            0xFF,  /* 127: Big 'delete' key upper right. */
226   NSDeleteFunctionKey,          0x9F,  /* 63272: Del forw key off main array. */
228   NSTabCharacter,               0x09,
229   0x19,                         0x09,  /* left tab->regular since pass shift */
230   NSCarriageReturnCharacter,    0x0D,
231   NSNewlineCharacter,           0x0D,
232   NSEnterCharacter,             0x8D,
234   0x41|NSEventModifierFlagNumericPad,   0xAE,  /* KP_Decimal */
235   0x43|NSEventModifierFlagNumericPad,   0xAA,  /* KP_Multiply */
236   0x45|NSEventModifierFlagNumericPad,   0xAB,  /* KP_Add */
237   0x4B|NSEventModifierFlagNumericPad,   0xAF,  /* KP_Divide */
238   0x4E|NSEventModifierFlagNumericPad,   0xAD,  /* KP_Subtract */
239   0x51|NSEventModifierFlagNumericPad,   0xBD,  /* KP_Equal */
240   0x52|NSEventModifierFlagNumericPad,   0xB0,  /* KP_0 */
241   0x53|NSEventModifierFlagNumericPad,   0xB1,  /* KP_1 */
242   0x54|NSEventModifierFlagNumericPad,   0xB2,  /* KP_2 */
243   0x55|NSEventModifierFlagNumericPad,   0xB3,  /* KP_3 */
244   0x56|NSEventModifierFlagNumericPad,   0xB4,  /* KP_4 */
245   0x57|NSEventModifierFlagNumericPad,   0xB5,  /* KP_5 */
246   0x58|NSEventModifierFlagNumericPad,   0xB6,  /* KP_6 */
247   0x59|NSEventModifierFlagNumericPad,   0xB7,  /* KP_7 */
248   0x5B|NSEventModifierFlagNumericPad,   0xB8,  /* KP_8 */
249   0x5C|NSEventModifierFlagNumericPad,   0xB9,  /* KP_9 */
251   0x1B,                         0x1B   /* escape */
254 /* On macOS picks up the default NSGlobalDomain AppleAntiAliasingThreshold,
255    the maximum font size to NOT antialias.  On GNUstep there is currently
256    no way to control this behavior. */
257 float ns_antialias_threshold;
259 NSArray *ns_send_types = 0, *ns_return_types = 0;
260 static NSArray *ns_drag_types = 0;
261 NSString *ns_app_name = @"Emacs";  /* default changed later */
263 /* Display variables */
264 struct ns_display_info *x_display_list; /* Chain of existing displays */
265 long context_menu_value = 0;
267 /* display update */
268 static struct frame *ns_updating_frame;
269 static NSView *focus_view = NULL;
270 static int ns_window_num = 0;
271 #ifdef NS_IMPL_GNUSTEP
272 static NSRect uRect;            // TODO: This is dead, remove it?
273 #endif
274 static BOOL gsaved = NO;
275 static BOOL ns_fake_keydown = NO;
276 #ifdef NS_IMPL_COCOA
277 static BOOL ns_menu_bar_is_hidden = NO;
278 #endif
279 /*static int debug_lock = 0; */
281 /* event loop */
282 static BOOL send_appdefined = YES;
283 #define NO_APPDEFINED_DATA (-8)
284 static int last_appdefined_event_data = NO_APPDEFINED_DATA;
285 static NSTimer *timed_entry = 0;
286 static NSTimer *scroll_repeat_entry = nil;
287 static fd_set select_readfds, select_writefds;
288 enum { SELECT_HAVE_READ = 1, SELECT_HAVE_WRITE = 2, SELECT_HAVE_TMO = 4 };
289 static int select_nfds = 0, select_valid = 0;
290 static struct timespec select_timeout = { 0, 0 };
291 static int selfds[2] = { -1, -1 };
292 static pthread_mutex_t select_mutex;
293 static NSAutoreleasePool *outerpool;
294 static struct input_event *emacs_event = NULL;
295 static struct input_event *q_event_ptr = NULL;
296 static int n_emacs_events_pending = 0;
297 static NSMutableArray *ns_pending_files, *ns_pending_service_names,
298   *ns_pending_service_args;
299 static BOOL ns_do_open_file = NO;
300 static BOOL ns_last_use_native_fullscreen;
302 /* Non-zero means that a HELP_EVENT has been generated since Emacs
303    start.  */
305 static BOOL any_help_event_p = NO;
307 static struct {
308   struct input_event *q;
309   int nr, cap;
310 } hold_event_q = {
311   NULL, 0, 0
314 static NSString *represented_filename = nil;
315 static struct frame *represented_frame = 0;
317 #ifdef NS_IMPL_COCOA
319  * State for pending menu activation:
320  * MENU_NONE     Normal state
321  * MENU_PENDING  A menu has been clicked on, but has been canceled so we can
322  *               run lisp to update the menu.
323  * MENU_OPENING  Menu is up to date, and the click event is redone so the menu
324  *               will open.
325  */
326 #define MENU_NONE 0
327 #define MENU_PENDING 1
328 #define MENU_OPENING 2
329 static int menu_will_open_state = MENU_NONE;
331 /* Saved position for menu click.  */
332 static CGPoint menu_mouse_point;
333 #endif
335 /* Convert modifiers in a NeXTstep event to emacs style modifiers.  */
336 #define NS_FUNCTION_KEY_MASK 0x800000
337 #define NSLeftControlKeyMask    (0x000001 | NSEventModifierFlagControl)
338 #define NSRightControlKeyMask   (0x002000 | NSEventModifierFlagControl)
339 #define NSLeftCommandKeyMask    (0x000008 | NSEventModifierFlagCommand)
340 #define NSRightCommandKeyMask   (0x000010 | NSEventModifierFlagCommand)
341 #define NSLeftAlternateKeyMask  (0x000020 | NSEventModifierFlagOption)
342 #define NSRightAlternateKeyMask (0x000040 | NSEventModifierFlagOption)
343 #define EV_MODIFIERS2(flags)                          \
344     (((flags & NSEventModifierFlagHelp) ?           \
345            hyper_modifier : 0)                        \
346      | (!EQ (ns_right_alternate_modifier, Qleft) && \
347         ((flags & NSRightAlternateKeyMask) \
348          == NSRightAlternateKeyMask) ? \
349            parse_solitary_modifier (ns_right_alternate_modifier) : 0) \
350      | ((flags & NSEventModifierFlagOption) ?                 \
351            parse_solitary_modifier (ns_alternate_modifier) : 0)   \
352      | ((flags & NSEventModifierFlagShift) ?     \
353            shift_modifier : 0)                        \
354      | (!EQ (ns_right_control_modifier, Qleft) && \
355         ((flags & NSRightControlKeyMask) \
356          == NSRightControlKeyMask) ? \
357            parse_solitary_modifier (ns_right_control_modifier) : 0) \
358      | ((flags & NSEventModifierFlagControl) ?      \
359            parse_solitary_modifier (ns_control_modifier) : 0)     \
360      | ((flags & NS_FUNCTION_KEY_MASK) ?  \
361            parse_solitary_modifier (ns_function_modifier) : 0)    \
362      | (!EQ (ns_right_command_modifier, Qleft) && \
363         ((flags & NSRightCommandKeyMask) \
364          == NSRightCommandKeyMask) ? \
365            parse_solitary_modifier (ns_right_command_modifier) : 0) \
366      | ((flags & NSEventModifierFlagCommand) ?      \
367            parse_solitary_modifier (ns_command_modifier):0))
368 #define EV_MODIFIERS(e) EV_MODIFIERS2 ([e modifierFlags])
370 #define EV_UDMODIFIERS(e)                                      \
371     ((([e type] == NSEventTypeLeftMouseDown) ? down_modifier : 0)       \
372      | (([e type] == NSEventTypeRightMouseDown) ? down_modifier : 0)    \
373      | (([e type] == NSEventTypeOtherMouseDown) ? down_modifier : 0)    \
374      | (([e type] == NSEventTypeLeftMouseDragged) ? down_modifier : 0)  \
375      | (([e type] == NSEventTypeRightMouseDragged) ? down_modifier : 0) \
376      | (([e type] == NSEventTypeOtherMouseDragged) ? down_modifier : 0) \
377      | (([e type] == NSEventTypeLeftMouseUp)   ? up_modifier   : 0)     \
378      | (([e type] == NSEventTypeRightMouseUp)   ? up_modifier   : 0)    \
379      | (([e type] == NSEventTypeOtherMouseUp)   ? up_modifier   : 0))
381 #define EV_BUTTON(e)                                                         \
382     ((([e type] == NSEventTypeLeftMouseDown) || ([e type] == NSEventTypeLeftMouseUp)) ? 0 :    \
383       (([e type] == NSEventTypeRightMouseDown) || ([e type] == NSEventTypeRightMouseUp)) ? 2 : \
384      [e buttonNumber] - 1)
386 /* Convert the time field to a timestamp in milliseconds. */
387 #define EV_TIMESTAMP(e) ([e timestamp] * 1000)
389 /* This is a piece of code which is common to all the event handling
390    methods.  Maybe it should even be a function.  */
391 #define EV_TRAILER(e)                                                   \
392   {                                                                     \
393     XSETFRAME (emacs_event->frame_or_window, emacsframe);               \
394     EV_TRAILER2 (e);                                                    \
395   }
397 #define EV_TRAILER2(e)                                                  \
398   {                                                                     \
399       if (e) emacs_event->timestamp = EV_TIMESTAMP (e);                 \
400       if (q_event_ptr)                                                  \
401         {                                                               \
402           Lisp_Object tem = Vinhibit_quit;                              \
403           Vinhibit_quit = Qt;                                           \
404           n_emacs_events_pending++;                                     \
405           kbd_buffer_store_event_hold (emacs_event, q_event_ptr);       \
406           Vinhibit_quit = tem;                                          \
407         }                                                               \
408       else                                                              \
409         hold_event (emacs_event);                                       \
410       EVENT_INIT (*emacs_event);                                        \
411       ns_send_appdefined (-1);                                          \
412     }
414 /* TODO: get rid of need for these forward declarations */
415 static void ns_condemn_scroll_bars (struct frame *f);
416 static void ns_judge_scroll_bars (struct frame *f);
419 /* ==========================================================================
421     Utilities
423    ========================================================================== */
425 void
426 ns_set_represented_filename (NSString* fstr, struct frame *f)
428   represented_filename = [fstr retain];
429   represented_frame = f;
432 void
433 ns_init_events (struct input_event* ev)
435   EVENT_INIT (*ev);
436   emacs_event = ev;
439 void
440 ns_finish_events (void)
442   emacs_event = NULL;
445 static void
446 hold_event (struct input_event *event)
448   if (hold_event_q.nr == hold_event_q.cap)
449     {
450       if (hold_event_q.cap == 0) hold_event_q.cap = 10;
451       else hold_event_q.cap *= 2;
452       hold_event_q.q =
453         xrealloc (hold_event_q.q, hold_event_q.cap * sizeof *hold_event_q.q);
454     }
456   hold_event_q.q[hold_event_q.nr++] = *event;
457   /* Make sure ns_read_socket is called, i.e. we have input.  */
458   raise (SIGIO);
459   send_appdefined = YES;
462 static Lisp_Object
463 append2 (Lisp_Object list, Lisp_Object item)
464 /* --------------------------------------------------------------------------
465    Utility to append to a list
466    -------------------------------------------------------------------------- */
468   return CALLN (Fnconc, list, list1 (item));
472 const char *
473 ns_etc_directory (void)
474 /* If running as a self-contained app bundle, return as a string the
475    filename of the etc directory, if present; else nil.  */
477   NSBundle *bundle = [NSBundle mainBundle];
478   NSString *resourceDir = [bundle resourcePath];
479   NSString *resourcePath;
480   NSFileManager *fileManager = [NSFileManager defaultManager];
481   BOOL isDir;
483   resourcePath = [resourceDir stringByAppendingPathComponent: @"etc"];
484   if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
485     {
486       if (isDir) return [resourcePath UTF8String];
487     }
488   return NULL;
492 const char *
493 ns_exec_path (void)
494 /* If running as a self-contained app bundle, return as a path string
495    the filenames of the libexec and bin directories, ie libexec:bin.
496    Otherwise, return nil.
497    Normally, Emacs does not add its own bin/ directory to the PATH.
498    However, a self-contained NS build has a different layout, with
499    bin/ and libexec/ subdirectories in the directory that contains
500    Emacs.app itself.
501    We put libexec first, because init_callproc_1 uses the first
502    element to initialize exec-directory.  An alternative would be
503    for init_callproc to check for invocation-directory/libexec.
506   NSBundle *bundle = [NSBundle mainBundle];
507   NSString *resourceDir = [bundle resourcePath];
508   NSString *binDir = [bundle bundlePath];
509   NSString *resourcePath, *resourcePaths;
510   NSRange range;
511   NSString *pathSeparator = [NSString stringWithFormat: @"%c", SEPCHAR];
512   NSFileManager *fileManager = [NSFileManager defaultManager];
513   NSArray *paths;
514   NSEnumerator *pathEnum;
515   BOOL isDir;
517   range = [resourceDir rangeOfString: @"Contents"];
518   if (range.location != NSNotFound)
519     {
520       binDir = [binDir stringByAppendingPathComponent: @"Contents"];
521 #ifdef NS_IMPL_COCOA
522       binDir = [binDir stringByAppendingPathComponent: @"MacOS"];
523 #endif
524     }
526   paths = [binDir stringsByAppendingPaths:
527                 [NSArray arrayWithObjects: @"libexec", @"bin", nil]];
528   pathEnum = [paths objectEnumerator];
529   resourcePaths = @"";
531   while ((resourcePath = [pathEnum nextObject]))
532     {
533       if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
534         if (isDir)
535           {
536             if ([resourcePaths length] > 0)
537               resourcePaths
538                 = [resourcePaths stringByAppendingString: pathSeparator];
539             resourcePaths
540               = [resourcePaths stringByAppendingString: resourcePath];
541           }
542     }
543   if ([resourcePaths length] > 0) return [resourcePaths UTF8String];
545   return NULL;
549 const char *
550 ns_load_path (void)
551 /* If running as a self-contained app bundle, return as a path string
552    the filenames of the site-lisp and lisp directories.
553    Ie, site-lisp:lisp.  Otherwise, return nil.  */
555   NSBundle *bundle = [NSBundle mainBundle];
556   NSString *resourceDir = [bundle resourcePath];
557   NSString *resourcePath, *resourcePaths;
558   NSString *pathSeparator = [NSString stringWithFormat: @"%c", SEPCHAR];
559   NSFileManager *fileManager = [NSFileManager defaultManager];
560   BOOL isDir;
561   NSArray *paths = [resourceDir stringsByAppendingPaths:
562                               [NSArray arrayWithObjects:
563                                          @"site-lisp", @"lisp", nil]];
564   NSEnumerator *pathEnum = [paths objectEnumerator];
565   resourcePaths = @"";
567   /* Hack to skip site-lisp.  */
568   if (no_site_lisp) resourcePath = [pathEnum nextObject];
570   while ((resourcePath = [pathEnum nextObject]))
571     {
572       if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
573         if (isDir)
574           {
575             if ([resourcePaths length] > 0)
576               resourcePaths
577                 = [resourcePaths stringByAppendingString: pathSeparator];
578             resourcePaths
579               = [resourcePaths stringByAppendingString: resourcePath];
580           }
581     }
582   if ([resourcePaths length] > 0) return [resourcePaths UTF8String];
584   return NULL;
588 void
589 ns_init_locale (void)
590 /* macOS doesn't set any environment variables for the locale when run
591    from the GUI. Get the locale from the OS and set LANG. */
593   NSLocale *locale = [NSLocale currentLocale];
595   NSTRACE ("ns_init_locale");
597   @try
598     {
599       /* It seems macOS should probably use UTF-8 everywhere.
600          'localeIdentifier' does not specify the encoding, and I can't
601          find any way to get the OS to tell us which encoding to use,
602          so hard-code '.UTF-8'. */
603       NSString *localeID = [NSString stringWithFormat:@"%@.UTF-8",
604                                      [locale localeIdentifier]];
606       /* Set LANG to locale, but not if LANG is already set. */
607       setenv("LANG", [localeID UTF8String], 0);
608     }
609   @catch (NSException *e)
610     {
611       NSLog (@"Locale detection failed: %@: %@", [e name], [e reason]);
612     }
616 void
617 ns_release_object (void *obj)
618 /* --------------------------------------------------------------------------
619     Release an object (callable from C)
620    -------------------------------------------------------------------------- */
622     [(id)obj release];
626 void
627 ns_retain_object (void *obj)
628 /* --------------------------------------------------------------------------
629     Retain an object (callable from C)
630    -------------------------------------------------------------------------- */
632     [(id)obj retain];
636 void *
637 ns_alloc_autorelease_pool (void)
638 /* --------------------------------------------------------------------------
639      Allocate a pool for temporary objects (callable from C)
640    -------------------------------------------------------------------------- */
642   return [[NSAutoreleasePool alloc] init];
646 void
647 ns_release_autorelease_pool (void *pool)
648 /* --------------------------------------------------------------------------
649      Free a pool and temporary objects it refers to (callable from C)
650    -------------------------------------------------------------------------- */
652   ns_release_object (pool);
656 static BOOL
657 ns_menu_bar_should_be_hidden (void)
658 /* True, if the menu bar should be hidden.  */
660   return !NILP (ns_auto_hide_menu_bar)
661     && [NSApp respondsToSelector:@selector(setPresentationOptions:)];
665 struct EmacsMargins
667   CGFloat top;
668   CGFloat bottom;
669   CGFloat left;
670   CGFloat right;
674 static struct EmacsMargins
675 ns_screen_margins (NSScreen *screen)
676 /* The parts of SCREEN used by the operating system.  */
678   NSTRACE ("ns_screen_margins");
680   struct EmacsMargins margins;
682   NSRect screenFrame = [screen frame];
683   NSRect screenVisibleFrame = [screen visibleFrame];
685   /* Sometimes, visibleFrame isn't up-to-date with respect to a hidden
686      menu bar, check this explicitly.  */
687   if (ns_menu_bar_should_be_hidden())
688     {
689       margins.top = 0;
690     }
691   else
692     {
693       CGFloat frameTop = screenFrame.origin.y + screenFrame.size.height;
694       CGFloat visibleFrameTop = (screenVisibleFrame.origin.y
695                                  + screenVisibleFrame.size.height);
697       margins.top = frameTop - visibleFrameTop;
698     }
700   {
701     CGFloat frameRight = screenFrame.origin.x + screenFrame.size.width;
702     CGFloat visibleFrameRight = (screenVisibleFrame.origin.x
703                                  + screenVisibleFrame.size.width);
704     margins.right = frameRight - visibleFrameRight;
705   }
707   margins.bottom = screenVisibleFrame.origin.y - screenFrame.origin.y;
708   margins.left   = screenVisibleFrame.origin.x - screenFrame.origin.x;
710   NSTRACE_MSG ("left:%g right:%g top:%g bottom:%g",
711                margins.left,
712                margins.right,
713                margins.top,
714                margins.bottom);
716   return margins;
720 /* A screen margin between 1 and DOCK_IGNORE_LIMIT (inclusive) is
721    assumed to contain a hidden dock.  macOS currently use 4 pixels for
722    this, however, to be future compatible, a larger value is used.  */
723 #define DOCK_IGNORE_LIMIT 6
725 static struct EmacsMargins
726 ns_screen_margins_ignoring_hidden_dock (NSScreen *screen)
727 /* The parts of SCREEN used by the operating system, excluding the parts
728 reserved for an hidden dock.  */
730   NSTRACE ("ns_screen_margins_ignoring_hidden_dock");
732   struct EmacsMargins margins = ns_screen_margins(screen);
734   /* macOS (currently) reserved 4 pixels along the edge where a hidden
735      dock is located.  Unfortunately, it's not possible to find the
736      location and information about if the dock is hidden.  Instead,
737      it is assumed that if the margin of an edge is less than
738      DOCK_IGNORE_LIMIT, it contains a hidden dock.  */
739   if (margins.left <= DOCK_IGNORE_LIMIT)
740     {
741       margins.left = 0;
742     }
743   if (margins.right <= DOCK_IGNORE_LIMIT)
744     {
745       margins.right = 0;
746     }
747   if (margins.top <= DOCK_IGNORE_LIMIT)
748     {
749       margins.top = 0;
750     }
751   /* Note: This doesn't occur in current versions of macOS, but
752      included for completeness and future compatibility.  */
753   if (margins.bottom <= DOCK_IGNORE_LIMIT)
754     {
755       margins.bottom = 0;
756     }
758   NSTRACE_MSG ("left:%g right:%g top:%g bottom:%g",
759                margins.left,
760                margins.right,
761                margins.top,
762                margins.bottom);
764   return margins;
768 static CGFloat
769 ns_menu_bar_height (NSScreen *screen)
770 /* The height of the menu bar, if visible.
772    Note: Don't use this when fullscreen is enabled -- the screen
773    sometimes includes, sometimes excludes the menu bar area.  */
775   struct EmacsMargins margins = ns_screen_margins(screen);
777   CGFloat res = margins.top;
779   NSTRACE ("ns_menu_bar_height " NSTRACE_FMT_RETURN " %.0f", res);
781   return res;
785 /* ==========================================================================
787     Focus (clipping) and screen update
789    ========================================================================== */
792 // Window constraining
793 // -------------------
795 // To ensure that the windows are not placed under the menu bar, they
796 // are typically moved by the call-back constrainFrameRect. However,
797 // by overriding it, it's possible to inhibit this, leaving the window
798 // in it's original position.
800 // It's possible to hide the menu bar. However, technically, it's only
801 // possible to hide it when the application is active. To ensure that
802 // this work properly, the menu bar and window constraining are
803 // deferred until the application becomes active.
805 // Even though it's not possible to manually move a window above the
806 // top of the screen, it is allowed if it's done programmatically,
807 // when the menu is hidden. This allows the editable area to cover the
808 // full screen height.
810 // Test cases
811 // ----------
813 // Use the following extra files:
815 //    init.el:
816 //       ;; Hide menu and place frame slightly above the top of the screen.
817 //       (setq ns-auto-hide-menu-bar t)
818 //       (set-frame-position (selected-frame) 0 -20)
820 // Test 1:
822 //    emacs -Q -l init.el
824 //    Result: No menu bar, and the title bar should be above the screen.
826 // Test 2:
828 //    emacs -Q
830 //    Result: Menu bar visible, frame placed immediately below the menu.
833 static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
835   NSTRACE ("constrain_frame_rect(" NSTRACE_FMT_RECT ")",
836              NSTRACE_ARG_RECT (frameRect));
838   // --------------------
839   // Collect information about the screen the frame is covering.
840   //
842   NSArray *screens = [NSScreen screens];
843   NSUInteger nr_screens = [screens count];
845   int i;
847   // The height of the menu bar, if present in any screen the frame is
848   // displayed in.
849   int menu_bar_height = 0;
851   // A rectangle covering all the screen the frame is displayed in.
852   NSRect multiscreenRect = NSMakeRect(0, 0, 0, 0);
853   for (i = 0; i < nr_screens; ++i )
854     {
855       NSScreen *s = [screens objectAtIndex: i];
856       NSRect scrRect = [s frame];
858       NSTRACE_MSG ("Screen %d: " NSTRACE_FMT_RECT,
859                    i, NSTRACE_ARG_RECT (scrRect));
861       if (NSIntersectionRect (frameRect, scrRect).size.height != 0)
862         {
863           multiscreenRect = NSUnionRect (multiscreenRect, scrRect);
865           if (!isFullscreen)
866             {
867               CGFloat screen_menu_bar_height = ns_menu_bar_height (s);
868               menu_bar_height = max(menu_bar_height, screen_menu_bar_height);
869             }
870         }
871     }
873   NSTRACE_RECT ("multiscreenRect", multiscreenRect);
875   NSTRACE_MSG ("menu_bar_height: %d", menu_bar_height);
877   if (multiscreenRect.size.width == 0
878       || multiscreenRect.size.height == 0)
879     {
880       // Failed to find any monitor, give up.
881       NSTRACE_MSG ("multiscreenRect empty");
882       NSTRACE_RETURN_RECT (frameRect);
883       return frameRect;
884     }
887   // --------------------
888   // Find a suitable placement.
889   //
891   if (ns_menu_bar_should_be_hidden())
892     {
893       // When the menu bar is hidden, the user may place part of the
894       // frame above the top of the screen, for example to hide the
895       // title bar.
896       //
897       // Hence, keep the original position.
898     }
899   else
900     {
901       // Ensure that the frame is below the menu bar, or below the top
902       // of the screen.
903       //
904       // This assume that the menu bar is placed at the top in the
905       // rectangle that covers the monitors.  (It doesn't have to be,
906       // but if it's not it's hard to do anything useful.)
907       CGFloat topOfWorkArea = (multiscreenRect.origin.y
908                                + multiscreenRect.size.height
909                                - menu_bar_height);
911       CGFloat topOfFrame = frameRect.origin.y + frameRect.size.height;
912       if (topOfFrame > topOfWorkArea)
913         {
914           frameRect.origin.y -= topOfFrame - topOfWorkArea;
915           NSTRACE_RECT ("After placement adjust", frameRect);
916         }
917     }
919   // Include the following section to restrict frame to the screens.
920   // (If so, update it to allow the frame to stretch down below the
921   // screen.)
922 #if 0
923   // --------------------
924   // Ensure frame doesn't stretch below the screens.
925   //
927   CGFloat diff = multiscreenRect.origin.y - frameRect.origin.y;
929   if (diff > 0)
930     {
931       frameRect.origin.y = multiscreenRect.origin.y;
932       frameRect.size.height -= diff;
933     }
934 #endif
936   NSTRACE_RETURN_RECT (frameRect);
937   return frameRect;
941 static void
942 ns_constrain_all_frames (void)
943 /* --------------------------------------------------------------------------
944      Ensure that the menu bar doesn't cover any frames.
945    -------------------------------------------------------------------------- */
947   Lisp_Object tail, frame;
949   NSTRACE ("ns_constrain_all_frames");
951   block_input ();
953   FOR_EACH_FRAME (tail, frame)
954     {
955       struct frame *f = XFRAME (frame);
956       if (FRAME_NS_P (f))
957         {
958           EmacsView *view = FRAME_NS_VIEW (f);
960           if (![view isFullscreen])
961             {
962               [[view window]
963                 setFrame:constrain_frame_rect([[view window] frame], false)
964                  display:NO];
965             }
966         }
967     }
969   unblock_input ();
973 static void
974 ns_update_auto_hide_menu_bar (void)
975 /* --------------------------------------------------------------------------
976      Show or hide the menu bar, based on user setting.
977    -------------------------------------------------------------------------- */
979 #ifdef NS_IMPL_COCOA
980   NSTRACE ("ns_update_auto_hide_menu_bar");
982   block_input ();
984   if (NSApp != nil && [NSApp isActive])
985     {
986       // Note, "setPresentationOptions" triggers an error unless the
987       // application is active.
988       BOOL menu_bar_should_be_hidden = ns_menu_bar_should_be_hidden ();
990       if (menu_bar_should_be_hidden != ns_menu_bar_is_hidden)
991         {
992           NSApplicationPresentationOptions options
993             = NSApplicationPresentationDefault;
995           if (menu_bar_should_be_hidden)
996             options |= NSApplicationPresentationAutoHideMenuBar
997               | NSApplicationPresentationAutoHideDock;
999           [NSApp setPresentationOptions: options];
1001           ns_menu_bar_is_hidden = menu_bar_should_be_hidden;
1003           if (!ns_menu_bar_is_hidden)
1004             {
1005               ns_constrain_all_frames ();
1006             }
1007         }
1008     }
1010   unblock_input ();
1011 #endif
1015 static void
1016 ns_update_begin (struct frame *f)
1017 /* --------------------------------------------------------------------------
1018    Prepare for a grouped sequence of drawing calls
1019    external (RIF) call; whole frame, called before update_window_begin
1020    -------------------------------------------------------------------------- */
1022   EmacsView *view = FRAME_NS_VIEW (f);
1023   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_begin");
1025   ns_update_auto_hide_menu_bar ();
1027 #ifdef NS_IMPL_COCOA
1028   if ([view isFullscreen] && [view fsIsNative])
1029   {
1030     // Fix reappearing tool bar in fullscreen for Mac OS X 10.7
1031     BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (f) ? YES : NO;
1032     NSToolbar *toolbar = [FRAME_NS_VIEW (f) toolbar];
1033     if (! tbar_visible != ! [toolbar isVisible])
1034       [toolbar setVisible: tbar_visible];
1035   }
1036 #endif
1038   ns_updating_frame = f;
1039   [view lockFocus];
1041   /* drawRect may have been called for say the minibuffer, and then clip path
1042      is for the minibuffer.  But the display engine may draw more because
1043      we have set the frame as garbaged.  So reset clip path to the whole
1044      view.  */
1045 #ifdef NS_IMPL_COCOA
1046   {
1047     NSBezierPath *bp;
1048     NSRect r = [view frame];
1049     NSRect cr = [[view window] frame];
1050     /* If a large frame size is set, r may be larger than the window frame
1051        before constrained.  In that case don't change the clip path, as we
1052        will clear in to the tool bar and title bar.  */
1053     if (r.size.height
1054         + FRAME_NS_TITLEBAR_HEIGHT (f)
1055         + FRAME_TOOLBAR_HEIGHT (f) <= cr.size.height)
1056       {
1057         bp = [[NSBezierPath bezierPathWithRect: r] retain];
1058         [bp setClip];
1059         [bp release];
1060       }
1061   }
1062 #endif
1064 #ifdef NS_IMPL_GNUSTEP
1065   uRect = NSMakeRect (0, 0, 0, 0);
1066 #endif
1070 static void
1071 ns_update_window_begin (struct window *w)
1072 /* --------------------------------------------------------------------------
1073    Prepare for a grouped sequence of drawing calls
1074    external (RIF) call; for one window, called after update_begin
1075    -------------------------------------------------------------------------- */
1077   struct frame *f = XFRAME (WINDOW_FRAME (w));
1078   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
1080   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_window_begin");
1081   w->output_cursor = w->cursor;
1083   block_input ();
1085   if (f == hlinfo->mouse_face_mouse_frame)
1086     {
1087       /* Don't do highlighting for mouse motion during the update.  */
1088       hlinfo->mouse_face_defer = 1;
1090         /* If the frame needs to be redrawn,
1091            simply forget about any prior mouse highlighting.  */
1092       if (FRAME_GARBAGED_P (f))
1093         hlinfo->mouse_face_window = Qnil;
1095       /* (further code for mouse faces ifdef'd out in other terms elided) */
1096     }
1098   unblock_input ();
1102 static void
1103 ns_update_window_end (struct window *w, bool cursor_on_p,
1104                       bool mouse_face_overwritten_p)
1105 /* --------------------------------------------------------------------------
1106    Finished a grouped sequence of drawing calls
1107    external (RIF) call; for one window called before update_end
1108    -------------------------------------------------------------------------- */
1110   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_window_end");
1112   /* note: this fn is nearly identical in all terms */
1113   if (!w->pseudo_window_p)
1114     {
1115       block_input ();
1117       if (cursor_on_p)
1118         display_and_set_cursor (w, 1,
1119                                 w->output_cursor.hpos, w->output_cursor.vpos,
1120                                 w->output_cursor.x, w->output_cursor.y);
1122       if (draw_window_fringes (w, 1))
1123         {
1124           if (WINDOW_RIGHT_DIVIDER_WIDTH (w))
1125             x_draw_right_divider (w);
1126           else
1127             x_draw_vertical_border (w);
1128         }
1130       unblock_input ();
1131     }
1133   /* If a row with mouse-face was overwritten, arrange for
1134      frame_up_to_date to redisplay the mouse highlight.  */
1135   if (mouse_face_overwritten_p)
1136     reset_mouse_highlight (MOUSE_HL_INFO (XFRAME (w->frame)));
1140 static void
1141 ns_update_end (struct frame *f)
1142 /* --------------------------------------------------------------------------
1143    Finished a grouped sequence of drawing calls
1144    external (RIF) call; for whole frame, called after update_window_end
1145    -------------------------------------------------------------------------- */
1147   EmacsView *view = FRAME_NS_VIEW (f);
1149   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_end");
1151 /*   if (f == MOUSE_HL_INFO (f)->mouse_face_mouse_frame) */
1152   MOUSE_HL_INFO (f)->mouse_face_defer = 0;
1154   block_input ();
1156   [view unlockFocus];
1157   [[view window] flushWindow];
1159   unblock_input ();
1160   ns_updating_frame = NULL;
1163 static void
1164 ns_focus (struct frame *f, NSRect *r, int n)
1165 /* --------------------------------------------------------------------------
1166    Internal: Focus on given frame.  During small local updates this is used to
1167      draw, however during large updates, ns_update_begin and ns_update_end are
1168      called to wrap the whole thing, in which case these calls are stubbed out.
1169      Except, on GNUstep, we accumulate the rectangle being drawn into, because
1170      the back end won't do this automatically, and will just end up flushing
1171      the entire window.
1172    -------------------------------------------------------------------------- */
1174   NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_focus");
1175   if (r != NULL)
1176     {
1177       NSTRACE_RECT ("r", *r);
1178     }
1180   if (f != ns_updating_frame)
1181     {
1182       NSView *view = FRAME_NS_VIEW (f);
1183       if (view != focus_view)
1184         {
1185           if (focus_view != NULL)
1186             {
1187               [focus_view unlockFocus];
1188               [[focus_view window] flushWindow];
1189 /*debug_lock--; */
1190             }
1192           if (view)
1193             [view lockFocus];
1194           focus_view = view;
1195 /*if (view) debug_lock++; */
1196         }
1197     }
1199   /* clipping */
1200   if (r)
1201     {
1202       [[NSGraphicsContext currentContext] saveGraphicsState];
1203       if (n == 2)
1204         NSRectClipList (r, 2);
1205       else
1206         NSRectClip (*r);
1207       gsaved = YES;
1208     }
1212 static void
1213 ns_unfocus (struct frame *f)
1214 /* --------------------------------------------------------------------------
1215      Internal: Remove focus on given frame
1216    -------------------------------------------------------------------------- */
1218   NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_unfocus");
1220   if (gsaved)
1221     {
1222       [[NSGraphicsContext currentContext] restoreGraphicsState];
1223       gsaved = NO;
1224     }
1226   if (f != ns_updating_frame)
1227     {
1228       if (focus_view != NULL)
1229         {
1230           [focus_view unlockFocus];
1231           [[focus_view window] flushWindow];
1232           focus_view = NULL;
1233 /*debug_lock--; */
1234         }
1235     }
1239 static void
1240 ns_clip_to_row (struct window *w, struct glyph_row *row,
1241                 enum glyph_row_area area, BOOL gc)
1242 /* --------------------------------------------------------------------------
1243      Internal (but parallels other terms): Focus drawing on given row
1244    -------------------------------------------------------------------------- */
1246   struct frame *f = XFRAME (WINDOW_FRAME (w));
1247   NSRect clip_rect;
1248   int window_x, window_y, window_width;
1250   window_box (w, area, &window_x, &window_y, &window_width, 0);
1252   clip_rect.origin.x = window_x;
1253   clip_rect.origin.y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, row->y));
1254   clip_rect.origin.y = max (clip_rect.origin.y, window_y);
1255   clip_rect.size.width = window_width;
1256   clip_rect.size.height = row->visible_height;
1258   ns_focus (f, &clip_rect, 1);
1262 /* ==========================================================================
1264     Visible bell and beep.
1266    ========================================================================== */
1269 // This bell implementation shows the visual bell image asynchronously
1270 // from the rest of Emacs. This is done by adding a NSView to the
1271 // superview of the Emacs window and removing it using a timer.
1273 // Unfortunately, some Emacs operations, like scrolling, is done using
1274 // low-level primitives that copy the content of the window, including
1275 // the bell image. To some extent, this is handled by removing the
1276 // image prior to scrolling and marking that the window is in need for
1277 // redisplay.
1279 // To test this code, make sure that there is no artifacts of the bell
1280 // image in the following situations. Use a non-empty buffer (like the
1281 // tutorial) to ensure that a scroll is performed:
1283 // * Single-window: C-g C-v
1285 // * Side-by-windows: C-x 3 C-g C-v
1287 // * Windows above each other: C-x 2 C-g C-v
1289 @interface EmacsBell : NSImageView
1291   // Number of currently active bell:s.
1292   unsigned int nestCount;
1293   NSView * mView;
1294   bool isAttached;
1296 - (void)show:(NSView *)view;
1297 - (void)hide;
1298 - (void)remove;
1299 @end
1301 @implementation EmacsBell
1303 - (id)init;
1305   NSTRACE ("[EmacsBell init]");
1306   if ((self = [super init]))
1307     {
1308       nestCount = 0;
1309       isAttached = false;
1310 #ifdef NS_IMPL_GNUSTEP
1311       // GNUstep doesn't provide named images.  This was reported in
1312       // 2011, see https://savannah.gnu.org/bugs/?33396
1313       //
1314       // As a drop in replacement, a semitransparent gray square is used.
1315       self.image = [[NSImage alloc] initWithSize:NSMakeSize(32 * 5, 32 * 5)];
1316       [self.image lockFocus];
1317       [[NSColor colorForEmacsRed:0.5 green:0.5 blue:0.5 alpha:0.5] set];
1318       NSRectFill(NSMakeRect(0, 0, 32, 32));
1319       [self.image unlockFocus];
1320 #else
1321       self.image = [NSImage imageNamed:NSImageNameCaution];
1322       [self.image setSize:NSMakeSize(self.image.size.width * 5,
1323                                      self.image.size.height * 5)];
1324 #endif
1325     }
1326   return self;
1329 - (void)show:(NSView *)view
1331   NSTRACE ("[EmacsBell show:]");
1332   NSTRACE_MSG ("nestCount: %u", nestCount);
1334   // Show the image, unless it's already shown.
1335   if (nestCount == 0)
1336     {
1337       NSRect rect = [view bounds];
1338       NSPoint pos;
1339       pos.x = rect.origin.x + (rect.size.width  - self.image.size.width )/2;
1340       pos.y = rect.origin.y + (rect.size.height - self.image.size.height)/2;
1342       [self setFrameOrigin:pos];
1343       [self setFrameSize:self.image.size];
1345       isAttached = true;
1346       mView = view;
1347       [[[view window] contentView] addSubview:self
1348                                    positioned:NSWindowAbove
1349                                    relativeTo:nil];
1350     }
1352   ++nestCount;
1354   [self performSelector:@selector(hide) withObject:self afterDelay:0.5];
1358 - (void)hide
1360   // Note: Trace output from this method isn't shown, reason unknown.
1361   // NSTRACE ("[EmacsBell hide]");
1363   if (nestCount > 0)
1364     --nestCount;
1366   // Remove the image once the last bell became inactive.
1367   if (nestCount == 0)
1368     {
1369       [self remove];
1370     }
1374 -(void)remove
1376   NSTRACE ("[EmacsBell remove]");
1377   if (isAttached)
1378     {
1379       NSTRACE_MSG ("removeFromSuperview");
1380       [self removeFromSuperview];
1381       mView.needsDisplay = YES;
1382       isAttached = false;
1383     }
1386 @end
1389 static EmacsBell * bell_view = nil;
1391 static void
1392 ns_ring_bell (struct frame *f)
1393 /* --------------------------------------------------------------------------
1394      "Beep" routine
1395    -------------------------------------------------------------------------- */
1397   NSTRACE ("ns_ring_bell");
1398   if (visible_bell)
1399     {
1400       struct frame *frame = SELECTED_FRAME ();
1401       NSView *view;
1403       if (bell_view == nil)
1404         {
1405           bell_view = [[EmacsBell alloc] init];
1406           [bell_view retain];
1407         }
1409       block_input ();
1411       view = FRAME_NS_VIEW (frame);
1412       if (view != nil)
1413         {
1414           [bell_view show:view];
1415         }
1417       unblock_input ();
1418     }
1419   else
1420     {
1421       NSBeep ();
1422     }
1426 static void
1427 hide_bell (void)
1428 /* --------------------------------------------------------------------------
1429      Ensure the bell is hidden.
1430    -------------------------------------------------------------------------- */
1432   NSTRACE ("hide_bell");
1434   if (bell_view != nil)
1435     {
1436       [bell_view remove];
1437     }
1441 /* ==========================================================================
1443     Frame / window manager related functions
1445    ========================================================================== */
1448 static void
1449 ns_raise_frame (struct frame *f)
1450 /* --------------------------------------------------------------------------
1451      Bring window to foreground and make it active
1452    -------------------------------------------------------------------------- */
1454   NSView *view;
1456   check_window_system (f);
1457   view = FRAME_NS_VIEW (f);
1458   block_input ();
1459   if (FRAME_VISIBLE_P (f))
1460     [[view window] makeKeyAndOrderFront: NSApp];
1461   unblock_input ();
1465 static void
1466 ns_lower_frame (struct frame *f)
1467 /* --------------------------------------------------------------------------
1468      Send window to back
1469    -------------------------------------------------------------------------- */
1471   NSView *view;
1473   check_window_system (f);
1474   view = FRAME_NS_VIEW (f);
1475   block_input ();
1476   [[view window] orderBack: NSApp];
1477   unblock_input ();
1481 static void
1482 ns_frame_raise_lower (struct frame *f, bool raise)
1483 /* --------------------------------------------------------------------------
1484      External (hook)
1485    -------------------------------------------------------------------------- */
1487   NSTRACE ("ns_frame_raise_lower");
1489   if (raise)
1490     ns_raise_frame (f);
1491   else
1492     ns_lower_frame (f);
1496 static void
1497 ns_frame_rehighlight (struct frame *frame)
1498 /* --------------------------------------------------------------------------
1499      External (hook): called on things like window switching within frame
1500    -------------------------------------------------------------------------- */
1502   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
1503   struct frame *old_highlight = dpyinfo->x_highlight_frame;
1505   NSTRACE ("ns_frame_rehighlight");
1506   if (dpyinfo->x_focus_frame)
1507     {
1508       dpyinfo->x_highlight_frame
1509         = (FRAMEP (FRAME_FOCUS_FRAME (dpyinfo->x_focus_frame))
1510            ? XFRAME (FRAME_FOCUS_FRAME (dpyinfo->x_focus_frame))
1511            : dpyinfo->x_focus_frame);
1512       if (!FRAME_LIVE_P (dpyinfo->x_highlight_frame))
1513         {
1514           fset_focus_frame (dpyinfo->x_focus_frame, Qnil);
1515           dpyinfo->x_highlight_frame = dpyinfo->x_focus_frame;
1516         }
1517     }
1518   else
1519       dpyinfo->x_highlight_frame = 0;
1521   if (dpyinfo->x_highlight_frame &&
1522          dpyinfo->x_highlight_frame != old_highlight)
1523     {
1524       if (old_highlight)
1525         {
1526           x_update_cursor (old_highlight, 1);
1527           x_set_frame_alpha (old_highlight);
1528         }
1529       if (dpyinfo->x_highlight_frame)
1530         {
1531           x_update_cursor (dpyinfo->x_highlight_frame, 1);
1532           x_set_frame_alpha (dpyinfo->x_highlight_frame);
1533         }
1534     }
1538 void
1539 x_make_frame_visible (struct frame *f)
1540 /* --------------------------------------------------------------------------
1541      External: Show the window (X11 semantics)
1542    -------------------------------------------------------------------------- */
1544   NSTRACE ("x_make_frame_visible");
1545   /* XXX: at some points in past this was not needed, as the only place that
1546      called this (frame.c:Fraise_frame ()) also called raise_lower;
1547      if this ends up the case again, comment this out again. */
1548   if (!FRAME_VISIBLE_P (f))
1549     {
1550       EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
1552       SET_FRAME_VISIBLE (f, 1);
1553       ns_raise_frame (f);
1555       /* Making a new frame from a fullscreen frame will make the new frame
1556          fullscreen also.  So skip handleFS as this will print an error.  */
1557       if ([view fsIsNative] && f->want_fullscreen == FULLSCREEN_BOTH
1558           && [view isFullscreen])
1559         return;
1561       if (f->want_fullscreen != FULLSCREEN_NONE)
1562         {
1563           block_input ();
1564           [view handleFS];
1565           unblock_input ();
1566         }
1567     }
1571 void
1572 x_make_frame_invisible (struct frame *f)
1573 /* --------------------------------------------------------------------------
1574      External: Hide the window (X11 semantics)
1575    -------------------------------------------------------------------------- */
1577   NSView *view;
1578   NSTRACE ("x_make_frame_invisible");
1579   check_window_system (f);
1580   view = FRAME_NS_VIEW (f);
1581   [[view window] orderOut: NSApp];
1582   SET_FRAME_VISIBLE (f, 0);
1583   SET_FRAME_ICONIFIED (f, 0);
1587 void
1588 x_iconify_frame (struct frame *f)
1589 /* --------------------------------------------------------------------------
1590      External: Iconify window
1591    -------------------------------------------------------------------------- */
1593   NSView *view;
1594   struct ns_display_info *dpyinfo;
1596   NSTRACE ("x_iconify_frame");
1597   check_window_system (f);
1598   view = FRAME_NS_VIEW (f);
1599   dpyinfo = FRAME_DISPLAY_INFO (f);
1601   if (dpyinfo->x_highlight_frame == f)
1602     dpyinfo->x_highlight_frame = 0;
1604   if ([[view window] windowNumber] <= 0)
1605     {
1606       /* the window is still deferred.  Make it very small, bring it
1607          on screen and order it out. */
1608       NSRect s = { { 100, 100}, {0, 0} };
1609       NSRect t;
1610       t = [[view window] frame];
1611       [[view window] setFrame: s display: NO];
1612       [[view window] orderBack: NSApp];
1613       [[view window] orderOut: NSApp];
1614       [[view window] setFrame: t display: NO];
1615     }
1617   /* Processing input while Emacs is being minimized can cause a
1618      crash, so block it for the duration. */
1619   block_input();
1620   [[view window] miniaturize: NSApp];
1621   unblock_input();
1624 /* Free X resources of frame F.  */
1626 void
1627 x_free_frame_resources (struct frame *f)
1629   NSView *view;
1630   struct ns_display_info *dpyinfo;
1631   Mouse_HLInfo *hlinfo;
1633   NSTRACE ("x_free_frame_resources");
1634   check_window_system (f);
1635   view = FRAME_NS_VIEW (f);
1636   dpyinfo = FRAME_DISPLAY_INFO (f);
1637   hlinfo = MOUSE_HL_INFO (f);
1639   [(EmacsView *)view setWindowClosing: YES]; /* may not have been informed */
1641   block_input ();
1643   free_frame_menubar (f);
1644   free_frame_faces (f);
1646   if (f == dpyinfo->x_focus_frame)
1647     dpyinfo->x_focus_frame = 0;
1648   if (f == dpyinfo->x_highlight_frame)
1649     dpyinfo->x_highlight_frame = 0;
1650   if (f == hlinfo->mouse_face_mouse_frame)
1651     reset_mouse_highlight (hlinfo);
1653   if (f->output_data.ns->miniimage != nil)
1654     [f->output_data.ns->miniimage release];
1656   [[view window] close];
1657   [view release];
1659   xfree (f->output_data.ns);
1661   unblock_input ();
1664 void
1665 x_destroy_window (struct frame *f)
1666 /* --------------------------------------------------------------------------
1667      External: Delete the window
1668    -------------------------------------------------------------------------- */
1670   NSTRACE ("x_destroy_window");
1671   check_window_system (f);
1672   x_free_frame_resources (f);
1673   ns_window_num--;
1677 void
1678 x_set_offset (struct frame *f, int xoff, int yoff, int change_grav)
1679 /* --------------------------------------------------------------------------
1680      External: Position the window
1681    -------------------------------------------------------------------------- */
1683   NSView *view = FRAME_NS_VIEW (f);
1684   NSArray *screens = [NSScreen screens];
1685   NSScreen *fscreen = [screens objectAtIndex: 0];
1686   NSScreen *screen = [[view window] screen];
1688   NSTRACE ("x_set_offset");
1690   block_input ();
1692   f->left_pos = xoff;
1693   f->top_pos = yoff;
1695   if (view != nil && screen && fscreen)
1696     {
1697       f->left_pos = f->size_hint_flags & XNegative
1698         ? [screen visibleFrame].size.width + f->left_pos - FRAME_PIXEL_WIDTH (f)
1699         : f->left_pos;
1700       /* We use visibleFrame here to take menu bar into account.
1701          Ideally we should also adjust left/top with visibleFrame.origin.  */
1703       f->top_pos = f->size_hint_flags & YNegative
1704         ? ([screen visibleFrame].size.height + f->top_pos
1705            - FRAME_PIXEL_HEIGHT (f) - FRAME_NS_TITLEBAR_HEIGHT (f)
1706            - FRAME_TOOLBAR_HEIGHT (f))
1707         : f->top_pos;
1708 #ifdef NS_IMPL_GNUSTEP
1709       if (f->left_pos < 100)
1710         f->left_pos = 100;  /* don't overlap menu */
1711 #endif
1712       /* Constrain the setFrameTopLeftPoint so we don't move behind the
1713          menu bar.  */
1714       NSPoint pt = NSMakePoint (SCREENMAXBOUND (f->left_pos),
1715                                 SCREENMAXBOUND ([fscreen frame].size.height
1716                                                 - NS_TOP_POS (f)));
1717       NSTRACE_POINT ("setFrameTopLeftPoint", pt);
1718       [[view window] setFrameTopLeftPoint: pt];
1719       f->size_hint_flags &= ~(XNegative|YNegative);
1720     }
1722   unblock_input ();
1726 void
1727 x_set_window_size (struct frame *f,
1728                    bool change_gravity,
1729                    int width,
1730                    int height,
1731                    bool pixelwise)
1732 /* --------------------------------------------------------------------------
1733      Adjust window pixel size based on given character grid size
1734      Impl is a bit more complex than other terms, need to do some
1735      internal clipping.
1736    -------------------------------------------------------------------------- */
1738   EmacsView *view = FRAME_NS_VIEW (f);
1739   NSWindow *window = [view window];
1740   NSRect wr = [window frame];
1741   int tb = FRAME_EXTERNAL_TOOL_BAR (f);
1742   int pixelwidth, pixelheight;
1743   int orig_height = wr.size.height;
1745   NSTRACE ("x_set_window_size");
1747   if (view == nil)
1748     return;
1750   NSTRACE_RECT ("current", wr);
1751   NSTRACE_MSG ("Width:%d Height:%d Pixelwise:%d", width, height, pixelwise);
1752   NSTRACE_MSG ("Font %d x %d", FRAME_COLUMN_WIDTH (f), FRAME_LINE_HEIGHT (f));
1754   block_input ();
1756   if (pixelwise)
1757     {
1758       pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
1759       pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
1760     }
1761   else
1762     {
1763       pixelwidth =  FRAME_TEXT_COLS_TO_PIXEL_WIDTH   (f, width);
1764       pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, height);
1765     }
1767   /* If we have a toolbar, take its height into account. */
1768   if (tb && ! [view isFullscreen])
1769     {
1770     /* NOTE: previously this would generate wrong result if toolbar not
1771              yet displayed and fixing toolbar_height=32 helped, but
1772              now (200903) seems no longer needed */
1773     FRAME_TOOLBAR_HEIGHT (f) =
1774       NSHeight ([window frameRectForContentRect: NSMakeRect (0, 0, 0, 0)])
1775         - FRAME_NS_TITLEBAR_HEIGHT (f);
1776 #if 0
1777       /* Only breaks things here, removed by martin 2015-09-30.  */
1778 #ifdef NS_IMPL_GNUSTEP
1779       FRAME_TOOLBAR_HEIGHT (f) -= 3;
1780 #endif
1781 #endif
1782     }
1783   else
1784     FRAME_TOOLBAR_HEIGHT (f) = 0;
1786   wr.size.width = pixelwidth + f->border_width;
1787   wr.size.height = pixelheight;
1788   if (! [view isFullscreen])
1789     wr.size.height += FRAME_NS_TITLEBAR_HEIGHT (f)
1790       + FRAME_TOOLBAR_HEIGHT (f);
1792   /* Do not try to constrain to this screen.  We may have multiple
1793      screens, and want Emacs to span those.  Constraining to screen
1794      prevents that, and that is not nice to the user.  */
1795  if (f->output_data.ns->zooming)
1796    f->output_data.ns->zooming = 0;
1797  else
1798    wr.origin.y += orig_height - wr.size.height;
1800  frame_size_history_add
1801    (f, Qx_set_window_size_1, width, height,
1802     list5 (Fcons (make_number (pixelwidth), make_number (pixelheight)),
1803            Fcons (make_number (wr.size.width), make_number (wr.size.height)),
1804            make_number (f->border_width),
1805            make_number (FRAME_NS_TITLEBAR_HEIGHT (f)),
1806            make_number (FRAME_TOOLBAR_HEIGHT (f))));
1808   [window setFrame: wr display: YES];
1810   [view updateFrameSize: NO];
1811   unblock_input ();
1815 static void
1816 ns_fullscreen_hook (struct frame *f)
1818   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
1820   NSTRACE ("ns_fullscreen_hook");
1822   if (!FRAME_VISIBLE_P (f))
1823     return;
1825    if (! [view fsIsNative] && f->want_fullscreen == FULLSCREEN_BOTH)
1826     {
1827       /* Old style fs don't initiate correctly if created from
1828          init/default-frame alist, so use a timer (not nice...).
1829       */
1830       [NSTimer scheduledTimerWithTimeInterval: 0.5 target: view
1831                                      selector: @selector (handleFS)
1832                                      userInfo: nil repeats: NO];
1833       return;
1834     }
1836   block_input ();
1837   [view handleFS];
1838   unblock_input ();
1841 /* ==========================================================================
1843     Color management
1845    ========================================================================== */
1848 NSColor *
1849 ns_lookup_indexed_color (unsigned long idx, struct frame *f)
1851   struct ns_color_table *color_table = FRAME_DISPLAY_INFO (f)->color_table;
1852   if (idx < 1 || idx >= color_table->avail)
1853     return nil;
1854   return color_table->colors[idx];
1858 unsigned long
1859 ns_index_color (NSColor *color, struct frame *f)
1861   struct ns_color_table *color_table = FRAME_DISPLAY_INFO (f)->color_table;
1862   ptrdiff_t idx;
1863   ptrdiff_t i;
1865   if (!color_table->colors)
1866     {
1867       color_table->size = NS_COLOR_CAPACITY;
1868       color_table->avail = 1; /* skip idx=0 as marker */
1869       color_table->colors = xmalloc (color_table->size * sizeof (NSColor *));
1870       color_table->colors[0] = nil;
1871       color_table->empty_indices = [[NSMutableSet alloc] init];
1872     }
1874   /* Do we already have this color?  */
1875   for (i = 1; i < color_table->avail; i++)
1876     if (color_table->colors[i] && [color_table->colors[i] isEqual: color])
1877       return i;
1879   if ([color_table->empty_indices count] > 0)
1880     {
1881       NSNumber *index = [color_table->empty_indices anyObject];
1882       [color_table->empty_indices removeObject: index];
1883       idx = [index unsignedLongValue];
1884     }
1885   else
1886     {
1887       if (color_table->avail == color_table->size)
1888         color_table->colors =
1889           xpalloc (color_table->colors, &color_table->size, 1,
1890                    min (ULONG_MAX, PTRDIFF_MAX), sizeof *color_table->colors);
1891       idx = color_table->avail++;
1892     }
1894   color_table->colors[idx] = color;
1895   [color retain];
1896 /*fprintf(stderr, "color_table: allocated %d\n",idx);*/
1897   return idx;
1901 static int
1902 ns_get_color (const char *name, NSColor **col)
1903 /* --------------------------------------------------------------------------
1904      Parse a color name
1905    -------------------------------------------------------------------------- */
1906 /* On *Step, we attempt to mimic the X11 platform here, down to installing an
1907    X11 rgb.txt-compatible color list in Emacs.clr (see ns_term_init()).
1908    See: http://thread.gmane.org/gmane.emacs.devel/113050/focus=113272). */
1910   NSColor *new = nil;
1911   static char hex[20];
1912   int scaling = 0;
1913   float r = -1.0, g, b;
1914   NSString *nsname = [NSString stringWithUTF8String: name];
1916   NSTRACE ("ns_get_color(%s, **)", name);
1918   block_input ();
1920   if ([nsname isEqualToString: @"ns_selection_bg_color"])
1921     {
1922 #ifdef NS_IMPL_COCOA
1923       NSString *defname = [[NSUserDefaults standardUserDefaults]
1924                             stringForKey: @"AppleHighlightColor"];
1925       if (defname != nil)
1926         nsname = defname;
1927       else
1928 #endif
1929       if ((new = [NSColor selectedTextBackgroundColor]) != nil)
1930         {
1931           *col = [new colorUsingDefaultColorSpace];
1932           unblock_input ();
1933           return 0;
1934         }
1935       else
1936         nsname = NS_SELECTION_BG_COLOR_DEFAULT;
1938       name = [nsname UTF8String];
1939     }
1940   else if ([nsname isEqualToString: @"ns_selection_fg_color"])
1941     {
1942       /* NOTE: macOS applications normally don't set foreground
1943          selection, but text may be unreadable if we don't.
1944       */
1945       if ((new = [NSColor selectedTextColor]) != nil)
1946         {
1947           *col = [new colorUsingDefaultColorSpace];
1948           unblock_input ();
1949           return 0;
1950         }
1952       nsname = NS_SELECTION_FG_COLOR_DEFAULT;
1953       name = [nsname UTF8String];
1954     }
1956   /* First, check for some sort of numeric specification. */
1957   hex[0] = '\0';
1959   if (name[0] == '0' || name[0] == '1' || name[0] == '.')  /* RGB decimal */
1960     {
1961       NSScanner *scanner = [NSScanner scannerWithString: nsname];
1962       [scanner scanFloat: &r];
1963       [scanner scanFloat: &g];
1964       [scanner scanFloat: &b];
1965     }
1966   else if (!strncmp(name, "rgb:", 4))  /* A newer X11 format -- rgb:r/g/b */
1967     scaling = (snprintf (hex, sizeof hex, "%s", name + 4) - 2) / 3;
1968   else if (name[0] == '#')        /* An old X11 format; convert to newer */
1969     {
1970       int len = (strlen(name) - 1);
1971       int start = (len % 3 == 0) ? 1 : len / 4 + 1;
1972       int i;
1973       scaling = strlen(name+start) / 3;
1974       for (i = 0; i < 3; i++)
1975         sprintf (hex + i * (scaling + 1), "%.*s/", scaling,
1976                  name + start + i * scaling);
1977       hex[3 * (scaling + 1) - 1] = '\0';
1978     }
1980   if (hex[0])
1981     {
1982       unsigned int rr, gg, bb;
1983       float fscale = scaling == 4 ? 65535.0 : (scaling == 2 ? 255.0 : 15.0);
1984       if (sscanf (hex, "%x/%x/%x", &rr, &gg, &bb))
1985         {
1986           r = rr / fscale;
1987           g = gg / fscale;
1988           b = bb / fscale;
1989         }
1990     }
1992   if (r >= 0.0F)
1993     {
1994       *col = [NSColor colorForEmacsRed: r green: g blue: b alpha: 1.0];
1995       unblock_input ();
1996       return 0;
1997     }
1999   /* Otherwise, color is expected to be from a list */
2000   {
2001     NSEnumerator *lenum, *cenum;
2002     NSString *name;
2003     NSColorList *clist;
2005 #ifdef NS_IMPL_GNUSTEP
2006     /* XXX: who is wrong, the requestor or the implementation? */
2007     if ([nsname compare: @"Highlight" options: NSCaseInsensitiveSearch]
2008         == NSOrderedSame)
2009       nsname = @"highlightColor";
2010 #endif
2012     lenum = [[NSColorList availableColorLists] objectEnumerator];
2013     while ( (clist = [lenum nextObject]) && new == nil)
2014       {
2015         cenum = [[clist allKeys] objectEnumerator];
2016         while ( (name = [cenum nextObject]) && new == nil )
2017           {
2018             if ([name compare: nsname
2019                       options: NSCaseInsensitiveSearch] == NSOrderedSame )
2020               new = [clist colorWithKey: name];
2021           }
2022       }
2023   }
2025   if (new)
2026     *col = [new colorUsingDefaultColorSpace];
2027   unblock_input ();
2028   return new ? 0 : 1;
2033 ns_lisp_to_color (Lisp_Object color, NSColor **col)
2034 /* --------------------------------------------------------------------------
2035      Convert a Lisp string object to a NS color
2036    -------------------------------------------------------------------------- */
2038   NSTRACE ("ns_lisp_to_color");
2039   if (STRINGP (color))
2040     return ns_get_color (SSDATA (color), col);
2041   else if (SYMBOLP (color))
2042     return ns_get_color (SSDATA (SYMBOL_NAME (color)), col);
2043   return 1;
2047 void
2048 ns_query_color(void *col, XColor *color_def, int setPixel)
2049 /* --------------------------------------------------------------------------
2050          Get ARGB values out of NSColor col and put them into color_def.
2051          If setPixel, set the pixel to a concatenated version.
2052          and set color_def pixel to the resulting index.
2053    -------------------------------------------------------------------------- */
2055   EmacsCGFloat r, g, b, a;
2057   [((NSColor *)col) getRed: &r green: &g blue: &b alpha: &a];
2058   color_def->red   = r * 65535;
2059   color_def->green = g * 65535;
2060   color_def->blue  = b * 65535;
2062   if (setPixel == YES)
2063     color_def->pixel
2064       = ARGB_TO_ULONG((int)(a*255),
2065                       (int)(r*255), (int)(g*255), (int)(b*255));
2069 bool
2070 ns_defined_color (struct frame *f,
2071                   const char *name,
2072                   XColor *color_def,
2073                   bool alloc,
2074                   bool makeIndex)
2075 /* --------------------------------------------------------------------------
2076          Return true if named color found, and set color_def rgb accordingly.
2077          If makeIndex and alloc are nonzero put the color in the color_table,
2078          and set color_def pixel to the resulting index.
2079          If makeIndex is zero, set color_def pixel to ARGB.
2080          Return false if not found
2081    -------------------------------------------------------------------------- */
2083   NSColor *col;
2084   NSTRACE_WHEN (NSTRACE_GROUP_COLOR, "ns_defined_color");
2086   block_input ();
2087   if (ns_get_color (name, &col) != 0) /* Color not found  */
2088     {
2089       unblock_input ();
2090       return 0;
2091     }
2092   if (makeIndex && alloc)
2093     color_def->pixel = ns_index_color (col, f);
2094   ns_query_color (col, color_def, !makeIndex);
2095   unblock_input ();
2096   return 1;
2100 void
2101 x_set_frame_alpha (struct frame *f)
2102 /* --------------------------------------------------------------------------
2103      change the entire-frame transparency
2104    -------------------------------------------------------------------------- */
2106   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
2107   double alpha = 1.0;
2108   double alpha_min = 1.0;
2110   NSTRACE ("x_set_frame_alpha");
2112   if (dpyinfo->x_highlight_frame == f)
2113     alpha = f->alpha[0];
2114   else
2115     alpha = f->alpha[1];
2117   if (FLOATP (Vframe_alpha_lower_limit))
2118     alpha_min = XFLOAT_DATA (Vframe_alpha_lower_limit);
2119   else if (INTEGERP (Vframe_alpha_lower_limit))
2120     alpha_min = (XINT (Vframe_alpha_lower_limit)) / 100.0;
2122   if (alpha < 0.0)
2123     return;
2124   else if (1.0 < alpha)
2125     alpha = 1.0;
2126   else if (0.0 <= alpha && alpha < alpha_min && alpha_min <= 1.0)
2127     alpha = alpha_min;
2129 #ifdef NS_IMPL_COCOA
2130   {
2131     EmacsView *view = FRAME_NS_VIEW (f);
2132   [[view window] setAlphaValue: alpha];
2133   }
2134 #endif
2138 /* ==========================================================================
2140     Mouse handling
2142    ========================================================================== */
2145 void
2146 frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y)
2147 /* --------------------------------------------------------------------------
2148      Programmatically reposition mouse pointer in pixel coordinates
2149    -------------------------------------------------------------------------- */
2151   NSTRACE ("frame_set_mouse_pixel_position");
2152   ns_raise_frame (f);
2153 #if 0
2154   /* FIXME: this does not work, and what about GNUstep? */
2155 #ifdef NS_IMPL_COCOA
2156   [FRAME_NS_VIEW (f) lockFocus];
2157   PSsetmouse ((float)pix_x, (float)pix_y);
2158   [FRAME_NS_VIEW (f) unlockFocus];
2159 #endif
2160 #endif
2163 static int
2164 note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y)
2165 /*   ------------------------------------------------------------------------
2166      Called by EmacsView on mouseMovement events.  Passes on
2167      to emacs mainstream code if we moved off of a rect of interest
2168      known as last_mouse_glyph.
2169      ------------------------------------------------------------------------ */
2171   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
2172   NSRect *r;
2174 //  NSTRACE ("note_mouse_movement");
2176   dpyinfo->last_mouse_motion_frame = frame;
2177   r = &dpyinfo->last_mouse_glyph;
2179   /* Note, this doesn't get called for enter/leave, since we don't have a
2180      position.  Those are taken care of in the corresponding NSView methods. */
2182   /* has movement gone beyond last rect we were tracking? */
2183   if (x < r->origin.x || x >= r->origin.x + r->size.width
2184       || y < r->origin.y || y >= r->origin.y + r->size.height)
2185     {
2186       ns_update_begin (frame);
2187       frame->mouse_moved = 1;
2188       note_mouse_highlight (frame, x, y);
2189       remember_mouse_glyph (frame, x, y, r);
2190       ns_update_end (frame);
2191       return 1;
2192     }
2194   return 0;
2198 static void
2199 ns_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window,
2200                    enum scroll_bar_part *part, Lisp_Object *x, Lisp_Object *y,
2201                    Time *time)
2202 /* --------------------------------------------------------------------------
2203     External (hook): inform emacs about mouse position and hit parts.
2204     If a scrollbar is being dragged, set bar_window, part, x, y, time.
2205     x & y should be position in the scrollbar (the whole bar, not the handle)
2206     and length of scrollbar respectively
2207    -------------------------------------------------------------------------- */
2209   id view;
2210   NSPoint position;
2211   Lisp_Object frame, tail;
2212   struct frame *f;
2213   struct ns_display_info *dpyinfo;
2215   NSTRACE ("ns_mouse_position");
2217   if (*fp == NULL)
2218     {
2219       fprintf (stderr, "Warning: ns_mouse_position () called with null *fp.\n");
2220       return;
2221     }
2223   dpyinfo = FRAME_DISPLAY_INFO (*fp);
2225   block_input ();
2227   /* Clear the mouse-moved flag for every frame on this display.  */
2228   FOR_EACH_FRAME (tail, frame)
2229     if (FRAME_NS_P (XFRAME (frame))
2230         && FRAME_NS_DISPLAY (XFRAME (frame)) == FRAME_NS_DISPLAY (*fp))
2231       XFRAME (frame)->mouse_moved = 0;
2233   dpyinfo->last_mouse_scroll_bar = nil;
2234   if (dpyinfo->last_mouse_frame
2235       && FRAME_LIVE_P (dpyinfo->last_mouse_frame))
2236     f = dpyinfo->last_mouse_frame;
2237   else
2238     f = dpyinfo->x_focus_frame ? dpyinfo->x_focus_frame : SELECTED_FRAME ();
2240   if (f && FRAME_NS_P (f))
2241     {
2242       view = FRAME_NS_VIEW (*fp);
2244       position = [[view window] mouseLocationOutsideOfEventStream];
2245       position = [view convertPoint: position fromView: nil];
2246       remember_mouse_glyph (f, position.x, position.y,
2247                             &dpyinfo->last_mouse_glyph);
2248       NSTRACE_POINT ("position", position);
2250       if (bar_window) *bar_window = Qnil;
2251       if (part) *part = scroll_bar_above_handle;
2253       if (x) XSETINT (*x, lrint (position.x));
2254       if (y) XSETINT (*y, lrint (position.y));
2255       if (time)
2256         *time = dpyinfo->last_mouse_movement_time;
2257       *fp = f;
2258     }
2260   unblock_input ();
2264 static void
2265 ns_frame_up_to_date (struct frame *f)
2266 /* --------------------------------------------------------------------------
2267     External (hook): Fix up mouse highlighting right after a full update.
2268     Can't use FRAME_MOUSE_UPDATE due to ns_frame_begin and ns_frame_end calls.
2269    -------------------------------------------------------------------------- */
2271   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_frame_up_to_date");
2273   if (FRAME_NS_P (f))
2274     {
2275       Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
2276       if (f == hlinfo->mouse_face_mouse_frame)
2277         {
2278           block_input ();
2279           ns_update_begin(f);
2280           note_mouse_highlight (hlinfo->mouse_face_mouse_frame,
2281                                 hlinfo->mouse_face_mouse_x,
2282                                 hlinfo->mouse_face_mouse_y);
2283           ns_update_end(f);
2284           unblock_input ();
2285         }
2286     }
2290 static void
2291 ns_define_frame_cursor (struct frame *f, Cursor cursor)
2292 /* --------------------------------------------------------------------------
2293     External (RIF): set frame mouse pointer type.
2294    -------------------------------------------------------------------------- */
2296   NSTRACE ("ns_define_frame_cursor");
2297   if (FRAME_POINTER_TYPE (f) != cursor)
2298     {
2299       EmacsView *view = FRAME_NS_VIEW (f);
2300       FRAME_POINTER_TYPE (f) = cursor;
2301       [[view window] invalidateCursorRectsForView: view];
2302       /* Redisplay assumes this function also draws the changed frame
2303          cursor, but this function doesn't, so do it explicitly.  */
2304       x_update_cursor (f, 1);
2305     }
2310 /* ==========================================================================
2312     Keyboard handling
2314    ========================================================================== */
2317 static unsigned
2318 ns_convert_key (unsigned code)
2319 /* --------------------------------------------------------------------------
2320     Internal call used by NSView-keyDown.
2321    -------------------------------------------------------------------------- */
2323   const unsigned last_keysym = ARRAYELTS (convert_ns_to_X_keysym);
2324   unsigned keysym;
2325   /* An array would be faster, but less easy to read. */
2326   for (keysym = 0; keysym < last_keysym; keysym += 2)
2327     if (code == convert_ns_to_X_keysym[keysym])
2328       return 0xFF00 | convert_ns_to_X_keysym[keysym+1];
2329   return 0;
2330 /* if decide to use keyCode and Carbon table, use this line:
2331      return code > 0xff ? 0 : 0xFF00 | ns_keycode_to_xkeysym_table[code]; */
2335 char *
2336 x_get_keysym_name (int keysym)
2337 /* --------------------------------------------------------------------------
2338     Called by keyboard.c.  Not sure if the return val is important, except
2339     that it be unique.
2340    -------------------------------------------------------------------------- */
2342   static char value[16];
2343   NSTRACE ("x_get_keysym_name");
2344   sprintf (value, "%d", keysym);
2345   return value;
2350 /* ==========================================================================
2352     Block drawing operations
2354    ========================================================================== */
2357 static void
2358 ns_redraw_scroll_bars (struct frame *f)
2360   int i;
2361   id view;
2362   NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews];
2363   NSTRACE ("ns_redraw_scroll_bars");
2364   for (i =[subviews count]-1; i >= 0; i--)
2365     {
2366       view = [subviews objectAtIndex: i];
2367       if (![view isKindOfClass: [EmacsScroller class]]) continue;
2368       [view display];
2369     }
2373 void
2374 ns_clear_frame (struct frame *f)
2375 /* --------------------------------------------------------------------------
2376       External (hook): Erase the entire frame
2377    -------------------------------------------------------------------------- */
2379   NSView *view = FRAME_NS_VIEW (f);
2380   NSRect r;
2382   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_clear_frame");
2384  /* comes on initial frame because we have
2385     after-make-frame-functions = select-frame */
2386  if (!FRAME_DEFAULT_FACE (f))
2387    return;
2389   mark_window_cursors_off (XWINDOW (FRAME_ROOT_WINDOW (f)));
2391   r = [view bounds];
2393   block_input ();
2394   ns_focus (f, &r, 1);
2395   [ns_lookup_indexed_color (NS_FACE_BACKGROUND
2396                             (FACE_FROM_ID (f, DEFAULT_FACE_ID)), f) set];
2397   NSRectFill (r);
2398   ns_unfocus (f);
2400   /* as of 2006/11 or so this is now needed */
2401   ns_redraw_scroll_bars (f);
2402   unblock_input ();
2406 static void
2407 ns_clear_frame_area (struct frame *f, int x, int y, int width, int height)
2408 /* --------------------------------------------------------------------------
2409     External (RIF):  Clear section of frame
2410    -------------------------------------------------------------------------- */
2412   NSRect r = NSMakeRect (x, y, width, height);
2413   NSView *view = FRAME_NS_VIEW (f);
2414   struct face *face = FRAME_DEFAULT_FACE (f);
2416   if (!view || !face)
2417     return;
2419   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_clear_frame_area");
2421   r = NSIntersectionRect (r, [view frame]);
2422   ns_focus (f, &r, 1);
2423   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), f) set];
2425   NSRectFill (r);
2427   ns_unfocus (f);
2428   return;
2431 static void
2432 ns_copy_bits (struct frame *f, NSRect src, NSRect dest)
2434   NSTRACE ("ns_copy_bits");
2436   if (FRAME_NS_VIEW (f))
2437     {
2438       hide_bell();              // Ensure the bell image isn't scrolled.
2440       ns_focus (f, &dest, 1);
2441       [FRAME_NS_VIEW (f) scrollRect: src
2442                                  by: NSMakeSize (dest.origin.x - src.origin.x,
2443                                                  dest.origin.y - src.origin.y)];
2444       ns_unfocus (f);
2445     }
2448 static void
2449 ns_scroll_run (struct window *w, struct run *run)
2450 /* --------------------------------------------------------------------------
2451     External (RIF):  Insert or delete n lines at line vpos
2452    -------------------------------------------------------------------------- */
2454   struct frame *f = XFRAME (w->frame);
2455   int x, y, width, height, from_y, to_y, bottom_y;
2457   NSTRACE ("ns_scroll_run");
2459   /* begin copy from other terms */
2460   /* Get frame-relative bounding box of the text display area of W,
2461      without mode lines.  Include in this box the left and right
2462      fringe of W.  */
2463   window_box (w, ANY_AREA, &x, &y, &width, &height);
2465   from_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->current_y);
2466   to_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->desired_y);
2467   bottom_y = y + height;
2469   if (to_y < from_y)
2470     {
2471       /* Scrolling up.  Make sure we don't copy part of the mode
2472          line at the bottom.  */
2473       if (from_y + run->height > bottom_y)
2474         height = bottom_y - from_y;
2475       else
2476         height = run->height;
2477     }
2478   else
2479     {
2480       /* Scrolling down.  Make sure we don't copy over the mode line.
2481          at the bottom.  */
2482       if (to_y + run->height > bottom_y)
2483         height = bottom_y - to_y;
2484       else
2485         height = run->height;
2486     }
2487   /* end copy from other terms */
2489   if (height == 0)
2490       return;
2492   block_input ();
2494   x_clear_cursor (w);
2496   {
2497     NSRect srcRect = NSMakeRect (x, from_y, width, height);
2498     NSRect dstRect = NSMakeRect (x, to_y, width, height);
2500     ns_copy_bits (f, srcRect , dstRect);
2501   }
2503   unblock_input ();
2507 static void
2508 ns_after_update_window_line (struct window *w, struct glyph_row *desired_row)
2509 /* --------------------------------------------------------------------------
2510     External (RIF): preparatory to fringe update after text was updated
2511    -------------------------------------------------------------------------- */
2513   struct frame *f;
2514   int width, height;
2516   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_after_update_window_line");
2518   /* begin copy from other terms */
2519   eassert (w);
2521   if (!desired_row->mode_line_p && !w->pseudo_window_p)
2522     desired_row->redraw_fringe_bitmaps_p = 1;
2524   /* When a window has disappeared, make sure that no rest of
2525      full-width rows stays visible in the internal border.  */
2526   if (windows_or_buffers_changed
2527       && desired_row->full_width_p
2528       && (f = XFRAME (w->frame),
2529           width = FRAME_INTERNAL_BORDER_WIDTH (f),
2530           width != 0)
2531       && (height = desired_row->visible_height,
2532           height > 0))
2533     {
2534       int y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, desired_row->y));
2536       block_input ();
2537       ns_clear_frame_area (f, 0, y, width, height);
2538       ns_clear_frame_area (f,
2539                            FRAME_PIXEL_WIDTH (f) - width,
2540                            y, width, height);
2541       unblock_input ();
2542     }
2546 static void
2547 ns_shift_glyphs_for_insert (struct frame *f,
2548                            int x, int y, int width, int height,
2549                            int shift_by)
2550 /* --------------------------------------------------------------------------
2551     External (RIF): copy an area horizontally, don't worry about clearing src
2552    -------------------------------------------------------------------------- */
2554   NSRect srcRect = NSMakeRect (x, y, width, height);
2555   NSRect dstRect = NSMakeRect (x+shift_by, y, width, height);
2557   NSTRACE ("ns_shift_glyphs_for_insert");
2559   ns_copy_bits (f, srcRect, dstRect);
2564 /* ==========================================================================
2566     Character encoding and metrics
2568    ========================================================================== */
2571 static void
2572 ns_compute_glyph_string_overhangs (struct glyph_string *s)
2573 /* --------------------------------------------------------------------------
2574      External (RIF); compute left/right overhang of whole string and set in s
2575    -------------------------------------------------------------------------- */
2577   struct font *font = s->font;
2579   if (s->char2b)
2580     {
2581       struct font_metrics metrics;
2582       unsigned int codes[2];
2583       codes[0] = *(s->char2b);
2584       codes[1] = *(s->char2b + s->nchars - 1);
2586       font->driver->text_extents (font, codes, 2, &metrics);
2587       s->left_overhang = -metrics.lbearing;
2588       s->right_overhang
2589         = metrics.rbearing > metrics.width
2590         ? metrics.rbearing - metrics.width : 0;
2591     }
2592   else
2593     {
2594       s->left_overhang = 0;
2595       if (EQ (font->driver->type, Qns))
2596         s->right_overhang = ((struct nsfont_info *)font)->ital ?
2597           FONT_HEIGHT (font) * 0.2 : 0;
2598       else
2599         s->right_overhang = 0;
2600     }
2605 /* ==========================================================================
2607     Fringe and cursor drawing
2609    ========================================================================== */
2612 extern int max_used_fringe_bitmap;
2613 static void
2614 ns_draw_fringe_bitmap (struct window *w, struct glyph_row *row,
2615                       struct draw_fringe_bitmap_params *p)
2616 /* --------------------------------------------------------------------------
2617     External (RIF); fringe-related
2618    -------------------------------------------------------------------------- */
2620   /* Fringe bitmaps comes in two variants, normal and periodic.  A
2621      periodic bitmap is used to create a continuous pattern.  Since a
2622      bitmap is rendered one text line at a time, the start offset (dh)
2623      of the bitmap varies.  Concretely, this is used for the empty
2624      line indicator.
2626      For a bitmap, "h + dh" is the full height and is always
2627      invariant.  For a normal bitmap "dh" is zero.
2629      For example, when the period is three and the full height is 72
2630      the following combinations exists:
2632        h=72 dh=0
2633        h=71 dh=1
2634        h=70 dh=2 */
2636   struct frame *f = XFRAME (WINDOW_FRAME (w));
2637   struct face *face = p->face;
2638   static EmacsImage **bimgs = NULL;
2639   static int nBimgs = 0;
2641   NSTRACE_WHEN (NSTRACE_GROUP_FRINGE, "ns_draw_fringe_bitmap");
2642   NSTRACE_MSG ("which:%d cursor:%d overlay:%d width:%d height:%d period:%d",
2643                p->which, p->cursor_p, p->overlay_p, p->wd, p->h, p->dh);
2645   /* grow bimgs if needed */
2646   if (nBimgs < max_used_fringe_bitmap)
2647     {
2648       bimgs = xrealloc (bimgs, max_used_fringe_bitmap * sizeof *bimgs);
2649       memset (bimgs + nBimgs, 0,
2650               (max_used_fringe_bitmap - nBimgs) * sizeof *bimgs);
2651       nBimgs = max_used_fringe_bitmap;
2652     }
2654   /* Must clip because of partially visible lines.  */
2655   ns_clip_to_row (w, row, ANY_AREA, YES);
2657   if (!p->overlay_p)
2658     {
2659       int bx = p->bx, by = p->by, nx = p->nx, ny = p->ny;
2661       if (bx >= 0 && nx > 0)
2662         {
2663           NSRect r = NSMakeRect (bx, by, nx, ny);
2664           NSRectClip (r);
2665           [ns_lookup_indexed_color (face->background, f) set];
2666           NSRectFill (r);
2667         }
2668     }
2670   if (p->which)
2671     {
2672       NSRect r = NSMakeRect (p->x, p->y, p->wd, p->h);
2673       EmacsImage *img = bimgs[p->which - 1];
2675       if (!img)
2676         {
2677           // Note: For "periodic" images, allocate one EmacsImage for
2678           // the base image, and use it for all dh:s.
2679           unsigned short *bits = p->bits;
2680           int full_height = p->h + p->dh;
2681           int i;
2682           unsigned char *cbits = xmalloc (full_height);
2684           for (i = 0; i < full_height; i++)
2685             cbits[i] = bits[i];
2686           img = [[EmacsImage alloc] initFromXBM: cbits width: 8
2687                                          height: full_height
2688                                              fg: 0 bg: 0];
2689           bimgs[p->which - 1] = img;
2690           xfree (cbits);
2691         }
2693       NSTRACE_RECT ("r", r);
2695       NSRectClip (r);
2696       /* Since we composite the bitmap instead of just blitting it, we need
2697          to erase the whole background. */
2698       [ns_lookup_indexed_color(face->background, f) set];
2699       NSRectFill (r);
2701       {
2702         NSColor *bm_color;
2703         if (!p->cursor_p)
2704           bm_color = ns_lookup_indexed_color(face->foreground, f);
2705         else if (p->overlay_p)
2706           bm_color = ns_lookup_indexed_color(face->background, f);
2707         else
2708           bm_color = f->output_data.ns->cursor_color;
2709         [img setXBMColor: bm_color];
2710       }
2712 #ifdef NS_IMPL_COCOA
2713       // Note: For periodic images, the full image height is "h + hd".
2714       // By using the height h, a suitable part of the image is used.
2715       NSRect fromRect = NSMakeRect(0, 0, p->wd, p->h);
2717       NSTRACE_RECT ("fromRect", fromRect);
2719       [img drawInRect: r
2720               fromRect: fromRect
2721              operation: NSCompositingOperationSourceOver
2722               fraction: 1.0
2723            respectFlipped: YES
2724                 hints: nil];
2725 #else
2726       {
2727         NSPoint pt = r.origin;
2728         pt.y += p->h;
2729         [img compositeToPoint: pt operation: NSCompositingOperationSourceOver];
2730       }
2731 #endif
2732     }
2733   ns_unfocus (f);
2737 static void
2738 ns_draw_window_cursor (struct window *w, struct glyph_row *glyph_row,
2739                        int x, int y, enum text_cursor_kinds cursor_type,
2740                        int cursor_width, bool on_p, bool active_p)
2741 /* --------------------------------------------------------------------------
2742      External call (RIF): draw cursor.
2743      Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
2744    -------------------------------------------------------------------------- */
2746   NSRect r, s;
2747   int fx, fy, h, cursor_height;
2748   struct frame *f = WINDOW_XFRAME (w);
2749   struct glyph *phys_cursor_glyph;
2750   struct glyph *cursor_glyph;
2751   struct face *face;
2752   NSColor *hollow_color = FRAME_BACKGROUND_COLOR (f);
2754   /* If cursor is out of bounds, don't draw garbage.  This can happen
2755      in mini-buffer windows when switching between echo area glyphs
2756      and mini-buffer.  */
2758   NSTRACE ("ns_draw_window_cursor");
2760   if (!on_p)
2761     return;
2763   w->phys_cursor_type = cursor_type;
2764   w->phys_cursor_on_p = on_p;
2766   if (cursor_type == NO_CURSOR)
2767     {
2768       w->phys_cursor_width = 0;
2769       return;
2770     }
2772   if ((phys_cursor_glyph = get_phys_cursor_glyph (w)) == NULL)
2773     {
2774       if (glyph_row->exact_window_width_line_p
2775           && w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])
2776         {
2777           glyph_row->cursor_in_fringe_p = 1;
2778           draw_fringe_bitmap (w, glyph_row, 0);
2779         }
2780       return;
2781     }
2783   /* We draw the cursor (with NSRectFill), then draw the glyph on top
2784      (other terminals do it the other way round).  We must set
2785      w->phys_cursor_width to the cursor width.  For bar cursors, that
2786      is CURSOR_WIDTH; for box cursors, it is the glyph width.  */
2787   get_phys_cursor_geometry (w, glyph_row, phys_cursor_glyph, &fx, &fy, &h);
2789   /* The above get_phys_cursor_geometry call set w->phys_cursor_width
2790      to the glyph width; replace with CURSOR_WIDTH for (V)BAR cursors. */
2791   if (cursor_type == BAR_CURSOR)
2792     {
2793       if (cursor_width < 1)
2794         cursor_width = max (FRAME_CURSOR_WIDTH (f), 1);
2796       /* The bar cursor should never be wider than the glyph. */
2797       if (cursor_width < w->phys_cursor_width)
2798         w->phys_cursor_width = cursor_width;
2799     }
2800   /* If we have an HBAR, "cursor_width" MAY specify height. */
2801   else if (cursor_type == HBAR_CURSOR)
2802     {
2803       cursor_height = (cursor_width < 1) ? lrint (0.25 * h) : cursor_width;
2804       if (cursor_height > glyph_row->height)
2805         cursor_height = glyph_row->height;
2806       if (h > cursor_height) // Cursor smaller than line height, move down
2807         fy += h - cursor_height;
2808       h = cursor_height;
2809     }
2811   r.origin.x = fx, r.origin.y = fy;
2812   r.size.height = h;
2813   r.size.width = w->phys_cursor_width;
2815   /* Prevent the cursor from being drawn outside the text area. */
2816   ns_clip_to_row (w, glyph_row, TEXT_AREA, NO); /* do ns_focus(f, &r, 1); if remove */
2819   face = FACE_FROM_ID_OR_NULL (f, phys_cursor_glyph->face_id);
2820   if (face && NS_FACE_BACKGROUND (face)
2821       == ns_index_color (FRAME_CURSOR_COLOR (f), f))
2822     {
2823       [ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), f) set];
2824       hollow_color = FRAME_CURSOR_COLOR (f);
2825     }
2826   else
2827     [FRAME_CURSOR_COLOR (f) set];
2829 #ifdef NS_IMPL_COCOA
2830   /* TODO: This makes drawing of cursor plus that of phys_cursor_glyph
2831            atomic.  Cleaner ways of doing this should be investigated.
2832            One way would be to set a global variable DRAWING_CURSOR
2833            when making the call to draw_phys..(), don't focus in that
2834            case, then move the ns_unfocus() here after that call. */
2835   NSDisableScreenUpdates ();
2836 #endif
2838   switch (cursor_type)
2839     {
2840     case DEFAULT_CURSOR:
2841     case NO_CURSOR:
2842       break;
2843     case FILLED_BOX_CURSOR:
2844       NSRectFill (r);
2845       break;
2846     case HOLLOW_BOX_CURSOR:
2847       NSRectFill (r);
2848       [hollow_color set];
2849       NSRectFill (NSInsetRect (r, 1, 1));
2850       [FRAME_CURSOR_COLOR (f) set];
2851       break;
2852     case HBAR_CURSOR:
2853       NSRectFill (r);
2854       break;
2855     case BAR_CURSOR:
2856       s = r;
2857       /* If the character under cursor is R2L, draw the bar cursor
2858          on the right of its glyph, rather than on the left.  */
2859       cursor_glyph = get_phys_cursor_glyph (w);
2860       if ((cursor_glyph->resolved_level & 1) != 0)
2861         s.origin.x += cursor_glyph->pixel_width - s.size.width;
2863       NSRectFill (s);
2864       break;
2865     }
2866   ns_unfocus (f);
2868   /* draw the character under the cursor */
2869   if (cursor_type != NO_CURSOR)
2870     draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
2872 #ifdef NS_IMPL_COCOA
2873   NSEnableScreenUpdates ();
2874 #endif
2879 static void
2880 ns_draw_vertical_window_border (struct window *w, int x, int y0, int y1)
2881 /* --------------------------------------------------------------------------
2882      External (RIF): Draw a vertical line.
2883    -------------------------------------------------------------------------- */
2885   struct frame *f = XFRAME (WINDOW_FRAME (w));
2886   struct face *face;
2887   NSRect r = NSMakeRect (x, y0, 1, y1-y0);
2889   NSTRACE ("ns_draw_vertical_window_border");
2891   face = FACE_FROM_ID_OR_NULL (f, VERTICAL_BORDER_FACE_ID);
2893   ns_focus (f, &r, 1);
2894   if (face)
2895     [ns_lookup_indexed_color(face->foreground, f) set];
2897   NSRectFill(r);
2898   ns_unfocus (f);
2902 static void
2903 ns_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1)
2904 /* --------------------------------------------------------------------------
2905      External (RIF): Draw a window divider.
2906    -------------------------------------------------------------------------- */
2908   struct frame *f = XFRAME (WINDOW_FRAME (w));
2909   struct face *face;
2910   NSRect r = NSMakeRect (x0, y0, x1-x0, y1-y0);
2912   NSTRACE ("ns_draw_window_divider");
2914   face = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FACE_ID);
2916   ns_focus (f, &r, 1);
2917   if (face)
2918     [ns_lookup_indexed_color(face->foreground, f) set];
2920   NSRectFill(r);
2921   ns_unfocus (f);
2924 static void
2925 ns_show_hourglass (struct frame *f)
2927   /* TODO: add NSProgressIndicator to all frames.  */
2930 static void
2931 ns_hide_hourglass (struct frame *f)
2933   /* TODO: remove NSProgressIndicator from all frames.  */
2936 /* ==========================================================================
2938     Glyph drawing operations
2940    ========================================================================== */
2942 static int
2943 ns_get_glyph_string_clip_rect (struct glyph_string *s, NativeRectangle *nr)
2944 /* --------------------------------------------------------------------------
2945     Wrapper utility to account for internal border width on full-width lines,
2946     and allow top full-width rows to hit the frame top.  nr should be pointer
2947     to two successive NSRects.  Number of rects actually used is returned.
2948    -------------------------------------------------------------------------- */
2950   int n = get_glyph_string_clip_rects (s, nr, 2);
2951   return n;
2954 /* --------------------------------------------------------------------
2955    Draw a wavy line under glyph string s. The wave fills wave_height
2956    pixels from y.
2958                     x          wave_length = 2
2959                                  --
2960                 y    *   *   *   *   *
2961                      |* * * * * * * * *
2962     wave_height = 3  | *   *   *   *
2963   --------------------------------------------------------------------- */
2965 static void
2966 ns_draw_underwave (struct glyph_string *s, EmacsCGFloat width, EmacsCGFloat x)
2968   int wave_height = 3, wave_length = 2;
2969   int y, dx, dy, odd, xmax;
2970   NSPoint a, b;
2971   NSRect waveClip;
2973   dx = wave_length;
2974   dy = wave_height - 1;
2975   y =  s->ybase - wave_height + 3;
2976   xmax = x + width;
2978   /* Find and set clipping rectangle */
2979   waveClip = NSMakeRect (x, y, width, wave_height);
2980   [[NSGraphicsContext currentContext] saveGraphicsState];
2981   NSRectClip (waveClip);
2983   /* Draw the waves */
2984   a.x = x - ((int)(x) % dx) + (EmacsCGFloat) 0.5;
2985   b.x = a.x + dx;
2986   odd = (int)(a.x/dx) % 2;
2987   a.y = b.y = y + 0.5;
2989   if (odd)
2990     a.y += dy;
2991   else
2992     b.y += dy;
2994   while (a.x <= xmax)
2995     {
2996       [NSBezierPath strokeLineFromPoint:a toPoint:b];
2997       a.x = b.x, a.y = b.y;
2998       b.x += dx, b.y = y + 0.5 + odd*dy;
2999       odd = !odd;
3000     }
3002   /* Restore previous clipping rectangle(s) */
3003   [[NSGraphicsContext currentContext] restoreGraphicsState];
3008 static void
3009 ns_draw_text_decoration (struct glyph_string *s, struct face *face,
3010                          NSColor *defaultCol, CGFloat width, CGFloat x)
3011 /* --------------------------------------------------------------------------
3012    Draw underline, overline, and strike-through on glyph string s.
3013    -------------------------------------------------------------------------- */
3015   if (s->for_overlaps)
3016     return;
3018   /* Do underline. */
3019   if (face->underline_p)
3020     {
3021       if (s->face->underline_type == FACE_UNDER_WAVE)
3022         {
3023           if (face->underline_defaulted_p)
3024             [defaultCol set];
3025           else
3026             [ns_lookup_indexed_color (face->underline_color, s->f) set];
3028           ns_draw_underwave (s, width, x);
3029         }
3030       else if (s->face->underline_type == FACE_UNDER_LINE)
3031         {
3033           NSRect r;
3034           unsigned long thickness, position;
3036           /* If the prev was underlined, match its appearance. */
3037           if (s->prev && s->prev->face->underline_p
3038               && s->prev->face->underline_type == FACE_UNDER_LINE
3039               && s->prev->underline_thickness > 0)
3040             {
3041               thickness = s->prev->underline_thickness;
3042               position = s->prev->underline_position;
3043             }
3044           else
3045             {
3046               struct font *font;
3047               unsigned long descent;
3049               font=s->font;
3050               descent = s->y + s->height - s->ybase;
3052               /* Use underline thickness of font, defaulting to 1. */
3053               thickness = (font && font->underline_thickness > 0)
3054                 ? font->underline_thickness : 1;
3056               /* Determine the offset of underlining from the baseline. */
3057               if (x_underline_at_descent_line)
3058                 position = descent - thickness;
3059               else if (x_use_underline_position_properties
3060                        && font && font->underline_position >= 0)
3061                 position = font->underline_position;
3062               else if (font)
3063                 position = lround (font->descent / 2);
3064               else
3065                 position = underline_minimum_offset;
3067               position = max (position, underline_minimum_offset);
3069               /* Ensure underlining is not cropped. */
3070               if (descent <= position)
3071                 {
3072                   position = descent - 1;
3073                   thickness = 1;
3074                 }
3075               else if (descent < position + thickness)
3076                 thickness = 1;
3077             }
3079           s->underline_thickness = thickness;
3080           s->underline_position = position;
3082           r = NSMakeRect (x, s->ybase + position, width, thickness);
3084           if (face->underline_defaulted_p)
3085             [defaultCol set];
3086           else
3087             [ns_lookup_indexed_color (face->underline_color, s->f) set];
3088           NSRectFill (r);
3089         }
3090     }
3091   /* Do overline. We follow other terms in using a thickness of 1
3092      and ignoring overline_margin. */
3093   if (face->overline_p)
3094     {
3095       NSRect r;
3096       r = NSMakeRect (x, s->y, width, 1);
3098       if (face->overline_color_defaulted_p)
3099         [defaultCol set];
3100       else
3101         [ns_lookup_indexed_color (face->overline_color, s->f) set];
3102       NSRectFill (r);
3103     }
3105   /* Do strike-through.  We follow other terms for thickness and
3106      vertical position.*/
3107   if (face->strike_through_p)
3108     {
3109       NSRect r;
3110       unsigned long dy;
3112       dy = lrint ((s->height - 1) / 2);
3113       r = NSMakeRect (x, s->y + dy, width, 1);
3115       if (face->strike_through_color_defaulted_p)
3116         [defaultCol set];
3117       else
3118         [ns_lookup_indexed_color (face->strike_through_color, s->f) set];
3119       NSRectFill (r);
3120     }
3123 static void
3124 ns_draw_box (NSRect r, CGFloat thickness, NSColor *col,
3125              char left_p, char right_p)
3126 /* --------------------------------------------------------------------------
3127     Draw an unfilled rect inside r, optionally leaving left and/or right open.
3128     Note we can't just use an NSDrawRect command, because of the possibility
3129     of some sides not being drawn, and because the rect will be filled.
3130    -------------------------------------------------------------------------- */
3132   NSRect s = r;
3133   [col set];
3135   /* top, bottom */
3136   s.size.height = thickness;
3137   NSRectFill (s);
3138   s.origin.y += r.size.height - thickness;
3139   NSRectFill (s);
3141   s.size.height = r.size.height;
3142   s.origin.y = r.origin.y;
3144   /* left, right (optional) */
3145   s.size.width = thickness;
3146   if (left_p)
3147     NSRectFill (s);
3148   if (right_p)
3149     {
3150       s.origin.x += r.size.width - thickness;
3151       NSRectFill (s);
3152     }
3156 static void
3157 ns_draw_relief (NSRect r, int thickness, char raised_p,
3158                char top_p, char bottom_p, char left_p, char right_p,
3159                struct glyph_string *s)
3160 /* --------------------------------------------------------------------------
3161     Draw a relief rect inside r, optionally leaving some sides open.
3162     Note we can't just use an NSDrawBezel command, because of the possibility
3163     of some sides not being drawn, and because the rect will be filled.
3164    -------------------------------------------------------------------------- */
3166   static NSColor *baseCol = nil, *lightCol = nil, *darkCol = nil;
3167   NSColor *newBaseCol = nil;
3168   NSRect sr = r;
3170   NSTRACE ("ns_draw_relief");
3172   /* set up colors */
3174   if (s->face->use_box_color_for_shadows_p)
3175     {
3176       newBaseCol = ns_lookup_indexed_color (s->face->box_color, s->f);
3177     }
3178 /*     else if (s->first_glyph->type == IMAGE_GLYPH
3179            && s->img->pixmap
3180            && !IMAGE_BACKGROUND_TRANSPARENT (s->img, s->f, 0))
3181        {
3182          newBaseCol = IMAGE_BACKGROUND  (s->img, s->f, 0);
3183        } */
3184   else
3185     {
3186       newBaseCol = ns_lookup_indexed_color (s->face->background, s->f);
3187     }
3189   if (newBaseCol == nil)
3190     newBaseCol = [NSColor grayColor];
3192   if (newBaseCol != baseCol)  /* TODO: better check */
3193     {
3194       [baseCol release];
3195       baseCol = [newBaseCol retain];
3196       [lightCol release];
3197       lightCol = [[baseCol highlightWithLevel: 0.2] retain];
3198       [darkCol release];
3199       darkCol = [[baseCol shadowWithLevel: 0.3] retain];
3200     }
3202   [(raised_p ? lightCol : darkCol) set];
3204   /* TODO: mitering. Using NSBezierPath doesn't work because of color switch. */
3206   /* top */
3207   sr.size.height = thickness;
3208   if (top_p) NSRectFill (sr);
3210   /* left */
3211   sr.size.height = r.size.height;
3212   sr.size.width = thickness;
3213   if (left_p) NSRectFill (sr);
3215   [(raised_p ? darkCol : lightCol) set];
3217   /* bottom */
3218   sr.size.width = r.size.width;
3219   sr.size.height = thickness;
3220   sr.origin.y += r.size.height - thickness;
3221   if (bottom_p) NSRectFill (sr);
3223   /* right */
3224   sr.size.height = r.size.height;
3225   sr.origin.y = r.origin.y;
3226   sr.size.width = thickness;
3227   sr.origin.x += r.size.width - thickness;
3228   if (right_p) NSRectFill (sr);
3232 static void
3233 ns_dumpglyphs_box_or_relief (struct glyph_string *s)
3234 /* --------------------------------------------------------------------------
3235       Function modeled after x_draw_glyph_string_box ().
3236       Sets up parameters for drawing.
3237    -------------------------------------------------------------------------- */
3239   int right_x, last_x;
3240   char left_p, right_p;
3241   struct glyph *last_glyph;
3242   NSRect r;
3243   int thickness;
3244   struct face *face;
3246   if (s->hl == DRAW_MOUSE_FACE)
3247     {
3248       face = FACE_FROM_ID_OR_NULL (s->f,
3249                                    MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3250       if (!face)
3251         face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3252     }
3253   else
3254     face = s->face;
3256   thickness = face->box_line_width;
3258   NSTRACE ("ns_dumpglyphs_box_or_relief");
3260   last_x = ((s->row->full_width_p && !s->w->pseudo_window_p)
3261             ? WINDOW_RIGHT_EDGE_X (s->w)
3262             : window_box_right (s->w, s->area));
3263   last_glyph = (s->cmp || s->img
3264                 ? s->first_glyph : s->first_glyph + s->nchars-1);
3266   right_x = ((s->row->full_width_p && s->extends_to_end_of_line_p
3267               ? last_x - 1 : min (last_x, s->x + s->background_width) - 1));
3269   left_p = (s->first_glyph->left_box_line_p
3270             || (s->hl == DRAW_MOUSE_FACE
3271                 && (s->prev == NULL || s->prev->hl != s->hl)));
3272   right_p = (last_glyph->right_box_line_p
3273              || (s->hl == DRAW_MOUSE_FACE
3274                  && (s->next == NULL || s->next->hl != s->hl)));
3276   r = NSMakeRect (s->x, s->y, right_x - s->x + 1, s->height);
3278   /* TODO: Sometimes box_color is 0 and this seems wrong; should investigate. */
3279   if (s->face->box == FACE_SIMPLE_BOX && s->face->box_color)
3280     {
3281       ns_draw_box (r, abs (thickness),
3282                    ns_lookup_indexed_color (face->box_color, s->f),
3283                   left_p, right_p);
3284     }
3285   else
3286     {
3287       ns_draw_relief (r, abs (thickness), s->face->box == FACE_RAISED_BOX,
3288                      1, 1, left_p, right_p, s);
3289     }
3293 static void
3294 ns_maybe_dumpglyphs_background (struct glyph_string *s, char force_p)
3295 /* --------------------------------------------------------------------------
3296       Modeled after x_draw_glyph_string_background, which draws BG in
3297       certain cases.  Others are left to the text rendering routine.
3298    -------------------------------------------------------------------------- */
3300   NSTRACE ("ns_maybe_dumpglyphs_background");
3302   if (!s->background_filled_p/* || s->hl == DRAW_MOUSE_FACE*/)
3303     {
3304       int box_line_width = max (s->face->box_line_width, 0);
3305       if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width
3306           /* When xdisp.c ignores FONT_HEIGHT, we cannot trust font
3307              dimensions, since the actual glyphs might be much
3308              smaller.  So in that case we always clear the rectangle
3309              with background color.  */
3310           || FONT_TOO_HIGH (s->font)
3311           || s->font_not_found_p || s->extends_to_end_of_line_p || force_p)
3312         {
3313           struct face *face;
3314           if (s->hl == DRAW_MOUSE_FACE)
3315             {
3316               face
3317                 = FACE_FROM_ID_OR_NULL (s->f,
3318                                         MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3319               if (!face)
3320                 face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3321             }
3322           else
3323             face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3324           if (!face->stipple)
3325             [(NS_FACE_BACKGROUND (face) != 0
3326               ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
3327               : FRAME_BACKGROUND_COLOR (s->f)) set];
3328           else
3329             {
3330               struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
3331               [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set];
3332             }
3334           if (s->hl != DRAW_CURSOR)
3335             {
3336               NSRect r = NSMakeRect (s->x, s->y + box_line_width,
3337                                     s->background_width,
3338                                     s->height-2*box_line_width);
3339               NSRectFill (r);
3340             }
3342           s->background_filled_p = 1;
3343         }
3344     }
3348 static void
3349 ns_dumpglyphs_image (struct glyph_string *s, NSRect r)
3350 /* --------------------------------------------------------------------------
3351       Renders an image and associated borders.
3352    -------------------------------------------------------------------------- */
3354   EmacsImage *img = s->img->pixmap;
3355   int box_line_vwidth = max (s->face->box_line_width, 0);
3356   int x = s->x, y = s->ybase - image_ascent (s->img, s->face, &s->slice);
3357   int bg_x, bg_y, bg_height;
3358   int th;
3359   char raised_p;
3360   NSRect br;
3361   struct face *face;
3362   NSColor *tdCol;
3364   NSTRACE ("ns_dumpglyphs_image");
3366   if (s->face->box != FACE_NO_BOX
3367       && s->first_glyph->left_box_line_p && s->slice.x == 0)
3368     x += abs (s->face->box_line_width);
3370   bg_x = x;
3371   bg_y =  s->slice.y == 0 ? s->y : s->y + box_line_vwidth;
3372   bg_height = s->height;
3373   /* other terms have this, but was causing problems w/tabbar mode */
3374   /* - 2 * box_line_vwidth; */
3376   if (s->slice.x == 0) x += s->img->hmargin;
3377   if (s->slice.y == 0) y += s->img->vmargin;
3379   /* Draw BG: if we need larger area than image itself cleared, do that,
3380      otherwise, since we composite the image under NS (instead of mucking
3381      with its background color), we must clear just the image area. */
3382   if (s->hl == DRAW_MOUSE_FACE)
3383     {
3384       face = FACE_FROM_ID_OR_NULL (s->f,
3385                                    MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3386       if (!face)
3387        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3388     }
3389   else
3390     face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3392   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f) set];
3394   if (bg_height > s->slice.height || s->img->hmargin || s->img->vmargin
3395       || s->img->mask || s->img->pixmap == 0 || s->width != s->background_width)
3396     {
3397       br = NSMakeRect (bg_x, bg_y, s->background_width, bg_height);
3398       s->background_filled_p = 1;
3399     }
3400   else
3401     {
3402       br = NSMakeRect (x, y, s->slice.width, s->slice.height);
3403     }
3405   NSRectFill (br);
3407   /* Draw the image.. do we need to draw placeholder if img ==nil? */
3408   if (img != nil)
3409     {
3410 #ifdef NS_IMPL_COCOA
3411       NSRect dr = NSMakeRect (x, y, s->slice.width, s->slice.height);
3412       NSRect ir = NSMakeRect (s->slice.x,
3413                               s->img->height - s->slice.y - s->slice.height,
3414                               s->slice.width, s->slice.height);
3415       [img drawInRect: dr
3416              fromRect: ir
3417              operation: NSCompositingOperationSourceOver
3418               fraction: 1.0
3419            respectFlipped: YES
3420                 hints: nil];
3421 #else
3422       [img compositeToPoint: NSMakePoint (x, y + s->slice.height)
3423                   operation: NSCompositingOperationSourceOver];
3424 #endif
3425     }
3427   if (s->hl == DRAW_CURSOR)
3428     {
3429     [FRAME_CURSOR_COLOR (s->f) set];
3430     if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
3431       tdCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
3432     else
3433       /* Currently on NS img->mask is always 0. Since
3434          get_window_cursor_type specifies a hollow box cursor when on
3435          a non-masked image we never reach this clause. But we put it
3436          in in anticipation of better support for image masks on
3437          NS. */
3438       tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3439     }
3440   else
3441     {
3442       tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3443     }
3445   /* Draw underline, overline, strike-through. */
3446   ns_draw_text_decoration (s, face, tdCol, br.size.width, br.origin.x);
3448   /* Draw relief, if requested */
3449   if (s->img->relief || s->hl ==DRAW_IMAGE_RAISED || s->hl ==DRAW_IMAGE_SUNKEN)
3450     {
3451       if (s->hl == DRAW_IMAGE_SUNKEN || s->hl == DRAW_IMAGE_RAISED)
3452         {
3453           th = tool_bar_button_relief >= 0 ?
3454             tool_bar_button_relief : DEFAULT_TOOL_BAR_BUTTON_RELIEF;
3455           raised_p = (s->hl == DRAW_IMAGE_RAISED);
3456         }
3457       else
3458         {
3459           th = abs (s->img->relief);
3460           raised_p = (s->img->relief > 0);
3461         }
3463       r.origin.x = x - th;
3464       r.origin.y = y - th;
3465       r.size.width = s->slice.width + 2*th-1;
3466       r.size.height = s->slice.height + 2*th-1;
3467       ns_draw_relief (r, th, raised_p,
3468                       s->slice.y == 0,
3469                       s->slice.y + s->slice.height == s->img->height,
3470                       s->slice.x == 0,
3471                       s->slice.x + s->slice.width == s->img->width, s);
3472     }
3474   /* If there is no mask, the background won't be seen,
3475      so draw a rectangle on the image for the cursor.
3476      Do this for all images, getting transparency right is not reliable.  */
3477   if (s->hl == DRAW_CURSOR)
3478     {
3479       int thickness = abs (s->img->relief);
3480       if (thickness == 0) thickness = 1;
3481       ns_draw_box (br, thickness, FRAME_CURSOR_COLOR (s->f), 1, 1);
3482     }
3486 static void
3487 ns_dumpglyphs_stretch (struct glyph_string *s)
3489   NSRect r[2];
3490   int n, i;
3491   struct face *face;
3492   NSColor *fgCol, *bgCol;
3494   if (!s->background_filled_p)
3495     {
3496       n = ns_get_glyph_string_clip_rect (s, r);
3497       *r = NSMakeRect (s->x, s->y, s->background_width, s->height);
3499       ns_focus (s->f, r, n);
3501       if (s->hl == DRAW_MOUSE_FACE)
3502        {
3503          face = FACE_FROM_ID_OR_NULL (s->f,
3504                                       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3505          if (!face)
3506            face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3507        }
3508       else
3509        face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3511       bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
3512       fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3514       for (i = 0; i < n; ++i)
3515         {
3516           if (!s->row->full_width_p)
3517             {
3518               int overrun, leftoverrun;
3520               /* truncate to avoid overwriting fringe and/or scrollbar */
3521               overrun = max (0, (s->x + s->background_width)
3522                              - (WINDOW_BOX_RIGHT_EDGE_X (s->w)
3523                                 - WINDOW_RIGHT_FRINGE_WIDTH (s->w)));
3524               r[i].size.width -= overrun;
3526               /* truncate to avoid overwriting to left of the window box */
3527               leftoverrun = (WINDOW_BOX_LEFT_EDGE_X (s->w)
3528                              + WINDOW_LEFT_FRINGE_WIDTH (s->w)) - s->x;
3530               if (leftoverrun > 0)
3531                 {
3532                   r[i].origin.x += leftoverrun;
3533                   r[i].size.width -= leftoverrun;
3534                 }
3536               /* XXX: Try to work between problem where a stretch glyph on
3537                  a partially-visible bottom row will clear part of the
3538                  modeline, and another where list-buffers headers and similar
3539                  rows erroneously have visible_height set to 0.  Not sure
3540                  where this is coming from as other terms seem not to show. */
3541               r[i].size.height = min (s->height, s->row->visible_height);
3542             }
3544           [bgCol set];
3546           /* NOTE: under NS this is NOT used to draw cursors, but we must avoid
3547              overwriting cursor (usually when cursor on a tab) */
3548           if (s->hl == DRAW_CURSOR)
3549             {
3550               CGFloat x, width;
3552               x = r[i].origin.x;
3553               width = s->w->phys_cursor_width;
3554               r[i].size.width -= width;
3555               r[i].origin.x += width;
3557               NSRectFill (r[i]);
3559               /* Draw overlining, etc. on the cursor. */
3560               if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
3561                 ns_draw_text_decoration (s, face, bgCol, width, x);
3562               else
3563                 ns_draw_text_decoration (s, face, fgCol, width, x);
3564             }
3565           else
3566             {
3567               NSRectFill (r[i]);
3568             }
3570           /* Draw overlining, etc. on the stretch glyph (or the part
3571              of the stretch glyph after the cursor). */
3572           ns_draw_text_decoration (s, face, fgCol, r[i].size.width,
3573                                    r[i].origin.x);
3574         }
3575       ns_unfocus (s->f);
3576       s->background_filled_p = 1;
3577     }
3581 static void
3582 ns_draw_glyph_string_foreground (struct glyph_string *s)
3584   int x, flags;
3585   struct font *font = s->font;
3587   /* If first glyph of S has a left box line, start drawing the text
3588      of S to the right of that box line.  */
3589   if (s->face && s->face->box != FACE_NO_BOX
3590       && s->first_glyph->left_box_line_p)
3591     x = s->x + eabs (s->face->box_line_width);
3592   else
3593     x = s->x;
3595   flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
3596     (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
3597      (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
3598       NS_DUMPGLYPH_NORMAL));
3600   font->driver->draw
3601     (s, s->cmp_from, s->nchars, x, s->ybase,
3602      (flags == NS_DUMPGLYPH_NORMAL && !s->background_filled_p)
3603      || flags == NS_DUMPGLYPH_MOUSEFACE);
3607 static void
3608 ns_draw_composite_glyph_string_foreground (struct glyph_string *s)
3610   int i, j, x;
3611   struct font *font = s->font;
3613   /* If first glyph of S has a left box line, start drawing the text
3614      of S to the right of that box line.  */
3615   if (s->face && s->face->box != FACE_NO_BOX
3616       && s->first_glyph->left_box_line_p)
3617     x = s->x + eabs (s->face->box_line_width);
3618   else
3619     x = s->x;
3621   /* S is a glyph string for a composition.  S->cmp_from is the index
3622      of the first character drawn for glyphs of this composition.
3623      S->cmp_from == 0 means we are drawing the very first character of
3624      this composition.  */
3626   /* Draw a rectangle for the composition if the font for the very
3627      first character of the composition could not be loaded.  */
3628   if (s->font_not_found_p)
3629     {
3630       if (s->cmp_from == 0)
3631         {
3632           NSRect r = NSMakeRect (s->x, s->y, s->width-1, s->height -1);
3633           ns_draw_box (r, 1, FRAME_CURSOR_COLOR (s->f), 1, 1);
3634         }
3635     }
3636   else if (! s->first_glyph->u.cmp.automatic)
3637     {
3638       int y = s->ybase;
3640       for (i = 0, j = s->cmp_from; i < s->nchars; i++, j++)
3641         /* TAB in a composition means display glyphs with padding
3642            space on the left or right.  */
3643         if (COMPOSITION_GLYPH (s->cmp, j) != '\t')
3644           {
3645             int xx = x + s->cmp->offsets[j * 2];
3646             int yy = y - s->cmp->offsets[j * 2 + 1];
3648             font->driver->draw (s, j, j + 1, xx, yy, false);
3649             if (s->face->overstrike)
3650               font->driver->draw (s, j, j + 1, xx + 1, yy, false);
3651           }
3652     }
3653   else
3654     {
3655       Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
3656       Lisp_Object glyph;
3657       int y = s->ybase;
3658       int width = 0;
3660       for (i = j = s->cmp_from; i < s->cmp_to; i++)
3661         {
3662           glyph = LGSTRING_GLYPH (gstring, i);
3663           if (NILP (LGLYPH_ADJUSTMENT (glyph)))
3664             width += LGLYPH_WIDTH (glyph);
3665           else
3666             {
3667               int xoff, yoff, wadjust;
3669               if (j < i)
3670                 {
3671                   font->driver->draw (s, j, i, x, y, false);
3672                   if (s->face->overstrike)
3673                     font->driver->draw (s, j, i, x + 1, y, false);
3674                   x += width;
3675                 }
3676               xoff = LGLYPH_XOFF (glyph);
3677               yoff = LGLYPH_YOFF (glyph);
3678               wadjust = LGLYPH_WADJUST (glyph);
3679               font->driver->draw (s, i, i + 1, x + xoff, y + yoff, false);
3680               if (s->face->overstrike)
3681                 font->driver->draw (s, i, i + 1, x + xoff + 1, y + yoff,
3682                                     false);
3683               x += wadjust;
3684               j = i + 1;
3685               width = 0;
3686             }
3687         }
3688       if (j < i)
3689         {
3690           font->driver->draw (s, j, i, x, y, false);
3691           if (s->face->overstrike)
3692             font->driver->draw (s, j, i, x + 1, y, false);
3693         }
3694     }
3697 static void
3698 ns_draw_glyph_string (struct glyph_string *s)
3699 /* --------------------------------------------------------------------------
3700       External (RIF): Main draw-text call.
3701    -------------------------------------------------------------------------- */
3703   /* TODO (optimize): focus for box and contents draw */
3704   NSRect r[2];
3705   int n;
3706   char box_drawn_p = 0;
3707   struct font *font = s->face->font;
3708   if (! font) font = FRAME_FONT (s->f);
3710   NSTRACE_WHEN (NSTRACE_GROUP_GLYPHS, "ns_draw_glyph_string");
3712   if (s->next && s->right_overhang && !s->for_overlaps/*&&s->hl!=DRAW_CURSOR*/)
3713     {
3714       int width;
3715       struct glyph_string *next;
3717       for (width = 0, next = s->next;
3718            next && width < s->right_overhang;
3719            width += next->width, next = next->next)
3720         if (next->first_glyph->type != IMAGE_GLYPH)
3721           {
3722             if (next->first_glyph->type != STRETCH_GLYPH)
3723               {
3724                 n = ns_get_glyph_string_clip_rect (s->next, r);
3725                 ns_focus (s->f, r, n);
3726                 ns_maybe_dumpglyphs_background (s->next, 1);
3727                 ns_unfocus (s->f);
3728               }
3729             else
3730               {
3731                 ns_dumpglyphs_stretch (s->next);
3732               }
3733             next->num_clips = 0;
3734           }
3735     }
3737   if (!s->for_overlaps && s->face->box != FACE_NO_BOX
3738         && (s->first_glyph->type == CHAR_GLYPH
3739             || s->first_glyph->type == COMPOSITE_GLYPH))
3740     {
3741       n = ns_get_glyph_string_clip_rect (s, r);
3742       ns_focus (s->f, r, n);
3743       ns_maybe_dumpglyphs_background (s, 1);
3744       ns_dumpglyphs_box_or_relief (s);
3745       ns_unfocus (s->f);
3746       box_drawn_p = 1;
3747     }
3749   switch (s->first_glyph->type)
3750     {
3752     case IMAGE_GLYPH:
3753       n = ns_get_glyph_string_clip_rect (s, r);
3754       ns_focus (s->f, r, n);
3755       ns_dumpglyphs_image (s, r[0]);
3756       ns_unfocus (s->f);
3757       break;
3759     case STRETCH_GLYPH:
3760       ns_dumpglyphs_stretch (s);
3761       break;
3763     case CHAR_GLYPH:
3764     case COMPOSITE_GLYPH:
3765       n = ns_get_glyph_string_clip_rect (s, r);
3766       ns_focus (s->f, r, n);
3768       if (s->for_overlaps || (s->cmp_from > 0
3769                               && ! s->first_glyph->u.cmp.automatic))
3770         s->background_filled_p = 1;
3771       else
3772         ns_maybe_dumpglyphs_background
3773           (s, s->first_glyph->type == COMPOSITE_GLYPH);
3775       if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
3776         {
3777           unsigned long tmp = NS_FACE_BACKGROUND (s->face);
3778           NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
3779           NS_FACE_FOREGROUND (s->face) = tmp;
3780         }
3782       {
3783         BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
3785         if (isComposite)
3786           ns_draw_composite_glyph_string_foreground (s);
3787         else
3788           ns_draw_glyph_string_foreground (s);
3789       }
3791       {
3792         NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
3793                         ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face),
3794                                                    s->f)
3795                         : FRAME_FOREGROUND_COLOR (s->f));
3796         [col set];
3798         /* Draw underline, overline, strike-through. */
3799         ns_draw_text_decoration (s, s->face, col, s->width, s->x);
3800       }
3802       if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
3803         {
3804           unsigned long tmp = NS_FACE_BACKGROUND (s->face);
3805           NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
3806           NS_FACE_FOREGROUND (s->face) = tmp;
3807         }
3809       ns_unfocus (s->f);
3810       break;
3812     case GLYPHLESS_GLYPH:
3813       n = ns_get_glyph_string_clip_rect (s, r);
3814       ns_focus (s->f, r, n);
3816       if (s->for_overlaps || (s->cmp_from > 0
3817                               && ! s->first_glyph->u.cmp.automatic))
3818         s->background_filled_p = 1;
3819       else
3820         ns_maybe_dumpglyphs_background
3821           (s, s->first_glyph->type == COMPOSITE_GLYPH);
3822       /* ... */
3823       /* Not yet implemented.  */
3824       /* ... */
3825       ns_unfocus (s->f);
3826       break;
3828     default:
3829       emacs_abort ();
3830     }
3832   /* Draw box if not done already. */
3833   if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX)
3834     {
3835       n = ns_get_glyph_string_clip_rect (s, r);
3836       ns_focus (s->f, r, n);
3837       ns_dumpglyphs_box_or_relief (s);
3838       ns_unfocus (s->f);
3839     }
3841   s->num_clips = 0;
3846 /* ==========================================================================
3848     Event loop
3850    ========================================================================== */
3853 static void
3854 ns_send_appdefined (int value)
3855 /* --------------------------------------------------------------------------
3856     Internal: post an appdefined event which EmacsApp-sendEvent will
3857               recognize and take as a command to halt the event loop.
3858    -------------------------------------------------------------------------- */
3860   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_send_appdefined(%d)", value);
3862   // GNUstep needs postEvent to happen on the main thread.
3863   // Cocoa needs nextEventMatchingMask to happen on the main thread too.
3864   if (! [[NSThread currentThread] isMainThread])
3865     {
3866       EmacsApp *app = (EmacsApp *)NSApp;
3867       app->nextappdefined = value;
3868       [app performSelectorOnMainThread:@selector (sendFromMainThread:)
3869                             withObject:nil
3870                          waitUntilDone:YES];
3871       return;
3872     }
3874   /* Only post this event if we haven't already posted one.  This will end
3875        the [NXApp run] main loop after having processed all events queued at
3876        this moment.  */
3878 #ifdef NS_IMPL_COCOA
3879   if (! send_appdefined)
3880     {
3881       /* OS X 10.10.1 swallows the AppDefined event we are sending ourselves
3882          in certain situations (rapid incoming events).
3883          So check if we have one, if not add one.  */
3884       NSEvent *appev = [NSApp nextEventMatchingMask:NSEventMaskApplicationDefined
3885                                           untilDate:[NSDate distantPast]
3886                                              inMode:NSDefaultRunLoopMode
3887                                             dequeue:NO];
3888       if (! appev) send_appdefined = YES;
3889     }
3890 #endif
3892   if (send_appdefined)
3893     {
3894       NSEvent *nxev;
3896       /* We only need one NX_APPDEFINED event to stop NXApp from running.  */
3897       send_appdefined = NO;
3899       /* Don't need wakeup timer any more */
3900       if (timed_entry)
3901         {
3902           [timed_entry invalidate];
3903           [timed_entry release];
3904           timed_entry = nil;
3905         }
3907       nxev = [NSEvent otherEventWithType: NSEventTypeApplicationDefined
3908                                 location: NSMakePoint (0, 0)
3909                            modifierFlags: 0
3910                                timestamp: 0
3911                             windowNumber: [[NSApp mainWindow] windowNumber]
3912                                  context: [NSApp context]
3913                                  subtype: 0
3914                                    data1: value
3915                                    data2: 0];
3917       /* Post an application defined event on the event queue.  When this is
3918          received the [NXApp run] will return, thus having processed all
3919          events which are currently queued.  */
3920       [NSApp postEvent: nxev atStart: NO];
3921     }
3924 #ifdef HAVE_NATIVE_FS
3925 static void
3926 check_native_fs ()
3928   Lisp_Object frame, tail;
3930   if (ns_last_use_native_fullscreen == ns_use_native_fullscreen)
3931     return;
3933   ns_last_use_native_fullscreen = ns_use_native_fullscreen;
3935   FOR_EACH_FRAME (tail, frame)
3936     {
3937       struct frame *f = XFRAME (frame);
3938       if (FRAME_NS_P (f))
3939         {
3940           EmacsView *view = FRAME_NS_VIEW (f);
3941           [view updateCollectionBehavior];
3942         }
3943     }
3945 #endif
3947 /* GNUstep does not have cancelTracking.  */
3948 #ifdef NS_IMPL_COCOA
3949 /* Check if menu open should be canceled or continued as normal.  */
3950 void
3951 ns_check_menu_open (NSMenu *menu)
3953   /* Click in menu bar? */
3954   NSArray *a = [[NSApp mainMenu] itemArray];
3955   int i;
3956   BOOL found = NO;
3958   if (menu == nil) // Menu tracking ended.
3959     {
3960       if (menu_will_open_state == MENU_OPENING)
3961         menu_will_open_state = MENU_NONE;
3962       return;
3963     }
3965   for (i = 0; ! found && i < [a count]; i++)
3966     found = menu == [[a objectAtIndex:i] submenu];
3967   if (found)
3968     {
3969       if (menu_will_open_state == MENU_NONE && emacs_event)
3970         {
3971           NSEvent *theEvent = [NSApp currentEvent];
3972           struct frame *emacsframe = SELECTED_FRAME ();
3974           [menu cancelTracking];
3975           menu_will_open_state = MENU_PENDING;
3976           emacs_event->kind = MENU_BAR_ACTIVATE_EVENT;
3977           EV_TRAILER (theEvent);
3979           CGEventRef ourEvent = CGEventCreate (NULL);
3980           menu_mouse_point = CGEventGetLocation (ourEvent);
3981           CFRelease (ourEvent);
3982         }
3983       else if (menu_will_open_state == MENU_OPENING)
3984         {
3985           menu_will_open_state = MENU_NONE;
3986         }
3987     }
3990 /* Redo saved menu click if state is MENU_PENDING.  */
3991 void
3992 ns_check_pending_open_menu ()
3994   if (menu_will_open_state == MENU_PENDING)
3995     {
3996       CGEventSourceRef source
3997         = CGEventSourceCreate (kCGEventSourceStateHIDSystemState);
3999       CGEventRef event = CGEventCreateMouseEvent (source,
4000                                                   kCGEventLeftMouseDown,
4001                                                   menu_mouse_point,
4002                                                   kCGMouseButtonLeft);
4003       CGEventSetType (event, kCGEventLeftMouseDown);
4004       CGEventPost (kCGHIDEventTap, event);
4005       CFRelease (event);
4006       CFRelease (source);
4008       menu_will_open_state = MENU_OPENING;
4009     }
4011 #endif /* NS_IMPL_COCOA */
4013 static int
4014 ns_read_socket (struct terminal *terminal, struct input_event *hold_quit)
4015 /* --------------------------------------------------------------------------
4016      External (hook): Post an event to ourself and keep reading events until
4017      we read it back again.  In effect process all events which were waiting.
4018      From 21+ we have to manage the event buffer ourselves.
4019    -------------------------------------------------------------------------- */
4021   struct input_event ev;
4022   int nevents;
4024   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_read_socket");
4026 #ifdef HAVE_NATIVE_FS
4027   check_native_fs ();
4028 #endif
4030   if ([NSApp modalWindow] != nil)
4031     return -1;
4033   if (hold_event_q.nr > 0)
4034     {
4035       int i;
4036       for (i = 0; i < hold_event_q.nr; ++i)
4037         kbd_buffer_store_event_hold (&hold_event_q.q[i], hold_quit);
4038       hold_event_q.nr = 0;
4039       return i;
4040     }
4042   if ([NSThread isMainThread])
4043     {
4044       block_input ();
4045       n_emacs_events_pending = 0;
4046       ns_init_events (&ev);
4047       q_event_ptr = hold_quit;
4049       /* we manage autorelease pools by allocate/reallocate each time around
4050          the loop; strict nesting is occasionally violated but seems not to
4051          matter.. earlier methods using full nesting caused major memory leaks */
4052       [outerpool release];
4053       outerpool = [[NSAutoreleasePool alloc] init];
4055       /* If have pending open-file requests, attend to the next one of those. */
4056       if (ns_pending_files && [ns_pending_files count] != 0
4057           && [(EmacsApp *)NSApp openFile: [ns_pending_files objectAtIndex: 0]])
4058         {
4059           [ns_pending_files removeObjectAtIndex: 0];
4060         }
4061       /* Deal with pending service requests. */
4062       else if (ns_pending_service_names && [ns_pending_service_names count] != 0
4063                && [(EmacsApp *)
4064                     NSApp fulfillService: [ns_pending_service_names objectAtIndex: 0]
4065                                  withArg: [ns_pending_service_args objectAtIndex: 0]])
4066         {
4067           [ns_pending_service_names removeObjectAtIndex: 0];
4068           [ns_pending_service_args removeObjectAtIndex: 0];
4069         }
4070       else
4071         {
4072           ptrdiff_t specpdl_count = SPECPDL_INDEX ();
4073           /* Run and wait for events.  We must always send one NX_APPDEFINED event
4074              to ourself, otherwise [NXApp run] will never exit.  */
4075           send_appdefined = YES;
4076           ns_send_appdefined (-1);
4078           [NSApp run];
4079         }
4081       nevents = n_emacs_events_pending;
4082       n_emacs_events_pending = 0;
4083       ns_finish_events ();
4084       q_event_ptr = NULL;
4085       unblock_input ();
4086     }
4088   return nevents;
4093 ns_select (int nfds, fd_set *readfds, fd_set *writefds,
4094            fd_set *exceptfds, struct timespec const *timeout,
4095            sigset_t const *sigmask)
4096 /* --------------------------------------------------------------------------
4097      Replacement for select, checking for events
4098    -------------------------------------------------------------------------- */
4100   int result;
4101   int t, k, nr = 0;
4102   struct input_event event;
4103   char c;
4105   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_select");
4107 #ifdef HAVE_NATIVE_FS
4108   check_native_fs ();
4109 #endif
4111   if (hold_event_q.nr > 0)
4112     {
4113       /* We already have events pending. */
4114       raise (SIGIO);
4115       errno = EINTR;
4116       return -1;
4117     }
4119   for (k = 0; k < nfds+1; k++)
4120     {
4121       if (readfds && FD_ISSET(k, readfds)) ++nr;
4122       if (writefds && FD_ISSET(k, writefds)) ++nr;
4123     }
4125   if (NSApp == nil
4126       || ![NSThread isMainThread]
4127       || (timeout && timeout->tv_sec == 0 && timeout->tv_nsec == 0))
4128     return pselect (nfds, readfds, writefds, exceptfds, timeout, sigmask);
4130   [outerpool release];
4131   outerpool = [[NSAutoreleasePool alloc] init];
4134   send_appdefined = YES;
4135   if (nr > 0)
4136     {
4137       pthread_mutex_lock (&select_mutex);
4138       select_nfds = nfds;
4139       select_valid = 0;
4140       if (readfds)
4141         {
4142           select_readfds = *readfds;
4143           select_valid += SELECT_HAVE_READ;
4144         }
4145       if (writefds)
4146         {
4147           select_writefds = *writefds;
4148           select_valid += SELECT_HAVE_WRITE;
4149         }
4151       if (timeout)
4152         {
4153           select_timeout = *timeout;
4154           select_valid += SELECT_HAVE_TMO;
4155         }
4157       pthread_mutex_unlock (&select_mutex);
4159       /* Inform fd_handler that select should be called */
4160       c = 'g';
4161       emacs_write_sig (selfds[1], &c, 1);
4162     }
4163   else if (nr == 0 && timeout)
4164     {
4165       /* No file descriptor, just a timeout, no need to wake fd_handler  */
4166       double time = timespectod (*timeout);
4167       timed_entry = [[NSTimer scheduledTimerWithTimeInterval: time
4168                                                       target: NSApp
4169                                                     selector:
4170                                   @selector (timeout_handler:)
4171                                                     userInfo: 0
4172                                                      repeats: NO]
4173                       retain];
4174     }
4175   else /* No timeout and no file descriptors, can this happen?  */
4176     {
4177       /* Send appdefined so we exit from the loop */
4178       ns_send_appdefined (-1);
4179     }
4181   block_input ();
4182   ns_init_events (&event);
4184   [NSApp run];
4186   ns_finish_events ();
4187   if (nr > 0 && readfds)
4188     {
4189       c = 's';
4190       emacs_write_sig (selfds[1], &c, 1);
4191     }
4192   unblock_input ();
4194   t = last_appdefined_event_data;
4196   if (t != NO_APPDEFINED_DATA)
4197     {
4198       last_appdefined_event_data = NO_APPDEFINED_DATA;
4200       if (t == -2)
4201         {
4202           /* The NX_APPDEFINED event we received was a timeout. */
4203           result = 0;
4204         }
4205       else if (t == -1)
4206         {
4207           /* The NX_APPDEFINED event we received was the result of
4208              at least one real input event arriving.  */
4209           errno = EINTR;
4210           result = -1;
4211         }
4212       else
4213         {
4214           /* Received back from select () in fd_handler; copy the results */
4215           pthread_mutex_lock (&select_mutex);
4216           if (readfds) *readfds = select_readfds;
4217           if (writefds) *writefds = select_writefds;
4218           pthread_mutex_unlock (&select_mutex);
4219           result = t;
4220         }
4221     }
4222   else
4223     {
4224       errno = EINTR;
4225       result = -1;
4226     }
4228   return result;
4233 /* ==========================================================================
4235     Scrollbar handling
4237    ========================================================================== */
4240 static void
4241 ns_set_vertical_scroll_bar (struct window *window,
4242                            int portion, int whole, int position)
4243 /* --------------------------------------------------------------------------
4244       External (hook): Update or add scrollbar
4245    -------------------------------------------------------------------------- */
4247   Lisp_Object win;
4248   NSRect r, v;
4249   struct frame *f = XFRAME (WINDOW_FRAME (window));
4250   EmacsView *view = FRAME_NS_VIEW (f);
4251   EmacsScroller *bar;
4252   int window_y, window_height;
4253   int top, left, height, width;
4254   BOOL update_p = YES;
4256   /* optimization; display engine sends WAY too many of these.. */
4257   if (!NILP (window->vertical_scroll_bar))
4258     {
4259       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4260       if ([bar checkSamePosition: position portion: portion whole: whole])
4261         {
4262           if (view->scrollbarsNeedingUpdate == 0)
4263             {
4264               if (!windows_or_buffers_changed)
4265                   return;
4266             }
4267           else
4268             view->scrollbarsNeedingUpdate--;
4269           update_p = NO;
4270         }
4271     }
4273   NSTRACE ("ns_set_vertical_scroll_bar");
4275   /* Get dimensions.  */
4276   window_box (window, ANY_AREA, 0, &window_y, 0, &window_height);
4277   top = window_y;
4278   height = window_height;
4279   width = NS_SCROLL_BAR_WIDTH (f);
4280   left = WINDOW_SCROLL_BAR_AREA_X (window);
4282   r = NSMakeRect (left, top, width, height);
4283   /* the parent view is flipped, so we need to flip y value */
4284   v = [view frame];
4285   r.origin.y = (v.size.height - r.size.height - r.origin.y);
4287   XSETWINDOW (win, window);
4288   block_input ();
4290   /* we want at least 5 lines to display a scrollbar */
4291   if (WINDOW_TOTAL_LINES (window) < 5)
4292     {
4293       if (!NILP (window->vertical_scroll_bar))
4294         {
4295           bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4296           [bar removeFromSuperview];
4297           wset_vertical_scroll_bar (window, Qnil);
4298           [bar release];
4299         }
4300       ns_clear_frame_area (f, left, top, width, height);
4301       unblock_input ();
4302       return;
4303     }
4305   if (NILP (window->vertical_scroll_bar))
4306     {
4307       if (width > 0 && height > 0)
4308         ns_clear_frame_area (f, left, top, width, height);
4310       bar = [[EmacsScroller alloc] initFrame: r window: win];
4311       wset_vertical_scroll_bar (window, make_save_ptr (bar));
4312       update_p = YES;
4313     }
4314   else
4315     {
4316       NSRect oldRect;
4317       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4318       oldRect = [bar frame];
4319       r.size.width = oldRect.size.width;
4320       if (FRAME_LIVE_P (f) && !NSEqualRects (oldRect, r))
4321         {
4322           if (oldRect.origin.x != r.origin.x)
4323               ns_clear_frame_area (f, left, top, width, height);
4324           [bar setFrame: r];
4325         }
4326     }
4328   if (update_p)
4329     [bar setPosition: position portion: portion whole: whole];
4330   unblock_input ();
4334 static void
4335 ns_set_horizontal_scroll_bar (struct window *window,
4336                               int portion, int whole, int position)
4337 /* --------------------------------------------------------------------------
4338       External (hook): Update or add scrollbar
4339    -------------------------------------------------------------------------- */
4341   Lisp_Object win;
4342   NSRect r, v;
4343   struct frame *f = XFRAME (WINDOW_FRAME (window));
4344   EmacsView *view = FRAME_NS_VIEW (f);
4345   EmacsScroller *bar;
4346   int top, height, left, width;
4347   int window_x, window_width;
4348   BOOL update_p = YES;
4350   /* optimization; display engine sends WAY too many of these.. */
4351   if (!NILP (window->horizontal_scroll_bar))
4352     {
4353       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4354       if ([bar checkSamePosition: position portion: portion whole: whole])
4355         {
4356           if (view->scrollbarsNeedingUpdate == 0)
4357             {
4358               if (!windows_or_buffers_changed)
4359                   return;
4360             }
4361           else
4362             view->scrollbarsNeedingUpdate--;
4363           update_p = NO;
4364         }
4365     }
4367   NSTRACE ("ns_set_horizontal_scroll_bar");
4369   /* Get dimensions.  */
4370   window_box (window, ANY_AREA, &window_x, 0, &window_width, 0);
4371   left = window_x;
4372   width = window_width;
4373   height = NS_SCROLL_BAR_HEIGHT (f);
4374   top = WINDOW_SCROLL_BAR_AREA_Y (window);
4376   r = NSMakeRect (left, top, width, height);
4377   /* the parent view is flipped, so we need to flip y value */
4378   v = [view frame];
4379   r.origin.y = (v.size.height - r.size.height - r.origin.y);
4381   XSETWINDOW (win, window);
4382   block_input ();
4384   if (NILP (window->horizontal_scroll_bar))
4385     {
4386       if (width > 0 && height > 0)
4387         ns_clear_frame_area (f, left, top, width, height);
4389       bar = [[EmacsScroller alloc] initFrame: r window: win];
4390       wset_horizontal_scroll_bar (window, make_save_ptr (bar));
4391       update_p = YES;
4392     }
4393   else
4394     {
4395       NSRect oldRect;
4396       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4397       oldRect = [bar frame];
4398       if (FRAME_LIVE_P (f) && !NSEqualRects (oldRect, r))
4399         {
4400           if (oldRect.origin.y != r.origin.y)
4401             ns_clear_frame_area (f, left, top, width, height);
4402           [bar setFrame: r];
4403           update_p = YES;
4404         }
4405     }
4407   /* If there are both horizontal and vertical scroll-bars they leave
4408      a square that belongs to neither. We need to clear it otherwise
4409      it fills with junk. */
4410   if (!NILP (window->vertical_scroll_bar))
4411     ns_clear_frame_area (f, WINDOW_SCROLL_BAR_AREA_X (window), top,
4412                          NS_SCROLL_BAR_HEIGHT (f), height);
4414   if (update_p)
4415     [bar setPosition: position portion: portion whole: whole];
4416   unblock_input ();
4420 static void
4421 ns_condemn_scroll_bars (struct frame *f)
4422 /* --------------------------------------------------------------------------
4423      External (hook): arrange for all frame's scrollbars to be removed
4424      at next call to judge_scroll_bars, except for those redeemed.
4425    -------------------------------------------------------------------------- */
4427   int i;
4428   id view;
4429   NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews];
4431   NSTRACE ("ns_condemn_scroll_bars");
4433   for (i =[subviews count]-1; i >= 0; i--)
4434     {
4435       view = [subviews objectAtIndex: i];
4436       if ([view isKindOfClass: [EmacsScroller class]])
4437         [view condemn];
4438     }
4442 static void
4443 ns_redeem_scroll_bar (struct window *window)
4444 /* --------------------------------------------------------------------------
4445      External (hook): arrange to spare this window's scrollbar
4446      at next call to judge_scroll_bars.
4447    -------------------------------------------------------------------------- */
4449   id bar;
4450   NSTRACE ("ns_redeem_scroll_bar");
4451   if (!NILP (window->vertical_scroll_bar)
4452       && WINDOW_HAS_VERTICAL_SCROLL_BAR (window))
4453     {
4454       bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4455       [bar reprieve];
4456     }
4458   if (!NILP (window->horizontal_scroll_bar)
4459       && WINDOW_HAS_HORIZONTAL_SCROLL_BAR (window))
4460     {
4461       bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4462       [bar reprieve];
4463     }
4467 static void
4468 ns_judge_scroll_bars (struct frame *f)
4469 /* --------------------------------------------------------------------------
4470      External (hook): destroy all scrollbars on frame that weren't
4471      redeemed after call to condemn_scroll_bars.
4472    -------------------------------------------------------------------------- */
4474   int i;
4475   id view;
4476   EmacsView *eview = FRAME_NS_VIEW (f);
4477   NSArray *subviews = [[eview superview] subviews];
4478   BOOL removed = NO;
4480   NSTRACE ("ns_judge_scroll_bars");
4481   for (i = [subviews count]-1; i >= 0; --i)
4482     {
4483       view = [subviews objectAtIndex: i];
4484       if (![view isKindOfClass: [EmacsScroller class]]) continue;
4485       if ([view judge])
4486         removed = YES;
4487     }
4489   if (removed)
4490     [eview updateFrameSize: NO];
4493 /* ==========================================================================
4495     Initialization
4497    ========================================================================== */
4500 x_display_pixel_height (struct ns_display_info *dpyinfo)
4502   NSArray *screens = [NSScreen screens];
4503   NSEnumerator *enumerator = [screens objectEnumerator];
4504   NSScreen *screen;
4505   NSRect frame;
4507   frame = NSZeroRect;
4508   while ((screen = [enumerator nextObject]) != nil)
4509     frame = NSUnionRect (frame, [screen frame]);
4511   return NSHeight (frame);
4515 x_display_pixel_width (struct ns_display_info *dpyinfo)
4517   NSArray *screens = [NSScreen screens];
4518   NSEnumerator *enumerator = [screens objectEnumerator];
4519   NSScreen *screen;
4520   NSRect frame;
4522   frame = NSZeroRect;
4523   while ((screen = [enumerator nextObject]) != nil)
4524     frame = NSUnionRect (frame, [screen frame]);
4526   return NSWidth (frame);
4530 static Lisp_Object ns_string_to_lispmod (const char *s)
4531 /* --------------------------------------------------------------------------
4532      Convert modifier name to lisp symbol
4533    -------------------------------------------------------------------------- */
4535   if (!strncmp (SSDATA (SYMBOL_NAME (Qmeta)), s, 10))
4536     return Qmeta;
4537   else if (!strncmp (SSDATA (SYMBOL_NAME (Qsuper)), s, 10))
4538     return Qsuper;
4539   else if (!strncmp (SSDATA (SYMBOL_NAME (Qcontrol)), s, 10))
4540     return Qcontrol;
4541   else if (!strncmp (SSDATA (SYMBOL_NAME (Qalt)), s, 10))
4542     return Qalt;
4543   else if (!strncmp (SSDATA (SYMBOL_NAME (Qhyper)), s, 10))
4544     return Qhyper;
4545   else if (!strncmp (SSDATA (SYMBOL_NAME (Qnone)), s, 10))
4546     return Qnone;
4547   else
4548     return Qnil;
4552 static void
4553 ns_default (const char *parameter, Lisp_Object *result,
4554            Lisp_Object yesval, Lisp_Object noval,
4555            BOOL is_float, BOOL is_modstring)
4556 /* --------------------------------------------------------------------------
4557       Check a parameter value in user's preferences
4558    -------------------------------------------------------------------------- */
4560   const char *value = ns_get_defaults_value (parameter);
4562   if (value)
4563     {
4564       double f;
4565       char *pos;
4566       if (c_strcasecmp (value, "YES") == 0)
4567         *result = yesval;
4568       else if (c_strcasecmp (value, "NO") == 0)
4569         *result = noval;
4570       else if (is_float && (f = strtod (value, &pos), pos != value))
4571         *result = make_float (f);
4572       else if (is_modstring && value)
4573         *result = ns_string_to_lispmod (value);
4574       else fprintf (stderr,
4575                    "Bad value for default \"%s\": \"%s\"\n", parameter, value);
4576     }
4580 static void
4581 ns_initialize_display_info (struct ns_display_info *dpyinfo)
4582 /* --------------------------------------------------------------------------
4583       Initialize global info and storage for display.
4584    -------------------------------------------------------------------------- */
4586     NSScreen *screen = [NSScreen mainScreen];
4587     NSWindowDepth depth = [screen depth];
4589     dpyinfo->resx = 72.27; /* used 75.0, but this makes pt == pixel, expected */
4590     dpyinfo->resy = 72.27;
4591     dpyinfo->color_p = ![NSDeviceWhiteColorSpace isEqualToString:
4592                                                   NSColorSpaceFromDepth (depth)]
4593                 && ![NSCalibratedWhiteColorSpace isEqualToString:
4594                                                  NSColorSpaceFromDepth (depth)];
4595     dpyinfo->n_planes = NSBitsPerPixelFromDepth (depth);
4596     dpyinfo->color_table = xmalloc (sizeof *dpyinfo->color_table);
4597     dpyinfo->color_table->colors = NULL;
4598     dpyinfo->root_window = 42; /* a placeholder.. */
4599     dpyinfo->x_highlight_frame = dpyinfo->x_focus_frame = NULL;
4600     dpyinfo->n_fonts = 0;
4601     dpyinfo->smallest_font_height = 1;
4602     dpyinfo->smallest_char_width = 1;
4604     reset_mouse_highlight (&dpyinfo->mouse_highlight);
4608 /* This and next define (many of the) public functions in this file. */
4609 /* x_... are generic versions in xdisp.c that we, and other terms, get away
4610          with using despite presence in the "system dependent" redisplay
4611          interface.  In addition, many of the ns_ methods have code that is
4612          shared with all terms, indicating need for further refactoring. */
4613 extern frame_parm_handler ns_frame_parm_handlers[];
4614 static struct redisplay_interface ns_redisplay_interface =
4616   ns_frame_parm_handlers,
4617   x_produce_glyphs,
4618   x_write_glyphs,
4619   x_insert_glyphs,
4620   x_clear_end_of_line,
4621   ns_scroll_run,
4622   ns_after_update_window_line,
4623   ns_update_window_begin,
4624   ns_update_window_end,
4625   0, /* flush_display */
4626   x_clear_window_mouse_face,
4627   x_get_glyph_overhangs,
4628   x_fix_overlapping_area,
4629   ns_draw_fringe_bitmap,
4630   0, /* define_fringe_bitmap */ /* FIXME: simplify ns_draw_fringe_bitmap */
4631   0, /* destroy_fringe_bitmap */
4632   ns_compute_glyph_string_overhangs,
4633   ns_draw_glyph_string,
4634   ns_define_frame_cursor,
4635   ns_clear_frame_area,
4636   ns_draw_window_cursor,
4637   ns_draw_vertical_window_border,
4638   ns_draw_window_divider,
4639   ns_shift_glyphs_for_insert,
4640   ns_show_hourglass,
4641   ns_hide_hourglass
4645 static void
4646 ns_delete_display (struct ns_display_info *dpyinfo)
4648   /* TODO... */
4652 /* This function is called when the last frame on a display is deleted. */
4653 static void
4654 ns_delete_terminal (struct terminal *terminal)
4656   struct ns_display_info *dpyinfo = terminal->display_info.ns;
4658   NSTRACE ("ns_delete_terminal");
4660   /* Protect against recursive calls.  delete_frame in
4661      delete_terminal calls us back when it deletes our last frame.  */
4662   if (!terminal->name)
4663     return;
4665   block_input ();
4667   x_destroy_all_bitmaps (dpyinfo);
4668   ns_delete_display (dpyinfo);
4669   unblock_input ();
4673 static struct terminal *
4674 ns_create_terminal (struct ns_display_info *dpyinfo)
4675 /* --------------------------------------------------------------------------
4676       Set up use of NS before we make the first connection.
4677    -------------------------------------------------------------------------- */
4679   struct terminal *terminal;
4681   NSTRACE ("ns_create_terminal");
4683   terminal = create_terminal (output_ns, &ns_redisplay_interface);
4685   terminal->display_info.ns = dpyinfo;
4686   dpyinfo->terminal = terminal;
4688   terminal->clear_frame_hook = ns_clear_frame;
4689   terminal->ring_bell_hook = ns_ring_bell;
4690   terminal->update_begin_hook = ns_update_begin;
4691   terminal->update_end_hook = ns_update_end;
4692   terminal->read_socket_hook = ns_read_socket;
4693   terminal->frame_up_to_date_hook = ns_frame_up_to_date;
4694   terminal->mouse_position_hook = ns_mouse_position;
4695   terminal->frame_rehighlight_hook = ns_frame_rehighlight;
4696   terminal->frame_raise_lower_hook = ns_frame_raise_lower;
4697   terminal->fullscreen_hook = ns_fullscreen_hook;
4698   terminal->menu_show_hook = ns_menu_show;
4699   terminal->popup_dialog_hook = ns_popup_dialog;
4700   terminal->set_vertical_scroll_bar_hook = ns_set_vertical_scroll_bar;
4701   terminal->set_horizontal_scroll_bar_hook = ns_set_horizontal_scroll_bar;
4702   terminal->condemn_scroll_bars_hook = ns_condemn_scroll_bars;
4703   terminal->redeem_scroll_bar_hook = ns_redeem_scroll_bar;
4704   terminal->judge_scroll_bars_hook = ns_judge_scroll_bars;
4705   terminal->delete_frame_hook = x_destroy_window;
4706   terminal->delete_terminal_hook = ns_delete_terminal;
4707   /* Other hooks are NULL by default.  */
4709   return terminal;
4713 struct ns_display_info *
4714 ns_term_init (Lisp_Object display_name)
4715 /* --------------------------------------------------------------------------
4716      Start the Application and get things rolling.
4717    -------------------------------------------------------------------------- */
4719   struct terminal *terminal;
4720   struct ns_display_info *dpyinfo;
4721   static int ns_initialized = 0;
4722   Lisp_Object tmp;
4724   if (ns_initialized) return x_display_list;
4725   ns_initialized = 1;
4727   block_input ();
4729   NSTRACE ("ns_term_init");
4731   [outerpool release];
4732   outerpool = [[NSAutoreleasePool alloc] init];
4734   /* count object allocs (About, click icon); on macOS use ObjectAlloc tool */
4735   /*GSDebugAllocationActive (YES); */
4736   block_input ();
4738   baud_rate = 38400;
4739   Fset_input_interrupt_mode (Qnil);
4741   if (selfds[0] == -1)
4742     {
4743       if (emacs_pipe (selfds) != 0)
4744         {
4745           fprintf (stderr, "Failed to create pipe: %s\n",
4746                    emacs_strerror (errno));
4747           emacs_abort ();
4748         }
4750       fcntl (selfds[0], F_SETFL, O_NONBLOCK|fcntl (selfds[0], F_GETFL));
4751       FD_ZERO (&select_readfds);
4752       FD_ZERO (&select_writefds);
4753       pthread_mutex_init (&select_mutex, NULL);
4754     }
4756   ns_pending_files = [[NSMutableArray alloc] init];
4757   ns_pending_service_names = [[NSMutableArray alloc] init];
4758   ns_pending_service_args = [[NSMutableArray alloc] init];
4760 /* Start app and create the main menu, window, view.
4761      Needs to be here because ns_initialize_display_info () uses AppKit classes.
4762      The view will then ask the NSApp to stop and return to Emacs. */
4763   [EmacsApp sharedApplication];
4764   if (NSApp == nil)
4765     return NULL;
4766   [NSApp setDelegate: NSApp];
4768   /* Start the select thread.  */
4769   [NSThread detachNewThreadSelector:@selector (fd_handler:)
4770                            toTarget:NSApp
4771                          withObject:nil];
4773   /* debugging: log all notifications */
4774   /*   [[NSNotificationCenter defaultCenter] addObserver: NSApp
4775                                          selector: @selector (logNotification:)
4776                                              name: nil object: nil]; */
4778   dpyinfo = xzalloc (sizeof *dpyinfo);
4780   ns_initialize_display_info (dpyinfo);
4781   terminal = ns_create_terminal (dpyinfo);
4783   terminal->kboard = allocate_kboard (Qns);
4784   /* Don't let the initial kboard remain current longer than necessary.
4785      That would cause problems if a file loaded on startup tries to
4786      prompt in the mini-buffer.  */
4787   if (current_kboard == initial_kboard)
4788     current_kboard = terminal->kboard;
4789   terminal->kboard->reference_count++;
4791   dpyinfo->next = x_display_list;
4792   x_display_list = dpyinfo;
4794   dpyinfo->name_list_element = Fcons (display_name, Qnil);
4796   terminal->name = xlispstrdup (display_name);
4798   unblock_input ();
4800   if (!inhibit_x_resources)
4801     {
4802       ns_default ("GSFontAntiAlias", &ns_antialias_text,
4803                  Qt, Qnil, NO, NO);
4804       tmp = Qnil;
4805       /* this is a standard variable */
4806       ns_default ("AppleAntiAliasingThreshold", &tmp,
4807                  make_float (10.0), make_float (6.0), YES, NO);
4808       ns_antialias_threshold = NILP (tmp) ? 10.0 : XFLOATINT (tmp);
4809     }
4811   NSTRACE_MSG ("Colors");
4813   {
4814     NSColorList *cl = [NSColorList colorListNamed: @"Emacs"];
4816     if ( cl == nil )
4817       {
4818         Lisp_Object color_file, color_map, color;
4819         unsigned long c;
4820         char *name;
4822         color_file = Fexpand_file_name (build_string ("rgb.txt"),
4823                          Fsymbol_value (intern ("data-directory")));
4825         color_map = Fx_load_color_file (color_file);
4826         if (NILP (color_map))
4827           fatal ("Could not read %s.\n", SDATA (color_file));
4829         cl = [[NSColorList alloc] initWithName: @"Emacs"];
4830         for ( ; CONSP (color_map); color_map = XCDR (color_map))
4831           {
4832             color = XCAR (color_map);
4833             name = SSDATA (XCAR (color));
4834             c = XINT (XCDR (color));
4835             [cl setColor:
4836                   [NSColor colorForEmacsRed: RED_FROM_ULONG (c) / 255.0
4837                                       green: GREEN_FROM_ULONG (c) / 255.0
4838                                        blue: BLUE_FROM_ULONG (c) / 255.0
4839                                       alpha: 1.0]
4840                   forKey: [NSString stringWithUTF8String: name]];
4841           }
4842         [cl writeToFile: nil];
4843       }
4844   }
4846   NSTRACE_MSG ("Versions");
4848   {
4849 #ifdef NS_IMPL_GNUSTEP
4850     Vwindow_system_version = build_string (gnustep_base_version);
4851 #else
4852     /*PSnextrelease (128, c); */
4853     char c[DBL_BUFSIZE_BOUND];
4854     int len = dtoastr (c, sizeof c, 0, 0, NSAppKitVersionNumber);
4855     Vwindow_system_version = make_unibyte_string (c, len);
4856 #endif
4857   }
4859   delete_keyboard_wait_descriptor (0);
4861   ns_app_name = [[NSProcessInfo processInfo] processName];
4863   /* Set up macOS app menu */
4865   NSTRACE_MSG ("Menu init");
4867 #ifdef NS_IMPL_COCOA
4868   {
4869     NSMenu *appMenu;
4870     NSMenuItem *item;
4871     /* set up the application menu */
4872     svcsMenu = [[EmacsMenu alloc] initWithTitle: @"Services"];
4873     [svcsMenu setAutoenablesItems: NO];
4874     appMenu = [[EmacsMenu alloc] initWithTitle: @"Emacs"];
4875     [appMenu setAutoenablesItems: NO];
4876     mainMenu = [[EmacsMenu alloc] initWithTitle: @""];
4877     dockMenu = [[EmacsMenu alloc] initWithTitle: @""];
4879     [appMenu insertItemWithTitle: @"About Emacs"
4880                           action: @selector (orderFrontStandardAboutPanel:)
4881                    keyEquivalent: @""
4882                          atIndex: 0];
4883     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 1];
4884     [appMenu insertItemWithTitle: @"Preferences..."
4885                           action: @selector (showPreferencesWindow:)
4886                    keyEquivalent: @","
4887                          atIndex: 2];
4888     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 3];
4889     item = [appMenu insertItemWithTitle: @"Services"
4890                                  action: @selector (menuDown:)
4891                           keyEquivalent: @""
4892                                 atIndex: 4];
4893     [appMenu setSubmenu: svcsMenu forItem: item];
4894     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 5];
4895     [appMenu insertItemWithTitle: @"Hide Emacs"
4896                           action: @selector (hide:)
4897                    keyEquivalent: @"h"
4898                          atIndex: 6];
4899     item =  [appMenu insertItemWithTitle: @"Hide Others"
4900                           action: @selector (hideOtherApplications:)
4901                    keyEquivalent: @"h"
4902                          atIndex: 7];
4903     [item setKeyEquivalentModifierMask: NSEventModifierFlagCommand | NSEventModifierFlagOption];
4904     [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 8];
4905     [appMenu insertItemWithTitle: @"Quit Emacs"
4906                           action: @selector (terminate:)
4907                    keyEquivalent: @"q"
4908                          atIndex: 9];
4910     item = [mainMenu insertItemWithTitle: ns_app_name
4911                                   action: @selector (menuDown:)
4912                            keyEquivalent: @""
4913                                  atIndex: 0];
4914     [mainMenu setSubmenu: appMenu forItem: item];
4915     [dockMenu insertItemWithTitle: @"New Frame"
4916                            action: @selector (newFrame:)
4917                     keyEquivalent: @""
4918                           atIndex: 0];
4920     [NSApp setMainMenu: mainMenu];
4921     [NSApp setAppleMenu: appMenu];
4922     [NSApp setServicesMenu: svcsMenu];
4923     /* Needed at least on Cocoa, to get dock menu to show windows */
4924     [NSApp setWindowsMenu: [[NSMenu alloc] init]];
4926     [[NSNotificationCenter defaultCenter]
4927       addObserver: mainMenu
4928          selector: @selector (trackingNotification:)
4929              name: NSMenuDidBeginTrackingNotification object: mainMenu];
4930     [[NSNotificationCenter defaultCenter]
4931       addObserver: mainMenu
4932          selector: @selector (trackingNotification:)
4933              name: NSMenuDidEndTrackingNotification object: mainMenu];
4934   }
4935 #endif /* macOS menu setup */
4937   /* Register our external input/output types, used for determining
4938      applicable services and also drag/drop eligibility. */
4940   NSTRACE_MSG ("Input/output types");
4942   ns_send_types = [[NSArray arrayWithObjects: NSStringPboardType, nil] retain];
4943   ns_return_types = [[NSArray arrayWithObjects: NSStringPboardType, nil]
4944                       retain];
4945   ns_drag_types = [[NSArray arrayWithObjects:
4946                             NSStringPboardType,
4947                             NSTabularTextPboardType,
4948                             NSFilenamesPboardType,
4949                             NSURLPboardType, nil] retain];
4951   /* If fullscreen is in init/default-frame-alist, focus isn't set
4952      right for fullscreen windows, so set this.  */
4953   [NSApp activateIgnoringOtherApps:YES];
4955   NSTRACE_MSG ("Call NSApp run");
4957   [NSApp run];
4958   ns_do_open_file = YES;
4960 #ifdef NS_IMPL_GNUSTEP
4961   /* GNUstep steals SIGCHLD for use in NSTask, but we don't use NSTask.
4962      We must re-catch it so subprocess works.  */
4963   catch_child_signal ();
4964 #endif
4966   NSTRACE_MSG ("ns_term_init done");
4968   unblock_input ();
4970   return dpyinfo;
4974 void
4975 ns_term_shutdown (int sig)
4977   [[NSUserDefaults standardUserDefaults] synchronize];
4979   /* code not reached in emacs.c after this is called by shut_down_emacs: */
4980   if (STRINGP (Vauto_save_list_file_name))
4981     unlink (SSDATA (Vauto_save_list_file_name));
4983   if (sig == 0 || sig == SIGTERM)
4984     {
4985       [NSApp terminate: NSApp];
4986     }
4987   else // force a stack trace to happen
4988     {
4989       emacs_abort ();
4990     }
4994 /* ==========================================================================
4996     EmacsApp implementation
4998    ========================================================================== */
5001 @implementation EmacsApp
5003 - (id)init
5005   NSTRACE ("[EmacsApp init]");
5007   if ((self = [super init]))
5008     {
5009 #ifdef NS_IMPL_COCOA
5010       self->isFirst = YES;
5011 #endif
5012 #ifdef NS_IMPL_GNUSTEP
5013       self->applicationDidFinishLaunchingCalled = NO;
5014 #endif
5015     }
5017   return self;
5020 #ifdef NS_IMPL_COCOA
5021 - (void)run
5023   NSTRACE ("[EmacsApp run]");
5025 #ifndef NSAppKitVersionNumber10_9
5026 #define NSAppKitVersionNumber10_9 1265
5027 #endif
5029     if ((int)NSAppKitVersionNumber != NSAppKitVersionNumber10_9)
5030       {
5031         [super run];
5032         return;
5033       }
5035   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5037   if (isFirst) [self finishLaunching];
5038   isFirst = NO;
5040   shouldKeepRunning = YES;
5041   do
5042     {
5043       [pool release];
5044       pool = [[NSAutoreleasePool alloc] init];
5046       NSEvent *event =
5047         [self nextEventMatchingMask:NSEventMaskAny
5048                           untilDate:[NSDate distantFuture]
5049                              inMode:NSDefaultRunLoopMode
5050                             dequeue:YES];
5052       [self sendEvent:event];
5053       [self updateWindows];
5054     } while (shouldKeepRunning);
5056   [pool release];
5059 - (void)stop: (id)sender
5061   NSTRACE ("[EmacsApp stop:]");
5063     shouldKeepRunning = NO;
5064     // Stop possible dialog also.  Noop if no dialog present.
5065     // The file dialog still leaks 7k - 10k on 10.9 though.
5066     [super stop:sender];
5068 #endif /* NS_IMPL_COCOA */
5070 - (void)logNotification: (NSNotification *)notification
5072   NSTRACE ("[EmacsApp logNotification:]");
5074   const char *name = [[notification name] UTF8String];
5075   if (!strstr (name, "Update") && !strstr (name, "NSMenu")
5076       && !strstr (name, "WindowNumber"))
5077     NSLog (@"notification: '%@'", [notification name]);
5081 - (void)sendEvent: (NSEvent *)theEvent
5082 /* --------------------------------------------------------------------------
5083      Called when NSApp is running for each event received.  Used to stop
5084      the loop when we choose, since there's no way to just run one iteration.
5085    -------------------------------------------------------------------------- */
5087   int type = [theEvent type];
5088   NSWindow *window = [theEvent window];
5090   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsApp sendEvent:]");
5091   NSTRACE_MSG ("Type: %d", type);
5093 #ifdef NS_IMPL_GNUSTEP
5094   // Keyboard events aren't propagated to file dialogs for some reason.
5095   if ([NSApp modalWindow] != nil &&
5096       (type == NSEventTypeKeyDown || type == NSEventTypeKeyUp || type == NSEventTypeFlagsChanged))
5097     {
5098       [[NSApp modalWindow] sendEvent: theEvent];
5099       return;
5100     }
5101 #endif
5103   if (represented_filename != nil && represented_frame)
5104     {
5105       NSString *fstr = represented_filename;
5106       NSView *view = FRAME_NS_VIEW (represented_frame);
5107 #ifdef NS_IMPL_COCOA
5108       /* work around a bug observed on 10.3 and later where
5109          setTitleWithRepresentedFilename does not clear out previous state
5110          if given filename does not exist */
5111       if (! [[NSFileManager defaultManager] fileExistsAtPath: fstr])
5112         [[view window] setRepresentedFilename: @""];
5113 #endif
5114       [[view window] setRepresentedFilename: fstr];
5115       [represented_filename release];
5116       represented_filename = nil;
5117       represented_frame = NULL;
5118     }
5120   if (type == NSEventTypeApplicationDefined)
5121     {
5122       switch ([theEvent data2])
5123         {
5124 #ifdef NS_IMPL_COCOA
5125         case NSAPP_DATA2_RUNASSCRIPT:
5126           ns_run_ascript ();
5127           [self stop: self];
5128           return;
5129 #endif
5130         case NSAPP_DATA2_RUNFILEDIALOG:
5131           ns_run_file_dialog ();
5132           [self stop: self];
5133           return;
5134         }
5135     }
5137   if (type == NSEventTypeCursorUpdate && window == nil)
5138     {
5139       fprintf (stderr, "Dropping external cursor update event.\n");
5140       return;
5141     }
5143   if (type == NSEventTypeApplicationDefined)
5144     {
5145       /* Events posted by ns_send_appdefined interrupt the run loop here.
5146          But, if a modal window is up, an appdefined can still come through,
5147          (e.g., from a makeKeyWindow event) but stopping self also stops the
5148          modal loop. Just defer it until later. */
5149       if ([NSApp modalWindow] == nil)
5150         {
5151           last_appdefined_event_data = [theEvent data1];
5152           [self stop: self];
5153         }
5154       else
5155         {
5156           send_appdefined = YES;
5157         }
5158     }
5161 #ifdef NS_IMPL_COCOA
5162   /* If no dialog and none of our frames have focus and it is a move, skip it.
5163      It is a mouse move in an auxiliary menu, i.e. on the top right on macOS,
5164      such as Wifi, sound, date or similar.
5165      This prevents "spooky" highlighting in the frame under the menu.  */
5166   if (type == NSEventTypeMouseMoved && [NSApp modalWindow] == nil)
5167     {
5168       struct ns_display_info *di;
5169       BOOL has_focus = NO;
5170       for (di = x_display_list; ! has_focus && di; di = di->next)
5171         has_focus = di->x_focus_frame != 0;
5172       if (! has_focus)
5173         return;
5174     }
5175 #endif
5177   NSTRACE_UNSILENCE();
5179   [super sendEvent: theEvent];
5183 - (void)showPreferencesWindow: (id)sender
5185   struct frame *emacsframe = SELECTED_FRAME ();
5186   NSEvent *theEvent = [NSApp currentEvent];
5188   if (!emacs_event)
5189     return;
5190   emacs_event->kind = NS_NONKEY_EVENT;
5191   emacs_event->code = KEY_NS_SHOW_PREFS;
5192   emacs_event->modifiers = 0;
5193   EV_TRAILER (theEvent);
5197 - (void)newFrame: (id)sender
5199   NSTRACE ("[EmacsApp newFrame:]");
5201   struct frame *emacsframe = SELECTED_FRAME ();
5202   NSEvent *theEvent = [NSApp currentEvent];
5204   if (!emacs_event)
5205     return;
5206   emacs_event->kind = NS_NONKEY_EVENT;
5207   emacs_event->code = KEY_NS_NEW_FRAME;
5208   emacs_event->modifiers = 0;
5209   EV_TRAILER (theEvent);
5213 /* Open a file (used by below, after going into queue read by ns_read_socket) */
5214 - (BOOL) openFile: (NSString *)fileName
5216   NSTRACE ("[EmacsApp openFile:]");
5218   struct frame *emacsframe = SELECTED_FRAME ();
5219   NSEvent *theEvent = [NSApp currentEvent];
5221   if (!emacs_event)
5222     return NO;
5224   emacs_event->kind = NS_NONKEY_EVENT;
5225   emacs_event->code = KEY_NS_OPEN_FILE_LINE;
5226   ns_input_file = append2 (ns_input_file, build_string ([fileName UTF8String]));
5227   ns_input_line = Qnil; /* can be start or cons start,end */
5228   emacs_event->modifiers =0;
5229   EV_TRAILER (theEvent);
5231   return YES;
5235 /* **************************************************************************
5237       EmacsApp delegate implementation
5239    ************************************************************************** */
5241 - (void)applicationDidFinishLaunching: (NSNotification *)notification
5242 /* --------------------------------------------------------------------------
5243      When application is loaded, terminate event loop in ns_term_init
5244    -------------------------------------------------------------------------- */
5246   NSTRACE ("[EmacsApp applicationDidFinishLaunching:]");
5248 #ifdef NS_IMPL_GNUSTEP
5249   ((EmacsApp *)self)->applicationDidFinishLaunchingCalled = YES;
5250 #endif
5251   [NSApp setServicesProvider: NSApp];
5253   [self antialiasThresholdDidChange:nil];
5254 #ifdef NS_IMPL_COCOA
5255   [[NSNotificationCenter defaultCenter]
5256     addObserver:self
5257        selector:@selector(antialiasThresholdDidChange:)
5258            name:NSAntialiasThresholdChangedNotification
5259          object:nil];
5260 #endif
5262   ns_send_appdefined (-2);
5265 - (void)antialiasThresholdDidChange:(NSNotification *)notification
5267 #ifdef NS_IMPL_COCOA
5268   macfont_update_antialias_threshold ();
5269 #endif
5273 /* Termination sequences:
5274     C-x C-c:
5275     Cmd-Q:
5276     MenuBar | File | Exit:
5277     Select Quit from App menubar:
5278         -terminate
5279         KEY_NS_POWER_OFF, (save-buffers-kill-emacs)
5280         ns_term_shutdown()
5282     Select Quit from Dock menu:
5283     Logout attempt:
5284         -appShouldTerminate
5285           Cancel -> Nothing else
5286           Accept ->
5288           -terminate
5289           KEY_NS_POWER_OFF, (save-buffers-kill-emacs)
5290           ns_term_shutdown()
5294 - (void) terminate: (id)sender
5296   NSTRACE ("[EmacsApp terminate:]");
5298   struct frame *emacsframe = SELECTED_FRAME ();
5300   if (!emacs_event)
5301     return;
5303   emacs_event->kind = NS_NONKEY_EVENT;
5304   emacs_event->code = KEY_NS_POWER_OFF;
5305   emacs_event->arg = Qt; /* mark as non-key event */
5306   EV_TRAILER ((id)nil);
5309 static bool
5310 runAlertPanel(NSString *title,
5311               NSString *msgFormat,
5312               NSString *defaultButton,
5313               NSString *alternateButton)
5315 #if !defined (NS_IMPL_COCOA) || \
5316   MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_9
5317   return NSRunAlertPanel(title, msgFormat, defaultButton, alternateButton, nil)
5318     == NSAlertDefaultReturn;
5319 #else
5320   NSAlert *alert = [[NSAlert alloc] init];
5321   [alert setAlertStyle: NSAlertStyleCritical];
5322   [alert setMessageText: msgFormat];
5323   [alert addButtonWithTitle: defaultButton];
5324   [alert addButtonWithTitle: alternateButton];
5325   NSInteger ret = [alert runModal];
5326   [alert release];
5327   return ret == NSAlertFirstButtonReturn;
5328 #endif
5332 - (NSApplicationTerminateReply)applicationShouldTerminate: (id)sender
5334   NSTRACE ("[EmacsApp applicationShouldTerminate:]");
5336   bool ret;
5338   if (NILP (ns_confirm_quit)) //   || ns_shutdown_properly  --> TO DO
5339     return NSTerminateNow;
5341   ret = runAlertPanel(ns_app_name,
5342                       @"Exit requested.  Would you like to Save Buffers and Exit, or Cancel the request?",
5343                       @"Save Buffers and Exit", @"Cancel");
5345   return ret ? NSTerminateNow : NSTerminateCancel;
5348 static int
5349 not_in_argv (NSString *arg)
5351   int k;
5352   const char *a = [arg UTF8String];
5353   for (k = 1; k < initial_argc; ++k)
5354     if (strcmp (a, initial_argv[k]) == 0) return 0;
5355   return 1;
5358 /*   Notification from the Workspace to open a file */
5359 - (BOOL)application: sender openFile: (NSString *)file
5361   if (ns_do_open_file || not_in_argv (file))
5362     [ns_pending_files addObject: file];
5363   return YES;
5367 /*   Open a file as a temporary file */
5368 - (BOOL)application: sender openTempFile: (NSString *)file
5370   if (ns_do_open_file || not_in_argv (file))
5371     [ns_pending_files addObject: file];
5372   return YES;
5376 /*   Notification from the Workspace to open a file noninteractively (?) */
5377 - (BOOL)application: sender openFileWithoutUI: (NSString *)file
5379   if (ns_do_open_file || not_in_argv (file))
5380     [ns_pending_files addObject: file];
5381   return YES;
5384 /*   Notification from the Workspace to open multiple files */
5385 - (void)application: sender openFiles: (NSArray *)fileList
5387   NSEnumerator *files = [fileList objectEnumerator];
5388   NSString *file;
5389   /* Don't open files from the command line unconditionally,
5390      Cocoa parses the command line wrong, --option value tries to open value
5391      if --option is the last option.  */
5392   while ((file = [files nextObject]) != nil)
5393     if (ns_do_open_file || not_in_argv (file))
5394       [ns_pending_files addObject: file];
5396   [self replyToOpenOrPrint: NSApplicationDelegateReplySuccess];
5401 /* Handle dock menu requests.  */
5402 - (NSMenu *)applicationDockMenu: (NSApplication *) sender
5404   return dockMenu;
5408 /* TODO: these may help w/IO switching btwn terminal and NSApp */
5409 - (void)applicationWillBecomeActive: (NSNotification *)notification
5411   NSTRACE ("[EmacsApp applicationWillBecomeActive:]");
5412   //ns_app_active=YES;
5415 - (void)applicationDidBecomeActive: (NSNotification *)notification
5417   NSTRACE ("[EmacsApp applicationDidBecomeActive:]");
5419 #ifdef NS_IMPL_GNUSTEP
5420   if (! applicationDidFinishLaunchingCalled)
5421     [self applicationDidFinishLaunching:notification];
5422 #endif
5423   //ns_app_active=YES;
5425   ns_update_auto_hide_menu_bar ();
5426   // No constraining takes place when the application is not active.
5427   ns_constrain_all_frames ();
5429 - (void)applicationDidResignActive: (NSNotification *)notification
5431   NSTRACE ("[EmacsApp applicationDidResignActive:]");
5433   //ns_app_active=NO;
5434   ns_send_appdefined (-1);
5439 /* ==========================================================================
5441     EmacsApp aux handlers for managing event loop
5443    ========================================================================== */
5446 - (void)timeout_handler: (NSTimer *)timedEntry
5447 /* --------------------------------------------------------------------------
5448      The timeout specified to ns_select has passed.
5449    -------------------------------------------------------------------------- */
5451   /*NSTRACE ("timeout_handler"); */
5452   ns_send_appdefined (-2);
5455 - (void)sendFromMainThread:(id)unused
5457   ns_send_appdefined (nextappdefined);
5460 - (void)fd_handler:(id)unused
5461 /* --------------------------------------------------------------------------
5462      Check data waiting on file descriptors and terminate if so
5463    -------------------------------------------------------------------------- */
5465   int result;
5466   int waiting = 1, nfds;
5467   char c;
5469   fd_set readfds, writefds, *wfds;
5470   struct timespec timeout, *tmo;
5471   NSAutoreleasePool *pool = nil;
5473   /* NSTRACE ("fd_handler"); */
5475   for (;;)
5476     {
5477       [pool release];
5478       pool = [[NSAutoreleasePool alloc] init];
5480       if (waiting)
5481         {
5482           fd_set fds;
5483           FD_ZERO (&fds);
5484           FD_SET (selfds[0], &fds);
5485           result = select (selfds[0]+1, &fds, NULL, NULL, NULL);
5486           if (result > 0 && read (selfds[0], &c, 1) == 1 && c == 'g')
5487             waiting = 0;
5488         }
5489       else
5490         {
5491           pthread_mutex_lock (&select_mutex);
5492           nfds = select_nfds;
5494           if (select_valid & SELECT_HAVE_READ)
5495             readfds = select_readfds;
5496           else
5497             FD_ZERO (&readfds);
5499           if (select_valid & SELECT_HAVE_WRITE)
5500             {
5501               writefds = select_writefds;
5502               wfds = &writefds;
5503             }
5504           else
5505             wfds = NULL;
5506           if (select_valid & SELECT_HAVE_TMO)
5507             {
5508               timeout = select_timeout;
5509               tmo = &timeout;
5510             }
5511           else
5512             tmo = NULL;
5514           pthread_mutex_unlock (&select_mutex);
5516           FD_SET (selfds[0], &readfds);
5517           if (selfds[0] >= nfds) nfds = selfds[0]+1;
5519           result = pselect (nfds, &readfds, wfds, NULL, tmo, NULL);
5521           if (result == 0)
5522             ns_send_appdefined (-2);
5523           else if (result > 0)
5524             {
5525               if (FD_ISSET (selfds[0], &readfds))
5526                 {
5527                   if (read (selfds[0], &c, 1) == 1 && c == 's')
5528                     waiting = 1;
5529                 }
5530               else
5531                 {
5532                   pthread_mutex_lock (&select_mutex);
5533                   if (select_valid & SELECT_HAVE_READ)
5534                     select_readfds = readfds;
5535                   if (select_valid & SELECT_HAVE_WRITE)
5536                     select_writefds = writefds;
5537                   if (select_valid & SELECT_HAVE_TMO)
5538                     select_timeout = timeout;
5539                   pthread_mutex_unlock (&select_mutex);
5541                   ns_send_appdefined (result);
5542                 }
5543             }
5544           waiting = 1;
5545         }
5546     }
5551 /* ==========================================================================
5553     Service provision
5555    ========================================================================== */
5557 /* called from system: queue for next pass through event loop */
5558 - (void)requestService: (NSPasteboard *)pboard
5559               userData: (NSString *)userData
5560                  error: (NSString **)error
5562   [ns_pending_service_names addObject: userData];
5563   [ns_pending_service_args addObject: [NSString stringWithUTF8String:
5564       SSDATA (ns_string_from_pasteboard (pboard))]];
5568 /* called from ns_read_socket to clear queue */
5569 - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
5571   struct frame *emacsframe = SELECTED_FRAME ();
5572   NSEvent *theEvent = [NSApp currentEvent];
5574   NSTRACE ("[EmacsApp fulfillService:withArg:]");
5576   if (!emacs_event)
5577     return NO;
5579   emacs_event->kind = NS_NONKEY_EVENT;
5580   emacs_event->code = KEY_NS_SPI_SERVICE_CALL;
5581   ns_input_spi_name = build_string ([name UTF8String]);
5582   ns_input_spi_arg = build_string ([arg UTF8String]);
5583   emacs_event->modifiers = EV_MODIFIERS (theEvent);
5584   EV_TRAILER (theEvent);
5586   return YES;
5590 @end  /* EmacsApp */
5594 /* ==========================================================================
5596     EmacsView implementation
5598    ========================================================================== */
5601 @implementation EmacsView
5603 /* needed to inform when window closed from LISP */
5604 - (void) setWindowClosing: (BOOL)closing
5606   NSTRACE ("[EmacsView setWindowClosing:%d]", closing);
5608   windowClosing = closing;
5612 - (void)dealloc
5614   NSTRACE ("[EmacsView dealloc]");
5615   [toolbar release];
5616   if (fs_state == FULLSCREEN_BOTH)
5617     [nonfs_window release];
5618   [super dealloc];
5622 /* called on font panel selection */
5623 - (void)changeFont: (id)sender
5625   NSEvent *e = [[self window] currentEvent];
5626   struct face *face = FACE_FROM_ID (emacsframe, DEFAULT_FACE_ID);
5627   struct font *font = face->font;
5628   id newFont;
5629   CGFloat size;
5630   NSFont *nsfont;
5632   NSTRACE ("[EmacsView changeFont:]");
5634   if (!emacs_event)
5635     return;
5637 #ifdef NS_IMPL_GNUSTEP
5638   nsfont = ((struct nsfont_info *)font)->nsfont;
5639 #endif
5640 #ifdef NS_IMPL_COCOA
5641   nsfont = (NSFont *) macfont_get_nsctfont (font);
5642 #endif
5644   if ((newFont = [sender convertFont: nsfont]))
5645     {
5646       SET_FRAME_GARBAGED (emacsframe); /* now needed as of 2008/10 */
5648       emacs_event->kind = NS_NONKEY_EVENT;
5649       emacs_event->modifiers = 0;
5650       emacs_event->code = KEY_NS_CHANGE_FONT;
5652       size = [newFont pointSize];
5653       ns_input_fontsize = make_number (lrint (size));
5654       ns_input_font = build_string ([[newFont familyName] UTF8String]);
5655       EV_TRAILER (e);
5656     }
5660 - (BOOL)acceptsFirstResponder
5662   NSTRACE ("[EmacsView acceptsFirstResponder]");
5663   return YES;
5667 - (void)resetCursorRects
5669   NSRect visible = [self visibleRect];
5670   NSCursor *currentCursor = FRAME_POINTER_TYPE (emacsframe);
5671   NSTRACE ("[EmacsView resetCursorRects]");
5673   if (currentCursor == nil)
5674     currentCursor = [NSCursor arrowCursor];
5676   if (!NSIsEmptyRect (visible))
5677     [self addCursorRect: visible cursor: currentCursor];
5678   [currentCursor setOnMouseEntered: YES];
5683 /*****************************************************************************/
5684 /* Keyboard handling. */
5685 #define NS_KEYLOG 0
5687 - (void)keyDown: (NSEvent *)theEvent
5689   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
5690   int code;
5691   unsigned fnKeysym = 0;
5692   static NSMutableArray *nsEvArray;
5693   int left_is_none;
5694   unsigned int flags = [theEvent modifierFlags];
5696   NSTRACE ("[EmacsView keyDown:]");
5698   /* Rhapsody and macOS give up and down events for the arrow keys */
5699   if (ns_fake_keydown == YES)
5700     ns_fake_keydown = NO;
5701   else if ([theEvent type] != NSEventTypeKeyDown)
5702     return;
5704   if (!emacs_event)
5705     return;
5707  if (![[self window] isKeyWindow]
5708      && [[theEvent window] isKindOfClass: [EmacsWindow class]]
5709      /* we must avoid an infinite loop here. */
5710      && (EmacsView *)[[theEvent window] delegate] != self)
5711    {
5712      /* XXX: There is an occasional condition in which, when Emacs display
5713          updates a different frame from the current one, and temporarily
5714          selects it, then processes some interrupt-driven input
5715          (dispnew.c:3878), OS will send the event to the correct NSWindow, but
5716          for some reason that window has its first responder set to the NSView
5717          most recently updated (I guess), which is not the correct one. */
5718      [(EmacsView *)[[theEvent window] delegate] keyDown: theEvent];
5719      return;
5720    }
5722   if (nsEvArray == nil)
5723     nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1];
5725   [NSCursor setHiddenUntilMouseMoves: YES];
5727   if (hlinfo->mouse_face_hidden && INTEGERP (Vmouse_highlight))
5728     {
5729       clear_mouse_face (hlinfo);
5730       hlinfo->mouse_face_hidden = 1;
5731     }
5733   if (!processingCompose)
5734     {
5735       /* When using screen sharing, no left or right information is sent,
5736          so use Left key in those cases.  */
5737       int is_left_key, is_right_key;
5739       code = ([[theEvent charactersIgnoringModifiers] length] == 0) ?
5740         0 : [[theEvent charactersIgnoringModifiers] characterAtIndex: 0];
5742       /* (Carbon way: [theEvent keyCode]) */
5744       /* is it a "function key"? */
5745       /* Note: Sometimes a plain key will have the NSEventModifierFlagNumericPad
5746          flag set (this is probably a bug in the OS).
5747       */
5748       if (code < 0x00ff && (flags&NSEventModifierFlagNumericPad))
5749         {
5750           fnKeysym = ns_convert_key ([theEvent keyCode] | NSEventModifierFlagNumericPad);
5751         }
5752       if (fnKeysym == 0)
5753         {
5754           fnKeysym = ns_convert_key (code);
5755         }
5757       if (fnKeysym)
5758         {
5759           /* COUNTERHACK: map 'Delete' on upper-right main KB to 'Backspace',
5760              because Emacs treats Delete and KP-Delete same (in simple.el). */
5761           if ((fnKeysym == 0xFFFF && [theEvent keyCode] == 0x33)
5762 #ifdef NS_IMPL_GNUSTEP
5763               /*  GNUstep uses incompatible keycodes, even for those that are
5764                   supposed to be hardware independent.  Just check for delete.
5765                   Keypad delete does not have keysym 0xFFFF.
5766                   See http://savannah.gnu.org/bugs/?25395
5767               */
5768               || (fnKeysym == 0xFFFF && code == 127)
5769 #endif
5770             )
5771             code = 0xFF08; /* backspace */
5772           else
5773             code = fnKeysym;
5774         }
5776       /* are there modifiers? */
5777       emacs_event->modifiers = 0;
5779       if (flags & NSEventModifierFlagHelp)
5780           emacs_event->modifiers |= hyper_modifier;
5782       if (flags & NSEventModifierFlagShift)
5783         emacs_event->modifiers |= shift_modifier;
5785       is_right_key = (flags & NSRightCommandKeyMask) == NSRightCommandKeyMask;
5786       is_left_key = (flags & NSLeftCommandKeyMask) == NSLeftCommandKeyMask
5787         || (! is_right_key && (flags & NSEventModifierFlagCommand) == NSEventModifierFlagCommand);
5789       if (is_right_key)
5790         emacs_event->modifiers |= parse_solitary_modifier
5791           (EQ (ns_right_command_modifier, Qleft)
5792            ? ns_command_modifier
5793            : ns_right_command_modifier);
5795       if (is_left_key)
5796         {
5797           emacs_event->modifiers |= parse_solitary_modifier
5798             (ns_command_modifier);
5800           /* if super (default), take input manager's word so things like
5801              dvorak / qwerty layout work */
5802           if (EQ (ns_command_modifier, Qsuper)
5803               && !fnKeysym
5804               && [[theEvent characters] length] != 0)
5805             {
5806               /* XXX: the code we get will be unshifted, so if we have
5807                  a shift modifier, must convert ourselves */
5808               if (!(flags & NSEventModifierFlagShift))
5809                 code = [[theEvent characters] characterAtIndex: 0];
5810 #if 0
5811               /* this is ugly and also requires linking w/Carbon framework
5812                  (for LMGetKbdType) so for now leave this rare (?) case
5813                  undealt with.. in future look into CGEvent methods */
5814               else
5815                 {
5816                   long smv = GetScriptManagerVariable (smKeyScript);
5817                   Handle uchrHandle = GetResource
5818                     ('uchr', GetScriptVariable (smv, smScriptKeys));
5819                   UInt32 dummy = 0;
5820                   UCKeyTranslate ((UCKeyboardLayout*)*uchrHandle,
5821                                  [[theEvent characters] characterAtIndex: 0],
5822                                  kUCKeyActionDisplay,
5823                                  (flags & ~NSEventModifierFlagCommand) >> 8,
5824                                  LMGetKbdType (), kUCKeyTranslateNoDeadKeysMask,
5825                                  &dummy, 1, &dummy, &code);
5826                   code &= 0xFF;
5827                 }
5828 #endif
5829             }
5830         }
5832       is_right_key = (flags & NSRightControlKeyMask) == NSRightControlKeyMask;
5833       is_left_key = (flags & NSLeftControlKeyMask) == NSLeftControlKeyMask
5834         || (! is_right_key && (flags & NSEventModifierFlagControl) == NSEventModifierFlagControl);
5836       if (is_right_key)
5837           emacs_event->modifiers |= parse_solitary_modifier
5838               (EQ (ns_right_control_modifier, Qleft)
5839                ? ns_control_modifier
5840                : ns_right_control_modifier);
5842       if (is_left_key)
5843         emacs_event->modifiers |= parse_solitary_modifier
5844           (ns_control_modifier);
5846       if (flags & NS_FUNCTION_KEY_MASK && !fnKeysym)
5847           emacs_event->modifiers |=
5848             parse_solitary_modifier (ns_function_modifier);
5850       left_is_none = NILP (ns_alternate_modifier)
5851         || EQ (ns_alternate_modifier, Qnone);
5853       is_right_key = (flags & NSRightAlternateKeyMask)
5854         == NSRightAlternateKeyMask;
5855       is_left_key = (flags & NSLeftAlternateKeyMask) == NSLeftAlternateKeyMask
5856         || (! is_right_key
5857             && (flags & NSEventModifierFlagOption) == NSEventModifierFlagOption);
5859       if (is_right_key)
5860         {
5861           if ((NILP (ns_right_alternate_modifier)
5862                || EQ (ns_right_alternate_modifier, Qnone)
5863                || (EQ (ns_right_alternate_modifier, Qleft) && left_is_none))
5864               && !fnKeysym)
5865             {   /* accept pre-interp alt comb */
5866               if ([[theEvent characters] length] > 0)
5867                 code = [[theEvent characters] characterAtIndex: 0];
5868               /*HACK: clear lone shift modifier to stop next if from firing */
5869               if (emacs_event->modifiers == shift_modifier)
5870                 emacs_event->modifiers = 0;
5871             }
5872           else
5873             emacs_event->modifiers |= parse_solitary_modifier
5874               (EQ (ns_right_alternate_modifier, Qleft)
5875                ? ns_alternate_modifier
5876                : ns_right_alternate_modifier);
5877         }
5879       if (is_left_key) /* default = meta */
5880         {
5881           if (left_is_none && !fnKeysym)
5882             {   /* accept pre-interp alt comb */
5883               if ([[theEvent characters] length] > 0)
5884                 code = [[theEvent characters] characterAtIndex: 0];
5885               /*HACK: clear lone shift modifier to stop next if from firing */
5886               if (emacs_event->modifiers == shift_modifier)
5887                 emacs_event->modifiers = 0;
5888             }
5889           else
5890               emacs_event->modifiers |=
5891                 parse_solitary_modifier (ns_alternate_modifier);
5892         }
5894   if (NS_KEYLOG)
5895     fprintf (stderr, "keyDown: code =%x\tfnKey =%x\tflags = %x\tmods = %x\n",
5896              (unsigned) code, fnKeysym, flags, emacs_event->modifiers);
5898       /* if it was a function key or had modifiers, pass it directly to emacs */
5899       if (fnKeysym || (emacs_event->modifiers
5900                        && (emacs_event->modifiers != shift_modifier)
5901                        && [[theEvent charactersIgnoringModifiers] length] > 0))
5902 /*[[theEvent characters] length] */
5903         {
5904           emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
5905           if (code < 0x20)
5906             code |= (1<<28)|(3<<16);
5907           else if (code == 0x7f)
5908             code |= (1<<28)|(3<<16);
5909           else if (!fnKeysym)
5910             emacs_event->kind = code > 0xFF
5911               ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT;
5913           emacs_event->code = code;
5914           EV_TRAILER (theEvent);
5915           processingCompose = NO;
5916           return;
5917         }
5918     }
5921   if (NS_KEYLOG && !processingCompose)
5922     fprintf (stderr, "keyDown: Begin compose sequence.\n");
5924   processingCompose = YES;
5925   [nsEvArray addObject: theEvent];
5926   [self interpretKeyEvents: nsEvArray];
5927   [nsEvArray removeObject: theEvent];
5931 #ifdef NS_IMPL_COCOA
5932 /* Needed to pick up Ctrl-tab and possibly other events that Mac OS X
5933    decided not to send key-down for.
5934    See http://osdir.com/ml/editors.vim.mac/2007-10/msg00141.html
5935    This only applies on Tiger and earlier.
5936    If it matches one of these, send it on to keyDown. */
5937 -(void)keyUp: (NSEvent *)theEvent
5939   int flags = [theEvent modifierFlags];
5940   int code = [theEvent keyCode];
5942   NSTRACE ("[EmacsView keyUp:]");
5944   if (floor (NSAppKitVersionNumber) <= 824 /*NSAppKitVersionNumber10_4*/ &&
5945       code == 0x30 && (flags & NSEventModifierFlagControl) && !(flags & NSEventModifierFlagCommand))
5946     {
5947       if (NS_KEYLOG)
5948         fprintf (stderr, "keyUp: passed test");
5949       ns_fake_keydown = YES;
5950       [self keyDown: theEvent];
5951     }
5953 #endif
5956 /* <NSTextInput> implementation (called through super interpretKeyEvents:]). */
5959 /* <NSTextInput>: called when done composing;
5960    NOTE: also called when we delete over working text, followed immed.
5961          by doCommandBySelector: deleteBackward: */
5962 - (void)insertText: (id)aString
5964   int code;
5965   int len = [(NSString *)aString length];
5966   int i;
5968   NSTRACE ("[EmacsView insertText:]");
5970   if (NS_KEYLOG)
5971     NSLog (@"insertText '%@'\tlen = %d", aString, len);
5972   processingCompose = NO;
5974   if (!emacs_event)
5975     return;
5977   /* first, clear any working text */
5978   if (workingText != nil)
5979     [self deleteWorkingText];
5981   /* now insert the string as keystrokes */
5982   for (i =0; i<len; i++)
5983     {
5984       code = [aString characterAtIndex: i];
5985       /* TODO: still need this? */
5986       if (code == 0x2DC)
5987         code = '~'; /* 0x7E */
5988       if (code != 32) /* Space */
5989         emacs_event->modifiers = 0;
5990       emacs_event->kind
5991         = code > 0xFF ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT;
5992       emacs_event->code = code;
5993       EV_TRAILER ((id)nil);
5994     }
5998 /* <NSTextInput>: inserts display of composing characters */
5999 - (void)setMarkedText: (id)aString selectedRange: (NSRange)selRange
6001   NSString *str = [aString respondsToSelector: @selector (string)] ?
6002     [aString string] : aString;
6004   NSTRACE ("[EmacsView setMarkedText:selectedRange:]");
6006   if (NS_KEYLOG)
6007     NSLog (@"setMarkedText '%@' len =%lu range %lu from %lu",
6008            str, (unsigned long)[str length],
6009            (unsigned long)selRange.length,
6010            (unsigned long)selRange.location);
6012   if (workingText != nil)
6013     [self deleteWorkingText];
6014   if ([str length] == 0)
6015     return;
6017   if (!emacs_event)
6018     return;
6020   processingCompose = YES;
6021   workingText = [str copy];
6022   ns_working_text = build_string ([workingText UTF8String]);
6024   emacs_event->kind = NS_TEXT_EVENT;
6025   emacs_event->code = KEY_NS_PUT_WORKING_TEXT;
6026   EV_TRAILER ((id)nil);
6030 /* delete display of composing characters [not in <NSTextInput>] */
6031 - (void)deleteWorkingText
6033   NSTRACE ("[EmacsView deleteWorkingText]");
6035   if (workingText == nil)
6036     return;
6037   if (NS_KEYLOG)
6038     NSLog(@"deleteWorkingText len =%lu\n", (unsigned long)[workingText length]);
6039   [workingText release];
6040   workingText = nil;
6041   processingCompose = NO;
6043   if (!emacs_event)
6044     return;
6046   emacs_event->kind = NS_TEXT_EVENT;
6047   emacs_event->code = KEY_NS_UNPUT_WORKING_TEXT;
6048   EV_TRAILER ((id)nil);
6052 - (BOOL)hasMarkedText
6054   NSTRACE ("[EmacsView hasMarkedText]");
6056   return workingText != nil;
6060 - (NSRange)markedRange
6062   NSTRACE ("[EmacsView markedRange]");
6064   NSRange rng = workingText != nil
6065     ? NSMakeRange (0, [workingText length]) : NSMakeRange (NSNotFound, 0);
6066   if (NS_KEYLOG)
6067     NSLog (@"markedRange request");
6068   return rng;
6072 - (void)unmarkText
6074   NSTRACE ("[EmacsView unmarkText]");
6076   if (NS_KEYLOG)
6077     NSLog (@"unmark (accept) text");
6078   [self deleteWorkingText];
6079   processingCompose = NO;
6083 /* used to position char selection windows, etc. */
6084 - (NSRect)firstRectForCharacterRange: (NSRange)theRange
6086   NSRect rect;
6087   NSPoint pt;
6088   struct window *win = XWINDOW (FRAME_SELECTED_WINDOW (emacsframe));
6090   NSTRACE ("[EmacsView firstRectForCharacterRange:]");
6092   if (NS_KEYLOG)
6093     NSLog (@"firstRectForCharRange request");
6095   rect.size.width = theRange.length * FRAME_COLUMN_WIDTH (emacsframe);
6096   rect.size.height = FRAME_LINE_HEIGHT (emacsframe);
6097   pt.x = WINDOW_TEXT_TO_FRAME_PIXEL_X (win, win->phys_cursor.x);
6098   pt.y = WINDOW_TO_FRAME_PIXEL_Y (win, win->phys_cursor.y
6099                                        +FRAME_LINE_HEIGHT (emacsframe));
6101   pt = [self convertPoint: pt toView: nil];
6102 #if !defined (NS_IMPL_COCOA) || \
6103   MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
6104   pt = [[self window] convertBaseToScreen: pt];
6105   rect.origin = pt;
6106 #else
6107   rect.origin = pt;
6108   rect = [[self window] convertRectToScreen: rect];
6109 #endif
6110   return rect;
6114 - (NSInteger)conversationIdentifier
6116   return (NSInteger)self;
6120 - (void)doCommandBySelector: (SEL)aSelector
6122   NSTRACE ("[EmacsView doCommandBySelector:]");
6124   if (NS_KEYLOG)
6125     NSLog (@"doCommandBySelector: %@", NSStringFromSelector (aSelector));
6127   processingCompose = NO;
6128   if (aSelector == @selector (deleteBackward:))
6129     {
6130       /* happens when user backspaces over an ongoing composition:
6131          throw a 'delete' into the event queue */
6132       if (!emacs_event)
6133         return;
6134       emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
6135       emacs_event->code = 0xFF08;
6136       EV_TRAILER ((id)nil);
6137     }
6140 - (NSArray *)validAttributesForMarkedText
6142   static NSArray *arr = nil;
6143   if (arr == nil) arr = [NSArray new];
6144  /* [[NSArray arrayWithObject: NSUnderlineStyleAttributeName] retain]; */
6145   return arr;
6148 - (NSRange)selectedRange
6150   if (NS_KEYLOG)
6151     NSLog (@"selectedRange request");
6152   return NSMakeRange (NSNotFound, 0);
6155 #if defined (NS_IMPL_COCOA) || GNUSTEP_GUI_MAJOR_VERSION > 0 || \
6156     GNUSTEP_GUI_MINOR_VERSION > 22
6157 - (NSUInteger)characterIndexForPoint: (NSPoint)thePoint
6158 #else
6159 - (unsigned int)characterIndexForPoint: (NSPoint)thePoint
6160 #endif
6162   if (NS_KEYLOG)
6163     NSLog (@"characterIndexForPoint request");
6164   return 0;
6167 - (NSAttributedString *)attributedSubstringFromRange: (NSRange)theRange
6169   static NSAttributedString *str = nil;
6170   if (str == nil) str = [NSAttributedString new];
6171   if (NS_KEYLOG)
6172     NSLog (@"attributedSubstringFromRange request");
6173   return str;
6176 /* End <NSTextInput> impl. */
6177 /*****************************************************************************/
6180 /* This is what happens when the user presses a mouse button.  */
6181 - (void)mouseDown: (NSEvent *)theEvent
6183   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6184   NSPoint p = [self convertPoint: [theEvent locationInWindow] fromView: nil];
6186   NSTRACE ("[EmacsView mouseDown:]");
6188   [self deleteWorkingText];
6190   if (!emacs_event)
6191     return;
6193   dpyinfo->last_mouse_frame = emacsframe;
6194   /* appears to be needed to prevent spurious movement events generated on
6195      button clicks */
6196   emacsframe->mouse_moved = 0;
6198   if ([theEvent type] == NSEventTypeScrollWheel)
6199     {
6200       CGFloat delta = [theEvent deltaY];
6201       /* Mac notebooks send wheel events w/delta =0 when trackpad scrolling */
6202       if (delta == 0)
6203         {
6204           delta = [theEvent deltaX];
6205           if (delta == 0)
6206             {
6207               NSTRACE_MSG ("deltaIsZero");
6208               return;
6209             }
6210           emacs_event->kind = HORIZ_WHEEL_EVENT;
6211         }
6212       else
6213         emacs_event->kind = WHEEL_EVENT;
6215       emacs_event->code = 0;
6216       emacs_event->modifiers = EV_MODIFIERS (theEvent) |
6217         ((delta > 0) ? up_modifier : down_modifier);
6218     }
6219   else
6220     {
6221       emacs_event->kind = MOUSE_CLICK_EVENT;
6222       emacs_event->code = EV_BUTTON (theEvent);
6223       emacs_event->modifiers = EV_MODIFIERS (theEvent)
6224                              | EV_UDMODIFIERS (theEvent);
6225     }
6226   XSETINT (emacs_event->x, lrint (p.x));
6227   XSETINT (emacs_event->y, lrint (p.y));
6228   EV_TRAILER (theEvent);
6232 - (void)rightMouseDown: (NSEvent *)theEvent
6234   NSTRACE ("[EmacsView rightMouseDown:]");
6235   [self mouseDown: theEvent];
6239 - (void)otherMouseDown: (NSEvent *)theEvent
6241   NSTRACE ("[EmacsView otherMouseDown:]");
6242   [self mouseDown: theEvent];
6246 - (void)mouseUp: (NSEvent *)theEvent
6248   NSTRACE ("[EmacsView mouseUp:]");
6249   [self mouseDown: theEvent];
6253 - (void)rightMouseUp: (NSEvent *)theEvent
6255   NSTRACE ("[EmacsView rightMouseUp:]");
6256   [self mouseDown: theEvent];
6260 - (void)otherMouseUp: (NSEvent *)theEvent
6262   NSTRACE ("[EmacsView otherMouseUp:]");
6263   [self mouseDown: theEvent];
6267 - (void) scrollWheel: (NSEvent *)theEvent
6269   NSTRACE ("[EmacsView scrollWheel:]");
6270   [self mouseDown: theEvent];
6274 /* Tell emacs the mouse has moved. */
6275 - (void)mouseMoved: (NSEvent *)e
6277   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
6278   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6279   Lisp_Object frame;
6280   NSPoint pt;
6282   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsView mouseMoved:]");
6284   dpyinfo->last_mouse_movement_time = EV_TIMESTAMP (e);
6285   pt = [self convertPoint: [e locationInWindow] fromView: nil];
6286   dpyinfo->last_mouse_motion_x = pt.x;
6287   dpyinfo->last_mouse_motion_y = pt.y;
6289   /* update any mouse face */
6290   if (hlinfo->mouse_face_hidden)
6291     {
6292       hlinfo->mouse_face_hidden = 0;
6293       clear_mouse_face (hlinfo);
6294     }
6296   /* tooltip handling */
6297   previous_help_echo_string = help_echo_string;
6298   help_echo_string = Qnil;
6300   if (!NILP (Vmouse_autoselect_window))
6301     {
6302       NSTRACE_MSG ("mouse_autoselect_window");
6303       static Lisp_Object last_mouse_window;
6304       Lisp_Object window
6305         = window_from_coordinates (emacsframe, pt.x, pt.y, 0, 0);
6307       if (WINDOWP (window)
6308           && !EQ (window, last_mouse_window)
6309           && !EQ (window, selected_window)
6310           && (focus_follows_mouse
6311               || (EQ (XWINDOW (window)->frame,
6312                       XWINDOW (selected_window)->frame))))
6313         {
6314           NSTRACE_MSG ("in_window");
6315           emacs_event->kind = SELECT_WINDOW_EVENT;
6316           emacs_event->frame_or_window = window;
6317           EV_TRAILER2 (e);
6318         }
6319       /* Remember the last window where we saw the mouse.  */
6320       last_mouse_window = window;
6321     }
6323   if (!note_mouse_movement (emacsframe, pt.x, pt.y))
6324     help_echo_string = previous_help_echo_string;
6326   XSETFRAME (frame, emacsframe);
6327   if (!NILP (help_echo_string) || !NILP (previous_help_echo_string))
6328     {
6329       /* NOTE: help_echo_{window,pos,object} are set in xdisp.c
6330          (note_mouse_highlight), which is called through the
6331          note_mouse_movement () call above */
6332       any_help_event_p = YES;
6333       gen_help_event (help_echo_string, frame, help_echo_window,
6334                       help_echo_object, help_echo_pos);
6335     }
6337   if (emacsframe->mouse_moved && send_appdefined)
6338     ns_send_appdefined (-1);
6342 - (void)mouseDragged: (NSEvent *)e
6344   NSTRACE ("[EmacsView mouseDragged:]");
6345   [self mouseMoved: e];
6349 - (void)rightMouseDragged: (NSEvent *)e
6351   NSTRACE ("[EmacsView rightMouseDragged:]");
6352   [self mouseMoved: e];
6356 - (void)otherMouseDragged: (NSEvent *)e
6358   NSTRACE ("[EmacsView otherMouseDragged:]");
6359   [self mouseMoved: e];
6363 - (BOOL)windowShouldClose: (id)sender
6365   NSEvent *e =[[self window] currentEvent];
6367   NSTRACE ("[EmacsView windowShouldClose:]");
6368   windowClosing = YES;
6369   if (!emacs_event)
6370     return NO;
6371   emacs_event->kind = DELETE_WINDOW_EVENT;
6372   emacs_event->modifiers = 0;
6373   emacs_event->code = 0;
6374   EV_TRAILER (e);
6375   /* Don't close this window, let this be done from lisp code.  */
6376   return NO;
6379 - (void) updateFrameSize: (BOOL) delay;
6381   NSWindow *window = [self window];
6382   NSRect wr = [window frame];
6383   int extra = 0;
6384   int oldc = cols, oldr = rows;
6385   int oldw = FRAME_PIXEL_WIDTH (emacsframe);
6386   int oldh = FRAME_PIXEL_HEIGHT (emacsframe);
6387   int neww, newh;
6389   NSTRACE ("[EmacsView updateFrameSize:]");
6390   NSTRACE_SIZE ("Original size", NSMakeSize (oldw, oldh));
6391   NSTRACE_RECT ("Original frame", wr);
6392   NSTRACE_MSG  ("Original columns: %d", cols);
6393   NSTRACE_MSG  ("Original rows: %d", rows);
6395   if (! [self isFullscreen])
6396     {
6397 #ifdef NS_IMPL_GNUSTEP
6398       // GNUstep does not always update the tool bar height.  Force it.
6399       if (toolbar && [toolbar isVisible])
6400           update_frame_tool_bar (emacsframe);
6401 #endif
6403       extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
6404         + FRAME_TOOLBAR_HEIGHT (emacsframe);
6405     }
6407   if (wait_for_tool_bar)
6408     {
6409       if (FRAME_TOOLBAR_HEIGHT (emacsframe) == 0)
6410         {
6411           NSTRACE_MSG ("Waiting for toolbar");
6412           return;
6413         }
6414       wait_for_tool_bar = NO;
6415     }
6417   neww = (int)wr.size.width - emacsframe->border_width;
6418   newh = (int)wr.size.height - extra;
6420   NSTRACE_SIZE ("New size", NSMakeSize (neww, newh));
6421   NSTRACE_MSG ("tool_bar_height: %d", emacsframe->tool_bar_height);
6423   cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, neww);
6424   rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe, newh);
6426   if (cols < MINWIDTH)
6427     cols = MINWIDTH;
6429   if (rows < MINHEIGHT)
6430     rows = MINHEIGHT;
6432   NSTRACE_MSG ("New columns: %d", cols);
6433   NSTRACE_MSG ("New rows: %d", rows);
6435   if (oldr != rows || oldc != cols || neww != oldw || newh != oldh)
6436     {
6437       NSView *view = FRAME_NS_VIEW (emacsframe);
6439       change_frame_size (emacsframe,
6440                          FRAME_PIXEL_TO_TEXT_WIDTH (emacsframe, neww),
6441                          FRAME_PIXEL_TO_TEXT_HEIGHT (emacsframe, newh),
6442                          0, delay, 0, 1);
6443       SET_FRAME_GARBAGED (emacsframe);
6444       cancel_mouse_face (emacsframe);
6446       wr = NSMakeRect (0, 0, neww, newh);
6448       [view setFrame: wr];
6450       // to do: consider using [NSNotificationCenter postNotificationName:].
6451       [self windowDidMove: // Update top/left.
6452               [NSNotification notificationWithName:NSWindowDidMoveNotification
6453                                             object:[view window]]];
6454     }
6455   else
6456     {
6457       NSTRACE_MSG ("No change");
6458     }
6461 - (NSSize)windowWillResize: (NSWindow *)sender toSize: (NSSize)frameSize
6462 /* normalize frame to gridded text size */
6464   int extra = 0;
6466   NSTRACE ("[EmacsView windowWillResize:toSize: " NSTRACE_FMT_SIZE "]",
6467            NSTRACE_ARG_SIZE (frameSize));
6468   NSTRACE_RECT   ("[sender frame]", [sender frame]);
6469   NSTRACE_FSTYPE ("fs_state", fs_state);
6471   if (fs_state == FULLSCREEN_MAXIMIZED
6472       && (maximized_width != (int)frameSize.width
6473           || maximized_height != (int)frameSize.height))
6474     [self setFSValue: FULLSCREEN_NONE];
6475   else if (fs_state == FULLSCREEN_WIDTH
6476            && maximized_width != (int)frameSize.width)
6477     [self setFSValue: FULLSCREEN_NONE];
6478   else if (fs_state == FULLSCREEN_HEIGHT
6479            && maximized_height != (int)frameSize.height)
6480     [self setFSValue: FULLSCREEN_NONE];
6482   if (fs_state == FULLSCREEN_NONE)
6483     maximized_width = maximized_height = -1;
6485   if (! [self isFullscreen])
6486     {
6487       extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
6488         + FRAME_TOOLBAR_HEIGHT (emacsframe);
6489     }
6491   cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, frameSize.width);
6492   if (cols < MINWIDTH)
6493     cols = MINWIDTH;
6495   rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe,
6496                                            frameSize.height - extra);
6497   if (rows < MINHEIGHT)
6498     rows = MINHEIGHT;
6499 #ifdef NS_IMPL_COCOA
6500   {
6501     /* this sets window title to have size in it; the wm does this under GS */
6502     NSRect r = [[self window] frame];
6503     if (r.size.height == frameSize.height && r.size.width == frameSize.width)
6504       {
6505         if (old_title != 0)
6506           {
6507             xfree (old_title);
6508             old_title = 0;
6509           }
6510       }
6511     else if (fs_state == FULLSCREEN_NONE && ! maximizing_resize)
6512       {
6513         char *size_title;
6514         NSWindow *window = [self window];
6515         if (old_title == 0)
6516           {
6517             char *t = strdup ([[[self window] title] UTF8String]);
6518             char *pos = strstr (t, "  â€”  ");
6519             if (pos)
6520               *pos = '\0';
6521             old_title = t;
6522           }
6523         size_title = xmalloc (strlen (old_title) + 40);
6524         esprintf (size_title, "%s  â€”  (%d x %d)", old_title, cols, rows);
6525         [window setTitle: [NSString stringWithUTF8String: size_title]];
6526         [window display];
6527         xfree (size_title);
6528       }
6529   }
6530 #endif /* NS_IMPL_COCOA */
6532   NSTRACE_MSG ("cols: %d  rows: %d", cols, rows);
6534   /* Restrict the new size to the text gird.
6536      Don't restrict the width if the user only adjusted the height, and
6537      vice versa.  (Without this, the frame would shrink, and move
6538      slightly, if the window was resized by dragging one of its
6539      borders.) */
6540   if (!frame_resize_pixelwise)
6541     {
6542       NSRect r = [[self window] frame];
6544       if (r.size.width != frameSize.width)
6545         {
6546           frameSize.width =
6547             FRAME_TEXT_COLS_TO_PIXEL_WIDTH  (emacsframe, cols);
6548         }
6550       if (r.size.height != frameSize.height)
6551         {
6552           frameSize.height =
6553             FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (emacsframe, rows) + extra;
6554         }
6555     }
6557   NSTRACE_RETURN_SIZE (frameSize);
6559   return frameSize;
6563 - (void)windowDidResize: (NSNotification *)notification
6565   NSTRACE ("[EmacsView windowDidResize:]");
6566   if (!FRAME_LIVE_P (emacsframe))
6567     {
6568       NSTRACE_MSG ("Ignored (frame dead)");
6569       return;
6570     }
6571   if (emacsframe->output_data.ns->in_animation)
6572     {
6573       NSTRACE_MSG ("Ignored (in animation)");
6574       return;
6575     }
6577   if (! [self fsIsNative])
6578     {
6579       NSWindow *theWindow = [notification object];
6580       /* We can get notification on the non-FS window when in
6581          fullscreen mode.  */
6582       if ([self window] != theWindow) return;
6583     }
6585   NSTRACE_RECT ("frame", [[notification object] frame]);
6587 #ifdef NS_IMPL_GNUSTEP
6588   NSWindow *theWindow = [notification object];
6590    /* In GNUstep, at least currently, it's possible to get a didResize
6591       without getting a willResize.. therefore we need to act as if we got
6592       the willResize now */
6593   NSSize sz = [theWindow frame].size;
6594   sz = [self windowWillResize: theWindow toSize: sz];
6595 #endif /* NS_IMPL_GNUSTEP */
6597   if (cols > 0 && rows > 0)
6598     {
6599       [self updateFrameSize: YES];
6600     }
6602   ns_send_appdefined (-1);
6605 #ifdef NS_IMPL_COCOA
6606 - (void)viewDidEndLiveResize
6608   NSTRACE ("[EmacsView viewDidEndLiveResize]");
6610   [super viewDidEndLiveResize];
6611   if (old_title != 0)
6612     {
6613       [[self window] setTitle: [NSString stringWithUTF8String: old_title]];
6614       xfree (old_title);
6615       old_title = 0;
6616     }
6617   maximizing_resize = NO;
6619 #endif /* NS_IMPL_COCOA */
6622 - (void)windowDidBecomeKey: (NSNotification *)notification
6623 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
6625   [self windowDidBecomeKey];
6629 - (void)windowDidBecomeKey      /* for direct calls */
6631   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6632   struct frame *old_focus = dpyinfo->x_focus_frame;
6634   NSTRACE ("[EmacsView windowDidBecomeKey]");
6636   if (emacsframe != old_focus)
6637     dpyinfo->x_focus_frame = emacsframe;
6639   ns_frame_rehighlight (emacsframe);
6641   if (emacs_event)
6642     {
6643       emacs_event->kind = FOCUS_IN_EVENT;
6644       EV_TRAILER ((id)nil);
6645     }
6649 - (void)windowDidResignKey: (NSNotification *)notification
6650 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
6652   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6653   BOOL is_focus_frame = dpyinfo->x_focus_frame == emacsframe;
6654   NSTRACE ("[EmacsView windowDidResignKey:]");
6656   if (is_focus_frame)
6657     dpyinfo->x_focus_frame = 0;
6659   emacsframe->mouse_moved = 0;
6660   ns_frame_rehighlight (emacsframe);
6662   /* FIXME: for some reason needed on second and subsequent clicks away
6663             from sole-frame Emacs to get hollow box to show */
6664   if (!windowClosing && [[self window] isVisible] == YES)
6665     {
6666       x_update_cursor (emacsframe, 1);
6667       x_set_frame_alpha (emacsframe);
6668     }
6670   if (any_help_event_p)
6671     {
6672       Lisp_Object frame;
6673       XSETFRAME (frame, emacsframe);
6674       help_echo_string = Qnil;
6675       gen_help_event (Qnil, frame, Qnil, Qnil, 0);
6676     }
6678   if (emacs_event && is_focus_frame)
6679     {
6680       [self deleteWorkingText];
6681       emacs_event->kind = FOCUS_OUT_EVENT;
6682       EV_TRAILER ((id)nil);
6683     }
6687 - (void)windowWillMiniaturize: sender
6689   NSTRACE ("[EmacsView windowWillMiniaturize:]");
6693 - (void)setFrame:(NSRect)frameRect;
6695   NSTRACE ("[EmacsView setFrame:" NSTRACE_FMT_RECT "]",
6696            NSTRACE_ARG_RECT (frameRect));
6698   [super setFrame:(NSRect)frameRect];
6702 - (BOOL)isFlipped
6704   return YES;
6708 - (BOOL)isOpaque
6710   return NO;
6714 - initFrameFromEmacs: (struct frame *)f
6716   NSRect r, wr;
6717   Lisp_Object tem;
6718   NSWindow *win;
6719   NSColor *col;
6720   NSString *name;
6722   NSTRACE ("[EmacsView initFrameFromEmacs:]");
6723   NSTRACE_MSG ("cols:%d lines:%d", f->text_cols, f->text_lines);
6725   windowClosing = NO;
6726   processingCompose = NO;
6727   scrollbarsNeedingUpdate = 0;
6728   fs_state = FULLSCREEN_NONE;
6729   fs_before_fs = next_maximized = -1;
6730 #ifdef HAVE_NATIVE_FS
6731   fs_is_native = ns_use_native_fullscreen;
6732 #else
6733   fs_is_native = NO;
6734 #endif
6735   maximized_width = maximized_height = -1;
6736   nonfs_window = nil;
6738   ns_userRect = NSMakeRect (0, 0, 0, 0);
6739   r = NSMakeRect (0, 0, FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, f->text_cols),
6740                  FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, f->text_lines));
6741   [self initWithFrame: r];
6742   [self setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
6744   FRAME_NS_VIEW (f) = self;
6745   emacsframe = f;
6746 #ifdef NS_IMPL_COCOA
6747   old_title = 0;
6748   maximizing_resize = NO;
6749 #endif
6751   win = [[EmacsWindow alloc]
6752             initWithContentRect: r
6753                       styleMask: (NSWindowStyleMaskResizable |
6754 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
6755                                   NSWindowStyleMaskTitled |
6756 #endif
6757                                   NSWindowStyleMaskMiniaturizable |
6758                                   NSWindowStyleMaskClosable)
6759                         backing: NSBackingStoreBuffered
6760                           defer: YES];
6762 #ifdef HAVE_NATIVE_FS
6763     [win setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
6764 #endif
6766   wr = [win frame];
6767   bwidth = f->border_width = wr.size.width - r.size.width;
6768   tibar_height = FRAME_NS_TITLEBAR_HEIGHT (f) = wr.size.height - r.size.height;
6770   [win setAcceptsMouseMovedEvents: YES];
6771   [win setDelegate: self];
6772 #if !defined (NS_IMPL_COCOA) || \
6773   MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_9
6774   [win useOptimizedDrawing: YES];
6775 #endif
6777   [[win contentView] addSubview: self];
6779   if (ns_drag_types)
6780     [self registerForDraggedTypes: ns_drag_types];
6782   tem = f->name;
6783   name = [NSString stringWithUTF8String:
6784                    NILP (tem) ? "Emacs" : SSDATA (tem)];
6785   [win setTitle: name];
6787   /* toolbar support */
6788   toolbar = [[EmacsToolbar alloc] initForView: self withIdentifier:
6789                          [NSString stringWithFormat: @"Emacs Frame %d",
6790                                    ns_window_num]];
6791   [win setToolbar: toolbar];
6792   [toolbar setVisible: NO];
6794   /* Don't set frame garbaged until tool bar is up to date?
6795      This avoids an extra clear and redraw (flicker) at frame creation.  */
6796   if (FRAME_EXTERNAL_TOOL_BAR (f)) wait_for_tool_bar = YES;
6797   else wait_for_tool_bar = NO;
6800 #ifdef NS_IMPL_COCOA
6801   {
6802     NSButton *toggleButton;
6803   toggleButton = [win standardWindowButton: NSWindowToolbarButton];
6804   [toggleButton setTarget: self];
6805   [toggleButton setAction: @selector (toggleToolbar: )];
6806   }
6807 #endif
6808   FRAME_TOOLBAR_HEIGHT (f) = 0;
6810   tem = f->icon_name;
6811   if (!NILP (tem))
6812     [win setMiniwindowTitle:
6813            [NSString stringWithUTF8String: SSDATA (tem)]];
6815   {
6816     NSScreen *screen = [win screen];
6818     if (screen != 0)
6819       {
6820         NSPoint pt = NSMakePoint
6821           (IN_BOUND (-SCREENMAX, f->left_pos, SCREENMAX),
6822            IN_BOUND (-SCREENMAX,
6823                      [screen frame].size.height - NS_TOP_POS (f), SCREENMAX));
6825         [win setFrameTopLeftPoint: pt];
6827         NSTRACE_RECT ("new frame", [win frame]);
6828       }
6829   }
6831   [win makeFirstResponder: self];
6833   col = ns_lookup_indexed_color (NS_FACE_BACKGROUND
6834                                  (FACE_FROM_ID (emacsframe, DEFAULT_FACE_ID)),
6835                                  emacsframe);
6836   [win setBackgroundColor: col];
6837   if ([col alphaComponent] != (EmacsCGFloat) 1.0)
6838     [win setOpaque: NO];
6840 #if !defined (NS_IMPL_COCOA) || \
6841   MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_9
6842   [self allocateGState];
6843 #endif
6844   [NSApp registerServicesMenuSendTypes: ns_send_types
6845                            returnTypes: nil];
6847   ns_window_num++;
6848   return self;
6852 - (void)windowDidMove: sender
6854   NSWindow *win = [self window];
6855   NSRect r = [win frame];
6856   NSArray *screens = [NSScreen screens];
6857   NSScreen *screen = [screens objectAtIndex: 0];
6859   NSTRACE ("[EmacsView windowDidMove:]");
6861   if (!emacsframe->output_data.ns)
6862     return;
6863   if (screen != nil)
6864     {
6865       emacsframe->left_pos = r.origin.x;
6866       emacsframe->top_pos =
6867         [screen frame].size.height - (r.origin.y + r.size.height);
6868     }
6872 /* Called AFTER method below, but before our windowWillResize call there leads
6873    to windowDidResize -> x_set_window_size.  Update emacs' notion of frame
6874    location so set_window_size moves the frame. */
6875 - (BOOL)windowShouldZoom: (NSWindow *)sender toFrame: (NSRect)newFrame
6877   NSTRACE (("[EmacsView windowShouldZoom:toFrame:" NSTRACE_FMT_RECT "]"
6878             NSTRACE_FMT_RETURN "YES"),
6879            NSTRACE_ARG_RECT (newFrame));
6881   emacsframe->output_data.ns->zooming = 1;
6882   return YES;
6886 /* Override to do something slightly nonstandard, but nice.  First click on
6887    zoom button will zoom vertically.  Second will zoom completely.  Third
6888    returns to original. */
6889 - (NSRect)windowWillUseStandardFrame:(NSWindow *)sender
6890                         defaultFrame:(NSRect)defaultFrame
6892   // TODO: Rename to "currentFrame" and assign "result" properly in
6893   // all paths.
6894   NSRect result = [sender frame];
6896   NSTRACE (("[EmacsView windowWillUseStandardFrame:defaultFrame:"
6897             NSTRACE_FMT_RECT "]"),
6898            NSTRACE_ARG_RECT (defaultFrame));
6899   NSTRACE_FSTYPE ("fs_state", fs_state);
6900   NSTRACE_FSTYPE ("fs_before_fs", fs_before_fs);
6901   NSTRACE_FSTYPE ("next_maximized", next_maximized);
6902   NSTRACE_RECT   ("ns_userRect", ns_userRect);
6903   NSTRACE_RECT   ("[sender frame]", [sender frame]);
6905   if (fs_before_fs != -1) /* Entering fullscreen */
6906     {
6907       NSTRACE_MSG ("Entering fullscreen");
6908       result = defaultFrame;
6909     }
6910   else
6911     {
6912       // Save the window size and position (frame) before the resize.
6913       if (fs_state != FULLSCREEN_MAXIMIZED
6914           && fs_state != FULLSCREEN_WIDTH)
6915         {
6916           ns_userRect.size.width = result.size.width;
6917           ns_userRect.origin.x   = result.origin.x;
6918         }
6920       if (fs_state != FULLSCREEN_MAXIMIZED
6921           && fs_state != FULLSCREEN_HEIGHT)
6922         {
6923           ns_userRect.size.height = result.size.height;
6924           ns_userRect.origin.y    = result.origin.y;
6925         }
6927       NSTRACE_RECT ("ns_userRect (2)", ns_userRect);
6929       if (next_maximized == FULLSCREEN_HEIGHT
6930           || (next_maximized == -1
6931               && abs ((int)(defaultFrame.size.height - result.size.height))
6932               > FRAME_LINE_HEIGHT (emacsframe)))
6933         {
6934           /* first click */
6935           NSTRACE_MSG ("FULLSCREEN_HEIGHT");
6936           maximized_height = result.size.height = defaultFrame.size.height;
6937           maximized_width = -1;
6938           result.origin.y = defaultFrame.origin.y;
6939           if (ns_userRect.size.height != 0)
6940             {
6941               result.origin.x = ns_userRect.origin.x;
6942               result.size.width = ns_userRect.size.width;
6943             }
6944           [self setFSValue: FULLSCREEN_HEIGHT];
6945 #ifdef NS_IMPL_COCOA
6946           maximizing_resize = YES;
6947 #endif
6948         }
6949       else if (next_maximized == FULLSCREEN_WIDTH)
6950         {
6951           NSTRACE_MSG ("FULLSCREEN_WIDTH");
6952           maximized_width = result.size.width = defaultFrame.size.width;
6953           maximized_height = -1;
6954           result.origin.x = defaultFrame.origin.x;
6955           if (ns_userRect.size.width != 0)
6956             {
6957               result.origin.y = ns_userRect.origin.y;
6958               result.size.height = ns_userRect.size.height;
6959             }
6960           [self setFSValue: FULLSCREEN_WIDTH];
6961         }
6962       else if (next_maximized == FULLSCREEN_MAXIMIZED
6963                || (next_maximized == -1
6964                    && abs ((int)(defaultFrame.size.width - result.size.width))
6965                    > FRAME_COLUMN_WIDTH (emacsframe)))
6966         {
6967           NSTRACE_MSG ("FULLSCREEN_MAXIMIZED");
6969           result = defaultFrame;  /* second click */
6970           maximized_width = result.size.width;
6971           maximized_height = result.size.height;
6972           [self setFSValue: FULLSCREEN_MAXIMIZED];
6973 #ifdef NS_IMPL_COCOA
6974           maximizing_resize = YES;
6975 #endif
6976         }
6977       else
6978         {
6979           /* restore */
6980           NSTRACE_MSG ("Restore");
6981           result = ns_userRect.size.height ? ns_userRect : result;
6982           NSTRACE_RECT ("restore (2)", result);
6983           ns_userRect = NSMakeRect (0, 0, 0, 0);
6984 #ifdef NS_IMPL_COCOA
6985           maximizing_resize = fs_state != FULLSCREEN_NONE;
6986 #endif
6987           [self setFSValue: FULLSCREEN_NONE];
6988           maximized_width = maximized_height = -1;
6989         }
6990     }
6992   if (fs_before_fs == -1) next_maximized = -1;
6994   NSTRACE_RECT   ("Final ns_userRect", ns_userRect);
6995   NSTRACE_MSG    ("Final maximized_width: %d", maximized_width);
6996   NSTRACE_MSG    ("Final maximized_height: %d", maximized_height);
6997   NSTRACE_FSTYPE ("Final next_maximized", next_maximized);
6999   [self windowWillResize: sender toSize: result.size];
7001   NSTRACE_RETURN_RECT (result);
7003   return result;
7007 - (void)windowDidDeminiaturize: sender
7009   NSTRACE ("[EmacsView windowDidDeminiaturize:]");
7010   if (!emacsframe->output_data.ns)
7011     return;
7013   SET_FRAME_ICONIFIED (emacsframe, 0);
7014   SET_FRAME_VISIBLE (emacsframe, 1);
7015   windows_or_buffers_changed = 63;
7017   if (emacs_event)
7018     {
7019       emacs_event->kind = DEICONIFY_EVENT;
7020       EV_TRAILER ((id)nil);
7021     }
7025 - (void)windowDidExpose: sender
7027   NSTRACE ("[EmacsView windowDidExpose:]");
7028   if (!emacsframe->output_data.ns)
7029     return;
7031   SET_FRAME_VISIBLE (emacsframe, 1);
7032   SET_FRAME_GARBAGED (emacsframe);
7034   if (send_appdefined)
7035     ns_send_appdefined (-1);
7039 - (void)windowDidMiniaturize: sender
7041   NSTRACE ("[EmacsView windowDidMiniaturize:]");
7042   if (!emacsframe->output_data.ns)
7043     return;
7045   SET_FRAME_ICONIFIED (emacsframe, 1);
7046   SET_FRAME_VISIBLE (emacsframe, 0);
7048   if (emacs_event)
7049     {
7050       emacs_event->kind = ICONIFY_EVENT;
7051       EV_TRAILER ((id)nil);
7052     }
7055 #ifdef HAVE_NATIVE_FS
7056 - (NSApplicationPresentationOptions)window:(NSWindow *)window
7057       willUseFullScreenPresentationOptions:
7058   (NSApplicationPresentationOptions)proposedOptions
7060   return proposedOptions|NSApplicationPresentationAutoHideToolbar;
7062 #endif
7064 - (void)windowWillEnterFullScreen:(NSNotification *)notification
7066   NSTRACE ("[EmacsView windowWillEnterFullScreen:]");
7067   [self windowWillEnterFullScreen];
7069 - (void)windowWillEnterFullScreen /* provided for direct calls */
7071   NSTRACE ("[EmacsView windowWillEnterFullScreen]");
7072   fs_before_fs = fs_state;
7075 - (void)windowDidEnterFullScreen:(NSNotification *)notification
7077   NSTRACE ("[EmacsView windowDidEnterFullScreen:]");
7078   [self windowDidEnterFullScreen];
7081 - (void)windowDidEnterFullScreen /* provided for direct calls */
7083   NSTRACE ("[EmacsView windowDidEnterFullScreen]");
7084   [self setFSValue: FULLSCREEN_BOTH];
7085   if (! [self fsIsNative])
7086     {
7087       [self windowDidBecomeKey];
7088       [nonfs_window orderOut:self];
7089     }
7090   else
7091     {
7092       BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (emacsframe) ? YES : NO;
7093 #ifdef NS_IMPL_COCOA
7094 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
7095       unsigned val = (unsigned)[NSApp presentationOptions];
7097       // Mac OS X 10.7 bug fix, the menu won't appear without this.
7098       // val is non-zero on other macOS versions.
7099       if (val == 0)
7100         {
7101           NSApplicationPresentationOptions options
7102             = NSApplicationPresentationAutoHideDock
7103             | NSApplicationPresentationAutoHideMenuBar
7104             | NSApplicationPresentationFullScreen
7105             | NSApplicationPresentationAutoHideToolbar;
7107           [NSApp setPresentationOptions: options];
7108         }
7109 #endif
7110 #endif
7111       [toolbar setVisible:tbar_visible];
7112     }
7115 - (void)windowWillExitFullScreen:(NSNotification *)notification
7117   NSTRACE ("[EmacsView windowWillExitFullScreen:]");
7118   [self windowWillExitFullScreen];
7121 - (void)windowWillExitFullScreen /* provided for direct calls */
7123   NSTRACE ("[EmacsView windowWillExitFullScreen]");
7124   if (!FRAME_LIVE_P (emacsframe))
7125     {
7126       NSTRACE_MSG ("Ignored (frame dead)");
7127       return;
7128     }
7129   if (next_maximized != -1)
7130     fs_before_fs = next_maximized;
7133 - (void)windowDidExitFullScreen:(NSNotification *)notification
7135   NSTRACE ("[EmacsView windowDidExitFullScreen:]");
7136   [self windowDidExitFullScreen];
7139 - (void)windowDidExitFullScreen /* provided for direct calls */
7141   NSTRACE ("[EmacsView windowDidExitFullScreen]");
7142   if (!FRAME_LIVE_P (emacsframe))
7143     {
7144       NSTRACE_MSG ("Ignored (frame dead)");
7145       return;
7146     }
7147   [self setFSValue: fs_before_fs];
7148   fs_before_fs = -1;
7149 #ifdef HAVE_NATIVE_FS
7150   [self updateCollectionBehavior];
7151 #endif
7152   if (FRAME_EXTERNAL_TOOL_BAR (emacsframe))
7153     {
7154       [toolbar setVisible:YES];
7155       update_frame_tool_bar (emacsframe);
7156       [self updateFrameSize:YES];
7157       [[self window] display];
7158     }
7159   else
7160     [toolbar setVisible:NO];
7162   if (next_maximized != -1)
7163     [[self window] performZoom:self];
7166 - (BOOL)fsIsNative
7168   return fs_is_native;
7171 - (BOOL)isFullscreen
7173   BOOL res;
7175   if (! fs_is_native)
7176     {
7177       res = (nonfs_window != nil);
7178     }
7179   else
7180     {
7181 #ifdef HAVE_NATIVE_FS
7182       res = (([[self window] styleMask] & NSWindowStyleMaskFullScreen) != 0);
7183 #else
7184       res = NO;
7185 #endif
7186     }
7188   NSTRACE ("[EmacsView isFullscreen] " NSTRACE_FMT_RETURN " %d",
7189            (int) res);
7191   return res;
7194 #ifdef HAVE_NATIVE_FS
7195 - (void)updateCollectionBehavior
7197   NSTRACE ("[EmacsView updateCollectionBehavior]");
7199   if (! [self isFullscreen])
7200     {
7201       NSWindow *win = [self window];
7202       NSWindowCollectionBehavior b = [win collectionBehavior];
7203       if (ns_use_native_fullscreen)
7204         b |= NSWindowCollectionBehaviorFullScreenPrimary;
7205       else
7206         b &= ~NSWindowCollectionBehaviorFullScreenPrimary;
7208       [win setCollectionBehavior: b];
7209       fs_is_native = ns_use_native_fullscreen;
7210     }
7212 #endif
7214 - (void)toggleFullScreen: (id)sender
7216   NSWindow *w, *fw;
7217   BOOL onFirstScreen;
7218   struct frame *f;
7219   NSRect r, wr;
7220   NSColor *col;
7222   NSTRACE ("[EmacsView toggleFullScreen:]");
7224   if (fs_is_native)
7225     {
7226 #ifdef HAVE_NATIVE_FS
7227       [[self window] toggleFullScreen:sender];
7228 #endif
7229       return;
7230     }
7232   w = [self window];
7233   onFirstScreen = [[w screen] isEqual:[[NSScreen screens] objectAtIndex:0]];
7234   f = emacsframe;
7235   wr = [w frame];
7236   col = ns_lookup_indexed_color (NS_FACE_BACKGROUND
7237                                  (FACE_FROM_ID (f, DEFAULT_FACE_ID)),
7238                                  f);
7240   if (fs_state != FULLSCREEN_BOTH)
7241     {
7242       NSScreen *screen = [w screen];
7244 #if defined (NS_IMPL_COCOA) && \
7245   MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
7246       /* Hide ghost menu bar on secondary monitor? */
7247       if (! onFirstScreen)
7248         onFirstScreen = [NSScreen screensHaveSeparateSpaces];
7249 #endif
7250       /* Hide dock and menubar if we are on the primary screen.  */
7251       if (onFirstScreen)
7252         {
7253 #ifdef NS_IMPL_COCOA
7254           NSApplicationPresentationOptions options
7255             = NSApplicationPresentationAutoHideDock
7256             | NSApplicationPresentationAutoHideMenuBar;
7258           [NSApp setPresentationOptions: options];
7259 #else
7260           [NSMenu setMenuBarVisible:NO];
7261 #endif
7262         }
7264       fw = [[EmacsFSWindow alloc]
7265                        initWithContentRect:[w contentRectForFrameRect:wr]
7266                                  styleMask:NSWindowStyleMaskBorderless
7267                                    backing:NSBackingStoreBuffered
7268                                      defer:YES
7269                                     screen:screen];
7271       [fw setContentView:[w contentView]];
7272       [fw setTitle:[w title]];
7273       [fw setDelegate:self];
7274       [fw setAcceptsMouseMovedEvents: YES];
7275 #if !defined (NS_IMPL_COCOA) || \
7276   MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_9
7277       [fw useOptimizedDrawing: YES];
7278 #endif
7279       [fw setBackgroundColor: col];
7280       if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7281         [fw setOpaque: NO];
7283       f->border_width = 0;
7284       FRAME_NS_TITLEBAR_HEIGHT (f) = 0;
7285       tobar_height = FRAME_TOOLBAR_HEIGHT (f);
7286       FRAME_TOOLBAR_HEIGHT (f) = 0;
7288       nonfs_window = w;
7290       [self windowWillEnterFullScreen];
7291       [fw makeKeyAndOrderFront:NSApp];
7292       [fw makeFirstResponder:self];
7293       [w orderOut:self];
7294       r = [fw frameRectForContentRect:[screen frame]];
7295       [fw setFrame: r display:YES animate:ns_use_fullscreen_animation];
7296       [self windowDidEnterFullScreen];
7297       [fw display];
7298     }
7299   else
7300     {
7301       fw = w;
7302       w = nonfs_window;
7303       nonfs_window = nil;
7305       if (onFirstScreen)
7306         {
7307 #ifdef NS_IMPL_COCOA
7308           [NSApp setPresentationOptions: NSApplicationPresentationDefault];
7309 #else
7310           [NSMenu setMenuBarVisible:YES];
7311 #endif
7312         }
7314       [w setContentView:[fw contentView]];
7315       [w setBackgroundColor: col];
7316       if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7317         [w setOpaque: NO];
7319       f->border_width = bwidth;
7320       FRAME_NS_TITLEBAR_HEIGHT (f) = tibar_height;
7321       if (FRAME_EXTERNAL_TOOL_BAR (f))
7322         FRAME_TOOLBAR_HEIGHT (f) = tobar_height;
7324       // to do: consider using [NSNotificationCenter postNotificationName:] to send notifications.
7326       [self windowWillExitFullScreen];
7327       [fw setFrame: [w frame] display:YES animate:ns_use_fullscreen_animation];
7328       [fw close];
7329       [w makeKeyAndOrderFront:NSApp];
7330       [self windowDidExitFullScreen];
7331       [self updateFrameSize:YES];
7332     }
7335 - (void)handleFS
7337   NSTRACE ("[EmacsView handleFS]");
7339   if (fs_state != emacsframe->want_fullscreen)
7340     {
7341       if (fs_state == FULLSCREEN_BOTH)
7342         {
7343           NSTRACE_MSG ("fs_state == FULLSCREEN_BOTH");
7344           [self toggleFullScreen:self];
7345         }
7347       switch (emacsframe->want_fullscreen)
7348         {
7349         case FULLSCREEN_BOTH:
7350           NSTRACE_MSG ("FULLSCREEN_BOTH");
7351           [self toggleFullScreen:self];
7352           break;
7353         case FULLSCREEN_WIDTH:
7354           NSTRACE_MSG ("FULLSCREEN_WIDTH");
7355           next_maximized = FULLSCREEN_WIDTH;
7356           if (fs_state != FULLSCREEN_BOTH)
7357             [[self window] performZoom:self];
7358           break;
7359         case FULLSCREEN_HEIGHT:
7360           NSTRACE_MSG ("FULLSCREEN_HEIGHT");
7361           next_maximized = FULLSCREEN_HEIGHT;
7362           if (fs_state != FULLSCREEN_BOTH)
7363             [[self window] performZoom:self];
7364           break;
7365         case FULLSCREEN_MAXIMIZED:
7366           NSTRACE_MSG ("FULLSCREEN_MAXIMIZED");
7367           next_maximized = FULLSCREEN_MAXIMIZED;
7368           if (fs_state != FULLSCREEN_BOTH)
7369             [[self window] performZoom:self];
7370           break;
7371         case FULLSCREEN_NONE:
7372           NSTRACE_MSG ("FULLSCREEN_NONE");
7373           if (fs_state != FULLSCREEN_BOTH)
7374             {
7375               next_maximized = FULLSCREEN_NONE;
7376               [[self window] performZoom:self];
7377             }
7378           break;
7379         }
7381       emacsframe->want_fullscreen = FULLSCREEN_NONE;
7382     }
7386 - (void) setFSValue: (int)value
7388   NSTRACE ("[EmacsView setFSValue:" NSTRACE_FMT_FSTYPE "]",
7389            NSTRACE_ARG_FSTYPE(value));
7391   Lisp_Object lval = Qnil;
7392   switch (value)
7393     {
7394     case FULLSCREEN_BOTH:
7395       lval = Qfullboth;
7396       break;
7397     case FULLSCREEN_WIDTH:
7398       lval = Qfullwidth;
7399       break;
7400     case FULLSCREEN_HEIGHT:
7401       lval = Qfullheight;
7402       break;
7403     case FULLSCREEN_MAXIMIZED:
7404       lval = Qmaximized;
7405       break;
7406     }
7407   store_frame_param (emacsframe, Qfullscreen, lval);
7408   fs_state = value;
7411 - (void)mouseEntered: (NSEvent *)theEvent
7413   NSTRACE ("[EmacsView mouseEntered:]");
7414   if (emacsframe)
7415     FRAME_DISPLAY_INFO (emacsframe)->last_mouse_movement_time
7416       = EV_TIMESTAMP (theEvent);
7420 - (void)mouseExited: (NSEvent *)theEvent
7422   Mouse_HLInfo *hlinfo = emacsframe ? MOUSE_HL_INFO (emacsframe) : NULL;
7424   NSTRACE ("[EmacsView mouseExited:]");
7426   if (!hlinfo)
7427     return;
7429   FRAME_DISPLAY_INFO (emacsframe)->last_mouse_movement_time
7430     = EV_TIMESTAMP (theEvent);
7432   if (emacsframe == hlinfo->mouse_face_mouse_frame)
7433     {
7434       clear_mouse_face (hlinfo);
7435       hlinfo->mouse_face_mouse_frame = 0;
7436     }
7440 - menuDown: sender
7442   NSTRACE ("[EmacsView menuDown:]");
7443   if (context_menu_value == -1)
7444     context_menu_value = [sender tag];
7445   else
7446     {
7447       NSInteger tag = [sender tag];
7448       find_and_call_menu_selection (emacsframe, emacsframe->menu_bar_items_used,
7449                                     emacsframe->menu_bar_vector,
7450                                     (void *)tag);
7451     }
7453   ns_send_appdefined (-1);
7454   return self;
7458 - (EmacsToolbar *)toolbar
7460   return toolbar;
7464 /* this gets called on toolbar button click */
7465 - toolbarClicked: (id)item
7467   NSEvent *theEvent;
7468   int idx = [item tag] * TOOL_BAR_ITEM_NSLOTS;
7470   NSTRACE ("[EmacsView toolbarClicked:]");
7472   if (!emacs_event)
7473     return self;
7475   /* send first event (for some reason two needed) */
7476   theEvent = [[self window] currentEvent];
7477   emacs_event->kind = TOOL_BAR_EVENT;
7478   XSETFRAME (emacs_event->arg, emacsframe);
7479   EV_TRAILER (theEvent);
7481   emacs_event->kind = TOOL_BAR_EVENT;
7482 /*   XSETINT (emacs_event->code, 0); */
7483   emacs_event->arg = AREF (emacsframe->tool_bar_items,
7484                            idx + TOOL_BAR_ITEM_KEY);
7485   emacs_event->modifiers = EV_MODIFIERS (theEvent);
7486   EV_TRAILER (theEvent);
7487   return self;
7491 - toggleToolbar: (id)sender
7493   NSTRACE ("[EmacsView toggleToolbar:]");
7495   if (!emacs_event)
7496     return self;
7498   emacs_event->kind = NS_NONKEY_EVENT;
7499   emacs_event->code = KEY_NS_TOGGLE_TOOLBAR;
7500   EV_TRAILER ((id)nil);
7501   return self;
7505 - (void)drawRect: (NSRect)rect
7507   int x = NSMinX (rect), y = NSMinY (rect);
7508   int width = NSWidth (rect), height = NSHeight (rect);
7510   NSTRACE ("[EmacsView drawRect:" NSTRACE_FMT_RECT "]",
7511            NSTRACE_ARG_RECT(rect));
7513   if (!emacsframe || !emacsframe->output_data.ns)
7514     return;
7516   ns_clear_frame_area (emacsframe, x, y, width, height);
7517   block_input ();
7518   expose_frame (emacsframe, x, y, width, height);
7519   unblock_input ();
7521   /*
7522     drawRect: may be called (at least in Mac OS X 10.5) for invisible
7523     views as well for some reason.  Thus, do not infer visibility
7524     here.
7526     emacsframe->async_visible = 1;
7527     emacsframe->async_iconified = 0;
7528   */
7532 /* NSDraggingDestination protocol methods.  Actually this is not really a
7533    protocol, but a category of Object.  O well...  */
7535 -(NSDragOperation) draggingEntered: (id <NSDraggingInfo>) sender
7537   NSTRACE ("[EmacsView draggingEntered:]");
7538   return NSDragOperationGeneric;
7542 -(BOOL)prepareForDragOperation: (id <NSDraggingInfo>) sender
7544   return YES;
7548 -(BOOL)performDragOperation: (id <NSDraggingInfo>) sender
7550   id pb;
7551   int x, y;
7552   NSString *type;
7553   NSEvent *theEvent = [[self window] currentEvent];
7554   NSPoint position;
7555   NSDragOperation op = [sender draggingSourceOperationMask];
7556   int modifiers = 0;
7558   NSTRACE ("[EmacsView performDragOperation:]");
7560   if (!emacs_event)
7561     return NO;
7563   position = [self convertPoint: [sender draggingLocation] fromView: nil];
7564   x = lrint (position.x);  y = lrint (position.y);
7566   pb = [sender draggingPasteboard];
7567   type = [pb availableTypeFromArray: ns_drag_types];
7569   if (! (op & (NSDragOperationMove|NSDragOperationDelete)) &&
7570       // URL drags contain all operations (0xf), don't allow all to be set.
7571       (op & 0xf) != 0xf)
7572     {
7573       if (op & NSDragOperationLink)
7574         modifiers |= NSEventModifierFlagControl;
7575       if (op & NSDragOperationCopy)
7576         modifiers |= NSEventModifierFlagOption;
7577       if (op & NSDragOperationGeneric)
7578         modifiers |= NSEventModifierFlagCommand;
7579     }
7581   modifiers = EV_MODIFIERS2 (modifiers);
7582   if (type == 0)
7583     {
7584       return NO;
7585     }
7586   else if ([type isEqualToString: NSFilenamesPboardType])
7587     {
7588       NSArray *files;
7589       NSEnumerator *fenum;
7590       NSString *file;
7592       if (!(files = [pb propertyListForType: type]))
7593         return NO;
7595       fenum = [files objectEnumerator];
7596       while ( (file = [fenum nextObject]) )
7597         {
7598           emacs_event->kind = DRAG_N_DROP_EVENT;
7599           XSETINT (emacs_event->x, x);
7600           XSETINT (emacs_event->y, y);
7601           ns_input_file = append2 (ns_input_file,
7602                                    build_string ([file UTF8String]));
7603           emacs_event->modifiers = modifiers;
7604           emacs_event->arg =  list2 (Qfile, build_string ([file UTF8String]));
7605           EV_TRAILER (theEvent);
7606         }
7607       return YES;
7608     }
7609   else if ([type isEqualToString: NSURLPboardType])
7610     {
7611       NSURL *url = [NSURL URLFromPasteboard: pb];
7612       if (url == nil) return NO;
7614       emacs_event->kind = DRAG_N_DROP_EVENT;
7615       XSETINT (emacs_event->x, x);
7616       XSETINT (emacs_event->y, y);
7617       emacs_event->modifiers = modifiers;
7618       emacs_event->arg =  list2 (Qurl,
7619                                  build_string ([[url absoluteString]
7620                                                  UTF8String]));
7621       EV_TRAILER (theEvent);
7623       if ([url isFileURL] != NO)
7624         {
7625           NSString *file = [url path];
7626           ns_input_file = append2 (ns_input_file,
7627                                    build_string ([file UTF8String]));
7628         }
7629       return YES;
7630     }
7631   else if ([type isEqualToString: NSStringPboardType]
7632            || [type isEqualToString: NSTabularTextPboardType])
7633     {
7634       NSString *data;
7636       if (! (data = [pb stringForType: type]))
7637         return NO;
7639       emacs_event->kind = DRAG_N_DROP_EVENT;
7640       XSETINT (emacs_event->x, x);
7641       XSETINT (emacs_event->y, y);
7642       emacs_event->modifiers = modifiers;
7643       emacs_event->arg =  list2 (Qnil, build_string ([data UTF8String]));
7644       EV_TRAILER (theEvent);
7645       return YES;
7646     }
7647   else
7648     {
7649       fprintf (stderr, "Invalid data type in dragging pasteboard");
7650       return NO;
7651     }
7655 - (id) validRequestorForSendType: (NSString *)typeSent
7656                       returnType: (NSString *)typeReturned
7658   NSTRACE ("[EmacsView validRequestorForSendType:returnType:]");
7659   if (typeSent != nil && [ns_send_types indexOfObject: typeSent] != NSNotFound
7660       && typeReturned == nil)
7661     {
7662       if (! NILP (ns_get_local_selection (QPRIMARY, QUTF8_STRING)))
7663         return self;
7664     }
7666   return [super validRequestorForSendType: typeSent
7667                                returnType: typeReturned];
7671 /* The next two methods are part of NSServicesRequests informal protocol,
7672    supposedly called when a services menu item is chosen from this app.
7673    But this should not happen because we override the services menu with our
7674    own entries which call ns-perform-service.
7675    Nonetheless, it appeared to happen (under strange circumstances): bug#1435.
7676    So let's at least stub them out until further investigation can be done. */
7678 - (BOOL) readSelectionFromPasteboard: (NSPasteboard *)pb
7680   /* we could call ns_string_from_pasteboard(pboard) here but then it should
7681      be written into the buffer in place of the existing selection..
7682      ordinary service calls go through functions defined in ns-win.el */
7683   return NO;
7686 - (BOOL) writeSelectionToPasteboard: (NSPasteboard *)pb types: (NSArray *)types
7688   NSArray *typesDeclared;
7689   Lisp_Object val;
7691   NSTRACE ("[EmacsView writeSelectionToPasteboard:types:]");
7693   /* We only support NSStringPboardType */
7694   if ([types containsObject:NSStringPboardType] == NO) {
7695     return NO;
7696   }
7698   val = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
7699   if (CONSP (val) && SYMBOLP (XCAR (val)))
7700     {
7701       val = XCDR (val);
7702       if (CONSP (val) && NILP (XCDR (val)))
7703         val = XCAR (val);
7704     }
7705   if (! STRINGP (val))
7706     return NO;
7708   typesDeclared = [NSArray arrayWithObject:NSStringPboardType];
7709   [pb declareTypes:typesDeclared owner:nil];
7710   ns_string_to_pasteboard (pb, val);
7711   return YES;
7715 /* setMini =YES means set from internal (gives a finder icon), NO means set nil
7716    (gives a miniaturized version of the window); currently we use the latter for
7717    frames whose active buffer doesn't correspond to any file
7718    (e.g., '*scratch*') */
7719 - setMiniwindowImage: (BOOL) setMini
7721   id image = [[self window] miniwindowImage];
7722   NSTRACE ("[EmacsView setMiniwindowImage:%d]", setMini);
7724   /* NOTE: under Cocoa miniwindowImage always returns nil, documentation
7725      about "AppleDockIconEnabled" notwithstanding, however the set message
7726      below has its effect nonetheless. */
7727   if (image != emacsframe->output_data.ns->miniimage)
7728     {
7729       if (image && [image isKindOfClass: [EmacsImage class]])
7730         [image release];
7731       [[self window] setMiniwindowImage:
7732                        setMini ? emacsframe->output_data.ns->miniimage : nil];
7733     }
7735   return self;
7739 - (void) setRows: (int) r andColumns: (int) c
7741   NSTRACE ("[EmacsView setRows:%d andColumns:%d]", r, c);
7742   rows = r;
7743   cols = c;
7746 - (int) fullscreenState
7748   return fs_state;
7751 @end  /* EmacsView */
7755 /* ==========================================================================
7757     EmacsWindow implementation
7759    ========================================================================== */
7761 @implementation EmacsWindow
7763 #ifdef NS_IMPL_COCOA
7764 - (id)accessibilityAttributeValue:(NSString *)attribute
7766   Lisp_Object str = Qnil;
7767   struct frame *f = SELECTED_FRAME ();
7768   struct buffer *curbuf = XBUFFER (XWINDOW (f->selected_window)->contents);
7770   NSTRACE ("[EmacsWindow accessibilityAttributeValue:]");
7772   if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
7773     return NSAccessibilityTextFieldRole;
7775   if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]
7776       && curbuf && ! NILP (BVAR (curbuf, mark_active)))
7777     {
7778       str = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
7779     }
7780   else if (curbuf && [attribute isEqualToString:NSAccessibilityValueAttribute])
7781     {
7782       if (! NILP (BVAR (curbuf, mark_active)))
7783           str = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
7785       if (NILP (str))
7786         {
7787           ptrdiff_t start_byte = BUF_BEGV_BYTE (curbuf);
7788           ptrdiff_t byte_range = BUF_ZV_BYTE (curbuf) - start_byte;
7789           ptrdiff_t range = BUF_ZV (curbuf) - BUF_BEGV (curbuf);
7791           if (! NILP (BVAR (curbuf, enable_multibyte_characters)))
7792             str = make_uninit_multibyte_string (range, byte_range);
7793           else
7794             str = make_uninit_string (range);
7795           /* To check: This returns emacs-utf-8, which is a superset of utf-8.
7796              Is this a problem?  */
7797           memcpy (SDATA (str), BYTE_POS_ADDR (start_byte), byte_range);
7798         }
7799     }
7802   if (! NILP (str))
7803     {
7804       if (CONSP (str) && SYMBOLP (XCAR (str)))
7805         {
7806           str = XCDR (str);
7807           if (CONSP (str) && NILP (XCDR (str)))
7808             str = XCAR (str);
7809         }
7810       if (STRINGP (str))
7811         {
7812           const char *utfStr = SSDATA (str);
7813           NSString *nsStr = [NSString stringWithUTF8String: utfStr];
7814           return nsStr;
7815         }
7816     }
7818   return [super accessibilityAttributeValue:attribute];
7820 #endif /* NS_IMPL_COCOA */
7822 /* Constrain size and placement of a frame.
7824    By returning the original "frameRect", the frame is not
7825    constrained. This can lead to unwanted situations where, for
7826    example, the menu bar covers the frame.
7828    The default implementation (accessed using "super") constrains the
7829    frame to the visible area of SCREEN, minus the menu bar (if
7830    present) and the Dock.  Note that default implementation also calls
7831    windowWillResize, with the frame it thinks should have.  (This can
7832    make the frame exit maximized mode.)
7834    Note that this should work in situations where multiple monitors
7835    are present.  Common configurations are side-by-side monitors and a
7836    monitor on top of another (e.g. when a laptop is placed under a
7837    large screen). */
7838 - (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
7840   NSTRACE ("[EmacsWindow constrainFrameRect:" NSTRACE_FMT_RECT " toScreen:]",
7841              NSTRACE_ARG_RECT (frameRect));
7843 #ifdef NS_IMPL_COCOA
7844 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
7845   // If separate spaces is on, it is like each screen is independent.  There is
7846   // no spanning of frames across screens.
7847   if ([NSScreen screensHaveSeparateSpaces])
7848     {
7849       NSTRACE_MSG ("Screens have separate spaces");
7850       frameRect = [super constrainFrameRect:frameRect toScreen:screen];
7851       NSTRACE_RETURN_RECT (frameRect);
7852       return frameRect;
7853     }
7854 #endif
7855 #endif
7857   return constrain_frame_rect(frameRect,
7858                               [(EmacsView *)[self delegate] isFullscreen]);
7862 - (void)performZoom:(id)sender
7864   NSTRACE ("[EmacsWindow performZoom:]");
7866   return [super performZoom:sender];
7869 - (void)zoom:(id)sender
7871   NSTRACE ("[EmacsWindow zoom:]");
7873   ns_update_auto_hide_menu_bar();
7875   // Below are three zoom implementations.  In the final commit, the
7876   // idea is that the last should be included.
7878 #if 0
7879   // Native zoom done using the standard zoom animation.  Size of the
7880   // resulting frame reduced to accommodate the Dock and, if present,
7881   // the menu-bar.
7882   [super zoom:sender];
7884 #elif 0
7885   // Native zoom done using the standard zoom animation, plus an
7886   // explicit resize to cover the full screen, except the menu-bar and
7887   // dock, if present.
7888   [super zoom:sender];
7890   // After the native zoom, resize the resulting frame to fill the
7891   // entire screen, except the menu-bar.
7892   //
7893   // This works for all practical purposes.  (The only minor oddity is
7894   // when transiting from full-height frame to a maximized, the
7895   // animation reduces the height of the frame slightly (to the 4
7896   // pixels needed to accommodate the Doc) before it snaps back into
7897   // full height.  The user would need a very trained eye to spot
7898   // this.)
7899   NSScreen * screen = [self screen];
7900   if (screen != nil)
7901     {
7902       int fs_state = [(EmacsView *)[self delegate] fullscreenState];
7904       NSTRACE_FSTYPE ("fullscreenState", fs_state);
7906       NSRect sr = [screen frame];
7907       struct EmacsMargins margins
7908         = ns_screen_margins_ignoring_hidden_dock(screen);
7910       NSRect wr = [self frame];
7911       NSTRACE_RECT ("Rect after zoom", wr);
7913       NSRect newWr = wr;
7915       if (fs_state == FULLSCREEN_MAXIMIZED
7916           || fs_state == FULLSCREEN_HEIGHT)
7917         {
7918           newWr.origin.y = sr.origin.y + margins.bottom;
7919           newWr.size.height = sr.size.height - margins.top - margins.bottom;
7920         }
7922       if (fs_state == FULLSCREEN_MAXIMIZED
7923           || fs_state == FULLSCREEN_WIDTH)
7924         {
7925           newWr.origin.x = sr.origin.x + margins.left;
7926           newWr.size.width = sr.size.width - margins.right - margins.left;
7927         }
7929       if (newWr.size.width     != wr.size.width
7930           || newWr.size.height != wr.size.height
7931           || newWr.origin.x    != wr.origin.x
7932           || newWr.origin.y    != wr.origin.y)
7933         {
7934           NSTRACE_MSG ("New frame different");
7935           [self setFrame: newWr display: NO];
7936         }
7937     }
7938 #else
7939   // Non-native zoom which is done instantaneously.  The resulting
7940   // frame covers the entire screen, except the menu-bar and dock, if
7941   // present.
7942   NSScreen * screen = [self screen];
7943   if (screen != nil)
7944     {
7945       NSRect sr = [screen frame];
7946       struct EmacsMargins margins
7947         = ns_screen_margins_ignoring_hidden_dock(screen);
7949       sr.size.height -= (margins.top + margins.bottom);
7950       sr.size.width  -= (margins.left + margins.right);
7951       sr.origin.x += margins.left;
7952       sr.origin.y += margins.bottom;
7954       sr = [[self delegate] windowWillUseStandardFrame:self
7955                                           defaultFrame:sr];
7956       [self setFrame: sr display: NO];
7957     }
7958 #endif
7961 - (void)setFrame:(NSRect)windowFrame
7962          display:(BOOL)displayViews
7964   NSTRACE ("[EmacsWindow setFrame:" NSTRACE_FMT_RECT " display:%d]",
7965            NSTRACE_ARG_RECT (windowFrame), displayViews);
7967   [super setFrame:windowFrame display:displayViews];
7970 - (void)setFrame:(NSRect)windowFrame
7971          display:(BOOL)displayViews
7972          animate:(BOOL)performAnimation
7974   NSTRACE ("[EmacsWindow setFrame:" NSTRACE_FMT_RECT
7975            " display:%d performAnimation:%d]",
7976            NSTRACE_ARG_RECT (windowFrame), displayViews, performAnimation);
7978   [super setFrame:windowFrame display:displayViews animate:performAnimation];
7981 - (void)setFrameTopLeftPoint:(NSPoint)point
7983   NSTRACE ("[EmacsWindow setFrameTopLeftPoint:" NSTRACE_FMT_POINT "]",
7984            NSTRACE_ARG_POINT (point));
7986   [super setFrameTopLeftPoint:point];
7988 @end /* EmacsWindow */
7991 @implementation EmacsFSWindow
7993 - (BOOL)canBecomeKeyWindow
7995   return YES;
7998 - (BOOL)canBecomeMainWindow
8000   return YES;
8003 @end
8005 /* ==========================================================================
8007     EmacsScroller implementation
8009    ========================================================================== */
8012 @implementation EmacsScroller
8014 /* for repeat button push */
8015 #define SCROLL_BAR_FIRST_DELAY 0.5
8016 #define SCROLL_BAR_CONTINUOUS_DELAY (1.0 / 15)
8018 + (CGFloat) scrollerWidth
8020   /* TODO: if we want to allow variable widths, this is the place to do it,
8021            however neither GNUstep nor Cocoa support it very well */
8022   CGFloat r;
8023 #if !defined (NS_IMPL_COCOA) || \
8024   MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
8025   r = [NSScroller scrollerWidth];
8026 #else
8027   r = [NSScroller scrollerWidthForControlSize: NSControlSizeRegular
8028                                 scrollerStyle: NSScrollerStyleLegacy];
8029 #endif
8030   return r;
8033 - initFrame: (NSRect )r window: (Lisp_Object)nwin
8035   NSTRACE ("[EmacsScroller initFrame: window:]");
8037   if (r.size.width > r.size.height)
8038       horizontal = YES;
8039   else
8040       horizontal = NO;
8042   [super initWithFrame: r/*NSMakeRect (0, 0, 0, 0)*/];
8043   [self setContinuous: YES];
8044   [self setEnabled: YES];
8046   /* Ensure auto resizing of scrollbars occurs within the emacs frame's view
8047      locked against the top and bottom edges, and right edge on macOS, where
8048      scrollers are on right. */
8049 #ifdef NS_IMPL_GNUSTEP
8050   [self setAutoresizingMask: NSViewMaxXMargin | NSViewHeightSizable];
8051 #else
8052   [self setAutoresizingMask: NSViewMinXMargin | NSViewHeightSizable];
8053 #endif
8055   window = XWINDOW (nwin);
8056   condemned = NO;
8057   if (horizontal)
8058     pixel_length = NSWidth (r);
8059   else
8060     pixel_length = NSHeight (r);
8061   if (pixel_length == 0) pixel_length = 1;
8062   min_portion = 20 / pixel_length;
8064   frame = XFRAME (window->frame);
8065   if (FRAME_LIVE_P (frame))
8066     {
8067       int i;
8068       EmacsView *view = FRAME_NS_VIEW (frame);
8069       NSView *sview = [[view window] contentView];
8070       NSArray *subs = [sview subviews];
8072       /* disable optimization stopping redraw of other scrollbars */
8073       view->scrollbarsNeedingUpdate = 0;
8074       for (i =[subs count]-1; i >= 0; i--)
8075         if ([[subs objectAtIndex: i] isKindOfClass: [EmacsScroller class]])
8076           view->scrollbarsNeedingUpdate++;
8077       [sview addSubview: self];
8078     }
8080 /*  [self setFrame: r]; */
8082   return self;
8086 - (void)setFrame: (NSRect)newRect
8088   NSTRACE ("[EmacsScroller setFrame:]");
8090 /*  block_input (); */
8091   if (horizontal)
8092     pixel_length = NSWidth (newRect);
8093   else
8094     pixel_length = NSHeight (newRect);
8095   if (pixel_length == 0) pixel_length = 1;
8096   min_portion = 20 / pixel_length;
8097   [super setFrame: newRect];
8098 /*  unblock_input (); */
8102 - (void)dealloc
8104   NSTRACE ("[EmacsScroller dealloc]");
8105   if (window)
8106     {
8107       if (horizontal)
8108         wset_horizontal_scroll_bar (window, Qnil);
8109       else
8110         wset_vertical_scroll_bar (window, Qnil);
8111     }
8112   window = 0;
8113   [super dealloc];
8117 - condemn
8119   NSTRACE ("[EmacsScroller condemn]");
8120   condemned =YES;
8121   return self;
8125 - reprieve
8127   NSTRACE ("[EmacsScroller reprieve]");
8128   condemned =NO;
8129   return self;
8133 -(bool)judge
8135   NSTRACE ("[EmacsScroller judge]");
8136   bool ret = condemned;
8137   if (condemned)
8138     {
8139       EmacsView *view;
8140       block_input ();
8141       /* ensure other scrollbar updates after deletion */
8142       view = (EmacsView *)FRAME_NS_VIEW (frame);
8143       if (view != nil)
8144         view->scrollbarsNeedingUpdate++;
8145       if (window)
8146         {
8147           if (horizontal)
8148             wset_horizontal_scroll_bar (window, Qnil);
8149           else
8150             wset_vertical_scroll_bar (window, Qnil);
8151         }
8152       window = 0;
8153       [self removeFromSuperview];
8154       [self release];
8155       unblock_input ();
8156     }
8157   return ret;
8161 - (void)resetCursorRects
8163   NSRect visible = [self visibleRect];
8164   NSTRACE ("[EmacsScroller resetCursorRects]");
8166   if (!NSIsEmptyRect (visible))
8167     [self addCursorRect: visible cursor: [NSCursor arrowCursor]];
8168   [[NSCursor arrowCursor] setOnMouseEntered: YES];
8172 - (int) checkSamePosition: (int) position portion: (int) portion
8173                     whole: (int) whole
8175   return em_position ==position && em_portion ==portion && em_whole ==whole
8176     && portion != whole; /* needed for resize empty buf */
8180 - setPosition: (int)position portion: (int)portion whole: (int)whole
8182   NSTRACE ("[EmacsScroller setPosition:portion:whole:]");
8184   em_position = position;
8185   em_portion = portion;
8186   em_whole = whole;
8188   if (portion >= whole)
8189     {
8190 #ifdef NS_IMPL_COCOA
8191       [self setKnobProportion: 1.0];
8192       [self setDoubleValue: 1.0];
8193 #else
8194       [self setFloatValue: 0.0 knobProportion: 1.0];
8195 #endif
8196     }
8197   else
8198     {
8199       float pos;
8200       CGFloat por;
8201       portion = max ((float)whole*min_portion/pixel_length, portion);
8202       pos = (float)position / (whole - portion);
8203       por = (CGFloat)portion/whole;
8204 #ifdef NS_IMPL_COCOA
8205       [self setKnobProportion: por];
8206       [self setDoubleValue: pos];
8207 #else
8208       [self setFloatValue: pos knobProportion: por];
8209 #endif
8210     }
8212   return self;
8215 /* set up emacs_event */
8216 - (void) sendScrollEventAtLoc: (float)loc fromEvent: (NSEvent *)e
8218   Lisp_Object win;
8220   NSTRACE ("[EmacsScroller sendScrollEventAtLoc:fromEvent:]");
8222   if (!emacs_event)
8223     return;
8225   emacs_event->part = last_hit_part;
8226   emacs_event->code = 0;
8227   emacs_event->modifiers = EV_MODIFIERS (e) | down_modifier;
8228   XSETWINDOW (win, window);
8229   emacs_event->frame_or_window = win;
8230   emacs_event->timestamp = EV_TIMESTAMP (e);
8231   emacs_event->arg = Qnil;
8233   if (horizontal)
8234     {
8235       emacs_event->kind = HORIZONTAL_SCROLL_BAR_CLICK_EVENT;
8236       XSETINT (emacs_event->x, em_whole * loc / pixel_length);
8237       XSETINT (emacs_event->y, em_whole);
8238     }
8239   else
8240     {
8241       emacs_event->kind = SCROLL_BAR_CLICK_EVENT;
8242       XSETINT (emacs_event->x, loc);
8243       XSETINT (emacs_event->y, pixel_length-20);
8244     }
8246   if (q_event_ptr)
8247     {
8248       n_emacs_events_pending++;
8249       kbd_buffer_store_event_hold (emacs_event, q_event_ptr);
8250     }
8251   else
8252     hold_event (emacs_event);
8253   EVENT_INIT (*emacs_event);
8254   ns_send_appdefined (-1);
8258 /* called manually thru timer to implement repeated button action w/hold-down */
8259 - repeatScroll: (NSTimer *)scrollEntry
8261   NSEvent *e = [[self window] currentEvent];
8262   NSPoint p =  [[self window] mouseLocationOutsideOfEventStream];
8263   BOOL inKnob = [self testPart: p] == NSScrollerKnob;
8265   NSTRACE ("[EmacsScroller repeatScroll:]");
8267   /* clear timer if need be */
8268   if (inKnob || [scroll_repeat_entry timeInterval] == SCROLL_BAR_FIRST_DELAY)
8269     {
8270         [scroll_repeat_entry invalidate];
8271         [scroll_repeat_entry release];
8272         scroll_repeat_entry = nil;
8274         if (inKnob)
8275           return self;
8277         scroll_repeat_entry
8278           = [[NSTimer scheduledTimerWithTimeInterval:
8279                         SCROLL_BAR_CONTINUOUS_DELAY
8280                                             target: self
8281                                           selector: @selector (repeatScroll:)
8282                                           userInfo: 0
8283                                            repeats: YES]
8284               retain];
8285     }
8287   [self sendScrollEventAtLoc: 0 fromEvent: e];
8288   return self;
8292 /* Asynchronous mouse tracking for scroller.  This allows us to dispatch
8293    mouseDragged events without going into a modal loop. */
8294 - (void)mouseDown: (NSEvent *)e
8296   NSRect sr, kr;
8297   /* hitPart is only updated AFTER event is passed on */
8298   NSScrollerPart part = [self testPart: [e locationInWindow]];
8299   CGFloat loc, kloc, pos UNINIT;
8300   int edge = 0;
8302   NSTRACE ("[EmacsScroller mouseDown:]");
8304   switch (part)
8305     {
8306     case NSScrollerDecrementPage:
8307       last_hit_part = horizontal ? scroll_bar_before_handle : scroll_bar_above_handle; break;
8308     case NSScrollerIncrementPage:
8309       last_hit_part = horizontal ? scroll_bar_after_handle : scroll_bar_below_handle; break;
8310     case NSScrollerDecrementLine:
8311       last_hit_part = horizontal ? scroll_bar_left_arrow : scroll_bar_up_arrow; break;
8312     case NSScrollerIncrementLine:
8313       last_hit_part = horizontal ? scroll_bar_right_arrow : scroll_bar_down_arrow; break;
8314     case NSScrollerKnob:
8315       last_hit_part = horizontal ? scroll_bar_horizontal_handle : scroll_bar_handle; break;
8316     case NSScrollerKnobSlot:  /* GNUstep-only */
8317       last_hit_part = scroll_bar_move_ratio; break;
8318     default:  /* NSScrollerNoPart? */
8319       fprintf (stderr, "EmacsScoller-mouseDown: unexpected part %ld\n",
8320                (long) part);
8321       return;
8322     }
8324   if (part == NSScrollerKnob || part == NSScrollerKnobSlot)
8325     {
8326       /* handle, or on GNUstep possibly slot */
8327       NSEvent *fake_event;
8328       int length;
8330       /* compute float loc in slot and mouse offset on knob */
8331       sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot]
8332                       toView: nil];
8333       if (horizontal)
8334         {
8335           length = NSWidth (sr);
8336           loc = ([e locationInWindow].x - NSMinX (sr));
8337         }
8338       else
8339         {
8340           length = NSHeight (sr);
8341           loc = length - ([e locationInWindow].y - NSMinY (sr));
8342         }
8344       if (loc <= 0.0)
8345         {
8346           loc = 0.0;
8347           edge = -1;
8348         }
8349       else if (loc >= length)
8350         {
8351           loc = length;
8352           edge = 1;
8353         }
8355       if (edge)
8356         kloc = 0.5 * edge;
8357       else
8358         {
8359           kr = [self convertRect: [self rectForPart: NSScrollerKnob]
8360                           toView: nil];
8361           if (horizontal)
8362             kloc = ([e locationInWindow].x - NSMinX (kr));
8363           else
8364             kloc = NSHeight (kr) - ([e locationInWindow].y - NSMinY (kr));
8365         }
8366       last_mouse_offset = kloc;
8368       if (part != NSScrollerKnob)
8369         /* this is a slot click on GNUstep: go straight there */
8370         pos = loc;
8372       /* send a fake mouse-up to super to preempt modal -trackKnob: mode */
8373       fake_event = [NSEvent mouseEventWithType: NSEventTypeLeftMouseUp
8374                                       location: [e locationInWindow]
8375                                  modifierFlags: [e modifierFlags]
8376                                      timestamp: [e timestamp]
8377                                   windowNumber: [e windowNumber]
8378                                        context: [e context]
8379                                    eventNumber: [e eventNumber]
8380                                     clickCount: [e clickCount]
8381                                       pressure: [e pressure]];
8382       [super mouseUp: fake_event];
8383     }
8384   else
8385     {
8386       pos = 0;      /* ignored */
8388       /* set a timer to repeat, as we can't let superclass do this modally */
8389       scroll_repeat_entry
8390         = [[NSTimer scheduledTimerWithTimeInterval: SCROLL_BAR_FIRST_DELAY
8391                                             target: self
8392                                           selector: @selector (repeatScroll:)
8393                                           userInfo: 0
8394                                            repeats: YES]
8395             retain];
8396     }
8398   if (part != NSScrollerKnob)
8399     [self sendScrollEventAtLoc: pos fromEvent: e];
8403 /* Called as we manually track scroller drags, rather than superclass. */
8404 - (void)mouseDragged: (NSEvent *)e
8406     NSRect sr;
8407     double loc, pos;
8408     int length;
8410     NSTRACE ("[EmacsScroller mouseDragged:]");
8412       sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot]
8413                       toView: nil];
8415       if (horizontal)
8416         {
8417           length = NSWidth (sr);
8418           loc = ([e locationInWindow].x - NSMinX (sr));
8419         }
8420       else
8421         {
8422           length = NSHeight (sr);
8423           loc = length - ([e locationInWindow].y - NSMinY (sr));
8424         }
8426       if (loc <= 0.0)
8427         {
8428           loc = 0.0;
8429         }
8430       else if (loc >= length + last_mouse_offset)
8431         {
8432           loc = length + last_mouse_offset;
8433         }
8435       pos = (loc - last_mouse_offset);
8436       [self sendScrollEventAtLoc: pos fromEvent: e];
8440 - (void)mouseUp: (NSEvent *)e
8442   NSTRACE ("[EmacsScroller mouseUp:]");
8444   if (scroll_repeat_entry)
8445     {
8446       [scroll_repeat_entry invalidate];
8447       [scroll_repeat_entry release];
8448       scroll_repeat_entry = nil;
8449     }
8450   last_hit_part = scroll_bar_above_handle;
8454 /* treat scrollwheel events in the bar as though they were in the main window */
8455 - (void) scrollWheel: (NSEvent *)theEvent
8457   NSTRACE ("[EmacsScroller scrollWheel:]");
8459   EmacsView *view = (EmacsView *)FRAME_NS_VIEW (frame);
8460   [view mouseDown: theEvent];
8463 @end  /* EmacsScroller */
8466 #ifdef NS_IMPL_GNUSTEP
8467 /* Dummy class to get rid of startup warnings.  */
8468 @implementation EmacsDocument
8470 @end
8471 #endif
8474 /* ==========================================================================
8476    Font-related functions; these used to be in nsfaces.m
8478    ========================================================================== */
8481 Lisp_Object
8482 x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
8484   struct font *font = XFONT_OBJECT (font_object);
8485   EmacsView *view = FRAME_NS_VIEW (f);
8486   int font_ascent, font_descent;
8488   if (fontset < 0)
8489     fontset = fontset_from_font (font_object);
8490   FRAME_FONTSET (f) = fontset;
8492   if (FRAME_FONT (f) == font)
8493     /* This font is already set in frame F.  There's nothing more to
8494        do.  */
8495     return font_object;
8497   FRAME_FONT (f) = font;
8499   FRAME_BASELINE_OFFSET (f) = font->baseline_offset;
8500   FRAME_COLUMN_WIDTH (f) = font->average_width;
8501   get_font_ascent_descent (font, &font_ascent, &font_descent);
8502   FRAME_LINE_HEIGHT (f) = font_ascent + font_descent;
8504   /* Compute the scroll bar width in character columns.  */
8505   if (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) > 0)
8506     {
8507       int wid = FRAME_COLUMN_WIDTH (f);
8508       FRAME_CONFIG_SCROLL_BAR_COLS (f)
8509         = (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) + wid - 1) / wid;
8510     }
8511   else
8512     {
8513       int wid = FRAME_COLUMN_WIDTH (f);
8514       FRAME_CONFIG_SCROLL_BAR_COLS (f) = (14 + wid - 1) / wid;
8515     }
8517   /* Compute the scroll bar height in character lines.  */
8518   if (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) > 0)
8519     {
8520       int height = FRAME_LINE_HEIGHT (f);
8521       FRAME_CONFIG_SCROLL_BAR_LINES (f)
8522         = (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) + height - 1) / height;
8523     }
8524   else
8525     {
8526       int height = FRAME_LINE_HEIGHT (f);
8527       FRAME_CONFIG_SCROLL_BAR_LINES (f) = (14 + height - 1) / height;
8528     }
8530   /* Now make the frame display the given font.  */
8531   if (FRAME_NS_WINDOW (f) != 0 && ! [view isFullscreen])
8532     adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
8533                        FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3,
8534                        false, Qfont);
8536   return font_object;
8540 /* XLFD: -foundry-family-weight-slant-swidth-adstyle-pxlsz-ptSz-resx-resy-spc-avgWidth-rgstry-encoding */
8541 /* Note: ns_font_to_xlfd and ns_fontname_to_xlfd no longer needed, removed
8542          in 1.43. */
8544 const char *
8545 ns_xlfd_to_fontname (const char *xlfd)
8546 /* --------------------------------------------------------------------------
8547     Convert an X font name (XLFD) to an NS font name.
8548     Only family is used.
8549     The string returned is temporarily allocated.
8550    -------------------------------------------------------------------------- */
8552   char *name = xmalloc (180);
8553   int i, len;
8554   const char *ret;
8556   if (!strncmp (xlfd, "--", 2))
8557     sscanf (xlfd, "--%*[^-]-%[^-]179-", name);
8558   else
8559     sscanf (xlfd, "-%*[^-]-%[^-]179-", name);
8561   /* stopgap for malformed XLFD input */
8562   if (strlen (name) == 0)
8563     strcpy (name, "Monaco");
8565   /* undo hack in ns_fontname_to_xlfd, converting '$' to '-', '_' to ' '
8566      also uppercase after '-' or ' ' */
8567   name[0] = c_toupper (name[0]);
8568   for (len =strlen (name), i =0; i<len; i++)
8569     {
8570       if (name[i] == '$')
8571         {
8572           name[i] = '-';
8573           if (i+1<len)
8574             name[i+1] = c_toupper (name[i+1]);
8575         }
8576       else if (name[i] == '_')
8577         {
8578           name[i] = ' ';
8579           if (i+1<len)
8580             name[i+1] = c_toupper (name[i+1]);
8581         }
8582     }
8583 /*fprintf (stderr, "converted '%s' to '%s'\n",xlfd,name);  */
8584   ret = [[NSString stringWithUTF8String: name] UTF8String];
8585   xfree (name);
8586   return ret;
8590 void
8591 syms_of_nsterm (void)
8593   NSTRACE ("syms_of_nsterm");
8595   ns_antialias_threshold = 10.0;
8597   /* from 23+ we need to tell emacs what modifiers there are.. */
8598   DEFSYM (Qmodifier_value, "modifier-value");
8599   DEFSYM (Qalt, "alt");
8600   DEFSYM (Qhyper, "hyper");
8601   DEFSYM (Qmeta, "meta");
8602   DEFSYM (Qsuper, "super");
8603   DEFSYM (Qcontrol, "control");
8604   DEFSYM (QUTF8_STRING, "UTF8_STRING");
8606   DEFSYM (Qfile, "file");
8607   DEFSYM (Qurl, "url");
8609   Fput (Qalt, Qmodifier_value, make_number (alt_modifier));
8610   Fput (Qhyper, Qmodifier_value, make_number (hyper_modifier));
8611   Fput (Qmeta, Qmodifier_value, make_number (meta_modifier));
8612   Fput (Qsuper, Qmodifier_value, make_number (super_modifier));
8613   Fput (Qcontrol, Qmodifier_value, make_number (ctrl_modifier));
8615   DEFVAR_LISP ("ns-input-file", ns_input_file,
8616               "The file specified in the last NS event.");
8617   ns_input_file =Qnil;
8619   DEFVAR_LISP ("ns-working-text", ns_working_text,
8620               "String for visualizing working composition sequence.");
8621   ns_working_text =Qnil;
8623   DEFVAR_LISP ("ns-input-font", ns_input_font,
8624               "The font specified in the last NS event.");
8625   ns_input_font =Qnil;
8627   DEFVAR_LISP ("ns-input-fontsize", ns_input_fontsize,
8628               "The fontsize specified in the last NS event.");
8629   ns_input_fontsize =Qnil;
8631   DEFVAR_LISP ("ns-input-line", ns_input_line,
8632                "The line specified in the last NS event.");
8633   ns_input_line =Qnil;
8635   DEFVAR_LISP ("ns-input-spi-name", ns_input_spi_name,
8636                "The service name specified in the last NS event.");
8637   ns_input_spi_name =Qnil;
8639   DEFVAR_LISP ("ns-input-spi-arg", ns_input_spi_arg,
8640                "The service argument specified in the last NS event.");
8641   ns_input_spi_arg =Qnil;
8643   DEFVAR_LISP ("ns-alternate-modifier", ns_alternate_modifier,
8644                "This variable describes the behavior of the alternate or option key.\n\
8645 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
8646 that key.\n\
8647 Set to none means that the alternate / option key is not interpreted by Emacs\n\
8648 at all, allowing it to be used at a lower level for accented character entry.");
8649   ns_alternate_modifier = Qmeta;
8651   DEFVAR_LISP ("ns-right-alternate-modifier", ns_right_alternate_modifier,
8652                "This variable describes the behavior of the right alternate or option key.\n\
8653 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
8654 that key.\n\
8655 Set to left means be the same key as `ns-alternate-modifier'.\n\
8656 Set to none means that the alternate / option key is not interpreted by Emacs\n\
8657 at all, allowing it to be used at a lower level for accented character entry.");
8658   ns_right_alternate_modifier = Qleft;
8660   DEFVAR_LISP ("ns-command-modifier", ns_command_modifier,
8661                "This variable describes the behavior of the command key.\n\
8662 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
8663 that key.");
8664   ns_command_modifier = Qsuper;
8666   DEFVAR_LISP ("ns-right-command-modifier", ns_right_command_modifier,
8667                "This variable describes the behavior of the right command key.\n\
8668 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
8669 that key.\n\
8670 Set to left means be the same key as `ns-command-modifier'.\n\
8671 Set to none means that the command / option key is not interpreted by Emacs\n\
8672 at all, allowing it to be used at a lower level for accented character entry.");
8673   ns_right_command_modifier = Qleft;
8675   DEFVAR_LISP ("ns-control-modifier", ns_control_modifier,
8676                "This variable describes the behavior of the control key.\n\
8677 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
8678 that key.");
8679   ns_control_modifier = Qcontrol;
8681   DEFVAR_LISP ("ns-right-control-modifier", ns_right_control_modifier,
8682                "This variable describes the behavior of the right control key.\n\
8683 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
8684 that key.\n\
8685 Set to left means be the same key as `ns-control-modifier'.\n\
8686 Set to none means that the control / option key is not interpreted by Emacs\n\
8687 at all, allowing it to be used at a lower level for accented character entry.");
8688   ns_right_control_modifier = Qleft;
8690   DEFVAR_LISP ("ns-function-modifier", ns_function_modifier,
8691                "This variable describes the behavior of the function key (on laptops).\n\
8692 Set to the symbol control, meta, alt, super, or hyper means it is taken to be\n\
8693 that key.\n\
8694 Set to none means that the function key is not interpreted by Emacs at all,\n\
8695 allowing it to be used at a lower level for accented character entry.");
8696   ns_function_modifier = Qnone;
8698   DEFVAR_LISP ("ns-antialias-text", ns_antialias_text,
8699                "Non-nil (the default) means to render text antialiased.");
8700   ns_antialias_text = Qt;
8702   DEFVAR_LISP ("ns-confirm-quit", ns_confirm_quit,
8703                "Whether to confirm application quit using dialog.");
8704   ns_confirm_quit = Qnil;
8706   DEFVAR_LISP ("ns-auto-hide-menu-bar", ns_auto_hide_menu_bar,
8707                doc: /* Non-nil means that the menu bar is hidden, but appears when the mouse is near.
8708 Only works on Mac OS X 10.6 or later.  */);
8709   ns_auto_hide_menu_bar = Qnil;
8711   DEFVAR_BOOL ("ns-use-native-fullscreen", ns_use_native_fullscreen,
8712      doc: /*Non-nil means to use native fullscreen on Mac OS X 10.7 and later.
8713 Nil means use fullscreen the old (< 10.7) way.  The old way works better with
8714 multiple monitors, but lacks tool bar.  This variable is ignored on
8715 Mac OS X < 10.7.  Default is t for 10.7 and later, nil otherwise.  */);
8716 #ifdef HAVE_NATIVE_FS
8717   ns_use_native_fullscreen = YES;
8718 #else
8719   ns_use_native_fullscreen = NO;
8720 #endif
8721   ns_last_use_native_fullscreen = ns_use_native_fullscreen;
8723   DEFVAR_BOOL ("ns-use-fullscreen-animation", ns_use_fullscreen_animation,
8724      doc: /*Non-nil means use animation on non-native fullscreen.
8725 For native fullscreen, this does nothing.
8726 Default is nil.  */);
8727   ns_use_fullscreen_animation = NO;
8729   DEFVAR_BOOL ("ns-use-srgb-colorspace", ns_use_srgb_colorspace,
8730      doc: /*Non-nil means to use sRGB colorspace on Mac OS X 10.7 and later.
8731 Note that this does not apply to images.
8732 This variable is ignored on Mac OS X < 10.7 and GNUstep.  */);
8733   ns_use_srgb_colorspace = YES;
8735   /* TODO: move to common code */
8736   DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars,
8737                doc: /* Which toolkit scroll bars Emacs uses, if any.
8738 A value of nil means Emacs doesn't use toolkit scroll bars.
8739 With the X Window system, the value is a symbol describing the
8740 X toolkit.  Possible values are: gtk, motif, xaw, or xaw3d.
8741 With MS Windows or Nextstep, the value is t.  */);
8742   Vx_toolkit_scroll_bars = Qt;
8744   DEFVAR_BOOL ("x-use-underline-position-properties",
8745                x_use_underline_position_properties,
8746      doc: /*Non-nil means make use of UNDERLINE_POSITION font properties.
8747 A value of nil means ignore them.  If you encounter fonts with bogus
8748 UNDERLINE_POSITION font properties, for example 7x13 on XFree prior
8749 to 4.1, set this to nil. */);
8750   x_use_underline_position_properties = 0;
8752   DEFVAR_BOOL ("x-underline-at-descent-line",
8753                x_underline_at_descent_line,
8754      doc: /* Non-nil means to draw the underline at the same place as the descent line.
8755 A value of nil means to draw the underline according to the value of the
8756 variable `x-use-underline-position-properties', which is usually at the
8757 baseline level.  The default value is nil.  */);
8758   x_underline_at_descent_line = 0;
8760   /* Tell Emacs about this window system.  */
8761   Fprovide (Qns, Qnil);
8763   DEFSYM (Qcocoa, "cocoa");
8764   DEFSYM (Qgnustep, "gnustep");
8766 #ifdef NS_IMPL_COCOA
8767   Fprovide (Qcocoa, Qnil);
8768   syms_of_macfont ();
8769 #else
8770   Fprovide (Qgnustep, Qnil);
8771   syms_of_nsfont ();
8772 #endif