]> code.delx.au - gnu-emacs/blob - src/nsterm.m
Merge branch 'emacs-25' of git.savannah.gnu.org:/srv/git/emacs into emacs-25
[gnu-emacs] / src / nsterm.m
1 /* NeXT/Open/GNUstep / MacOSX communication module. -*- coding: utf-8 -*-
2
3 Copyright (C) 1989, 1993-1994, 2005-2006, 2008-2016 Free Software
4 Foundation, Inc.
5
6 This file is part of GNU Emacs.
7
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.
12
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.
17
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/>. */
20
21 /*
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 MacOSX/Aqua port by Christophe de Dinechin (descubes@earthlink.net)
26 GNUstep port and post-20 update by Adrian Robert (arobert@cogsci.ucsd.edu)
27 */
28
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>
32
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>
40
41 #include <c-ctype.h>
42 #include <c-strcase.h>
43 #include <ftoastr.h>
44
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"
54
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"
62
63 #ifdef NS_IMPL_GNUSTEP
64 #include "process.h"
65 #endif
66
67 #ifdef NS_IMPL_COCOA
68 #include "macfont.h"
69 #endif
70
71
72 extern NSString *NSMenuDidBeginTrackingNotification;
73
74
75 /* ==========================================================================
76
77 NSTRACE, Trace support.
78
79 ========================================================================== */
80
81 #if NSTRACE_ENABLED
82
83 /* The following use "volatile" since they can be accessed from
84 parallel threads. */
85 volatile int nstrace_num = 0;
86 volatile int nstrace_depth = 0;
87
88 /* When 0, no trace is emitted. This is used by NSTRACE_WHEN and
89 NSTRACE_UNLESS to silence functions called.
90
91 TODO: This should really be a thread-local variable, to avoid that
92 a function with disabled trace thread silence trace output in
93 another. However, in practice this seldom is a problem. */
94 volatile int nstrace_enabled_global = 1;
95
96 /* Called when nstrace_enabled goes out of scope. */
97 void nstrace_leave(int * pointer_to_nstrace_enabled)
98 {
99 if (*pointer_to_nstrace_enabled)
100 {
101 --nstrace_depth;
102 }
103 }
104
105
106 /* Called when nstrace_saved_enabled_global goes out of scope. */
107 void nstrace_restore_global_trace_state(int * pointer_to_saved_enabled_global)
108 {
109 nstrace_enabled_global = *pointer_to_saved_enabled_global;
110 }
111
112
113 char const * nstrace_fullscreen_type_name (int fs_type)
114 {
115 switch (fs_type)
116 {
117 case -1: return "-1";
118 case FULLSCREEN_NONE: return "FULLSCREEN_NONE";
119 case FULLSCREEN_WIDTH: return "FULLSCREEN_WIDTH";
120 case FULLSCREEN_HEIGHT: return "FULLSCREEN_HEIGHT";
121 case FULLSCREEN_BOTH: return "FULLSCREEN_BOTH";
122 case FULLSCREEN_MAXIMIZED: return "FULLSCREEN_MAXIMIZED";
123 default: return "FULLSCREEN_?????";
124 }
125 }
126 #endif
127
128
129 /* ==========================================================================
130
131 NSColor, EmacsColor category.
132
133 ========================================================================== */
134 @implementation NSColor (EmacsColor)
135 + (NSColor *)colorForEmacsRed:(CGFloat)red green:(CGFloat)green
136 blue:(CGFloat)blue alpha:(CGFloat)alpha
137 {
138 #ifdef NS_IMPL_COCOA
139 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
140 if (ns_use_srgb_colorspace)
141 return [NSColor colorWithSRGBRed: red
142 green: green
143 blue: blue
144 alpha: alpha];
145 #endif
146 #endif
147 return [NSColor colorWithCalibratedRed: red
148 green: green
149 blue: blue
150 alpha: alpha];
151 }
152
153 - (NSColor *)colorUsingDefaultColorSpace
154 {
155 #ifdef NS_IMPL_COCOA
156 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
157 if (ns_use_srgb_colorspace)
158 return [self colorUsingColorSpace: [NSColorSpace sRGBColorSpace]];
159 #endif
160 #endif
161 return [self colorUsingColorSpaceName: NSCalibratedRGBColorSpace];
162 }
163
164 @end
165
166 /* ==========================================================================
167
168 Local declarations
169
170 ========================================================================== */
171
172 /* Convert a symbol indexed with an NSxxx value to a value as defined
173 in keyboard.c (lispy_function_key). I hope this is a correct way
174 of doing things... */
175 static unsigned convert_ns_to_X_keysym[] =
176 {
177 NSHomeFunctionKey, 0x50,
178 NSLeftArrowFunctionKey, 0x51,
179 NSUpArrowFunctionKey, 0x52,
180 NSRightArrowFunctionKey, 0x53,
181 NSDownArrowFunctionKey, 0x54,
182 NSPageUpFunctionKey, 0x55,
183 NSPageDownFunctionKey, 0x56,
184 NSEndFunctionKey, 0x57,
185 NSBeginFunctionKey, 0x58,
186 NSSelectFunctionKey, 0x60,
187 NSPrintFunctionKey, 0x61,
188 NSClearLineFunctionKey, 0x0B,
189 NSExecuteFunctionKey, 0x62,
190 NSInsertFunctionKey, 0x63,
191 NSUndoFunctionKey, 0x65,
192 NSRedoFunctionKey, 0x66,
193 NSMenuFunctionKey, 0x67,
194 NSFindFunctionKey, 0x68,
195 NSHelpFunctionKey, 0x6A,
196 NSBreakFunctionKey, 0x6B,
197
198 NSF1FunctionKey, 0xBE,
199 NSF2FunctionKey, 0xBF,
200 NSF3FunctionKey, 0xC0,
201 NSF4FunctionKey, 0xC1,
202 NSF5FunctionKey, 0xC2,
203 NSF6FunctionKey, 0xC3,
204 NSF7FunctionKey, 0xC4,
205 NSF8FunctionKey, 0xC5,
206 NSF9FunctionKey, 0xC6,
207 NSF10FunctionKey, 0xC7,
208 NSF11FunctionKey, 0xC8,
209 NSF12FunctionKey, 0xC9,
210 NSF13FunctionKey, 0xCA,
211 NSF14FunctionKey, 0xCB,
212 NSF15FunctionKey, 0xCC,
213 NSF16FunctionKey, 0xCD,
214 NSF17FunctionKey, 0xCE,
215 NSF18FunctionKey, 0xCF,
216 NSF19FunctionKey, 0xD0,
217 NSF20FunctionKey, 0xD1,
218 NSF21FunctionKey, 0xD2,
219 NSF22FunctionKey, 0xD3,
220 NSF23FunctionKey, 0xD4,
221 NSF24FunctionKey, 0xD5,
222
223 NSBackspaceCharacter, 0x08, /* 8: Not on some KBs. */
224 NSDeleteCharacter, 0xFF, /* 127: Big 'delete' key upper right. */
225 NSDeleteFunctionKey, 0x9F, /* 63272: Del forw key off main array. */
226
227 NSTabCharacter, 0x09,
228 0x19, 0x09, /* left tab->regular since pass shift */
229 NSCarriageReturnCharacter, 0x0D,
230 NSNewlineCharacter, 0x0D,
231 NSEnterCharacter, 0x8D,
232
233 0x41|NSNumericPadKeyMask, 0xAE, /* KP_Decimal */
234 0x43|NSNumericPadKeyMask, 0xAA, /* KP_Multiply */
235 0x45|NSNumericPadKeyMask, 0xAB, /* KP_Add */
236 0x4B|NSNumericPadKeyMask, 0xAF, /* KP_Divide */
237 0x4E|NSNumericPadKeyMask, 0xAD, /* KP_Subtract */
238 0x51|NSNumericPadKeyMask, 0xBD, /* KP_Equal */
239 0x52|NSNumericPadKeyMask, 0xB0, /* KP_0 */
240 0x53|NSNumericPadKeyMask, 0xB1, /* KP_1 */
241 0x54|NSNumericPadKeyMask, 0xB2, /* KP_2 */
242 0x55|NSNumericPadKeyMask, 0xB3, /* KP_3 */
243 0x56|NSNumericPadKeyMask, 0xB4, /* KP_4 */
244 0x57|NSNumericPadKeyMask, 0xB5, /* KP_5 */
245 0x58|NSNumericPadKeyMask, 0xB6, /* KP_6 */
246 0x59|NSNumericPadKeyMask, 0xB7, /* KP_7 */
247 0x5B|NSNumericPadKeyMask, 0xB8, /* KP_8 */
248 0x5C|NSNumericPadKeyMask, 0xB9, /* KP_9 */
249
250 0x1B, 0x1B /* escape */
251 };
252
253 /* On OS X picks up the default NSGlobalDomain AppleAntiAliasingThreshold,
254 the maximum font size to NOT antialias. On GNUstep there is currently
255 no way to control this behavior. */
256 float ns_antialias_threshold;
257
258 NSArray *ns_send_types =0, *ns_return_types =0, *ns_drag_types =0;
259 NSString *ns_app_name = @"Emacs"; /* default changed later */
260
261 /* Display variables */
262 struct ns_display_info *x_display_list; /* Chain of existing displays */
263 long context_menu_value = 0;
264
265 /* display update */
266 static struct frame *ns_updating_frame;
267 static NSView *focus_view = NULL;
268 static int ns_window_num = 0;
269 #ifdef NS_IMPL_GNUSTEP
270 static NSRect uRect; // TODO: This is dead, remove it?
271 #endif
272 static BOOL gsaved = NO;
273 static BOOL ns_fake_keydown = NO;
274 #ifdef NS_IMPL_COCOA
275 static BOOL ns_menu_bar_is_hidden = NO;
276 #endif
277 /*static int debug_lock = 0; */
278
279 /* event loop */
280 static BOOL send_appdefined = YES;
281 #define NO_APPDEFINED_DATA (-8)
282 static int last_appdefined_event_data = NO_APPDEFINED_DATA;
283 static NSTimer *timed_entry = 0;
284 static NSTimer *scroll_repeat_entry = nil;
285 static fd_set select_readfds, select_writefds;
286 enum { SELECT_HAVE_READ = 1, SELECT_HAVE_WRITE = 2, SELECT_HAVE_TMO = 4 };
287 static int select_nfds = 0, select_valid = 0;
288 static struct timespec select_timeout = { 0, 0 };
289 static int selfds[2] = { -1, -1 };
290 static pthread_mutex_t select_mutex;
291 static int apploopnr = 0;
292 static NSAutoreleasePool *outerpool;
293 static struct input_event *emacs_event = NULL;
294 static struct input_event *q_event_ptr = NULL;
295 static int n_emacs_events_pending = 0;
296 static NSMutableArray *ns_pending_files, *ns_pending_service_names,
297 *ns_pending_service_args;
298 static BOOL ns_do_open_file = NO;
299 static BOOL ns_last_use_native_fullscreen;
300
301 /* Non-zero means that a HELP_EVENT has been generated since Emacs
302 start. */
303
304 static BOOL any_help_event_p = NO;
305
306 static struct {
307 struct input_event *q;
308 int nr, cap;
309 } hold_event_q = {
310 NULL, 0, 0
311 };
312
313 static NSString *represented_filename = nil;
314 static struct frame *represented_frame = 0;
315
316 #ifdef NS_IMPL_COCOA
317 /*
318 * State for pending menu activation:
319 * MENU_NONE Normal state
320 * MENU_PENDING A menu has been clicked on, but has been canceled so we can
321 * run lisp to update the menu.
322 * MENU_OPENING Menu is up to date, and the click event is redone so the menu
323 * will open.
324 */
325 #define MENU_NONE 0
326 #define MENU_PENDING 1
327 #define MENU_OPENING 2
328 static int menu_will_open_state = MENU_NONE;
329
330 /* Saved position for menu click. */
331 static CGPoint menu_mouse_point;
332 #endif
333
334 /* Convert modifiers in a NeXTstep event to emacs style modifiers. */
335 #define NS_FUNCTION_KEY_MASK 0x800000
336 #define NSLeftControlKeyMask (0x000001 | NSControlKeyMask)
337 #define NSRightControlKeyMask (0x002000 | NSControlKeyMask)
338 #define NSLeftCommandKeyMask (0x000008 | NSCommandKeyMask)
339 #define NSRightCommandKeyMask (0x000010 | NSCommandKeyMask)
340 #define NSLeftAlternateKeyMask (0x000020 | NSAlternateKeyMask)
341 #define NSRightAlternateKeyMask (0x000040 | NSAlternateKeyMask)
342 #define EV_MODIFIERS2(flags) \
343 (((flags & NSHelpKeyMask) ? \
344 hyper_modifier : 0) \
345 | (!EQ (ns_right_alternate_modifier, Qleft) && \
346 ((flags & NSRightAlternateKeyMask) \
347 == NSRightAlternateKeyMask) ? \
348 parse_solitary_modifier (ns_right_alternate_modifier) : 0) \
349 | ((flags & NSAlternateKeyMask) ? \
350 parse_solitary_modifier (ns_alternate_modifier) : 0) \
351 | ((flags & NSShiftKeyMask) ? \
352 shift_modifier : 0) \
353 | (!EQ (ns_right_control_modifier, Qleft) && \
354 ((flags & NSRightControlKeyMask) \
355 == NSRightControlKeyMask) ? \
356 parse_solitary_modifier (ns_right_control_modifier) : 0) \
357 | ((flags & NSControlKeyMask) ? \
358 parse_solitary_modifier (ns_control_modifier) : 0) \
359 | ((flags & NS_FUNCTION_KEY_MASK) ? \
360 parse_solitary_modifier (ns_function_modifier) : 0) \
361 | (!EQ (ns_right_command_modifier, Qleft) && \
362 ((flags & NSRightCommandKeyMask) \
363 == NSRightCommandKeyMask) ? \
364 parse_solitary_modifier (ns_right_command_modifier) : 0) \
365 | ((flags & NSCommandKeyMask) ? \
366 parse_solitary_modifier (ns_command_modifier):0))
367 #define EV_MODIFIERS(e) EV_MODIFIERS2 ([e modifierFlags])
368
369 #define EV_UDMODIFIERS(e) \
370 ((([e type] == NSLeftMouseDown) ? down_modifier : 0) \
371 | (([e type] == NSRightMouseDown) ? down_modifier : 0) \
372 | (([e type] == NSOtherMouseDown) ? down_modifier : 0) \
373 | (([e type] == NSLeftMouseDragged) ? down_modifier : 0) \
374 | (([e type] == NSRightMouseDragged) ? down_modifier : 0) \
375 | (([e type] == NSOtherMouseDragged) ? down_modifier : 0) \
376 | (([e type] == NSLeftMouseUp) ? up_modifier : 0) \
377 | (([e type] == NSRightMouseUp) ? up_modifier : 0) \
378 | (([e type] == NSOtherMouseUp) ? up_modifier : 0))
379
380 #define EV_BUTTON(e) \
381 ((([e type] == NSLeftMouseDown) || ([e type] == NSLeftMouseUp)) ? 0 : \
382 (([e type] == NSRightMouseDown) || ([e type] == NSRightMouseUp)) ? 2 : \
383 [e buttonNumber] - 1)
384
385 /* Convert the time field to a timestamp in milliseconds. */
386 #define EV_TIMESTAMP(e) ([e timestamp] * 1000)
387
388 /* This is a piece of code which is common to all the event handling
389 methods. Maybe it should even be a function. */
390 #define EV_TRAILER(e) \
391 { \
392 XSETFRAME (emacs_event->frame_or_window, emacsframe); \
393 EV_TRAILER2 (e); \
394 }
395
396 #define EV_TRAILER2(e) \
397 { \
398 if (e) emacs_event->timestamp = EV_TIMESTAMP (e); \
399 if (q_event_ptr) \
400 { \
401 Lisp_Object tem = Vinhibit_quit; \
402 Vinhibit_quit = Qt; \
403 n_emacs_events_pending++; \
404 kbd_buffer_store_event_hold (emacs_event, q_event_ptr); \
405 Vinhibit_quit = tem; \
406 } \
407 else \
408 hold_event (emacs_event); \
409 EVENT_INIT (*emacs_event); \
410 ns_send_appdefined (-1); \
411 }
412
413 /* TODO: get rid of need for these forward declarations */
414 static void ns_condemn_scroll_bars (struct frame *f);
415 static void ns_judge_scroll_bars (struct frame *f);
416 void x_set_frame_alpha (struct frame *f);
417
418
419 /* ==========================================================================
420
421 Utilities
422
423 ========================================================================== */
424
425 void
426 ns_set_represented_filename (NSString* fstr, struct frame *f)
427 {
428 represented_filename = [fstr retain];
429 represented_frame = f;
430 }
431
432 void
433 ns_init_events (struct input_event* ev)
434 {
435 EVENT_INIT (*ev);
436 emacs_event = ev;
437 }
438
439 void
440 ns_finish_events ()
441 {
442 emacs_event = NULL;
443 }
444
445 static void
446 hold_event (struct input_event *event)
447 {
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 }
455
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;
460 }
461
462 static Lisp_Object
463 append2 (Lisp_Object list, Lisp_Object item)
464 /* --------------------------------------------------------------------------
465 Utility to append to a list
466 -------------------------------------------------------------------------- */
467 {
468 return CALLN (Fnconc, list, list1 (item));
469 }
470
471
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. */
476 {
477 NSBundle *bundle = [NSBundle mainBundle];
478 NSString *resourceDir = [bundle resourcePath];
479 NSString *resourcePath;
480 NSFileManager *fileManager = [NSFileManager defaultManager];
481 BOOL isDir;
482
483 resourcePath = [resourceDir stringByAppendingPathComponent: @"etc"];
484 if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir])
485 {
486 if (isDir) return [resourcePath UTF8String];
487 }
488 return NULL;
489 }
490
491
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.
504 */
505 {
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;
516
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 }
525
526 paths = [binDir stringsByAppendingPaths:
527 [NSArray arrayWithObjects: @"libexec", @"bin", nil]];
528 pathEnum = [paths objectEnumerator];
529 resourcePaths = @"";
530
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];
544
545 return NULL;
546 }
547
548
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. */
554 {
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 = @"";
566
567 /* Hack to skip site-lisp. */
568 if (no_site_lisp) resourcePath = [pathEnum nextObject];
569
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];
583
584 return NULL;
585 }
586
587
588 void
589 ns_init_locale (void)
590 /* OS X doesn't set any environment variables for the locale when run
591 from the GUI. Get the locale from the OS and set LANG. */
592 {
593 NSLocale *locale = [NSLocale currentLocale];
594
595 NSTRACE ("ns_init_locale");
596
597 @try
598 {
599 /* It seems OS X 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]];
605
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 }
613 }
614
615
616 void
617 ns_release_object (void *obj)
618 /* --------------------------------------------------------------------------
619 Release an object (callable from C)
620 -------------------------------------------------------------------------- */
621 {
622 [(id)obj release];
623 }
624
625
626 void
627 ns_retain_object (void *obj)
628 /* --------------------------------------------------------------------------
629 Retain an object (callable from C)
630 -------------------------------------------------------------------------- */
631 {
632 [(id)obj retain];
633 }
634
635
636 void *
637 ns_alloc_autorelease_pool (void)
638 /* --------------------------------------------------------------------------
639 Allocate a pool for temporary objects (callable from C)
640 -------------------------------------------------------------------------- */
641 {
642 return [[NSAutoreleasePool alloc] init];
643 }
644
645
646 void
647 ns_release_autorelease_pool (void *pool)
648 /* --------------------------------------------------------------------------
649 Free a pool and temporary objects it refers to (callable from C)
650 -------------------------------------------------------------------------- */
651 {
652 ns_release_object (pool);
653 }
654
655
656 static BOOL
657 ns_menu_bar_should_be_hidden (void)
658 /* True, if the menu bar should be hidden. */
659 {
660 return !NILP (ns_auto_hide_menu_bar)
661 && [NSApp respondsToSelector:@selector(setPresentationOptions:)];
662 }
663
664
665 struct EmacsMargins
666 {
667 CGFloat top;
668 CGFloat bottom;
669 CGFloat left;
670 CGFloat right;
671 };
672
673
674 static struct EmacsMargins
675 ns_screen_margins (NSScreen *screen)
676 /* The parts of SCREEN used by the operating system. */
677 {
678 NSTRACE ("ns_screen_margins");
679
680 struct EmacsMargins margins;
681
682 NSRect screenFrame = [screen frame];
683 NSRect screenVisibleFrame = [screen visibleFrame];
684
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);
696
697 margins.top = frameTop - visibleFrameTop;
698 }
699
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 }
706
707 margins.bottom = screenVisibleFrame.origin.y - screenFrame.origin.y;
708 margins.left = screenVisibleFrame.origin.x - screenFrame.origin.x;
709
710 NSTRACE_MSG ("left:%g right:%g top:%g bottom:%g",
711 margins.left,
712 margins.right,
713 margins.top,
714 margins.bottom);
715
716 return margins;
717 }
718
719
720 /* A screen margin between 1 and DOCK_IGNORE_LIMIT (inclusive) is
721 assumed to contain a hidden dock. OS X currently use 4 pixels for
722 this, however, to be future compatible, a larger value is used. */
723 #define DOCK_IGNORE_LIMIT 6
724
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. */
729 {
730 NSTRACE ("ns_screen_margins_ignoring_hidden_dock");
731
732 struct EmacsMargins margins = ns_screen_margins(screen);
733
734 /* OS X (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 OS X, but
752 included for completeness and future compatibility. */
753 if (margins.bottom <= DOCK_IGNORE_LIMIT)
754 {
755 margins.bottom = 0;
756 }
757
758 NSTRACE_MSG ("left:%g right:%g top:%g bottom:%g",
759 margins.left,
760 margins.right,
761 margins.top,
762 margins.bottom);
763
764 return margins;
765 }
766
767
768 static CGFloat
769 ns_menu_bar_height (NSScreen *screen)
770 /* The height of the menu bar, if visible.
771
772 Note: Don't use this when fullscreen is enabled -- the screen
773 sometimes includes, sometimes excludes the menu bar area. */
774 {
775 struct EmacsMargins margins = ns_screen_margins(screen);
776
777 CGFloat res = margins.top;
778
779 NSTRACE ("ns_menu_bar_height " NSTRACE_FMT_RETURN " %.0f", res);
780
781 return res;
782 }
783
784
785 /* ==========================================================================
786
787 Focus (clipping) and screen update
788
789 ========================================================================== */
790
791 //
792 // Window constraining
793 // -------------------
794 //
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.
799 //
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.
804 //
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.
809 //
810 // Test cases
811 // ----------
812 //
813 // Use the following extra files:
814 //
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)
819 //
820 // Test 1:
821 //
822 // emacs -Q -l init.el
823 //
824 // Result: No menu bar, and the title bar should be above the screen.
825 //
826 // Test 2:
827 //
828 // emacs -Q
829 //
830 // Result: Menu bar visible, frame placed immediately below the menu.
831 //
832
833 static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
834 {
835 NSTRACE ("constrain_frame_rect(" NSTRACE_FMT_RECT ")",
836 NSTRACE_ARG_RECT (frameRect));
837
838 // --------------------
839 // Collect information about the screen the frame is covering.
840 //
841
842 NSArray *screens = [NSScreen screens];
843 NSUInteger nr_screens = [screens count];
844
845 int i;
846
847 // The height of the menu bar, if present in any screen the frame is
848 // displayed in.
849 int menu_bar_height = 0;
850
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];
857
858 NSTRACE_MSG ("Screen %d: " NSTRACE_FMT_RECT,
859 i, NSTRACE_ARG_RECT (scrRect));
860
861 if (NSIntersectionRect (frameRect, scrRect).size.height != 0)
862 {
863 multiscreenRect = NSUnionRect (multiscreenRect, scrRect);
864
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 }
872
873 NSTRACE_RECT ("multiscreenRect", multiscreenRect);
874
875 NSTRACE_MSG ("menu_bar_height: %d", menu_bar_height);
876
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 }
885
886
887 // --------------------
888 // Find a suitable placement.
889 //
890
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);
910
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 }
918
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 //
926
927 CGFloat diff = multiscreenRect.origin.y - frameRect.origin.y;
928
929 if (diff > 0)
930 {
931 frameRect.origin.y = multiscreenRect.origin.y;
932 frameRect.size.height -= diff;
933 }
934 #endif
935
936 NSTRACE_RETURN_RECT (frameRect);
937 return frameRect;
938 }
939
940
941 static void
942 ns_constrain_all_frames (void)
943 /* --------------------------------------------------------------------------
944 Ensure that the menu bar doesn't cover any frames.
945 -------------------------------------------------------------------------- */
946 {
947 Lisp_Object tail, frame;
948
949 NSTRACE ("ns_constrain_all_frames");
950
951 block_input ();
952
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);
959
960 if (![view isFullscreen])
961 {
962 [[view window]
963 setFrame:constrain_frame_rect([[view window] frame], false)
964 display:NO];
965 }
966 }
967 }
968
969 unblock_input ();
970 }
971
972
973 static void
974 ns_update_auto_hide_menu_bar (void)
975 /* --------------------------------------------------------------------------
976 Show or hide the menu bar, based on user setting.
977 -------------------------------------------------------------------------- */
978 {
979 #ifdef NS_IMPL_COCOA
980 NSTRACE ("ns_update_auto_hide_menu_bar");
981
982 block_input ();
983
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 ();
989
990 if (menu_bar_should_be_hidden != ns_menu_bar_is_hidden)
991 {
992 NSApplicationPresentationOptions options
993 = NSApplicationPresentationDefault;
994
995 if (menu_bar_should_be_hidden)
996 options |= NSApplicationPresentationAutoHideMenuBar
997 | NSApplicationPresentationAutoHideDock;
998
999 [NSApp setPresentationOptions: options];
1000
1001 ns_menu_bar_is_hidden = menu_bar_should_be_hidden;
1002
1003 if (!ns_menu_bar_is_hidden)
1004 {
1005 ns_constrain_all_frames ();
1006 }
1007 }
1008 }
1009
1010 unblock_input ();
1011 #endif
1012 }
1013
1014
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 -------------------------------------------------------------------------- */
1021 {
1022 EmacsView *view = FRAME_NS_VIEW (f);
1023 NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_begin");
1024
1025 ns_update_auto_hide_menu_bar ();
1026
1027 #ifdef NS_IMPL_COCOA
1028 if ([view isFullscreen] && [view fsIsNative])
1029 {
1030 // Fix reappearing tool bar in fullscreen for OSX 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
1037
1038 ns_updating_frame = f;
1039 [view lockFocus];
1040
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
1063
1064 #ifdef NS_IMPL_GNUSTEP
1065 uRect = NSMakeRect (0, 0, 0, 0);
1066 #endif
1067 }
1068
1069
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 -------------------------------------------------------------------------- */
1076 {
1077 struct frame *f = XFRAME (WINDOW_FRAME (w));
1078 Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
1079
1080 NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_window_begin");
1081 w->output_cursor = w->cursor;
1082
1083 block_input ();
1084
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;
1089
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;
1094
1095 /* (further code for mouse faces ifdef'd out in other terms elided) */
1096 }
1097
1098 unblock_input ();
1099 }
1100
1101
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 -------------------------------------------------------------------------- */
1109 {
1110 NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_window_end");
1111
1112 /* note: this fn is nearly identical in all terms */
1113 if (!w->pseudo_window_p)
1114 {
1115 block_input ();
1116
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);
1121
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 }
1129
1130 unblock_input ();
1131 }
1132
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)));
1137 }
1138
1139
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 -------------------------------------------------------------------------- */
1146 {
1147 EmacsView *view = FRAME_NS_VIEW (f);
1148
1149 NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_end");
1150
1151 /* if (f == MOUSE_HL_INFO (f)->mouse_face_mouse_frame) */
1152 MOUSE_HL_INFO (f)->mouse_face_defer = 0;
1153
1154 block_input ();
1155
1156 [view unlockFocus];
1157 [[view window] flushWindow];
1158
1159 unblock_input ();
1160 ns_updating_frame = NULL;
1161 }
1162
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 -------------------------------------------------------------------------- */
1173 {
1174 NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_focus");
1175 if (r != NULL)
1176 {
1177 NSTRACE_RECT ("r", *r);
1178 }
1179
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 }
1191
1192 if (view)
1193 [view lockFocus];
1194 focus_view = view;
1195 /*if (view) debug_lock++; */
1196 }
1197 }
1198
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 }
1209 }
1210
1211
1212 static void
1213 ns_unfocus (struct frame *f)
1214 /* --------------------------------------------------------------------------
1215 Internal: Remove focus on given frame
1216 -------------------------------------------------------------------------- */
1217 {
1218 NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_unfocus");
1219
1220 if (gsaved)
1221 {
1222 [[NSGraphicsContext currentContext] restoreGraphicsState];
1223 gsaved = NO;
1224 }
1225
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 }
1236 }
1237
1238
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 -------------------------------------------------------------------------- */
1245 {
1246 struct frame *f = XFRAME (WINDOW_FRAME (w));
1247 NSRect clip_rect;
1248 int window_x, window_y, window_width;
1249
1250 window_box (w, area, &window_x, &window_y, &window_width, 0);
1251
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;
1257
1258 ns_focus (f, &clip_rect, 1);
1259 }
1260
1261
1262 /* ==========================================================================
1263
1264 Visible bell and beep.
1265
1266 ========================================================================== */
1267
1268
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.
1272 //
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.
1278 //
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:
1282 //
1283 // * Single-window: C-g C-v
1284 //
1285 // * Side-by-windows: C-x 3 C-g C-v
1286 //
1287 // * Windows above each other: C-x 2 C-g C-v
1288
1289 @interface EmacsBell : NSImageView
1290 {
1291 // Number of currently active bell:s.
1292 unsigned int nestCount;
1293 NSView * mView;
1294 bool isAttached;
1295 }
1296 - (void)show:(NSView *)view;
1297 - (void)hide;
1298 - (void)remove;
1299 @end
1300
1301 @implementation EmacsBell
1302
1303 - (id)init;
1304 {
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;
1327 }
1328
1329 - (void)show:(NSView *)view
1330 {
1331 NSTRACE ("[EmacsBell show:]");
1332 NSTRACE_MSG ("nestCount: %u", nestCount);
1333
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;
1341
1342 [self setFrameOrigin:pos];
1343 [self setFrameSize:self.image.size];
1344
1345 isAttached = true;
1346 mView = view;
1347 [[[view window] contentView] addSubview:self
1348 positioned:NSWindowAbove
1349 relativeTo:nil];
1350 }
1351
1352 ++nestCount;
1353
1354 [self performSelector:@selector(hide) withObject:self afterDelay:0.5];
1355 }
1356
1357
1358 - (void)hide
1359 {
1360 // Note: Trace output from this method isn't shown, reason unknown.
1361 // NSTRACE ("[EmacsBell hide]");
1362
1363 if (nestCount > 0)
1364 --nestCount;
1365
1366 // Remove the image once the last bell became inactive.
1367 if (nestCount == 0)
1368 {
1369 [self remove];
1370 }
1371 }
1372
1373
1374 -(void)remove
1375 {
1376 NSTRACE ("[EmacsBell remove]");
1377 if (isAttached)
1378 {
1379 NSTRACE_MSG ("removeFromSuperview");
1380 [self removeFromSuperview];
1381 mView.needsDisplay = YES;
1382 isAttached = false;
1383 }
1384 }
1385
1386 @end
1387
1388
1389 static EmacsBell * bell_view = nil;
1390
1391 static void
1392 ns_ring_bell (struct frame *f)
1393 /* --------------------------------------------------------------------------
1394 "Beep" routine
1395 -------------------------------------------------------------------------- */
1396 {
1397 NSTRACE ("ns_ring_bell");
1398 if (visible_bell)
1399 {
1400 struct frame *frame = SELECTED_FRAME ();
1401 NSView *view;
1402
1403 if (bell_view == nil)
1404 {
1405 bell_view = [[EmacsBell alloc] init];
1406 [bell_view retain];
1407 }
1408
1409 block_input ();
1410
1411 view = FRAME_NS_VIEW (frame);
1412 if (view != nil)
1413 {
1414 [bell_view show:view];
1415 }
1416
1417 unblock_input ();
1418 }
1419 else
1420 {
1421 NSBeep ();
1422 }
1423 }
1424
1425
1426 static void hide_bell ()
1427 /* --------------------------------------------------------------------------
1428 Ensure the bell is hidden.
1429 -------------------------------------------------------------------------- */
1430 {
1431 NSTRACE ("hide_bell");
1432
1433 if (bell_view != nil)
1434 {
1435 [bell_view remove];
1436 }
1437 }
1438
1439
1440 /* ==========================================================================
1441
1442 Frame / window manager related functions
1443
1444 ========================================================================== */
1445
1446
1447 static void
1448 ns_raise_frame (struct frame *f)
1449 /* --------------------------------------------------------------------------
1450 Bring window to foreground and make it active
1451 -------------------------------------------------------------------------- */
1452 {
1453 NSView *view;
1454
1455 check_window_system (f);
1456 view = FRAME_NS_VIEW (f);
1457 block_input ();
1458 if (FRAME_VISIBLE_P (f))
1459 [[view window] makeKeyAndOrderFront: NSApp];
1460 unblock_input ();
1461 }
1462
1463
1464 static void
1465 ns_lower_frame (struct frame *f)
1466 /* --------------------------------------------------------------------------
1467 Send window to back
1468 -------------------------------------------------------------------------- */
1469 {
1470 NSView *view;
1471
1472 check_window_system (f);
1473 view = FRAME_NS_VIEW (f);
1474 block_input ();
1475 [[view window] orderBack: NSApp];
1476 unblock_input ();
1477 }
1478
1479
1480 static void
1481 ns_frame_raise_lower (struct frame *f, bool raise)
1482 /* --------------------------------------------------------------------------
1483 External (hook)
1484 -------------------------------------------------------------------------- */
1485 {
1486 NSTRACE ("ns_frame_raise_lower");
1487
1488 if (raise)
1489 ns_raise_frame (f);
1490 else
1491 ns_lower_frame (f);
1492 }
1493
1494
1495 static void
1496 ns_frame_rehighlight (struct frame *frame)
1497 /* --------------------------------------------------------------------------
1498 External (hook): called on things like window switching within frame
1499 -------------------------------------------------------------------------- */
1500 {
1501 struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
1502 struct frame *old_highlight = dpyinfo->x_highlight_frame;
1503
1504 NSTRACE ("ns_frame_rehighlight");
1505 if (dpyinfo->x_focus_frame)
1506 {
1507 dpyinfo->x_highlight_frame
1508 = (FRAMEP (FRAME_FOCUS_FRAME (dpyinfo->x_focus_frame))
1509 ? XFRAME (FRAME_FOCUS_FRAME (dpyinfo->x_focus_frame))
1510 : dpyinfo->x_focus_frame);
1511 if (!FRAME_LIVE_P (dpyinfo->x_highlight_frame))
1512 {
1513 fset_focus_frame (dpyinfo->x_focus_frame, Qnil);
1514 dpyinfo->x_highlight_frame = dpyinfo->x_focus_frame;
1515 }
1516 }
1517 else
1518 dpyinfo->x_highlight_frame = 0;
1519
1520 if (dpyinfo->x_highlight_frame &&
1521 dpyinfo->x_highlight_frame != old_highlight)
1522 {
1523 if (old_highlight)
1524 {
1525 x_update_cursor (old_highlight, 1);
1526 x_set_frame_alpha (old_highlight);
1527 }
1528 if (dpyinfo->x_highlight_frame)
1529 {
1530 x_update_cursor (dpyinfo->x_highlight_frame, 1);
1531 x_set_frame_alpha (dpyinfo->x_highlight_frame);
1532 }
1533 }
1534 }
1535
1536
1537 void
1538 x_make_frame_visible (struct frame *f)
1539 /* --------------------------------------------------------------------------
1540 External: Show the window (X11 semantics)
1541 -------------------------------------------------------------------------- */
1542 {
1543 NSTRACE ("x_make_frame_visible");
1544 /* XXX: at some points in past this was not needed, as the only place that
1545 called this (frame.c:Fraise_frame ()) also called raise_lower;
1546 if this ends up the case again, comment this out again. */
1547 if (!FRAME_VISIBLE_P (f))
1548 {
1549 EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
1550
1551 SET_FRAME_VISIBLE (f, 1);
1552 ns_raise_frame (f);
1553
1554 /* Making a new frame from a fullscreen frame will make the new frame
1555 fullscreen also. So skip handleFS as this will print an error. */
1556 if ([view fsIsNative] && f->want_fullscreen == FULLSCREEN_BOTH
1557 && [view isFullscreen])
1558 return;
1559
1560 if (f->want_fullscreen != FULLSCREEN_NONE)
1561 {
1562 block_input ();
1563 [view handleFS];
1564 unblock_input ();
1565 }
1566 }
1567 }
1568
1569
1570 void
1571 x_make_frame_invisible (struct frame *f)
1572 /* --------------------------------------------------------------------------
1573 External: Hide the window (X11 semantics)
1574 -------------------------------------------------------------------------- */
1575 {
1576 NSView *view;
1577 NSTRACE ("x_make_frame_invisible");
1578 check_window_system (f);
1579 view = FRAME_NS_VIEW (f);
1580 [[view window] orderOut: NSApp];
1581 SET_FRAME_VISIBLE (f, 0);
1582 SET_FRAME_ICONIFIED (f, 0);
1583 }
1584
1585
1586 void
1587 x_iconify_frame (struct frame *f)
1588 /* --------------------------------------------------------------------------
1589 External: Iconify window
1590 -------------------------------------------------------------------------- */
1591 {
1592 NSView *view;
1593 struct ns_display_info *dpyinfo;
1594
1595 NSTRACE ("x_iconify_frame");
1596 check_window_system (f);
1597 view = FRAME_NS_VIEW (f);
1598 dpyinfo = FRAME_DISPLAY_INFO (f);
1599
1600 if (dpyinfo->x_highlight_frame == f)
1601 dpyinfo->x_highlight_frame = 0;
1602
1603 if ([[view window] windowNumber] <= 0)
1604 {
1605 /* the window is still deferred. Make it very small, bring it
1606 on screen and order it out. */
1607 NSRect s = { { 100, 100}, {0, 0} };
1608 NSRect t;
1609 t = [[view window] frame];
1610 [[view window] setFrame: s display: NO];
1611 [[view window] orderBack: NSApp];
1612 [[view window] orderOut: NSApp];
1613 [[view window] setFrame: t display: NO];
1614 }
1615 [[view window] miniaturize: NSApp];
1616 }
1617
1618 /* Free X resources of frame F. */
1619
1620 void
1621 x_free_frame_resources (struct frame *f)
1622 {
1623 NSView *view;
1624 struct ns_display_info *dpyinfo;
1625 Mouse_HLInfo *hlinfo;
1626
1627 NSTRACE ("x_free_frame_resources");
1628 check_window_system (f);
1629 view = FRAME_NS_VIEW (f);
1630 dpyinfo = FRAME_DISPLAY_INFO (f);
1631 hlinfo = MOUSE_HL_INFO (f);
1632
1633 [(EmacsView *)view setWindowClosing: YES]; /* may not have been informed */
1634
1635 block_input ();
1636
1637 free_frame_menubar (f);
1638 free_frame_faces (f);
1639
1640 if (f == dpyinfo->x_focus_frame)
1641 dpyinfo->x_focus_frame = 0;
1642 if (f == dpyinfo->x_highlight_frame)
1643 dpyinfo->x_highlight_frame = 0;
1644 if (f == hlinfo->mouse_face_mouse_frame)
1645 reset_mouse_highlight (hlinfo);
1646
1647 if (f->output_data.ns->miniimage != nil)
1648 [f->output_data.ns->miniimage release];
1649
1650 [[view window] close];
1651 [view release];
1652
1653 xfree (f->output_data.ns);
1654
1655 unblock_input ();
1656 }
1657
1658 void
1659 x_destroy_window (struct frame *f)
1660 /* --------------------------------------------------------------------------
1661 External: Delete the window
1662 -------------------------------------------------------------------------- */
1663 {
1664 NSTRACE ("x_destroy_window");
1665 check_window_system (f);
1666 x_free_frame_resources (f);
1667 ns_window_num--;
1668 }
1669
1670
1671 void
1672 x_set_offset (struct frame *f, int xoff, int yoff, int change_grav)
1673 /* --------------------------------------------------------------------------
1674 External: Position the window
1675 -------------------------------------------------------------------------- */
1676 {
1677 NSView *view = FRAME_NS_VIEW (f);
1678 NSArray *screens = [NSScreen screens];
1679 NSScreen *fscreen = [screens objectAtIndex: 0];
1680 NSScreen *screen = [[view window] screen];
1681
1682 NSTRACE ("x_set_offset");
1683
1684 block_input ();
1685
1686 f->left_pos = xoff;
1687 f->top_pos = yoff;
1688
1689 if (view != nil && screen && fscreen)
1690 {
1691 f->left_pos = f->size_hint_flags & XNegative
1692 ? [screen visibleFrame].size.width + f->left_pos - FRAME_PIXEL_WIDTH (f)
1693 : f->left_pos;
1694 /* We use visibleFrame here to take menu bar into account.
1695 Ideally we should also adjust left/top with visibleFrame.origin. */
1696
1697 f->top_pos = f->size_hint_flags & YNegative
1698 ? ([screen visibleFrame].size.height + f->top_pos
1699 - FRAME_PIXEL_HEIGHT (f) - FRAME_NS_TITLEBAR_HEIGHT (f)
1700 - FRAME_TOOLBAR_HEIGHT (f))
1701 : f->top_pos;
1702 #ifdef NS_IMPL_GNUSTEP
1703 if (f->left_pos < 100)
1704 f->left_pos = 100; /* don't overlap menu */
1705 #endif
1706 /* Constrain the setFrameTopLeftPoint so we don't move behind the
1707 menu bar. */
1708 NSPoint pt = NSMakePoint (SCREENMAXBOUND (f->left_pos),
1709 SCREENMAXBOUND ([fscreen frame].size.height
1710 - NS_TOP_POS (f)));
1711 NSTRACE_POINT ("setFrameTopLeftPoint", pt);
1712 [[view window] setFrameTopLeftPoint: pt];
1713 f->size_hint_flags &= ~(XNegative|YNegative);
1714 }
1715
1716 unblock_input ();
1717 }
1718
1719
1720 void
1721 x_set_window_size (struct frame *f,
1722 bool change_gravity,
1723 int width,
1724 int height,
1725 bool pixelwise)
1726 /* --------------------------------------------------------------------------
1727 Adjust window pixel size based on given character grid size
1728 Impl is a bit more complex than other terms, need to do some
1729 internal clipping.
1730 -------------------------------------------------------------------------- */
1731 {
1732 EmacsView *view = FRAME_NS_VIEW (f);
1733 NSWindow *window = [view window];
1734 NSRect wr = [window frame];
1735 int tb = FRAME_EXTERNAL_TOOL_BAR (f);
1736 int pixelwidth, pixelheight;
1737 int orig_height = wr.size.height;
1738
1739 NSTRACE ("x_set_window_size");
1740
1741 if (view == nil)
1742 return;
1743
1744 NSTRACE_RECT ("current", wr);
1745 NSTRACE_MSG ("Width:%d Height:%d Pixelwise:%d", width, height, pixelwise);
1746 NSTRACE_MSG ("Font %d x %d", FRAME_COLUMN_WIDTH (f), FRAME_LINE_HEIGHT (f));
1747
1748 block_input ();
1749
1750 if (pixelwise)
1751 {
1752 pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
1753 pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
1754 }
1755 else
1756 {
1757 pixelwidth = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, width);
1758 pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, height);
1759 }
1760
1761 /* If we have a toolbar, take its height into account. */
1762 if (tb && ! [view isFullscreen])
1763 {
1764 /* NOTE: previously this would generate wrong result if toolbar not
1765 yet displayed and fixing toolbar_height=32 helped, but
1766 now (200903) seems no longer needed */
1767 FRAME_TOOLBAR_HEIGHT (f) =
1768 NSHeight ([window frameRectForContentRect: NSMakeRect (0, 0, 0, 0)])
1769 - FRAME_NS_TITLEBAR_HEIGHT (f);
1770 #if 0
1771 /* Only breaks things here, removed by martin 2015-09-30. */
1772 #ifdef NS_IMPL_GNUSTEP
1773 FRAME_TOOLBAR_HEIGHT (f) -= 3;
1774 #endif
1775 #endif
1776 }
1777 else
1778 FRAME_TOOLBAR_HEIGHT (f) = 0;
1779
1780 wr.size.width = pixelwidth + f->border_width;
1781 wr.size.height = pixelheight;
1782 if (! [view isFullscreen])
1783 wr.size.height += FRAME_NS_TITLEBAR_HEIGHT (f)
1784 + FRAME_TOOLBAR_HEIGHT (f);
1785
1786 /* Do not try to constrain to this screen. We may have multiple
1787 screens, and want Emacs to span those. Constraining to screen
1788 prevents that, and that is not nice to the user. */
1789 if (f->output_data.ns->zooming)
1790 f->output_data.ns->zooming = 0;
1791 else
1792 wr.origin.y += orig_height - wr.size.height;
1793
1794 frame_size_history_add
1795 (f, Qx_set_window_size_1, width, height,
1796 list5 (Fcons (make_number (pixelwidth), make_number (pixelheight)),
1797 Fcons (make_number (wr.size.width), make_number (wr.size.height)),
1798 make_number (f->border_width),
1799 make_number (FRAME_NS_TITLEBAR_HEIGHT (f)),
1800 make_number (FRAME_TOOLBAR_HEIGHT (f))));
1801
1802 [window setFrame: wr display: YES];
1803
1804 /* This is a trick to compensate for Emacs' managing the scrollbar area
1805 as a fixed number of standard character columns. Instead of leaving
1806 blank space for the extra, we chopped it off above. Now for
1807 left-hand scrollbars, we shift all rendering to the left by the
1808 difference between the real width and Emacs' imagined one. For
1809 right-hand bars, don't worry about it since the extra is never used.
1810 (Obviously doesn't work for vertically split windows tho..) */
1811 {
1812 NSPoint origin = FRAME_HAS_VERTICAL_SCROLL_BARS_ON_LEFT (f)
1813 ? NSMakePoint (FRAME_SCROLL_BAR_COLS (f) * FRAME_COLUMN_WIDTH (f)
1814 - NS_SCROLL_BAR_WIDTH (f), 0)
1815 : NSMakePoint (0, 0);
1816
1817 [view setFrame: NSMakeRect (0, 0, pixelwidth, pixelheight)];
1818 [view setBoundsOrigin: origin];
1819 }
1820
1821 [view updateFrameSize: NO];
1822 unblock_input ();
1823 }
1824
1825
1826 static void
1827 ns_fullscreen_hook (struct frame *f)
1828 {
1829 EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
1830
1831 NSTRACE ("ns_fullscreen_hook");
1832
1833 if (!FRAME_VISIBLE_P (f))
1834 return;
1835
1836 if (! [view fsIsNative] && f->want_fullscreen == FULLSCREEN_BOTH)
1837 {
1838 /* Old style fs don't initiate correctly if created from
1839 init/default-frame alist, so use a timer (not nice...).
1840 */
1841 [NSTimer scheduledTimerWithTimeInterval: 0.5 target: view
1842 selector: @selector (handleFS)
1843 userInfo: nil repeats: NO];
1844 return;
1845 }
1846
1847 block_input ();
1848 [view handleFS];
1849 unblock_input ();
1850 }
1851
1852 /* ==========================================================================
1853
1854 Color management
1855
1856 ========================================================================== */
1857
1858
1859 NSColor *
1860 ns_lookup_indexed_color (unsigned long idx, struct frame *f)
1861 {
1862 struct ns_color_table *color_table = FRAME_DISPLAY_INFO (f)->color_table;
1863 if (idx < 1 || idx >= color_table->avail)
1864 return nil;
1865 return color_table->colors[idx];
1866 }
1867
1868
1869 unsigned long
1870 ns_index_color (NSColor *color, struct frame *f)
1871 {
1872 struct ns_color_table *color_table = FRAME_DISPLAY_INFO (f)->color_table;
1873 ptrdiff_t idx;
1874 ptrdiff_t i;
1875
1876 if (!color_table->colors)
1877 {
1878 color_table->size = NS_COLOR_CAPACITY;
1879 color_table->avail = 1; /* skip idx=0 as marker */
1880 color_table->colors = xmalloc (color_table->size * sizeof (NSColor *));
1881 color_table->colors[0] = nil;
1882 color_table->empty_indices = [[NSMutableSet alloc] init];
1883 }
1884
1885 /* Do we already have this color? */
1886 for (i = 1; i < color_table->avail; i++)
1887 if (color_table->colors[i] && [color_table->colors[i] isEqual: color])
1888 return i;
1889
1890 if ([color_table->empty_indices count] > 0)
1891 {
1892 NSNumber *index = [color_table->empty_indices anyObject];
1893 [color_table->empty_indices removeObject: index];
1894 idx = [index unsignedLongValue];
1895 }
1896 else
1897 {
1898 if (color_table->avail == color_table->size)
1899 color_table->colors =
1900 xpalloc (color_table->colors, &color_table->size, 1,
1901 min (ULONG_MAX, PTRDIFF_MAX), sizeof *color_table->colors);
1902 idx = color_table->avail++;
1903 }
1904
1905 color_table->colors[idx] = color;
1906 [color retain];
1907 /*fprintf(stderr, "color_table: allocated %d\n",idx);*/
1908 return idx;
1909 }
1910
1911
1912 void
1913 ns_free_indexed_color (unsigned long idx, struct frame *f)
1914 {
1915 struct ns_color_table *color_table;
1916 NSColor *color;
1917 NSNumber *index;
1918
1919 if (!f)
1920 return;
1921
1922 color_table = FRAME_DISPLAY_INFO (f)->color_table;
1923
1924 if (idx <= 0 || idx >= color_table->size) {
1925 message1 ("ns_free_indexed_color: Color index out of range.\n");
1926 return;
1927 }
1928
1929 index = [NSNumber numberWithUnsignedInt: idx];
1930 if ([color_table->empty_indices containsObject: index]) {
1931 message1 ("ns_free_indexed_color: attempt to free already freed color.\n");
1932 return;
1933 }
1934
1935 color = color_table->colors[idx];
1936 [color release];
1937 color_table->colors[idx] = nil;
1938 [color_table->empty_indices addObject: index];
1939 /*fprintf(stderr, "color_table: FREED %d\n",idx);*/
1940 }
1941
1942
1943 static int
1944 ns_get_color (const char *name, NSColor **col)
1945 /* --------------------------------------------------------------------------
1946 Parse a color name
1947 -------------------------------------------------------------------------- */
1948 /* On *Step, we attempt to mimic the X11 platform here, down to installing an
1949 X11 rgb.txt-compatible color list in Emacs.clr (see ns_term_init()).
1950 See: http://thread.gmane.org/gmane.emacs.devel/113050/focus=113272). */
1951 {
1952 NSColor *new = nil;
1953 static char hex[20];
1954 int scaling = 0;
1955 float r = -1.0, g, b;
1956 NSString *nsname = [NSString stringWithUTF8String: name];
1957
1958 NSTRACE ("ns_get_color(%s, **)", name);
1959
1960 block_input ();
1961
1962 if ([nsname isEqualToString: @"ns_selection_bg_color"])
1963 {
1964 #ifdef NS_IMPL_COCOA
1965 NSString *defname = [[NSUserDefaults standardUserDefaults]
1966 stringForKey: @"AppleHighlightColor"];
1967 if (defname != nil)
1968 nsname = defname;
1969 else
1970 #endif
1971 if ((new = [NSColor selectedTextBackgroundColor]) != nil)
1972 {
1973 *col = [new colorUsingDefaultColorSpace];
1974 unblock_input ();
1975 return 0;
1976 }
1977 else
1978 nsname = NS_SELECTION_BG_COLOR_DEFAULT;
1979
1980 name = [nsname UTF8String];
1981 }
1982 else if ([nsname isEqualToString: @"ns_selection_fg_color"])
1983 {
1984 /* NOTE: OSX applications normally don't set foreground selection, but
1985 text may be unreadable if we don't.
1986 */
1987 if ((new = [NSColor selectedTextColor]) != nil)
1988 {
1989 *col = [new colorUsingDefaultColorSpace];
1990 unblock_input ();
1991 return 0;
1992 }
1993
1994 nsname = NS_SELECTION_FG_COLOR_DEFAULT;
1995 name = [nsname UTF8String];
1996 }
1997
1998 /* First, check for some sort of numeric specification. */
1999 hex[0] = '\0';
2000
2001 if (name[0] == '0' || name[0] == '1' || name[0] == '.') /* RGB decimal */
2002 {
2003 NSScanner *scanner = [NSScanner scannerWithString: nsname];
2004 [scanner scanFloat: &r];
2005 [scanner scanFloat: &g];
2006 [scanner scanFloat: &b];
2007 }
2008 else if (!strncmp(name, "rgb:", 4)) /* A newer X11 format -- rgb:r/g/b */
2009 scaling = (snprintf (hex, sizeof hex, "%s", name + 4) - 2) / 3;
2010 else if (name[0] == '#') /* An old X11 format; convert to newer */
2011 {
2012 int len = (strlen(name) - 1);
2013 int start = (len % 3 == 0) ? 1 : len / 4 + 1;
2014 int i;
2015 scaling = strlen(name+start) / 3;
2016 for (i = 0; i < 3; i++)
2017 sprintf (hex + i * (scaling + 1), "%.*s/", scaling,
2018 name + start + i * scaling);
2019 hex[3 * (scaling + 1) - 1] = '\0';
2020 }
2021
2022 if (hex[0])
2023 {
2024 int rr, gg, bb;
2025 float fscale = scaling == 4 ? 65535.0 : (scaling == 2 ? 255.0 : 15.0);
2026 if (sscanf (hex, "%x/%x/%x", &rr, &gg, &bb))
2027 {
2028 r = rr / fscale;
2029 g = gg / fscale;
2030 b = bb / fscale;
2031 }
2032 }
2033
2034 if (r >= 0.0F)
2035 {
2036 *col = [NSColor colorForEmacsRed: r green: g blue: b alpha: 1.0];
2037 unblock_input ();
2038 return 0;
2039 }
2040
2041 /* Otherwise, color is expected to be from a list */
2042 {
2043 NSEnumerator *lenum, *cenum;
2044 NSString *name;
2045 NSColorList *clist;
2046
2047 #ifdef NS_IMPL_GNUSTEP
2048 /* XXX: who is wrong, the requestor or the implementation? */
2049 if ([nsname compare: @"Highlight" options: NSCaseInsensitiveSearch]
2050 == NSOrderedSame)
2051 nsname = @"highlightColor";
2052 #endif
2053
2054 lenum = [[NSColorList availableColorLists] objectEnumerator];
2055 while ( (clist = [lenum nextObject]) && new == nil)
2056 {
2057 cenum = [[clist allKeys] objectEnumerator];
2058 while ( (name = [cenum nextObject]) && new == nil )
2059 {
2060 if ([name compare: nsname
2061 options: NSCaseInsensitiveSearch] == NSOrderedSame )
2062 new = [clist colorWithKey: name];
2063 }
2064 }
2065 }
2066
2067 if (new)
2068 *col = [new colorUsingDefaultColorSpace];
2069 unblock_input ();
2070 return new ? 0 : 1;
2071 }
2072
2073
2074 int
2075 ns_lisp_to_color (Lisp_Object color, NSColor **col)
2076 /* --------------------------------------------------------------------------
2077 Convert a Lisp string object to a NS color
2078 -------------------------------------------------------------------------- */
2079 {
2080 NSTRACE ("ns_lisp_to_color");
2081 if (STRINGP (color))
2082 return ns_get_color (SSDATA (color), col);
2083 else if (SYMBOLP (color))
2084 return ns_get_color (SSDATA (SYMBOL_NAME (color)), col);
2085 return 1;
2086 }
2087
2088
2089 Lisp_Object
2090 ns_color_to_lisp (NSColor *col)
2091 /* --------------------------------------------------------------------------
2092 Convert a color to a lisp string with the RGB equivalent
2093 -------------------------------------------------------------------------- */
2094 {
2095 EmacsCGFloat red, green, blue, alpha, gray;
2096 char buf[1024];
2097 const char *str;
2098 NSTRACE ("ns_color_to_lisp");
2099
2100 block_input ();
2101 if ([[col colorSpaceName] isEqualToString: NSNamedColorSpace])
2102
2103 if ((str =[[col colorNameComponent] UTF8String]))
2104 {
2105 unblock_input ();
2106 return build_string ((char *)str);
2107 }
2108
2109 [[col colorUsingDefaultColorSpace]
2110 getRed: &red green: &green blue: &blue alpha: &alpha];
2111 if (red == green && red == blue)
2112 {
2113 [[col colorUsingColorSpaceName: NSCalibratedWhiteColorSpace]
2114 getWhite: &gray alpha: &alpha];
2115 snprintf (buf, sizeof (buf), "#%2.2lx%2.2lx%2.2lx",
2116 lrint (gray * 0xff), lrint (gray * 0xff), lrint (gray * 0xff));
2117 unblock_input ();
2118 return build_string (buf);
2119 }
2120
2121 snprintf (buf, sizeof (buf), "#%2.2lx%2.2lx%2.2lx",
2122 lrint (red*0xff), lrint (green*0xff), lrint (blue*0xff));
2123
2124 unblock_input ();
2125 return build_string (buf);
2126 }
2127
2128
2129 void
2130 ns_query_color(void *col, XColor *color_def, int setPixel)
2131 /* --------------------------------------------------------------------------
2132 Get ARGB values out of NSColor col and put them into color_def.
2133 If setPixel, set the pixel to a concatenated version.
2134 and set color_def pixel to the resulting index.
2135 -------------------------------------------------------------------------- */
2136 {
2137 EmacsCGFloat r, g, b, a;
2138
2139 [((NSColor *)col) getRed: &r green: &g blue: &b alpha: &a];
2140 color_def->red = r * 65535;
2141 color_def->green = g * 65535;
2142 color_def->blue = b * 65535;
2143
2144 if (setPixel == YES)
2145 color_def->pixel
2146 = ARGB_TO_ULONG((int)(a*255),
2147 (int)(r*255), (int)(g*255), (int)(b*255));
2148 }
2149
2150
2151 bool
2152 ns_defined_color (struct frame *f,
2153 const char *name,
2154 XColor *color_def,
2155 bool alloc,
2156 bool makeIndex)
2157 /* --------------------------------------------------------------------------
2158 Return true if named color found, and set color_def rgb accordingly.
2159 If makeIndex and alloc are nonzero put the color in the color_table,
2160 and set color_def pixel to the resulting index.
2161 If makeIndex is zero, set color_def pixel to ARGB.
2162 Return false if not found
2163 -------------------------------------------------------------------------- */
2164 {
2165 NSColor *col;
2166 NSTRACE_WHEN (NSTRACE_GROUP_COLOR, "ns_defined_color");
2167
2168 block_input ();
2169 if (ns_get_color (name, &col) != 0) /* Color not found */
2170 {
2171 unblock_input ();
2172 return 0;
2173 }
2174 if (makeIndex && alloc)
2175 color_def->pixel = ns_index_color (col, f);
2176 ns_query_color (col, color_def, !makeIndex);
2177 unblock_input ();
2178 return 1;
2179 }
2180
2181
2182 void
2183 x_set_frame_alpha (struct frame *f)
2184 /* --------------------------------------------------------------------------
2185 change the entire-frame transparency
2186 -------------------------------------------------------------------------- */
2187 {
2188 struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
2189 double alpha = 1.0;
2190 double alpha_min = 1.0;
2191
2192 NSTRACE ("x_set_frame_alpha");
2193
2194 if (dpyinfo->x_highlight_frame == f)
2195 alpha = f->alpha[0];
2196 else
2197 alpha = f->alpha[1];
2198
2199 if (FLOATP (Vframe_alpha_lower_limit))
2200 alpha_min = XFLOAT_DATA (Vframe_alpha_lower_limit);
2201 else if (INTEGERP (Vframe_alpha_lower_limit))
2202 alpha_min = (XINT (Vframe_alpha_lower_limit)) / 100.0;
2203
2204 if (alpha < 0.0)
2205 return;
2206 else if (1.0 < alpha)
2207 alpha = 1.0;
2208 else if (0.0 <= alpha && alpha < alpha_min && alpha_min <= 1.0)
2209 alpha = alpha_min;
2210
2211 #ifdef NS_IMPL_COCOA
2212 {
2213 EmacsView *view = FRAME_NS_VIEW (f);
2214 [[view window] setAlphaValue: alpha];
2215 }
2216 #endif
2217 }
2218
2219
2220 /* ==========================================================================
2221
2222 Mouse handling
2223
2224 ========================================================================== */
2225
2226
2227 void
2228 frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y)
2229 /* --------------------------------------------------------------------------
2230 Programmatically reposition mouse pointer in pixel coordinates
2231 -------------------------------------------------------------------------- */
2232 {
2233 NSTRACE ("frame_set_mouse_pixel_position");
2234 ns_raise_frame (f);
2235 #if 0
2236 /* FIXME: this does not work, and what about GNUstep? */
2237 #ifdef NS_IMPL_COCOA
2238 [FRAME_NS_VIEW (f) lockFocus];
2239 PSsetmouse ((float)pix_x, (float)pix_y);
2240 [FRAME_NS_VIEW (f) unlockFocus];
2241 #endif
2242 #endif
2243 }
2244
2245 static int
2246 note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y)
2247 /* ------------------------------------------------------------------------
2248 Called by EmacsView on mouseMovement events. Passes on
2249 to emacs mainstream code if we moved off of a rect of interest
2250 known as last_mouse_glyph.
2251 ------------------------------------------------------------------------ */
2252 {
2253 struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
2254 NSRect *r;
2255
2256 // NSTRACE ("note_mouse_movement");
2257
2258 dpyinfo->last_mouse_motion_frame = frame;
2259 r = &dpyinfo->last_mouse_glyph;
2260
2261 /* Note, this doesn't get called for enter/leave, since we don't have a
2262 position. Those are taken care of in the corresponding NSView methods. */
2263
2264 /* has movement gone beyond last rect we were tracking? */
2265 if (x < r->origin.x || x >= r->origin.x + r->size.width
2266 || y < r->origin.y || y >= r->origin.y + r->size.height)
2267 {
2268 ns_update_begin (frame);
2269 frame->mouse_moved = 1;
2270 note_mouse_highlight (frame, x, y);
2271 remember_mouse_glyph (frame, x, y, r);
2272 ns_update_end (frame);
2273 return 1;
2274 }
2275
2276 return 0;
2277 }
2278
2279
2280 static void
2281 ns_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window,
2282 enum scroll_bar_part *part, Lisp_Object *x, Lisp_Object *y,
2283 Time *time)
2284 /* --------------------------------------------------------------------------
2285 External (hook): inform emacs about mouse position and hit parts.
2286 If a scrollbar is being dragged, set bar_window, part, x, y, time.
2287 x & y should be position in the scrollbar (the whole bar, not the handle)
2288 and length of scrollbar respectively
2289 -------------------------------------------------------------------------- */
2290 {
2291 id view;
2292 NSPoint position;
2293 Lisp_Object frame, tail;
2294 struct frame *f;
2295 struct ns_display_info *dpyinfo;
2296
2297 NSTRACE ("ns_mouse_position");
2298
2299 if (*fp == NULL)
2300 {
2301 fprintf (stderr, "Warning: ns_mouse_position () called with null *fp.\n");
2302 return;
2303 }
2304
2305 dpyinfo = FRAME_DISPLAY_INFO (*fp);
2306
2307 block_input ();
2308
2309 /* Clear the mouse-moved flag for every frame on this display. */
2310 FOR_EACH_FRAME (tail, frame)
2311 if (FRAME_NS_P (XFRAME (frame))
2312 && FRAME_NS_DISPLAY (XFRAME (frame)) == FRAME_NS_DISPLAY (*fp))
2313 XFRAME (frame)->mouse_moved = 0;
2314
2315 dpyinfo->last_mouse_scroll_bar = nil;
2316 if (dpyinfo->last_mouse_frame
2317 && FRAME_LIVE_P (dpyinfo->last_mouse_frame))
2318 f = dpyinfo->last_mouse_frame;
2319 else
2320 f = dpyinfo->x_focus_frame ? dpyinfo->x_focus_frame : SELECTED_FRAME ();
2321
2322 if (f && FRAME_NS_P (f))
2323 {
2324 view = FRAME_NS_VIEW (*fp);
2325
2326 position = [[view window] mouseLocationOutsideOfEventStream];
2327 position = [view convertPoint: position fromView: nil];
2328 remember_mouse_glyph (f, position.x, position.y,
2329 &dpyinfo->last_mouse_glyph);
2330 NSTRACE_POINT ("position", position);
2331
2332 if (bar_window) *bar_window = Qnil;
2333 if (part) *part = scroll_bar_above_handle;
2334
2335 if (x) XSETINT (*x, lrint (position.x));
2336 if (y) XSETINT (*y, lrint (position.y));
2337 if (time)
2338 *time = dpyinfo->last_mouse_movement_time;
2339 *fp = f;
2340 }
2341
2342 unblock_input ();
2343 }
2344
2345
2346 static void
2347 ns_frame_up_to_date (struct frame *f)
2348 /* --------------------------------------------------------------------------
2349 External (hook): Fix up mouse highlighting right after a full update.
2350 Can't use FRAME_MOUSE_UPDATE due to ns_frame_begin and ns_frame_end calls.
2351 -------------------------------------------------------------------------- */
2352 {
2353 NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_frame_up_to_date");
2354
2355 if (FRAME_NS_P (f))
2356 {
2357 Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
2358 if (f == hlinfo->mouse_face_mouse_frame)
2359 {
2360 block_input ();
2361 ns_update_begin(f);
2362 note_mouse_highlight (hlinfo->mouse_face_mouse_frame,
2363 hlinfo->mouse_face_mouse_x,
2364 hlinfo->mouse_face_mouse_y);
2365 ns_update_end(f);
2366 unblock_input ();
2367 }
2368 }
2369 }
2370
2371
2372 static void
2373 ns_define_frame_cursor (struct frame *f, Cursor cursor)
2374 /* --------------------------------------------------------------------------
2375 External (RIF): set frame mouse pointer type.
2376 -------------------------------------------------------------------------- */
2377 {
2378 NSTRACE ("ns_define_frame_cursor");
2379 if (FRAME_POINTER_TYPE (f) != cursor)
2380 {
2381 EmacsView *view = FRAME_NS_VIEW (f);
2382 FRAME_POINTER_TYPE (f) = cursor;
2383 [[view window] invalidateCursorRectsForView: view];
2384 /* Redisplay assumes this function also draws the changed frame
2385 cursor, but this function doesn't, so do it explicitly. */
2386 x_update_cursor (f, 1);
2387 }
2388 }
2389
2390
2391
2392 /* ==========================================================================
2393
2394 Keyboard handling
2395
2396 ========================================================================== */
2397
2398
2399 static unsigned
2400 ns_convert_key (unsigned code)
2401 /* --------------------------------------------------------------------------
2402 Internal call used by NSView-keyDown.
2403 -------------------------------------------------------------------------- */
2404 {
2405 const unsigned last_keysym = ARRAYELTS (convert_ns_to_X_keysym);
2406 unsigned keysym;
2407 /* An array would be faster, but less easy to read. */
2408 for (keysym = 0; keysym < last_keysym; keysym += 2)
2409 if (code == convert_ns_to_X_keysym[keysym])
2410 return 0xFF00 | convert_ns_to_X_keysym[keysym+1];
2411 return 0;
2412 /* if decide to use keyCode and Carbon table, use this line:
2413 return code > 0xff ? 0 : 0xFF00 | ns_keycode_to_xkeysym_table[code]; */
2414 }
2415
2416
2417 char *
2418 x_get_keysym_name (int keysym)
2419 /* --------------------------------------------------------------------------
2420 Called by keyboard.c. Not sure if the return val is important, except
2421 that it be unique.
2422 -------------------------------------------------------------------------- */
2423 {
2424 static char value[16];
2425 NSTRACE ("x_get_keysym_name");
2426 sprintf (value, "%d", keysym);
2427 return value;
2428 }
2429
2430
2431
2432 /* ==========================================================================
2433
2434 Block drawing operations
2435
2436 ========================================================================== */
2437
2438
2439 static void
2440 ns_redraw_scroll_bars (struct frame *f)
2441 {
2442 int i;
2443 id view;
2444 NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews];
2445 NSTRACE ("ns_redraw_scroll_bars");
2446 for (i =[subviews count]-1; i >= 0; i--)
2447 {
2448 view = [subviews objectAtIndex: i];
2449 if (![view isKindOfClass: [EmacsScroller class]]) continue;
2450 [view display];
2451 }
2452 }
2453
2454
2455 void
2456 ns_clear_frame (struct frame *f)
2457 /* --------------------------------------------------------------------------
2458 External (hook): Erase the entire frame
2459 -------------------------------------------------------------------------- */
2460 {
2461 NSView *view = FRAME_NS_VIEW (f);
2462 NSRect r;
2463
2464 NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_clear_frame");
2465
2466 /* comes on initial frame because we have
2467 after-make-frame-functions = select-frame */
2468 if (!FRAME_DEFAULT_FACE (f))
2469 return;
2470
2471 mark_window_cursors_off (XWINDOW (FRAME_ROOT_WINDOW (f)));
2472
2473 r = [view bounds];
2474
2475 block_input ();
2476 ns_focus (f, &r, 1);
2477 [ns_lookup_indexed_color (NS_FACE_BACKGROUND (FRAME_DEFAULT_FACE (f)), f) set];
2478 NSRectFill (r);
2479 ns_unfocus (f);
2480
2481 /* as of 2006/11 or so this is now needed */
2482 ns_redraw_scroll_bars (f);
2483 unblock_input ();
2484 }
2485
2486
2487 static void
2488 ns_clear_frame_area (struct frame *f, int x, int y, int width, int height)
2489 /* --------------------------------------------------------------------------
2490 External (RIF): Clear section of frame
2491 -------------------------------------------------------------------------- */
2492 {
2493 NSRect r = NSMakeRect (x, y, width, height);
2494 NSView *view = FRAME_NS_VIEW (f);
2495 struct face *face = FRAME_DEFAULT_FACE (f);
2496
2497 if (!view || !face)
2498 return;
2499
2500 NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_clear_frame_area");
2501
2502 r = NSIntersectionRect (r, [view frame]);
2503 ns_focus (f, &r, 1);
2504 [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), f) set];
2505
2506 NSRectFill (r);
2507
2508 ns_unfocus (f);
2509 return;
2510 }
2511
2512 static void
2513 ns_copy_bits (struct frame *f, NSRect src, NSRect dest)
2514 {
2515 NSTRACE ("ns_copy_bits");
2516
2517 if (FRAME_NS_VIEW (f))
2518 {
2519 hide_bell(); // Ensure the bell image isn't scrolled.
2520
2521 ns_focus (f, &dest, 1);
2522 [FRAME_NS_VIEW (f) scrollRect: src
2523 by: NSMakeSize (dest.origin.x - src.origin.x,
2524 dest.origin.y - src.origin.y)];
2525 ns_unfocus (f);
2526 }
2527 }
2528
2529 static void
2530 ns_scroll_run (struct window *w, struct run *run)
2531 /* --------------------------------------------------------------------------
2532 External (RIF): Insert or delete n lines at line vpos
2533 -------------------------------------------------------------------------- */
2534 {
2535 struct frame *f = XFRAME (w->frame);
2536 int x, y, width, height, from_y, to_y, bottom_y;
2537
2538 NSTRACE ("ns_scroll_run");
2539
2540 /* begin copy from other terms */
2541 /* Get frame-relative bounding box of the text display area of W,
2542 without mode lines. Include in this box the left and right
2543 fringe of W. */
2544 window_box (w, ANY_AREA, &x, &y, &width, &height);
2545
2546 from_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->current_y);
2547 to_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->desired_y);
2548 bottom_y = y + height;
2549
2550 if (to_y < from_y)
2551 {
2552 /* Scrolling up. Make sure we don't copy part of the mode
2553 line at the bottom. */
2554 if (from_y + run->height > bottom_y)
2555 height = bottom_y - from_y;
2556 else
2557 height = run->height;
2558 }
2559 else
2560 {
2561 /* Scrolling down. Make sure we don't copy over the mode line.
2562 at the bottom. */
2563 if (to_y + run->height > bottom_y)
2564 height = bottom_y - to_y;
2565 else
2566 height = run->height;
2567 }
2568 /* end copy from other terms */
2569
2570 if (height == 0)
2571 return;
2572
2573 block_input ();
2574
2575 x_clear_cursor (w);
2576
2577 {
2578 NSRect srcRect = NSMakeRect (x, from_y, width, height);
2579 NSRect dstRect = NSMakeRect (x, to_y, width, height);
2580
2581 ns_copy_bits (f, srcRect , dstRect);
2582 }
2583
2584 unblock_input ();
2585 }
2586
2587
2588 static void
2589 ns_after_update_window_line (struct window *w, struct glyph_row *desired_row)
2590 /* --------------------------------------------------------------------------
2591 External (RIF): preparatory to fringe update after text was updated
2592 -------------------------------------------------------------------------- */
2593 {
2594 struct frame *f;
2595 int width, height;
2596
2597 NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_after_update_window_line");
2598
2599 /* begin copy from other terms */
2600 eassert (w);
2601
2602 if (!desired_row->mode_line_p && !w->pseudo_window_p)
2603 desired_row->redraw_fringe_bitmaps_p = 1;
2604
2605 /* When a window has disappeared, make sure that no rest of
2606 full-width rows stays visible in the internal border. */
2607 if (windows_or_buffers_changed
2608 && desired_row->full_width_p
2609 && (f = XFRAME (w->frame),
2610 width = FRAME_INTERNAL_BORDER_WIDTH (f),
2611 width != 0)
2612 && (height = desired_row->visible_height,
2613 height > 0))
2614 {
2615 int y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, desired_row->y));
2616
2617 block_input ();
2618 ns_clear_frame_area (f, 0, y, width, height);
2619 ns_clear_frame_area (f,
2620 FRAME_PIXEL_WIDTH (f) - width,
2621 y, width, height);
2622 unblock_input ();
2623 }
2624 }
2625
2626
2627 static void
2628 ns_shift_glyphs_for_insert (struct frame *f,
2629 int x, int y, int width, int height,
2630 int shift_by)
2631 /* --------------------------------------------------------------------------
2632 External (RIF): copy an area horizontally, don't worry about clearing src
2633 -------------------------------------------------------------------------- */
2634 {
2635 NSRect srcRect = NSMakeRect (x, y, width, height);
2636 NSRect dstRect = NSMakeRect (x+shift_by, y, width, height);
2637
2638 NSTRACE ("ns_shift_glyphs_for_insert");
2639
2640 ns_copy_bits (f, srcRect, dstRect);
2641 }
2642
2643
2644
2645 /* ==========================================================================
2646
2647 Character encoding and metrics
2648
2649 ========================================================================== */
2650
2651
2652 static void
2653 ns_compute_glyph_string_overhangs (struct glyph_string *s)
2654 /* --------------------------------------------------------------------------
2655 External (RIF); compute left/right overhang of whole string and set in s
2656 -------------------------------------------------------------------------- */
2657 {
2658 struct font *font = s->font;
2659
2660 if (s->char2b)
2661 {
2662 struct font_metrics metrics;
2663 unsigned int codes[2];
2664 codes[0] = *(s->char2b);
2665 codes[1] = *(s->char2b + s->nchars - 1);
2666
2667 font->driver->text_extents (font, codes, 2, &metrics);
2668 s->left_overhang = -metrics.lbearing;
2669 s->right_overhang
2670 = metrics.rbearing > metrics.width
2671 ? metrics.rbearing - metrics.width : 0;
2672 }
2673 else
2674 {
2675 s->left_overhang = 0;
2676 if (EQ (font->driver->type, Qns))
2677 s->right_overhang = ((struct nsfont_info *)font)->ital ?
2678 FONT_HEIGHT (font) * 0.2 : 0;
2679 else
2680 s->right_overhang = 0;
2681 }
2682 }
2683
2684
2685
2686 /* ==========================================================================
2687
2688 Fringe and cursor drawing
2689
2690 ========================================================================== */
2691
2692
2693 extern int max_used_fringe_bitmap;
2694 static void
2695 ns_draw_fringe_bitmap (struct window *w, struct glyph_row *row,
2696 struct draw_fringe_bitmap_params *p)
2697 /* --------------------------------------------------------------------------
2698 External (RIF); fringe-related
2699 -------------------------------------------------------------------------- */
2700 {
2701 /* Fringe bitmaps comes in two variants, normal and periodic. A
2702 periodic bitmap is used to create a continuous pattern. Since a
2703 bitmap is rendered one text line at a time, the start offset (dh)
2704 of the bitmap varies. Concretely, this is used for the empty
2705 line indicator.
2706
2707 For a bitmap, "h + dh" is the full height and is always
2708 invariant. For a normal bitmap "dh" is zero.
2709
2710 For example, when the period is three and the full height is 72
2711 the following combinations exists:
2712
2713 h=72 dh=0
2714 h=71 dh=1
2715 h=70 dh=2 */
2716
2717 struct frame *f = XFRAME (WINDOW_FRAME (w));
2718 struct face *face = p->face;
2719 static EmacsImage **bimgs = NULL;
2720 static int nBimgs = 0;
2721
2722 NSTRACE_WHEN (NSTRACE_GROUP_FRINGE, "ns_draw_fringe_bitmap");
2723 NSTRACE_MSG ("which:%d cursor:%d overlay:%d width:%d height:%d period:%d",
2724 p->which, p->cursor_p, p->overlay_p, p->wd, p->h, p->dh);
2725
2726 /* grow bimgs if needed */
2727 if (nBimgs < max_used_fringe_bitmap)
2728 {
2729 bimgs = xrealloc (bimgs, max_used_fringe_bitmap * sizeof *bimgs);
2730 memset (bimgs + nBimgs, 0,
2731 (max_used_fringe_bitmap - nBimgs) * sizeof *bimgs);
2732 nBimgs = max_used_fringe_bitmap;
2733 }
2734
2735 /* Must clip because of partially visible lines. */
2736 ns_clip_to_row (w, row, ANY_AREA, YES);
2737
2738 if (!p->overlay_p)
2739 {
2740 int bx = p->bx, by = p->by, nx = p->nx, ny = p->ny;
2741
2742 if (bx >= 0 && nx > 0)
2743 {
2744 NSRect r = NSMakeRect (bx, by, nx, ny);
2745 NSRectClip (r);
2746 [ns_lookup_indexed_color (face->background, f) set];
2747 NSRectFill (r);
2748 }
2749 }
2750
2751 if (p->which)
2752 {
2753 NSRect r = NSMakeRect (p->x, p->y, p->wd, p->h);
2754 EmacsImage *img = bimgs[p->which - 1];
2755
2756 if (!img)
2757 {
2758 // Note: For "periodic" images, allocate one EmacsImage for
2759 // the base image, and use it for all dh:s.
2760 unsigned short *bits = p->bits;
2761 int full_height = p->h + p->dh;
2762 int i;
2763 unsigned char *cbits = xmalloc (full_height);
2764
2765 for (i = 0; i < full_height; i++)
2766 cbits[i] = bits[i];
2767 img = [[EmacsImage alloc] initFromXBM: cbits width: 8
2768 height: full_height
2769 fg: 0 bg: 0];
2770 bimgs[p->which - 1] = img;
2771 xfree (cbits);
2772 }
2773
2774 NSTRACE_RECT ("r", r);
2775
2776 NSRectClip (r);
2777 /* Since we composite the bitmap instead of just blitting it, we need
2778 to erase the whole background. */
2779 [ns_lookup_indexed_color(face->background, f) set];
2780 NSRectFill (r);
2781
2782 {
2783 NSColor *bm_color;
2784 if (!p->cursor_p)
2785 bm_color = ns_lookup_indexed_color(face->foreground, f);
2786 else if (p->overlay_p)
2787 bm_color = ns_lookup_indexed_color(face->background, f);
2788 else
2789 bm_color = f->output_data.ns->cursor_color;
2790 [img setXBMColor: bm_color];
2791 }
2792
2793 #ifdef NS_IMPL_COCOA
2794 // Note: For periodic images, the full image height is "h + hd".
2795 // By using the height h, a suitable part of the image is used.
2796 NSRect fromRect = NSMakeRect(0, 0, p->wd, p->h);
2797
2798 NSTRACE_RECT ("fromRect", fromRect);
2799
2800 [img drawInRect: r
2801 fromRect: fromRect
2802 operation: NSCompositeSourceOver
2803 fraction: 1.0
2804 respectFlipped: YES
2805 hints: nil];
2806 #else
2807 {
2808 NSPoint pt = r.origin;
2809 pt.y += p->h;
2810 [img compositeToPoint: pt operation: NSCompositeSourceOver];
2811 }
2812 #endif
2813 }
2814 ns_unfocus (f);
2815 }
2816
2817
2818 static void
2819 ns_draw_window_cursor (struct window *w, struct glyph_row *glyph_row,
2820 int x, int y, enum text_cursor_kinds cursor_type,
2821 int cursor_width, bool on_p, bool active_p)
2822 /* --------------------------------------------------------------------------
2823 External call (RIF): draw cursor.
2824 Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
2825 -------------------------------------------------------------------------- */
2826 {
2827 NSRect r, s;
2828 int fx, fy, h, cursor_height;
2829 struct frame *f = WINDOW_XFRAME (w);
2830 struct glyph *phys_cursor_glyph;
2831 struct glyph *cursor_glyph;
2832 struct face *face;
2833 NSColor *hollow_color = FRAME_BACKGROUND_COLOR (f);
2834
2835 /* If cursor is out of bounds, don't draw garbage. This can happen
2836 in mini-buffer windows when switching between echo area glyphs
2837 and mini-buffer. */
2838
2839 NSTRACE ("ns_draw_window_cursor");
2840
2841 if (!on_p)
2842 return;
2843
2844 w->phys_cursor_type = cursor_type;
2845 w->phys_cursor_on_p = on_p;
2846
2847 if (cursor_type == NO_CURSOR)
2848 {
2849 w->phys_cursor_width = 0;
2850 return;
2851 }
2852
2853 if ((phys_cursor_glyph = get_phys_cursor_glyph (w)) == NULL)
2854 {
2855 if (glyph_row->exact_window_width_line_p
2856 && w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])
2857 {
2858 glyph_row->cursor_in_fringe_p = 1;
2859 draw_fringe_bitmap (w, glyph_row, 0);
2860 }
2861 return;
2862 }
2863
2864 /* We draw the cursor (with NSRectFill), then draw the glyph on top
2865 (other terminals do it the other way round). We must set
2866 w->phys_cursor_width to the cursor width. For bar cursors, that
2867 is CURSOR_WIDTH; for box cursors, it is the glyph width. */
2868 get_phys_cursor_geometry (w, glyph_row, phys_cursor_glyph, &fx, &fy, &h);
2869
2870 /* The above get_phys_cursor_geometry call set w->phys_cursor_width
2871 to the glyph width; replace with CURSOR_WIDTH for (V)BAR cursors. */
2872 if (cursor_type == BAR_CURSOR)
2873 {
2874 if (cursor_width < 1)
2875 cursor_width = max (FRAME_CURSOR_WIDTH (f), 1);
2876 w->phys_cursor_width = cursor_width;
2877 }
2878 /* If we have an HBAR, "cursor_width" MAY specify height. */
2879 else if (cursor_type == HBAR_CURSOR)
2880 {
2881 cursor_height = (cursor_width < 1) ? lrint (0.25 * h) : cursor_width;
2882 if (cursor_height > glyph_row->height)
2883 cursor_height = glyph_row->height;
2884 if (h > cursor_height) // Cursor smaller than line height, move down
2885 fy += h - cursor_height;
2886 h = cursor_height;
2887 }
2888
2889 r.origin.x = fx, r.origin.y = fy;
2890 r.size.height = h;
2891 r.size.width = w->phys_cursor_width;
2892
2893 /* TODO: only needed in rare cases with last-resort font in HELLO..
2894 should we do this more efficiently? */
2895 ns_clip_to_row (w, glyph_row, ANY_AREA, NO); /* do ns_focus(f, &r, 1); if remove */
2896
2897
2898 face = FACE_FROM_ID (f, phys_cursor_glyph->face_id);
2899 if (face && NS_FACE_BACKGROUND (face)
2900 == ns_index_color (FRAME_CURSOR_COLOR (f), f))
2901 {
2902 [ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), f) set];
2903 hollow_color = FRAME_CURSOR_COLOR (f);
2904 }
2905 else
2906 [FRAME_CURSOR_COLOR (f) set];
2907
2908 #ifdef NS_IMPL_COCOA
2909 /* TODO: This makes drawing of cursor plus that of phys_cursor_glyph
2910 atomic. Cleaner ways of doing this should be investigated.
2911 One way would be to set a global variable DRAWING_CURSOR
2912 when making the call to draw_phys..(), don't focus in that
2913 case, then move the ns_unfocus() here after that call. */
2914 NSDisableScreenUpdates ();
2915 #endif
2916
2917 switch (cursor_type)
2918 {
2919 case DEFAULT_CURSOR:
2920 case NO_CURSOR:
2921 break;
2922 case FILLED_BOX_CURSOR:
2923 NSRectFill (r);
2924 break;
2925 case HOLLOW_BOX_CURSOR:
2926 NSRectFill (r);
2927 [hollow_color set];
2928 NSRectFill (NSInsetRect (r, 1, 1));
2929 [FRAME_CURSOR_COLOR (f) set];
2930 break;
2931 case HBAR_CURSOR:
2932 NSRectFill (r);
2933 break;
2934 case BAR_CURSOR:
2935 s = r;
2936 /* If the character under cursor is R2L, draw the bar cursor
2937 on the right of its glyph, rather than on the left. */
2938 cursor_glyph = get_phys_cursor_glyph (w);
2939 if ((cursor_glyph->resolved_level & 1) != 0)
2940 s.origin.x += cursor_glyph->pixel_width - s.size.width;
2941
2942 NSRectFill (s);
2943 break;
2944 }
2945 ns_unfocus (f);
2946
2947 /* draw the character under the cursor */
2948 if (cursor_type != NO_CURSOR)
2949 draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
2950
2951 #ifdef NS_IMPL_COCOA
2952 NSEnableScreenUpdates ();
2953 #endif
2954
2955 }
2956
2957
2958 static void
2959 ns_draw_vertical_window_border (struct window *w, int x, int y0, int y1)
2960 /* --------------------------------------------------------------------------
2961 External (RIF): Draw a vertical line.
2962 -------------------------------------------------------------------------- */
2963 {
2964 struct frame *f = XFRAME (WINDOW_FRAME (w));
2965 struct face *face;
2966 NSRect r = NSMakeRect (x, y0, 1, y1-y0);
2967
2968 NSTRACE ("ns_draw_vertical_window_border");
2969
2970 face = FACE_FROM_ID (f, VERTICAL_BORDER_FACE_ID);
2971 if (face)
2972 [ns_lookup_indexed_color(face->foreground, f) set];
2973
2974 ns_focus (f, &r, 1);
2975 NSRectFill(r);
2976 ns_unfocus (f);
2977 }
2978
2979
2980 static void
2981 ns_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1)
2982 /* --------------------------------------------------------------------------
2983 External (RIF): Draw a window divider.
2984 -------------------------------------------------------------------------- */
2985 {
2986 struct frame *f = XFRAME (WINDOW_FRAME (w));
2987 struct face *face;
2988 NSRect r = NSMakeRect (x0, y0, x1-x0, y1-y0);
2989
2990 NSTRACE ("ns_draw_window_divider");
2991
2992 face = FACE_FROM_ID (f, WINDOW_DIVIDER_FACE_ID);
2993 if (face)
2994 [ns_lookup_indexed_color(face->foreground, f) set];
2995
2996 ns_focus (f, &r, 1);
2997 NSRectFill(r);
2998 ns_unfocus (f);
2999 }
3000
3001 static void
3002 ns_show_hourglass (struct frame *f)
3003 {
3004 /* TODO: add NSProgressIndicator to all frames. */
3005 }
3006
3007 static void
3008 ns_hide_hourglass (struct frame *f)
3009 {
3010 /* TODO: remove NSProgressIndicator from all frames. */
3011 }
3012
3013 /* ==========================================================================
3014
3015 Glyph drawing operations
3016
3017 ========================================================================== */
3018
3019 static int
3020 ns_get_glyph_string_clip_rect (struct glyph_string *s, NativeRectangle *nr)
3021 /* --------------------------------------------------------------------------
3022 Wrapper utility to account for internal border width on full-width lines,
3023 and allow top full-width rows to hit the frame top. nr should be pointer
3024 to two successive NSRects. Number of rects actually used is returned.
3025 -------------------------------------------------------------------------- */
3026 {
3027 int n = get_glyph_string_clip_rects (s, nr, 2);
3028 return n;
3029 }
3030
3031 /* --------------------------------------------------------------------
3032 Draw a wavy line under glyph string s. The wave fills wave_height
3033 pixels from y.
3034
3035 x wave_length = 2
3036 --
3037 y * * * * *
3038 |* * * * * * * * *
3039 wave_height = 3 | * * * *
3040 --------------------------------------------------------------------- */
3041
3042 static void
3043 ns_draw_underwave (struct glyph_string *s, EmacsCGFloat width, EmacsCGFloat x)
3044 {
3045 int wave_height = 3, wave_length = 2;
3046 int y, dx, dy, odd, xmax;
3047 NSPoint a, b;
3048 NSRect waveClip;
3049
3050 dx = wave_length;
3051 dy = wave_height - 1;
3052 y = s->ybase - wave_height + 3;
3053 xmax = x + width;
3054
3055 /* Find and set clipping rectangle */
3056 waveClip = NSMakeRect (x, y, width, wave_height);
3057 [[NSGraphicsContext currentContext] saveGraphicsState];
3058 NSRectClip (waveClip);
3059
3060 /* Draw the waves */
3061 a.x = x - ((int)(x) % dx) + (EmacsCGFloat) 0.5;
3062 b.x = a.x + dx;
3063 odd = (int)(a.x/dx) % 2;
3064 a.y = b.y = y + 0.5;
3065
3066 if (odd)
3067 a.y += dy;
3068 else
3069 b.y += dy;
3070
3071 while (a.x <= xmax)
3072 {
3073 [NSBezierPath strokeLineFromPoint:a toPoint:b];
3074 a.x = b.x, a.y = b.y;
3075 b.x += dx, b.y = y + 0.5 + odd*dy;
3076 odd = !odd;
3077 }
3078
3079 /* Restore previous clipping rectangle(s) */
3080 [[NSGraphicsContext currentContext] restoreGraphicsState];
3081 }
3082
3083
3084
3085 void
3086 ns_draw_text_decoration (struct glyph_string *s, struct face *face,
3087 NSColor *defaultCol, CGFloat width, CGFloat x)
3088 /* --------------------------------------------------------------------------
3089 Draw underline, overline, and strike-through on glyph string s.
3090 -------------------------------------------------------------------------- */
3091 {
3092 if (s->for_overlaps)
3093 return;
3094
3095 /* Do underline. */
3096 if (face->underline_p)
3097 {
3098 if (s->face->underline_type == FACE_UNDER_WAVE)
3099 {
3100 if (face->underline_defaulted_p)
3101 [defaultCol set];
3102 else
3103 [ns_lookup_indexed_color (face->underline_color, s->f) set];
3104
3105 ns_draw_underwave (s, width, x);
3106 }
3107 else if (s->face->underline_type == FACE_UNDER_LINE)
3108 {
3109
3110 NSRect r;
3111 unsigned long thickness, position;
3112
3113 /* If the prev was underlined, match its appearance. */
3114 if (s->prev && s->prev->face->underline_p
3115 && s->prev->face->underline_type == FACE_UNDER_LINE
3116 && s->prev->underline_thickness > 0)
3117 {
3118 thickness = s->prev->underline_thickness;
3119 position = s->prev->underline_position;
3120 }
3121 else
3122 {
3123 struct font *font;
3124 unsigned long descent;
3125
3126 font=s->font;
3127 descent = s->y + s->height - s->ybase;
3128
3129 /* Use underline thickness of font, defaulting to 1. */
3130 thickness = (font && font->underline_thickness > 0)
3131 ? font->underline_thickness : 1;
3132
3133 /* Determine the offset of underlining from the baseline. */
3134 if (x_underline_at_descent_line)
3135 position = descent - thickness;
3136 else if (x_use_underline_position_properties
3137 && font && font->underline_position >= 0)
3138 position = font->underline_position;
3139 else if (font)
3140 position = lround (font->descent / 2);
3141 else
3142 position = underline_minimum_offset;
3143
3144 position = max (position, underline_minimum_offset);
3145
3146 /* Ensure underlining is not cropped. */
3147 if (descent <= position)
3148 {
3149 position = descent - 1;
3150 thickness = 1;
3151 }
3152 else if (descent < position + thickness)
3153 thickness = 1;
3154 }
3155
3156 s->underline_thickness = thickness;
3157 s->underline_position = position;
3158
3159 r = NSMakeRect (x, s->ybase + position, width, thickness);
3160
3161 if (face->underline_defaulted_p)
3162 [defaultCol set];
3163 else
3164 [ns_lookup_indexed_color (face->underline_color, s->f) set];
3165 NSRectFill (r);
3166 }
3167 }
3168 /* Do overline. We follow other terms in using a thickness of 1
3169 and ignoring overline_margin. */
3170 if (face->overline_p)
3171 {
3172 NSRect r;
3173 r = NSMakeRect (x, s->y, width, 1);
3174
3175 if (face->overline_color_defaulted_p)
3176 [defaultCol set];
3177 else
3178 [ns_lookup_indexed_color (face->overline_color, s->f) set];
3179 NSRectFill (r);
3180 }
3181
3182 /* Do strike-through. We follow other terms for thickness and
3183 vertical position.*/
3184 if (face->strike_through_p)
3185 {
3186 NSRect r;
3187 unsigned long dy;
3188
3189 dy = lrint ((s->height - 1) / 2);
3190 r = NSMakeRect (x, s->y + dy, width, 1);
3191
3192 if (face->strike_through_color_defaulted_p)
3193 [defaultCol set];
3194 else
3195 [ns_lookup_indexed_color (face->strike_through_color, s->f) set];
3196 NSRectFill (r);
3197 }
3198 }
3199
3200 static void
3201 ns_draw_box (NSRect r, CGFloat thickness, NSColor *col,
3202 char left_p, char right_p)
3203 /* --------------------------------------------------------------------------
3204 Draw an unfilled rect inside r, optionally leaving left and/or right open.
3205 Note we can't just use an NSDrawRect command, because of the possibility
3206 of some sides not being drawn, and because the rect will be filled.
3207 -------------------------------------------------------------------------- */
3208 {
3209 NSRect s = r;
3210 [col set];
3211
3212 /* top, bottom */
3213 s.size.height = thickness;
3214 NSRectFill (s);
3215 s.origin.y += r.size.height - thickness;
3216 NSRectFill (s);
3217
3218 s.size.height = r.size.height;
3219 s.origin.y = r.origin.y;
3220
3221 /* left, right (optional) */
3222 s.size.width = thickness;
3223 if (left_p)
3224 NSRectFill (s);
3225 if (right_p)
3226 {
3227 s.origin.x += r.size.width - thickness;
3228 NSRectFill (s);
3229 }
3230 }
3231
3232
3233 static void
3234 ns_draw_relief (NSRect r, int thickness, char raised_p,
3235 char top_p, char bottom_p, char left_p, char right_p,
3236 struct glyph_string *s)
3237 /* --------------------------------------------------------------------------
3238 Draw a relief rect inside r, optionally leaving some sides open.
3239 Note we can't just use an NSDrawBezel command, because of the possibility
3240 of some sides not being drawn, and because the rect will be filled.
3241 -------------------------------------------------------------------------- */
3242 {
3243 static NSColor *baseCol = nil, *lightCol = nil, *darkCol = nil;
3244 NSColor *newBaseCol = nil;
3245 NSRect sr = r;
3246
3247 NSTRACE ("ns_draw_relief");
3248
3249 /* set up colors */
3250
3251 if (s->face->use_box_color_for_shadows_p)
3252 {
3253 newBaseCol = ns_lookup_indexed_color (s->face->box_color, s->f);
3254 }
3255 /* else if (s->first_glyph->type == IMAGE_GLYPH
3256 && s->img->pixmap
3257 && !IMAGE_BACKGROUND_TRANSPARENT (s->img, s->f, 0))
3258 {
3259 newBaseCol = IMAGE_BACKGROUND (s->img, s->f, 0);
3260 } */
3261 else
3262 {
3263 newBaseCol = ns_lookup_indexed_color (s->face->background, s->f);
3264 }
3265
3266 if (newBaseCol == nil)
3267 newBaseCol = [NSColor grayColor];
3268
3269 if (newBaseCol != baseCol) /* TODO: better check */
3270 {
3271 [baseCol release];
3272 baseCol = [newBaseCol retain];
3273 [lightCol release];
3274 lightCol = [[baseCol highlightWithLevel: 0.2] retain];
3275 [darkCol release];
3276 darkCol = [[baseCol shadowWithLevel: 0.3] retain];
3277 }
3278
3279 [(raised_p ? lightCol : darkCol) set];
3280
3281 /* TODO: mitering. Using NSBezierPath doesn't work because of color switch. */
3282
3283 /* top */
3284 sr.size.height = thickness;
3285 if (top_p) NSRectFill (sr);
3286
3287 /* left */
3288 sr.size.height = r.size.height;
3289 sr.size.width = thickness;
3290 if (left_p) NSRectFill (sr);
3291
3292 [(raised_p ? darkCol : lightCol) set];
3293
3294 /* bottom */
3295 sr.size.width = r.size.width;
3296 sr.size.height = thickness;
3297 sr.origin.y += r.size.height - thickness;
3298 if (bottom_p) NSRectFill (sr);
3299
3300 /* right */
3301 sr.size.height = r.size.height;
3302 sr.origin.y = r.origin.y;
3303 sr.size.width = thickness;
3304 sr.origin.x += r.size.width - thickness;
3305 if (right_p) NSRectFill (sr);
3306 }
3307
3308
3309 static void
3310 ns_dumpglyphs_box_or_relief (struct glyph_string *s)
3311 /* --------------------------------------------------------------------------
3312 Function modeled after x_draw_glyph_string_box ().
3313 Sets up parameters for drawing.
3314 -------------------------------------------------------------------------- */
3315 {
3316 int right_x, last_x;
3317 char left_p, right_p;
3318 struct glyph *last_glyph;
3319 NSRect r;
3320 int thickness;
3321 struct face *face;
3322
3323 if (s->hl == DRAW_MOUSE_FACE)
3324 {
3325 face = FACE_FROM_ID (s->f, MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3326 if (!face)
3327 face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3328 }
3329 else
3330 face = s->face;
3331
3332 thickness = face->box_line_width;
3333
3334 NSTRACE ("ns_dumpglyphs_box_or_relief");
3335
3336 last_x = ((s->row->full_width_p && !s->w->pseudo_window_p)
3337 ? WINDOW_RIGHT_EDGE_X (s->w)
3338 : window_box_right (s->w, s->area));
3339 last_glyph = (s->cmp || s->img
3340 ? s->first_glyph : s->first_glyph + s->nchars-1);
3341
3342 right_x = ((s->row->full_width_p && s->extends_to_end_of_line_p
3343 ? last_x - 1 : min (last_x, s->x + s->background_width) - 1));
3344
3345 left_p = (s->first_glyph->left_box_line_p
3346 || (s->hl == DRAW_MOUSE_FACE
3347 && (s->prev == NULL || s->prev->hl != s->hl)));
3348 right_p = (last_glyph->right_box_line_p
3349 || (s->hl == DRAW_MOUSE_FACE
3350 && (s->next == NULL || s->next->hl != s->hl)));
3351
3352 r = NSMakeRect (s->x, s->y, right_x - s->x + 1, s->height);
3353
3354 /* TODO: Sometimes box_color is 0 and this seems wrong; should investigate. */
3355 if (s->face->box == FACE_SIMPLE_BOX && s->face->box_color)
3356 {
3357 ns_draw_box (r, abs (thickness),
3358 ns_lookup_indexed_color (face->box_color, s->f),
3359 left_p, right_p);
3360 }
3361 else
3362 {
3363 ns_draw_relief (r, abs (thickness), s->face->box == FACE_RAISED_BOX,
3364 1, 1, left_p, right_p, s);
3365 }
3366 }
3367
3368
3369 static void
3370 ns_maybe_dumpglyphs_background (struct glyph_string *s, char force_p)
3371 /* --------------------------------------------------------------------------
3372 Modeled after x_draw_glyph_string_background, which draws BG in
3373 certain cases. Others are left to the text rendering routine.
3374 -------------------------------------------------------------------------- */
3375 {
3376 NSTRACE ("ns_maybe_dumpglyphs_background");
3377
3378 if (!s->background_filled_p/* || s->hl == DRAW_MOUSE_FACE*/)
3379 {
3380 int box_line_width = max (s->face->box_line_width, 0);
3381 if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width
3382 /* When xdisp.c ignores FONT_HEIGHT, we cannot trust font
3383 dimensions, since the actual glyphs might be much
3384 smaller. So in that case we always clear the rectangle
3385 with background color. */
3386 || FONT_TOO_HIGH (s->font)
3387 || s->font_not_found_p || s->extends_to_end_of_line_p || force_p)
3388 {
3389 struct face *face;
3390 if (s->hl == DRAW_MOUSE_FACE)
3391 {
3392 face = FACE_FROM_ID (s->f,
3393 MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3394 if (!face)
3395 face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3396 }
3397 else
3398 face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3399 if (!face->stipple)
3400 [(NS_FACE_BACKGROUND (face) != 0
3401 ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
3402 : FRAME_BACKGROUND_COLOR (s->f)) set];
3403 else
3404 {
3405 struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
3406 [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set];
3407 }
3408
3409 if (s->hl != DRAW_CURSOR)
3410 {
3411 NSRect r = NSMakeRect (s->x, s->y + box_line_width,
3412 s->background_width,
3413 s->height-2*box_line_width);
3414 NSRectFill (r);
3415 }
3416
3417 s->background_filled_p = 1;
3418 }
3419 }
3420 }
3421
3422
3423 static void
3424 ns_dumpglyphs_image (struct glyph_string *s, NSRect r)
3425 /* --------------------------------------------------------------------------
3426 Renders an image and associated borders.
3427 -------------------------------------------------------------------------- */
3428 {
3429 EmacsImage *img = s->img->pixmap;
3430 int box_line_vwidth = max (s->face->box_line_width, 0);
3431 int x = s->x, y = s->ybase - image_ascent (s->img, s->face, &s->slice);
3432 int bg_x, bg_y, bg_height;
3433 int th;
3434 char raised_p;
3435 NSRect br;
3436 struct face *face;
3437 NSColor *tdCol;
3438
3439 NSTRACE ("ns_dumpglyphs_image");
3440
3441 if (s->face->box != FACE_NO_BOX
3442 && s->first_glyph->left_box_line_p && s->slice.x == 0)
3443 x += abs (s->face->box_line_width);
3444
3445 bg_x = x;
3446 bg_y = s->slice.y == 0 ? s->y : s->y + box_line_vwidth;
3447 bg_height = s->height;
3448 /* other terms have this, but was causing problems w/tabbar mode */
3449 /* - 2 * box_line_vwidth; */
3450
3451 if (s->slice.x == 0) x += s->img->hmargin;
3452 if (s->slice.y == 0) y += s->img->vmargin;
3453
3454 /* Draw BG: if we need larger area than image itself cleared, do that,
3455 otherwise, since we composite the image under NS (instead of mucking
3456 with its background color), we must clear just the image area. */
3457 if (s->hl == DRAW_MOUSE_FACE)
3458 {
3459 face = FACE_FROM_ID (s->f, MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3460 if (!face)
3461 face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3462 }
3463 else
3464 face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3465
3466 [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f) set];
3467
3468 if (bg_height > s->slice.height || s->img->hmargin || s->img->vmargin
3469 || s->img->mask || s->img->pixmap == 0 || s->width != s->background_width)
3470 {
3471 br = NSMakeRect (bg_x, bg_y, s->background_width, bg_height);
3472 s->background_filled_p = 1;
3473 }
3474 else
3475 {
3476 br = NSMakeRect (x, y, s->slice.width, s->slice.height);
3477 }
3478
3479 NSRectFill (br);
3480
3481 /* Draw the image.. do we need to draw placeholder if img ==nil? */
3482 if (img != nil)
3483 {
3484 #ifdef NS_IMPL_COCOA
3485 NSRect dr = NSMakeRect (x, y, s->slice.width, s->slice.height);
3486 NSRect ir = NSMakeRect (s->slice.x, s->slice.y,
3487 s->slice.width, s->slice.height);
3488 [img drawInRect: dr
3489 fromRect: ir
3490 operation: NSCompositeSourceOver
3491 fraction: 1.0
3492 respectFlipped: YES
3493 hints: nil];
3494 #else
3495 [img compositeToPoint: NSMakePoint (x, y + s->slice.height)
3496 operation: NSCompositeSourceOver];
3497 #endif
3498 }
3499
3500 if (s->hl == DRAW_CURSOR)
3501 {
3502 [FRAME_CURSOR_COLOR (s->f) set];
3503 if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
3504 tdCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
3505 else
3506 /* Currently on NS img->mask is always 0. Since
3507 get_window_cursor_type specifies a hollow box cursor when on
3508 a non-masked image we never reach this clause. But we put it
3509 in in anticipation of better support for image masks on
3510 NS. */
3511 tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3512 }
3513 else
3514 {
3515 tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3516 }
3517
3518 /* Draw underline, overline, strike-through. */
3519 ns_draw_text_decoration (s, face, tdCol, br.size.width, br.origin.x);
3520
3521 /* Draw relief, if requested */
3522 if (s->img->relief || s->hl ==DRAW_IMAGE_RAISED || s->hl ==DRAW_IMAGE_SUNKEN)
3523 {
3524 if (s->hl == DRAW_IMAGE_SUNKEN || s->hl == DRAW_IMAGE_RAISED)
3525 {
3526 th = tool_bar_button_relief >= 0 ?
3527 tool_bar_button_relief : DEFAULT_TOOL_BAR_BUTTON_RELIEF;
3528 raised_p = (s->hl == DRAW_IMAGE_RAISED);
3529 }
3530 else
3531 {
3532 th = abs (s->img->relief);
3533 raised_p = (s->img->relief > 0);
3534 }
3535
3536 r.origin.x = x - th;
3537 r.origin.y = y - th;
3538 r.size.width = s->slice.width + 2*th-1;
3539 r.size.height = s->slice.height + 2*th-1;
3540 ns_draw_relief (r, th, raised_p,
3541 s->slice.y == 0,
3542 s->slice.y + s->slice.height == s->img->height,
3543 s->slice.x == 0,
3544 s->slice.x + s->slice.width == s->img->width, s);
3545 }
3546
3547 /* If there is no mask, the background won't be seen,
3548 so draw a rectangle on the image for the cursor.
3549 Do this for all images, getting transparency right is not reliable. */
3550 if (s->hl == DRAW_CURSOR)
3551 {
3552 int thickness = abs (s->img->relief);
3553 if (thickness == 0) thickness = 1;
3554 ns_draw_box (br, thickness, FRAME_CURSOR_COLOR (s->f), 1, 1);
3555 }
3556 }
3557
3558
3559 static void
3560 ns_dumpglyphs_stretch (struct glyph_string *s)
3561 {
3562 NSRect r[2];
3563 int n, i;
3564 struct face *face;
3565 NSColor *fgCol, *bgCol;
3566
3567 if (!s->background_filled_p)
3568 {
3569 n = ns_get_glyph_string_clip_rect (s, r);
3570 *r = NSMakeRect (s->x, s->y, s->background_width, s->height);
3571
3572 ns_focus (s->f, r, n);
3573
3574 if (s->hl == DRAW_MOUSE_FACE)
3575 {
3576 face = FACE_FROM_ID (s->f, MOUSE_HL_INFO (s->f)->mouse_face_face_id);
3577 if (!face)
3578 face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
3579 }
3580 else
3581 face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
3582
3583 bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
3584 fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
3585
3586 for (i = 0; i < n; ++i)
3587 {
3588 if (!s->row->full_width_p)
3589 {
3590 int overrun, leftoverrun;
3591
3592 /* truncate to avoid overwriting fringe and/or scrollbar */
3593 overrun = max (0, (s->x + s->background_width)
3594 - (WINDOW_BOX_RIGHT_EDGE_X (s->w)
3595 - WINDOW_RIGHT_FRINGE_WIDTH (s->w)));
3596 r[i].size.width -= overrun;
3597
3598 /* truncate to avoid overwriting to left of the window box */
3599 leftoverrun = (WINDOW_BOX_LEFT_EDGE_X (s->w)
3600 + WINDOW_LEFT_FRINGE_WIDTH (s->w)) - s->x;
3601
3602 if (leftoverrun > 0)
3603 {
3604 r[i].origin.x += leftoverrun;
3605 r[i].size.width -= leftoverrun;
3606 }
3607
3608 /* XXX: Try to work between problem where a stretch glyph on
3609 a partially-visible bottom row will clear part of the
3610 modeline, and another where list-buffers headers and similar
3611 rows erroneously have visible_height set to 0. Not sure
3612 where this is coming from as other terms seem not to show. */
3613 r[i].size.height = min (s->height, s->row->visible_height);
3614 }
3615
3616 [bgCol set];
3617
3618 /* NOTE: under NS this is NOT used to draw cursors, but we must avoid
3619 overwriting cursor (usually when cursor on a tab) */
3620 if (s->hl == DRAW_CURSOR)
3621 {
3622 CGFloat x, width;
3623
3624 x = r[i].origin.x;
3625 width = s->w->phys_cursor_width;
3626 r[i].size.width -= width;
3627 r[i].origin.x += width;
3628
3629 NSRectFill (r[i]);
3630
3631 /* Draw overlining, etc. on the cursor. */
3632 if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
3633 ns_draw_text_decoration (s, face, bgCol, width, x);
3634 else
3635 ns_draw_text_decoration (s, face, fgCol, width, x);
3636 }
3637 else
3638 {
3639 NSRectFill (r[i]);
3640 }
3641
3642 /* Draw overlining, etc. on the stretch glyph (or the part
3643 of the stretch glyph after the cursor). */
3644 ns_draw_text_decoration (s, face, fgCol, r[i].size.width,
3645 r[i].origin.x);
3646 }
3647 ns_unfocus (s->f);
3648 s->background_filled_p = 1;
3649 }
3650 }
3651
3652
3653 static void
3654 ns_draw_composite_glyph_string_foreground (struct glyph_string *s)
3655 {
3656 int i, j, x;
3657 struct font *font = s->font;
3658
3659 /* If first glyph of S has a left box line, start drawing the text
3660 of S to the right of that box line. */
3661 if (s->face && s->face->box != FACE_NO_BOX
3662 && s->first_glyph->left_box_line_p)
3663 x = s->x + eabs (s->face->box_line_width);
3664 else
3665 x = s->x;
3666
3667 /* S is a glyph string for a composition. S->cmp_from is the index
3668 of the first character drawn for glyphs of this composition.
3669 S->cmp_from == 0 means we are drawing the very first character of
3670 this composition. */
3671
3672 /* Draw a rectangle for the composition if the font for the very
3673 first character of the composition could not be loaded. */
3674 if (s->font_not_found_p)
3675 {
3676 if (s->cmp_from == 0)
3677 {
3678 NSRect r = NSMakeRect (s->x, s->y, s->width-1, s->height -1);
3679 ns_draw_box (r, 1, FRAME_CURSOR_COLOR (s->f), 1, 1);
3680 }
3681 }
3682 else if (! s->first_glyph->u.cmp.automatic)
3683 {
3684 int y = s->ybase;
3685
3686 for (i = 0, j = s->cmp_from; i < s->nchars; i++, j++)
3687 /* TAB in a composition means display glyphs with padding
3688 space on the left or right. */
3689 if (COMPOSITION_GLYPH (s->cmp, j) != '\t')
3690 {
3691 int xx = x + s->cmp->offsets[j * 2];
3692 int yy = y - s->cmp->offsets[j * 2 + 1];
3693
3694 font->driver->draw (s, j, j + 1, xx, yy, false);
3695 if (s->face->overstrike)
3696 font->driver->draw (s, j, j + 1, xx + 1, yy, false);
3697 }
3698 }
3699 else
3700 {
3701 Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
3702 Lisp_Object glyph;
3703 int y = s->ybase;
3704 int width = 0;
3705
3706 for (i = j = s->cmp_from; i < s->cmp_to; i++)
3707 {
3708 glyph = LGSTRING_GLYPH (gstring, i);
3709 if (NILP (LGLYPH_ADJUSTMENT (glyph)))
3710 width += LGLYPH_WIDTH (glyph);
3711 else
3712 {
3713 int xoff, yoff, wadjust;
3714
3715 if (j < i)
3716 {
3717 font->driver->draw (s, j, i, x, y, false);
3718 if (s->face->overstrike)
3719 font->driver->draw (s, j, i, x + 1, y, false);
3720 x += width;
3721 }
3722 xoff = LGLYPH_XOFF (glyph);
3723 yoff = LGLYPH_YOFF (glyph);
3724 wadjust = LGLYPH_WADJUST (glyph);
3725 font->driver->draw (s, i, i + 1, x + xoff, y + yoff, false);
3726 if (s->face->overstrike)
3727 font->driver->draw (s, i, i + 1, x + xoff + 1, y + yoff,
3728 false);
3729 x += wadjust;
3730 j = i + 1;
3731 width = 0;
3732 }
3733 }
3734 if (j < i)
3735 {
3736 font->driver->draw (s, j, i, x, y, false);
3737 if (s->face->overstrike)
3738 font->driver->draw (s, j, i, x + 1, y, false);
3739 }
3740 }
3741 }
3742
3743 static void
3744 ns_draw_glyph_string (struct glyph_string *s)
3745 /* --------------------------------------------------------------------------
3746 External (RIF): Main draw-text call.
3747 -------------------------------------------------------------------------- */
3748 {
3749 /* TODO (optimize): focus for box and contents draw */
3750 NSRect r[2];
3751 int n, flags;
3752 char box_drawn_p = 0;
3753 struct font *font = s->face->font;
3754 if (! font) font = FRAME_FONT (s->f);
3755
3756 NSTRACE_WHEN (NSTRACE_GROUP_GLYPHS, "ns_draw_glyph_string");
3757
3758 if (s->next && s->right_overhang && !s->for_overlaps/*&&s->hl!=DRAW_CURSOR*/)
3759 {
3760 int width;
3761 struct glyph_string *next;
3762
3763 for (width = 0, next = s->next;
3764 next && width < s->right_overhang;
3765 width += next->width, next = next->next)
3766 if (next->first_glyph->type != IMAGE_GLYPH)
3767 {
3768 if (next->first_glyph->type != STRETCH_GLYPH)
3769 {
3770 n = ns_get_glyph_string_clip_rect (s->next, r);
3771 ns_focus (s->f, r, n);
3772 ns_maybe_dumpglyphs_background (s->next, 1);
3773 ns_unfocus (s->f);
3774 }
3775 else
3776 {
3777 ns_dumpglyphs_stretch (s->next);
3778 }
3779 next->num_clips = 0;
3780 }
3781 }
3782
3783 if (!s->for_overlaps && s->face->box != FACE_NO_BOX
3784 && (s->first_glyph->type == CHAR_GLYPH
3785 || s->first_glyph->type == COMPOSITE_GLYPH))
3786 {
3787 n = ns_get_glyph_string_clip_rect (s, r);
3788 ns_focus (s->f, r, n);
3789 ns_maybe_dumpglyphs_background (s, 1);
3790 ns_dumpglyphs_box_or_relief (s);
3791 ns_unfocus (s->f);
3792 box_drawn_p = 1;
3793 }
3794
3795 switch (s->first_glyph->type)
3796 {
3797
3798 case IMAGE_GLYPH:
3799 n = ns_get_glyph_string_clip_rect (s, r);
3800 ns_focus (s->f, r, n);
3801 ns_dumpglyphs_image (s, r[0]);
3802 ns_unfocus (s->f);
3803 break;
3804
3805 case STRETCH_GLYPH:
3806 ns_dumpglyphs_stretch (s);
3807 break;
3808
3809 case CHAR_GLYPH:
3810 case COMPOSITE_GLYPH:
3811 n = ns_get_glyph_string_clip_rect (s, r);
3812 ns_focus (s->f, r, n);
3813
3814 if (s->for_overlaps || (s->cmp_from > 0
3815 && ! s->first_glyph->u.cmp.automatic))
3816 s->background_filled_p = 1;
3817 else
3818 ns_maybe_dumpglyphs_background
3819 (s, s->first_glyph->type == COMPOSITE_GLYPH);
3820
3821 flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
3822 (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
3823 (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
3824 NS_DUMPGLYPH_NORMAL));
3825
3826 if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
3827 {
3828 unsigned long tmp = NS_FACE_BACKGROUND (s->face);
3829 NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
3830 NS_FACE_FOREGROUND (s->face) = tmp;
3831 }
3832
3833 {
3834 BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
3835
3836 if (isComposite)
3837 ns_draw_composite_glyph_string_foreground (s);
3838 else
3839 font->driver->draw
3840 (s, s->cmp_from, s->nchars, s->x, s->ybase,
3841 (flags == NS_DUMPGLYPH_NORMAL && !s->background_filled_p)
3842 || flags == NS_DUMPGLYPH_MOUSEFACE);
3843 }
3844
3845 {
3846 NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
3847 ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face),
3848 s->f)
3849 : FRAME_FOREGROUND_COLOR (s->f));
3850 [col set];
3851
3852 /* Draw underline, overline, strike-through. */
3853 ns_draw_text_decoration (s, s->face, col, s->width, s->x);
3854 }
3855
3856 if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
3857 {
3858 unsigned long tmp = NS_FACE_BACKGROUND (s->face);
3859 NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
3860 NS_FACE_FOREGROUND (s->face) = tmp;
3861 }
3862
3863 ns_unfocus (s->f);
3864 break;
3865
3866 case GLYPHLESS_GLYPH:
3867 n = ns_get_glyph_string_clip_rect (s, r);
3868 ns_focus (s->f, r, n);
3869
3870 if (s->for_overlaps || (s->cmp_from > 0
3871 && ! s->first_glyph->u.cmp.automatic))
3872 s->background_filled_p = 1;
3873 else
3874 ns_maybe_dumpglyphs_background
3875 (s, s->first_glyph->type == COMPOSITE_GLYPH);
3876 /* ... */
3877 /* Not yet implemented. */
3878 /* ... */
3879 ns_unfocus (s->f);
3880 break;
3881
3882 default:
3883 emacs_abort ();
3884 }
3885
3886 /* Draw box if not done already. */
3887 if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX)
3888 {
3889 n = ns_get_glyph_string_clip_rect (s, r);
3890 ns_focus (s->f, r, n);
3891 ns_dumpglyphs_box_or_relief (s);
3892 ns_unfocus (s->f);
3893 }
3894
3895 s->num_clips = 0;
3896 }
3897
3898
3899
3900 /* ==========================================================================
3901
3902 Event loop
3903
3904 ========================================================================== */
3905
3906
3907 static void
3908 ns_send_appdefined (int value)
3909 /* --------------------------------------------------------------------------
3910 Internal: post an appdefined event which EmacsApp-sendEvent will
3911 recognize and take as a command to halt the event loop.
3912 -------------------------------------------------------------------------- */
3913 {
3914 NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_send_appdefined(%d)", value);
3915
3916 #ifdef NS_IMPL_GNUSTEP
3917 // GNUstep needs postEvent to happen on the main thread.
3918 if (! [[NSThread currentThread] isMainThread])
3919 {
3920 EmacsApp *app = (EmacsApp *)NSApp;
3921 app->nextappdefined = value;
3922 [app performSelectorOnMainThread:@selector (sendFromMainThread:)
3923 withObject:nil
3924 waitUntilDone:YES];
3925 return;
3926 }
3927 #endif
3928
3929 /* Only post this event if we haven't already posted one. This will end
3930 the [NXApp run] main loop after having processed all events queued at
3931 this moment. */
3932
3933 #ifdef NS_IMPL_COCOA
3934 if (! send_appdefined)
3935 {
3936 /* OSX 10.10.1 swallows the AppDefined event we are sending ourselves
3937 in certain situations (rapid incoming events).
3938 So check if we have one, if not add one. */
3939 NSEvent *appev = [NSApp nextEventMatchingMask:NSApplicationDefinedMask
3940 untilDate:[NSDate distantPast]
3941 inMode:NSDefaultRunLoopMode
3942 dequeue:NO];
3943 if (! appev) send_appdefined = YES;
3944 }
3945 #endif
3946
3947 if (send_appdefined)
3948 {
3949 NSEvent *nxev;
3950
3951 /* We only need one NX_APPDEFINED event to stop NXApp from running. */
3952 send_appdefined = NO;
3953
3954 /* Don't need wakeup timer any more */
3955 if (timed_entry)
3956 {
3957 [timed_entry invalidate];
3958 [timed_entry release];
3959 timed_entry = nil;
3960 }
3961
3962 nxev = [NSEvent otherEventWithType: NSApplicationDefined
3963 location: NSMakePoint (0, 0)
3964 modifierFlags: 0
3965 timestamp: 0
3966 windowNumber: [[NSApp mainWindow] windowNumber]
3967 context: [NSApp context]
3968 subtype: 0
3969 data1: value
3970 data2: 0];
3971
3972 /* Post an application defined event on the event queue. When this is
3973 received the [NXApp run] will return, thus having processed all
3974 events which are currently queued. */
3975 [NSApp postEvent: nxev atStart: NO];
3976 }
3977 }
3978
3979 #ifdef HAVE_NATIVE_FS
3980 static void
3981 check_native_fs ()
3982 {
3983 Lisp_Object frame, tail;
3984
3985 if (ns_last_use_native_fullscreen == ns_use_native_fullscreen)
3986 return;
3987
3988 ns_last_use_native_fullscreen = ns_use_native_fullscreen;
3989
3990 FOR_EACH_FRAME (tail, frame)
3991 {
3992 struct frame *f = XFRAME (frame);
3993 if (FRAME_NS_P (f))
3994 {
3995 EmacsView *view = FRAME_NS_VIEW (f);
3996 [view updateCollectionBehavior];
3997 }
3998 }
3999 }
4000 #endif
4001
4002 /* GNUstep does not have cancelTracking. */
4003 #ifdef NS_IMPL_COCOA
4004 /* Check if menu open should be canceled or continued as normal. */
4005 void
4006 ns_check_menu_open (NSMenu *menu)
4007 {
4008 /* Click in menu bar? */
4009 NSArray *a = [[NSApp mainMenu] itemArray];
4010 int i;
4011 BOOL found = NO;
4012
4013 if (menu == nil) // Menu tracking ended.
4014 {
4015 if (menu_will_open_state == MENU_OPENING)
4016 menu_will_open_state = MENU_NONE;
4017 return;
4018 }
4019
4020 for (i = 0; ! found && i < [a count]; i++)
4021 found = menu == [[a objectAtIndex:i] submenu];
4022 if (found)
4023 {
4024 if (menu_will_open_state == MENU_NONE && emacs_event)
4025 {
4026 NSEvent *theEvent = [NSApp currentEvent];
4027 struct frame *emacsframe = SELECTED_FRAME ();
4028
4029 [menu cancelTracking];
4030 menu_will_open_state = MENU_PENDING;
4031 emacs_event->kind = MENU_BAR_ACTIVATE_EVENT;
4032 EV_TRAILER (theEvent);
4033
4034 CGEventRef ourEvent = CGEventCreate (NULL);
4035 menu_mouse_point = CGEventGetLocation (ourEvent);
4036 CFRelease (ourEvent);
4037 }
4038 else if (menu_will_open_state == MENU_OPENING)
4039 {
4040 menu_will_open_state = MENU_NONE;
4041 }
4042 }
4043 }
4044
4045 /* Redo saved menu click if state is MENU_PENDING. */
4046 void
4047 ns_check_pending_open_menu ()
4048 {
4049 if (menu_will_open_state == MENU_PENDING)
4050 {
4051 CGEventSourceRef source
4052 = CGEventSourceCreate (kCGEventSourceStateHIDSystemState);
4053
4054 CGEventRef event = CGEventCreateMouseEvent (source,
4055 kCGEventLeftMouseDown,
4056 menu_mouse_point,
4057 kCGMouseButtonLeft);
4058 CGEventSetType (event, kCGEventLeftMouseDown);
4059 CGEventPost (kCGHIDEventTap, event);
4060 CFRelease (event);
4061 CFRelease (source);
4062
4063 menu_will_open_state = MENU_OPENING;
4064 }
4065 }
4066 #endif /* NS_IMPL_COCOA */
4067
4068 static void
4069 unwind_apploopnr (Lisp_Object not_used)
4070 {
4071 --apploopnr;
4072 n_emacs_events_pending = 0;
4073 ns_finish_events ();
4074 q_event_ptr = NULL;
4075 }
4076
4077 static int
4078 ns_read_socket (struct terminal *terminal, struct input_event *hold_quit)
4079 /* --------------------------------------------------------------------------
4080 External (hook): Post an event to ourself and keep reading events until
4081 we read it back again. In effect process all events which were waiting.
4082 From 21+ we have to manage the event buffer ourselves.
4083 -------------------------------------------------------------------------- */
4084 {
4085 struct input_event ev;
4086 int nevents;
4087
4088 NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_read_socket");
4089
4090 #ifdef HAVE_NATIVE_FS
4091 check_native_fs ();
4092 #endif
4093
4094 if ([NSApp modalWindow] != nil)
4095 return -1;
4096
4097 if (hold_event_q.nr > 0)
4098 {
4099 int i;
4100 for (i = 0; i < hold_event_q.nr; ++i)
4101 kbd_buffer_store_event_hold (&hold_event_q.q[i], hold_quit);
4102 hold_event_q.nr = 0;
4103 return i;
4104 }
4105
4106 block_input ();
4107 n_emacs_events_pending = 0;
4108 ns_init_events (&ev);
4109 q_event_ptr = hold_quit;
4110
4111 /* we manage autorelease pools by allocate/reallocate each time around
4112 the loop; strict nesting is occasionally violated but seems not to
4113 matter.. earlier methods using full nesting caused major memory leaks */
4114 [outerpool release];
4115 outerpool = [[NSAutoreleasePool alloc] init];
4116
4117 /* If have pending open-file requests, attend to the next one of those. */
4118 if (ns_pending_files && [ns_pending_files count] != 0
4119 && [(EmacsApp *)NSApp openFile: [ns_pending_files objectAtIndex: 0]])
4120 {
4121 [ns_pending_files removeObjectAtIndex: 0];
4122 }
4123 /* Deal with pending service requests. */
4124 else if (ns_pending_service_names && [ns_pending_service_names count] != 0
4125 && [(EmacsApp *)
4126 NSApp fulfillService: [ns_pending_service_names objectAtIndex: 0]
4127 withArg: [ns_pending_service_args objectAtIndex: 0]])
4128 {
4129 [ns_pending_service_names removeObjectAtIndex: 0];
4130 [ns_pending_service_args removeObjectAtIndex: 0];
4131 }
4132 else
4133 {
4134 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
4135 /* Run and wait for events. We must always send one NX_APPDEFINED event
4136 to ourself, otherwise [NXApp run] will never exit. */
4137 send_appdefined = YES;
4138 ns_send_appdefined (-1);
4139
4140 if (++apploopnr != 1)
4141 {
4142 emacs_abort ();
4143 }
4144 record_unwind_protect (unwind_apploopnr, Qt);
4145 [NSApp run];
4146 unbind_to (specpdl_count, Qnil); /* calls unwind_apploopnr */
4147 }
4148
4149 nevents = n_emacs_events_pending;
4150 n_emacs_events_pending = 0;
4151 ns_finish_events ();
4152 q_event_ptr = NULL;
4153 unblock_input ();
4154
4155 return nevents;
4156 }
4157
4158
4159 int
4160 ns_select (int nfds, fd_set *readfds, fd_set *writefds,
4161 fd_set *exceptfds, struct timespec const *timeout,
4162 sigset_t const *sigmask)
4163 /* --------------------------------------------------------------------------
4164 Replacement for select, checking for events
4165 -------------------------------------------------------------------------- */
4166 {
4167 int result;
4168 int t, k, nr = 0;
4169 struct input_event event;
4170 char c;
4171
4172 NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_select");
4173
4174 #ifdef HAVE_NATIVE_FS
4175 check_native_fs ();
4176 #endif
4177
4178 if (hold_event_q.nr > 0)
4179 {
4180 /* We already have events pending. */
4181 raise (SIGIO);
4182 errno = EINTR;
4183 return -1;
4184 }
4185
4186 for (k = 0; k < nfds+1; k++)
4187 {
4188 if (readfds && FD_ISSET(k, readfds)) ++nr;
4189 if (writefds && FD_ISSET(k, writefds)) ++nr;
4190 }
4191
4192 if (NSApp == nil
4193 || (timeout && timeout->tv_sec == 0 && timeout->tv_nsec == 0))
4194 return pselect (nfds, readfds, writefds, exceptfds, timeout, sigmask);
4195
4196 [outerpool release];
4197 outerpool = [[NSAutoreleasePool alloc] init];
4198
4199
4200 send_appdefined = YES;
4201 if (nr > 0)
4202 {
4203 pthread_mutex_lock (&select_mutex);
4204 select_nfds = nfds;
4205 select_valid = 0;
4206 if (readfds)
4207 {
4208 select_readfds = *readfds;
4209 select_valid += SELECT_HAVE_READ;
4210 }
4211 if (writefds)
4212 {
4213 select_writefds = *writefds;
4214 select_valid += SELECT_HAVE_WRITE;
4215 }
4216
4217 if (timeout)
4218 {
4219 select_timeout = *timeout;
4220 select_valid += SELECT_HAVE_TMO;
4221 }
4222
4223 pthread_mutex_unlock (&select_mutex);
4224
4225 /* Inform fd_handler that select should be called */
4226 c = 'g';
4227 emacs_write_sig (selfds[1], &c, 1);
4228 }
4229 else if (nr == 0 && timeout)
4230 {
4231 /* No file descriptor, just a timeout, no need to wake fd_handler */
4232 double time = timespectod (*timeout);
4233 timed_entry = [[NSTimer scheduledTimerWithTimeInterval: time
4234 target: NSApp
4235 selector:
4236 @selector (timeout_handler:)
4237 userInfo: 0
4238 repeats: NO]
4239 retain];
4240 }
4241 else /* No timeout and no file descriptors, can this happen? */
4242 {
4243 /* Send appdefined so we exit from the loop */
4244 ns_send_appdefined (-1);
4245 }
4246
4247 block_input ();
4248 ns_init_events (&event);
4249 if (++apploopnr != 1)
4250 {
4251 emacs_abort ();
4252 }
4253
4254 {
4255 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
4256 record_unwind_protect (unwind_apploopnr, Qt);
4257 [NSApp run];
4258 unbind_to (specpdl_count, Qnil); /* calls unwind_apploopnr */
4259 }
4260
4261 ns_finish_events ();
4262 if (nr > 0 && readfds)
4263 {
4264 c = 's';
4265 emacs_write_sig (selfds[1], &c, 1);
4266 }
4267 unblock_input ();
4268
4269 t = last_appdefined_event_data;
4270
4271 if (t != NO_APPDEFINED_DATA)
4272 {
4273 last_appdefined_event_data = NO_APPDEFINED_DATA;
4274
4275 if (t == -2)
4276 {
4277 /* The NX_APPDEFINED event we received was a timeout. */
4278 result = 0;
4279 }
4280 else if (t == -1)
4281 {
4282 /* The NX_APPDEFINED event we received was the result of
4283 at least one real input event arriving. */
4284 errno = EINTR;
4285 result = -1;
4286 }
4287 else
4288 {
4289 /* Received back from select () in fd_handler; copy the results */
4290 pthread_mutex_lock (&select_mutex);
4291 if (readfds) *readfds = select_readfds;
4292 if (writefds) *writefds = select_writefds;
4293 pthread_mutex_unlock (&select_mutex);
4294 result = t;
4295 }
4296 }
4297 else
4298 {
4299 errno = EINTR;
4300 result = -1;
4301 }
4302
4303 return result;
4304 }
4305
4306
4307
4308 /* ==========================================================================
4309
4310 Scrollbar handling
4311
4312 ========================================================================== */
4313
4314
4315 static void
4316 ns_set_vertical_scroll_bar (struct window *window,
4317 int portion, int whole, int position)
4318 /* --------------------------------------------------------------------------
4319 External (hook): Update or add scrollbar
4320 -------------------------------------------------------------------------- */
4321 {
4322 Lisp_Object win;
4323 NSRect r, v;
4324 struct frame *f = XFRAME (WINDOW_FRAME (window));
4325 EmacsView *view = FRAME_NS_VIEW (f);
4326 EmacsScroller *bar;
4327 int window_y, window_height;
4328 int top, left, height, width;
4329 BOOL update_p = YES;
4330
4331 /* optimization; display engine sends WAY too many of these.. */
4332 if (!NILP (window->vertical_scroll_bar))
4333 {
4334 bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4335 if ([bar checkSamePosition: position portion: portion whole: whole])
4336 {
4337 if (view->scrollbarsNeedingUpdate == 0)
4338 {
4339 if (!windows_or_buffers_changed)
4340 return;
4341 }
4342 else
4343 view->scrollbarsNeedingUpdate--;
4344 update_p = NO;
4345 }
4346 }
4347
4348 NSTRACE ("ns_set_vertical_scroll_bar");
4349
4350 /* Get dimensions. */
4351 window_box (window, ANY_AREA, 0, &window_y, 0, &window_height);
4352 top = window_y;
4353 height = window_height;
4354 width = WINDOW_CONFIG_SCROLL_BAR_COLS (window) * FRAME_COLUMN_WIDTH (f);
4355 left = WINDOW_SCROLL_BAR_AREA_X (window);
4356
4357 r = NSMakeRect (left, top, width, height);
4358 /* the parent view is flipped, so we need to flip y value */
4359 v = [view frame];
4360 r.origin.y = (v.size.height - r.size.height - r.origin.y);
4361
4362 XSETWINDOW (win, window);
4363 block_input ();
4364
4365 /* we want at least 5 lines to display a scrollbar */
4366 if (WINDOW_TOTAL_LINES (window) < 5)
4367 {
4368 if (!NILP (window->vertical_scroll_bar))
4369 {
4370 bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4371 [bar removeFromSuperview];
4372 wset_vertical_scroll_bar (window, Qnil);
4373 [bar release];
4374 }
4375 ns_clear_frame_area (f, left, top, width, height);
4376 unblock_input ();
4377 return;
4378 }
4379
4380 if (NILP (window->vertical_scroll_bar))
4381 {
4382 if (width > 0 && height > 0)
4383 ns_clear_frame_area (f, left, top, width, height);
4384
4385 bar = [[EmacsScroller alloc] initFrame: r window: win];
4386 wset_vertical_scroll_bar (window, make_save_ptr (bar));
4387 update_p = YES;
4388 }
4389 else
4390 {
4391 NSRect oldRect;
4392 bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4393 oldRect = [bar frame];
4394 r.size.width = oldRect.size.width;
4395 if (FRAME_LIVE_P (f) && !NSEqualRects (oldRect, r))
4396 {
4397 if (oldRect.origin.x != r.origin.x)
4398 ns_clear_frame_area (f, left, top, width, height);
4399 [bar setFrame: r];
4400 }
4401 }
4402
4403 if (update_p)
4404 [bar setPosition: position portion: portion whole: whole];
4405 unblock_input ();
4406 }
4407
4408
4409 static void
4410 ns_set_horizontal_scroll_bar (struct window *window,
4411 int portion, int whole, int position)
4412 /* --------------------------------------------------------------------------
4413 External (hook): Update or add scrollbar
4414 -------------------------------------------------------------------------- */
4415 {
4416 Lisp_Object win;
4417 NSRect r, v;
4418 struct frame *f = XFRAME (WINDOW_FRAME (window));
4419 EmacsView *view = FRAME_NS_VIEW (f);
4420 EmacsScroller *bar;
4421 int top, height, left, width;
4422 int window_x, window_width;
4423 BOOL update_p = YES;
4424
4425 /* optimization; display engine sends WAY too many of these.. */
4426 if (!NILP (window->horizontal_scroll_bar))
4427 {
4428 bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4429 if ([bar checkSamePosition: position portion: portion whole: whole])
4430 {
4431 if (view->scrollbarsNeedingUpdate == 0)
4432 {
4433 if (!windows_or_buffers_changed)
4434 return;
4435 }
4436 else
4437 view->scrollbarsNeedingUpdate--;
4438 update_p = NO;
4439 }
4440 }
4441
4442 NSTRACE ("ns_set_horizontal_scroll_bar");
4443
4444 /* Get dimensions. */
4445 window_box (window, ANY_AREA, 0, &window_x, &window_width, 0);
4446 left = window_x;
4447 width = window_width;
4448 height = WINDOW_CONFIG_SCROLL_BAR_LINES (window) * FRAME_LINE_HEIGHT (f);
4449 top = WINDOW_SCROLL_BAR_AREA_Y (window);
4450
4451 r = NSMakeRect (left, top, width, height);
4452 /* the parent view is flipped, so we need to flip y value */
4453 v = [view frame];
4454 /* ??????? PXW/scrollbars !!!!!!!!!!!!!!!!!!!! */
4455 r.origin.y = (v.size.height - r.size.height - r.origin.y);
4456
4457 XSETWINDOW (win, window);
4458 block_input ();
4459
4460 if (WINDOW_TOTAL_COLS (window) < 5)
4461 {
4462 if (!NILP (window->horizontal_scroll_bar))
4463 {
4464 bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4465 [bar removeFromSuperview];
4466 wset_horizontal_scroll_bar (window, Qnil);
4467 }
4468 ns_clear_frame_area (f, left, top, width, height);
4469 unblock_input ();
4470 return;
4471 }
4472
4473 if (NILP (window->horizontal_scroll_bar))
4474 {
4475 if (width > 0 && height > 0)
4476 ns_clear_frame_area (f, left, top, width, height);
4477
4478 bar = [[EmacsScroller alloc] initFrame: r window: win];
4479 wset_horizontal_scroll_bar (window, make_save_ptr (bar));
4480 update_p = YES;
4481 }
4482 else
4483 {
4484 NSRect oldRect;
4485 bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4486 oldRect = [bar frame];
4487 r.size.width = oldRect.size.width;
4488 if (FRAME_LIVE_P (f) && !NSEqualRects (oldRect, r))
4489 {
4490 if (oldRect.origin.x != r.origin.x)
4491 ns_clear_frame_area (f, left, top, width, height);
4492 [bar setFrame: r];
4493 update_p = YES;
4494 }
4495 }
4496
4497 if (update_p)
4498 [bar setPosition: position portion: portion whole: whole];
4499 unblock_input ();
4500 }
4501
4502
4503 static void
4504 ns_condemn_scroll_bars (struct frame *f)
4505 /* --------------------------------------------------------------------------
4506 External (hook): arrange for all frame's scrollbars to be removed
4507 at next call to judge_scroll_bars, except for those redeemed.
4508 -------------------------------------------------------------------------- */
4509 {
4510 int i;
4511 id view;
4512 NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews];
4513
4514 NSTRACE ("ns_condemn_scroll_bars");
4515
4516 for (i =[subviews count]-1; i >= 0; i--)
4517 {
4518 view = [subviews objectAtIndex: i];
4519 if ([view isKindOfClass: [EmacsScroller class]])
4520 [view condemn];
4521 }
4522 }
4523
4524
4525 static void
4526 ns_redeem_scroll_bar (struct window *window)
4527 /* --------------------------------------------------------------------------
4528 External (hook): arrange to spare this window's scrollbar
4529 at next call to judge_scroll_bars.
4530 -------------------------------------------------------------------------- */
4531 {
4532 id bar;
4533 NSTRACE ("ns_redeem_scroll_bar");
4534 if (!NILP (window->vertical_scroll_bar))
4535 {
4536 bar = XNS_SCROLL_BAR (window->vertical_scroll_bar);
4537 [bar reprieve];
4538 }
4539
4540 if (!NILP (window->horizontal_scroll_bar))
4541 {
4542 bar = XNS_SCROLL_BAR (window->horizontal_scroll_bar);
4543 [bar reprieve];
4544 }
4545 }
4546
4547
4548 static void
4549 ns_judge_scroll_bars (struct frame *f)
4550 /* --------------------------------------------------------------------------
4551 External (hook): destroy all scrollbars on frame that weren't
4552 redeemed after call to condemn_scroll_bars.
4553 -------------------------------------------------------------------------- */
4554 {
4555 int i;
4556 id view;
4557 EmacsView *eview = FRAME_NS_VIEW (f);
4558 NSArray *subviews = [[eview superview] subviews];
4559 BOOL removed = NO;
4560
4561 NSTRACE ("ns_judge_scroll_bars");
4562 for (i = [subviews count]-1; i >= 0; --i)
4563 {
4564 view = [subviews objectAtIndex: i];
4565 if (![view isKindOfClass: [EmacsScroller class]]) continue;
4566 if ([view judge])
4567 removed = YES;
4568 }
4569
4570 if (removed)
4571 [eview updateFrameSize: NO];
4572 }
4573
4574 /* ==========================================================================
4575
4576 Initialization
4577
4578 ========================================================================== */
4579
4580 int
4581 x_display_pixel_height (struct ns_display_info *dpyinfo)
4582 {
4583 NSArray *screens = [NSScreen screens];
4584 NSEnumerator *enumerator = [screens objectEnumerator];
4585 NSScreen *screen;
4586 NSRect frame;
4587
4588 frame = NSZeroRect;
4589 while ((screen = [enumerator nextObject]) != nil)
4590 frame = NSUnionRect (frame, [screen frame]);
4591
4592 return NSHeight (frame);
4593 }
4594
4595 int
4596 x_display_pixel_width (struct ns_display_info *dpyinfo)
4597 {
4598 NSArray *screens = [NSScreen screens];
4599 NSEnumerator *enumerator = [screens objectEnumerator];
4600 NSScreen *screen;
4601 NSRect frame;
4602
4603 frame = NSZeroRect;
4604 while ((screen = [enumerator nextObject]) != nil)
4605 frame = NSUnionRect (frame, [screen frame]);
4606
4607 return NSWidth (frame);
4608 }
4609
4610
4611 static Lisp_Object ns_string_to_lispmod (const char *s)
4612 /* --------------------------------------------------------------------------
4613 Convert modifier name to lisp symbol
4614 -------------------------------------------------------------------------- */
4615 {
4616 if (!strncmp (SSDATA (SYMBOL_NAME (Qmeta)), s, 10))
4617 return Qmeta;
4618 else if (!strncmp (SSDATA (SYMBOL_NAME (Qsuper)), s, 10))
4619 return Qsuper;
4620 else if (!strncmp (SSDATA (SYMBOL_NAME (Qcontrol)), s, 10))
4621 return Qcontrol;
4622 else if (!strncmp (SSDATA (SYMBOL_NAME (Qalt)), s, 10))
4623 return Qalt;
4624 else if (!strncmp (SSDATA (SYMBOL_NAME (Qhyper)), s, 10))
4625 return Qhyper;
4626 else if (!strncmp (SSDATA (SYMBOL_NAME (Qnone)), s, 10))
4627 return Qnone;
4628 else
4629 return Qnil;
4630 }
4631
4632
4633 static void
4634 ns_default (const char *parameter, Lisp_Object *result,
4635 Lisp_Object yesval, Lisp_Object noval,
4636 BOOL is_float, BOOL is_modstring)
4637 /* --------------------------------------------------------------------------
4638 Check a parameter value in user's preferences
4639 -------------------------------------------------------------------------- */
4640 {
4641 const char *value = ns_get_defaults_value (parameter);
4642
4643 if (value)
4644 {
4645 double f;
4646 char *pos;
4647 if (c_strcasecmp (value, "YES") == 0)
4648 *result = yesval;
4649 else if (c_strcasecmp (value, "NO") == 0)
4650 *result = noval;
4651 else if (is_float && (f = strtod (value, &pos), pos != value))
4652 *result = make_float (f);
4653 else if (is_modstring && value)
4654 *result = ns_string_to_lispmod (value);
4655 else fprintf (stderr,
4656 "Bad value for default \"%s\": \"%s\"\n", parameter, value);
4657 }
4658 }
4659
4660
4661 static void
4662 ns_initialize_display_info (struct ns_display_info *dpyinfo)
4663 /* --------------------------------------------------------------------------
4664 Initialize global info and storage for display.
4665 -------------------------------------------------------------------------- */
4666 {
4667 NSScreen *screen = [NSScreen mainScreen];
4668 NSWindowDepth depth = [screen depth];
4669
4670 dpyinfo->resx = 72.27; /* used 75.0, but this makes pt == pixel, expected */
4671 dpyinfo->resy = 72.27;
4672 dpyinfo->color_p = ![NSDeviceWhiteColorSpace isEqualToString:
4673 NSColorSpaceFromDepth (depth)]
4674 && ![NSCalibratedWhiteColorSpace isEqualToString:
4675 NSColorSpaceFromDepth (depth)];
4676 dpyinfo->n_planes = NSBitsPerPixelFromDepth (depth);
4677 dpyinfo->color_table = xmalloc (sizeof *dpyinfo->color_table);
4678 dpyinfo->color_table->colors = NULL;
4679 dpyinfo->root_window = 42; /* a placeholder.. */
4680 dpyinfo->x_highlight_frame = dpyinfo->x_focus_frame = NULL;
4681 dpyinfo->n_fonts = 0;
4682 dpyinfo->smallest_font_height = 1;
4683 dpyinfo->smallest_char_width = 1;
4684
4685 reset_mouse_highlight (&dpyinfo->mouse_highlight);
4686 }
4687
4688
4689 /* This and next define (many of the) public functions in this file. */
4690 /* x_... are generic versions in xdisp.c that we, and other terms, get away
4691 with using despite presence in the "system dependent" redisplay
4692 interface. In addition, many of the ns_ methods have code that is
4693 shared with all terms, indicating need for further refactoring. */
4694 extern frame_parm_handler ns_frame_parm_handlers[];
4695 static struct redisplay_interface ns_redisplay_interface =
4696 {
4697 ns_frame_parm_handlers,
4698 x_produce_glyphs,
4699 x_write_glyphs,
4700 x_insert_glyphs,
4701 x_clear_end_of_line,
4702 ns_scroll_run,
4703 ns_after_update_window_line,
4704 ns_update_window_begin,
4705 ns_update_window_end,
4706 0, /* flush_display */
4707 x_clear_window_mouse_face,
4708 x_get_glyph_overhangs,
4709 x_fix_overlapping_area,
4710 ns_draw_fringe_bitmap,
4711 0, /* define_fringe_bitmap */ /* FIXME: simplify ns_draw_fringe_bitmap */
4712 0, /* destroy_fringe_bitmap */
4713 ns_compute_glyph_string_overhangs,
4714 ns_draw_glyph_string,
4715 ns_define_frame_cursor,
4716 ns_clear_frame_area,
4717 ns_draw_window_cursor,
4718 ns_draw_vertical_window_border,
4719 ns_draw_window_divider,
4720 ns_shift_glyphs_for_insert,
4721 ns_show_hourglass,
4722 ns_hide_hourglass
4723 };
4724
4725
4726 static void
4727 ns_delete_display (struct ns_display_info *dpyinfo)
4728 {
4729 /* TODO... */
4730 }
4731
4732
4733 /* This function is called when the last frame on a display is deleted. */
4734 static void
4735 ns_delete_terminal (struct terminal *terminal)
4736 {
4737 struct ns_display_info *dpyinfo = terminal->display_info.ns;
4738
4739 NSTRACE ("ns_delete_terminal");
4740
4741 /* Protect against recursive calls. delete_frame in
4742 delete_terminal calls us back when it deletes our last frame. */
4743 if (!terminal->name)
4744 return;
4745
4746 block_input ();
4747
4748 x_destroy_all_bitmaps (dpyinfo);
4749 ns_delete_display (dpyinfo);
4750 unblock_input ();
4751 }
4752
4753
4754 static struct terminal *
4755 ns_create_terminal (struct ns_display_info *dpyinfo)
4756 /* --------------------------------------------------------------------------
4757 Set up use of NS before we make the first connection.
4758 -------------------------------------------------------------------------- */
4759 {
4760 struct terminal *terminal;
4761
4762 NSTRACE ("ns_create_terminal");
4763
4764 terminal = create_terminal (output_ns, &ns_redisplay_interface);
4765
4766 terminal->display_info.ns = dpyinfo;
4767 dpyinfo->terminal = terminal;
4768
4769 terminal->clear_frame_hook = ns_clear_frame;
4770 terminal->ring_bell_hook = ns_ring_bell;
4771 terminal->update_begin_hook = ns_update_begin;
4772 terminal->update_end_hook = ns_update_end;
4773 terminal->read_socket_hook = ns_read_socket;
4774 terminal->frame_up_to_date_hook = ns_frame_up_to_date;
4775 terminal->mouse_position_hook = ns_mouse_position;
4776 terminal->frame_rehighlight_hook = ns_frame_rehighlight;
4777 terminal->frame_raise_lower_hook = ns_frame_raise_lower;
4778 terminal->fullscreen_hook = ns_fullscreen_hook;
4779 terminal->menu_show_hook = ns_menu_show;
4780 terminal->popup_dialog_hook = ns_popup_dialog;
4781 terminal->set_vertical_scroll_bar_hook = ns_set_vertical_scroll_bar;
4782 terminal->set_horizontal_scroll_bar_hook = ns_set_horizontal_scroll_bar;
4783 terminal->condemn_scroll_bars_hook = ns_condemn_scroll_bars;
4784 terminal->redeem_scroll_bar_hook = ns_redeem_scroll_bar;
4785 terminal->judge_scroll_bars_hook = ns_judge_scroll_bars;
4786 terminal->delete_frame_hook = x_destroy_window;
4787 terminal->delete_terminal_hook = ns_delete_terminal;
4788 /* Other hooks are NULL by default. */
4789
4790 return terminal;
4791 }
4792
4793
4794 struct ns_display_info *
4795 ns_term_init (Lisp_Object display_name)
4796 /* --------------------------------------------------------------------------
4797 Start the Application and get things rolling.
4798 -------------------------------------------------------------------------- */
4799 {
4800 struct terminal *terminal;
4801 struct ns_display_info *dpyinfo;
4802 static int ns_initialized = 0;
4803 Lisp_Object tmp;
4804
4805 if (ns_initialized) return x_display_list;
4806 ns_initialized = 1;
4807
4808 block_input ();
4809
4810 NSTRACE ("ns_term_init");
4811
4812 [outerpool release];
4813 outerpool = [[NSAutoreleasePool alloc] init];
4814
4815 /* count object allocs (About, click icon); on OS X use ObjectAlloc tool */
4816 /*GSDebugAllocationActive (YES); */
4817 block_input ();
4818
4819 baud_rate = 38400;
4820 Fset_input_interrupt_mode (Qnil);
4821
4822 if (selfds[0] == -1)
4823 {
4824 if (emacs_pipe (selfds) != 0)
4825 {
4826 fprintf (stderr, "Failed to create pipe: %s\n",
4827 emacs_strerror (errno));
4828 emacs_abort ();
4829 }
4830
4831 fcntl (selfds[0], F_SETFL, O_NONBLOCK|fcntl (selfds[0], F_GETFL));
4832 FD_ZERO (&select_readfds);
4833 FD_ZERO (&select_writefds);
4834 pthread_mutex_init (&select_mutex, NULL);
4835 }
4836
4837 ns_pending_files = [[NSMutableArray alloc] init];
4838 ns_pending_service_names = [[NSMutableArray alloc] init];
4839 ns_pending_service_args = [[NSMutableArray alloc] init];
4840
4841 /* Start app and create the main menu, window, view.
4842 Needs to be here because ns_initialize_display_info () uses AppKit classes.
4843 The view will then ask the NSApp to stop and return to Emacs. */
4844 [EmacsApp sharedApplication];
4845 if (NSApp == nil)
4846 return NULL;
4847 [NSApp setDelegate: NSApp];
4848
4849 /* Start the select thread. */
4850 [NSThread detachNewThreadSelector:@selector (fd_handler:)
4851 toTarget:NSApp
4852 withObject:nil];
4853
4854 /* debugging: log all notifications */
4855 /* [[NSNotificationCenter defaultCenter] addObserver: NSApp
4856 selector: @selector (logNotification:)
4857 name: nil object: nil]; */
4858
4859 dpyinfo = xzalloc (sizeof *dpyinfo);
4860
4861 ns_initialize_display_info (dpyinfo);
4862 terminal = ns_create_terminal (dpyinfo);
4863
4864 terminal->kboard = allocate_kboard (Qns);
4865 /* Don't let the initial kboard remain current longer than necessary.
4866 That would cause problems if a file loaded on startup tries to
4867 prompt in the mini-buffer. */
4868 if (current_kboard == initial_kboard)
4869 current_kboard = terminal->kboard;
4870 terminal->kboard->reference_count++;
4871
4872 dpyinfo->next = x_display_list;
4873 x_display_list = dpyinfo;
4874
4875 dpyinfo->name_list_element = Fcons (display_name, Qnil);
4876
4877 terminal->name = xlispstrdup (display_name);
4878
4879 unblock_input ();
4880
4881 if (!inhibit_x_resources)
4882 {
4883 ns_default ("GSFontAntiAlias", &ns_antialias_text,
4884 Qt, Qnil, NO, NO);
4885 tmp = Qnil;
4886 /* this is a standard variable */
4887 ns_default ("AppleAntiAliasingThreshold", &tmp,
4888 make_float (10.0), make_float (6.0), YES, NO);
4889 ns_antialias_threshold = NILP (tmp) ? 10.0 : XFLOATINT (tmp);
4890 }
4891
4892 NSTRACE_MSG ("Colors");
4893
4894 {
4895 NSColorList *cl = [NSColorList colorListNamed: @"Emacs"];
4896
4897 if ( cl == nil )
4898 {
4899 Lisp_Object color_file, color_map, color;
4900 unsigned long c;
4901 char *name;
4902
4903 color_file = Fexpand_file_name (build_string ("rgb.txt"),
4904 Fsymbol_value (intern ("data-directory")));
4905
4906 color_map = Fx_load_color_file (color_file);
4907 if (NILP (color_map))
4908 fatal ("Could not read %s.\n", SDATA (color_file));
4909
4910 cl = [[NSColorList alloc] initWithName: @"Emacs"];
4911 for ( ; CONSP (color_map); color_map = XCDR (color_map))
4912 {
4913 color = XCAR (color_map);
4914 name = SSDATA (XCAR (color));
4915 c = XINT (XCDR (color));
4916 [cl setColor:
4917 [NSColor colorForEmacsRed: RED_FROM_ULONG (c) / 255.0
4918 green: GREEN_FROM_ULONG (c) / 255.0
4919 blue: BLUE_FROM_ULONG (c) / 255.0
4920 alpha: 1.0]
4921 forKey: [NSString stringWithUTF8String: name]];
4922 }
4923 [cl writeToFile: nil];
4924 }
4925 }
4926
4927 NSTRACE_MSG ("Versions");
4928
4929 {
4930 #ifdef NS_IMPL_GNUSTEP
4931 Vwindow_system_version = build_string (gnustep_base_version);
4932 #else
4933 /*PSnextrelease (128, c); */
4934 char c[DBL_BUFSIZE_BOUND];
4935 int len = dtoastr (c, sizeof c, 0, 0, NSAppKitVersionNumber);
4936 Vwindow_system_version = make_unibyte_string (c, len);
4937 #endif
4938 }
4939
4940 delete_keyboard_wait_descriptor (0);
4941
4942 ns_app_name = [[NSProcessInfo processInfo] processName];
4943
4944 /* Set up OS X app menu */
4945
4946 NSTRACE_MSG ("Menu init");
4947
4948 #ifdef NS_IMPL_COCOA
4949 {
4950 NSMenu *appMenu;
4951 NSMenuItem *item;
4952 /* set up the application menu */
4953 svcsMenu = [[EmacsMenu alloc] initWithTitle: @"Services"];
4954 [svcsMenu setAutoenablesItems: NO];
4955 appMenu = [[EmacsMenu alloc] initWithTitle: @"Emacs"];
4956 [appMenu setAutoenablesItems: NO];
4957 mainMenu = [[EmacsMenu alloc] initWithTitle: @""];
4958 dockMenu = [[EmacsMenu alloc] initWithTitle: @""];
4959
4960 [appMenu insertItemWithTitle: @"About Emacs"
4961 action: @selector (orderFrontStandardAboutPanel:)
4962 keyEquivalent: @""
4963 atIndex: 0];
4964 [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 1];
4965 [appMenu insertItemWithTitle: @"Preferences..."
4966 action: @selector (showPreferencesWindow:)
4967 keyEquivalent: @","
4968 atIndex: 2];
4969 [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 3];
4970 item = [appMenu insertItemWithTitle: @"Services"
4971 action: @selector (menuDown:)
4972 keyEquivalent: @""
4973 atIndex: 4];
4974 [appMenu setSubmenu: svcsMenu forItem: item];
4975 [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 5];
4976 [appMenu insertItemWithTitle: @"Hide Emacs"
4977 action: @selector (hide:)
4978 keyEquivalent: @"h"
4979 atIndex: 6];
4980 item = [appMenu insertItemWithTitle: @"Hide Others"
4981 action: @selector (hideOtherApplications:)
4982 keyEquivalent: @"h"
4983 atIndex: 7];
4984 [item setKeyEquivalentModifierMask: NSCommandKeyMask | NSAlternateKeyMask];
4985 [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 8];
4986 [appMenu insertItemWithTitle: @"Quit Emacs"
4987 action: @selector (terminate:)
4988 keyEquivalent: @"q"
4989 atIndex: 9];
4990
4991 item = [mainMenu insertItemWithTitle: ns_app_name
4992 action: @selector (menuDown:)
4993 keyEquivalent: @""
4994 atIndex: 0];
4995 [mainMenu setSubmenu: appMenu forItem: item];
4996 [dockMenu insertItemWithTitle: @"New Frame"
4997 action: @selector (newFrame:)
4998 keyEquivalent: @""
4999 atIndex: 0];
5000
5001 [NSApp setMainMenu: mainMenu];
5002 [NSApp setAppleMenu: appMenu];
5003 [NSApp setServicesMenu: svcsMenu];
5004 /* Needed at least on Cocoa, to get dock menu to show windows */
5005 [NSApp setWindowsMenu: [[NSMenu alloc] init]];
5006
5007 [[NSNotificationCenter defaultCenter]
5008 addObserver: mainMenu
5009 selector: @selector (trackingNotification:)
5010 name: NSMenuDidBeginTrackingNotification object: mainMenu];
5011 [[NSNotificationCenter defaultCenter]
5012 addObserver: mainMenu
5013 selector: @selector (trackingNotification:)
5014 name: NSMenuDidEndTrackingNotification object: mainMenu];
5015 }
5016 #endif /* MAC OS X menu setup */
5017
5018 /* Register our external input/output types, used for determining
5019 applicable services and also drag/drop eligibility. */
5020
5021 NSTRACE_MSG ("Input/output types");
5022
5023 ns_send_types = [[NSArray arrayWithObjects: NSStringPboardType, nil] retain];
5024 ns_return_types = [[NSArray arrayWithObjects: NSStringPboardType, nil]
5025 retain];
5026 ns_drag_types = [[NSArray arrayWithObjects:
5027 NSStringPboardType,
5028 NSTabularTextPboardType,
5029 NSFilenamesPboardType,
5030 NSURLPboardType, nil] retain];
5031
5032 /* If fullscreen is in init/default-frame-alist, focus isn't set
5033 right for fullscreen windows, so set this. */
5034 [NSApp activateIgnoringOtherApps:YES];
5035
5036 NSTRACE_MSG ("Call NSApp run");
5037
5038 [NSApp run];
5039 ns_do_open_file = YES;
5040
5041 #ifdef NS_IMPL_GNUSTEP
5042 /* GNUstep steals SIGCHLD for use in NSTask, but we don't use NSTask.
5043 We must re-catch it so subprocess works. */
5044 catch_child_signal ();
5045 #endif
5046
5047 NSTRACE_MSG ("ns_term_init done");
5048
5049 unblock_input ();
5050
5051 return dpyinfo;
5052 }
5053
5054
5055 void
5056 ns_term_shutdown (int sig)
5057 {
5058 [[NSUserDefaults standardUserDefaults] synchronize];
5059
5060 /* code not reached in emacs.c after this is called by shut_down_emacs: */
5061 if (STRINGP (Vauto_save_list_file_name))
5062 unlink (SSDATA (Vauto_save_list_file_name));
5063
5064 if (sig == 0 || sig == SIGTERM)
5065 {
5066 [NSApp terminate: NSApp];
5067 }
5068 else // force a stack trace to happen
5069 {
5070 emacs_abort ();
5071 }
5072 }
5073
5074
5075 /* ==========================================================================
5076
5077 EmacsApp implementation
5078
5079 ========================================================================== */
5080
5081
5082 @implementation EmacsApp
5083
5084 - (id)init
5085 {
5086 NSTRACE ("[EmacsApp init]");
5087
5088 if ((self = [super init]))
5089 {
5090 #ifdef NS_IMPL_COCOA
5091 self->isFirst = YES;
5092 #endif
5093 #ifdef NS_IMPL_GNUSTEP
5094 self->applicationDidFinishLaunchingCalled = NO;
5095 #endif
5096 }
5097
5098 return self;
5099 }
5100
5101 #ifdef NS_IMPL_COCOA
5102 - (void)run
5103 {
5104 NSTRACE ("[EmacsApp run]");
5105
5106 #ifndef NSAppKitVersionNumber10_9
5107 #define NSAppKitVersionNumber10_9 1265
5108 #endif
5109
5110 if ((int)NSAppKitVersionNumber != NSAppKitVersionNumber10_9)
5111 {
5112 [super run];
5113 return;
5114 }
5115
5116 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5117
5118 if (isFirst) [self finishLaunching];
5119 isFirst = NO;
5120
5121 shouldKeepRunning = YES;
5122 do
5123 {
5124 [pool release];
5125 pool = [[NSAutoreleasePool alloc] init];
5126
5127 NSEvent *event =
5128 [self nextEventMatchingMask:NSAnyEventMask
5129 untilDate:[NSDate distantFuture]
5130 inMode:NSDefaultRunLoopMode
5131 dequeue:YES];
5132
5133 [self sendEvent:event];
5134 [self updateWindows];
5135 } while (shouldKeepRunning);
5136
5137 [pool release];
5138 }
5139
5140 - (void)stop: (id)sender
5141 {
5142 NSTRACE ("[EmacsApp stop:]");
5143
5144 shouldKeepRunning = NO;
5145 // Stop possible dialog also. Noop if no dialog present.
5146 // The file dialog still leaks 7k - 10k on 10.9 though.
5147 [super stop:sender];
5148 }
5149 #endif /* NS_IMPL_COCOA */
5150
5151 - (void)logNotification: (NSNotification *)notification
5152 {
5153 NSTRACE ("[EmacsApp logNotification:]");
5154
5155 const char *name = [[notification name] UTF8String];
5156 if (!strstr (name, "Update") && !strstr (name, "NSMenu")
5157 && !strstr (name, "WindowNumber"))
5158 NSLog (@"notification: '%@'", [notification name]);
5159 }
5160
5161
5162 - (void)sendEvent: (NSEvent *)theEvent
5163 /* --------------------------------------------------------------------------
5164 Called when NSApp is running for each event received. Used to stop
5165 the loop when we choose, since there's no way to just run one iteration.
5166 -------------------------------------------------------------------------- */
5167 {
5168 int type = [theEvent type];
5169 NSWindow *window = [theEvent window];
5170
5171 NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsApp sendEvent:]");
5172 NSTRACE_MSG ("Type: %d", type);
5173
5174 #ifdef NS_IMPL_GNUSTEP
5175 // Keyboard events aren't propagated to file dialogs for some reason.
5176 if ([NSApp modalWindow] != nil &&
5177 (type == NSKeyDown || type == NSKeyUp || type == NSFlagsChanged))
5178 {
5179 [[NSApp modalWindow] sendEvent: theEvent];
5180 return;
5181 }
5182 #endif
5183
5184 if (represented_filename != nil && represented_frame)
5185 {
5186 NSString *fstr = represented_filename;
5187 NSView *view = FRAME_NS_VIEW (represented_frame);
5188 #ifdef NS_IMPL_COCOA
5189 /* work around a bug observed on 10.3 and later where
5190 setTitleWithRepresentedFilename does not clear out previous state
5191 if given filename does not exist */
5192 if (! [[NSFileManager defaultManager] fileExistsAtPath: fstr])
5193 [[view window] setRepresentedFilename: @""];
5194 #endif
5195 [[view window] setRepresentedFilename: fstr];
5196 [represented_filename release];
5197 represented_filename = nil;
5198 represented_frame = NULL;
5199 }
5200
5201 if (type == NSApplicationDefined)
5202 {
5203 switch ([theEvent data2])
5204 {
5205 #ifdef NS_IMPL_COCOA
5206 case NSAPP_DATA2_RUNASSCRIPT:
5207 ns_run_ascript ();
5208 [self stop: self];
5209 return;
5210 #endif
5211 case NSAPP_DATA2_RUNFILEDIALOG:
5212 ns_run_file_dialog ();
5213 [self stop: self];
5214 return;
5215 }
5216 }
5217
5218 if (type == NSCursorUpdate && window == nil)
5219 {
5220 fprintf (stderr, "Dropping external cursor update event.\n");
5221 return;
5222 }
5223
5224 if (type == NSApplicationDefined)
5225 {
5226 /* Events posted by ns_send_appdefined interrupt the run loop here.
5227 But, if a modal window is up, an appdefined can still come through,
5228 (e.g., from a makeKeyWindow event) but stopping self also stops the
5229 modal loop. Just defer it until later. */
5230 if ([NSApp modalWindow] == nil)
5231 {
5232 last_appdefined_event_data = [theEvent data1];
5233 [self stop: self];
5234 }
5235 else
5236 {
5237 send_appdefined = YES;
5238 }
5239 }
5240
5241
5242 #ifdef NS_IMPL_COCOA
5243 /* If no dialog and none of our frames have focus and it is a move, skip it.
5244 It is a mouse move in an auxiliary menu, i.e. on the top right on OSX,
5245 such as Wifi, sound, date or similar.
5246 This prevents "spooky" highlighting in the frame under the menu. */
5247 if (type == NSMouseMoved && [NSApp modalWindow] == nil)
5248 {
5249 struct ns_display_info *di;
5250 BOOL has_focus = NO;
5251 for (di = x_display_list; ! has_focus && di; di = di->next)
5252 has_focus = di->x_focus_frame != 0;
5253 if (! has_focus)
5254 return;
5255 }
5256 #endif
5257
5258 NSTRACE_UNSILENCE();
5259
5260 [super sendEvent: theEvent];
5261 }
5262
5263
5264 - (void)showPreferencesWindow: (id)sender
5265 {
5266 struct frame *emacsframe = SELECTED_FRAME ();
5267 NSEvent *theEvent = [NSApp currentEvent];
5268
5269 if (!emacs_event)
5270 return;
5271 emacs_event->kind = NS_NONKEY_EVENT;
5272 emacs_event->code = KEY_NS_SHOW_PREFS;
5273 emacs_event->modifiers = 0;
5274 EV_TRAILER (theEvent);
5275 }
5276
5277
5278 - (void)newFrame: (id)sender
5279 {
5280 NSTRACE ("[EmacsApp newFrame:]");
5281
5282 struct frame *emacsframe = SELECTED_FRAME ();
5283 NSEvent *theEvent = [NSApp currentEvent];
5284
5285 if (!emacs_event)
5286 return;
5287 emacs_event->kind = NS_NONKEY_EVENT;
5288 emacs_event->code = KEY_NS_NEW_FRAME;
5289 emacs_event->modifiers = 0;
5290 EV_TRAILER (theEvent);
5291 }
5292
5293
5294 /* Open a file (used by below, after going into queue read by ns_read_socket) */
5295 - (BOOL) openFile: (NSString *)fileName
5296 {
5297 NSTRACE ("[EmacsApp openFile:]");
5298
5299 struct frame *emacsframe = SELECTED_FRAME ();
5300 NSEvent *theEvent = [NSApp currentEvent];
5301
5302 if (!emacs_event)
5303 return NO;
5304
5305 emacs_event->kind = NS_NONKEY_EVENT;
5306 emacs_event->code = KEY_NS_OPEN_FILE_LINE;
5307 ns_input_file = append2 (ns_input_file, build_string ([fileName UTF8String]));
5308 ns_input_line = Qnil; /* can be start or cons start,end */
5309 emacs_event->modifiers =0;
5310 EV_TRAILER (theEvent);
5311
5312 return YES;
5313 }
5314
5315
5316 /* **************************************************************************
5317
5318 EmacsApp delegate implementation
5319
5320 ************************************************************************** */
5321
5322 - (void)applicationDidFinishLaunching: (NSNotification *)notification
5323 /* --------------------------------------------------------------------------
5324 When application is loaded, terminate event loop in ns_term_init
5325 -------------------------------------------------------------------------- */
5326 {
5327 NSTRACE ("[EmacsApp applicationDidFinishLaunching:]");
5328
5329 #ifdef NS_IMPL_GNUSTEP
5330 ((EmacsApp *)self)->applicationDidFinishLaunchingCalled = YES;
5331 #endif
5332 [NSApp setServicesProvider: NSApp];
5333
5334 [self antialiasThresholdDidChange:nil];
5335 #ifdef NS_IMPL_COCOA
5336 [[NSNotificationCenter defaultCenter]
5337 addObserver:self
5338 selector:@selector(antialiasThresholdDidChange:)
5339 name:NSAntialiasThresholdChangedNotification
5340 object:nil];
5341 #endif
5342
5343 ns_send_appdefined (-2);
5344 }
5345
5346 - (void)antialiasThresholdDidChange:(NSNotification *)notification
5347 {
5348 #ifdef NS_IMPL_COCOA
5349 macfont_update_antialias_threshold ();
5350 #endif
5351 }
5352
5353
5354 /* Termination sequences:
5355 C-x C-c:
5356 Cmd-Q:
5357 MenuBar | File | Exit:
5358 Select Quit from App menubar:
5359 -terminate
5360 KEY_NS_POWER_OFF, (save-buffers-kill-emacs)
5361 ns_term_shutdown()
5362
5363 Select Quit from Dock menu:
5364 Logout attempt:
5365 -appShouldTerminate
5366 Cancel -> Nothing else
5367 Accept ->
5368
5369 -terminate
5370 KEY_NS_POWER_OFF, (save-buffers-kill-emacs)
5371 ns_term_shutdown()
5372
5373 */
5374
5375 - (void) terminate: (id)sender
5376 {
5377 NSTRACE ("[EmacsApp terminate:]");
5378
5379 struct frame *emacsframe = SELECTED_FRAME ();
5380
5381 if (!emacs_event)
5382 return;
5383
5384 emacs_event->kind = NS_NONKEY_EVENT;
5385 emacs_event->code = KEY_NS_POWER_OFF;
5386 emacs_event->arg = Qt; /* mark as non-key event */
5387 EV_TRAILER ((id)nil);
5388 }
5389
5390 static bool
5391 runAlertPanel(NSString *title,
5392 NSString *msgFormat,
5393 NSString *defaultButton,
5394 NSString *alternateButton)
5395 {
5396 #if !defined (NS_IMPL_COCOA) || \
5397 MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_9
5398 return NSRunAlertPanel(title, msgFormat, defaultButton, alternateButton, nil)
5399 == NSAlertDefaultReturn;
5400 #else
5401 NSAlert *alert = [[NSAlert alloc] init];
5402 [alert setAlertStyle: NSCriticalAlertStyle];
5403 [alert setMessageText: msgFormat];
5404 [alert addButtonWithTitle: defaultButton];
5405 [alert addButtonWithTitle: alternateButton];
5406 NSInteger ret = [alert runModal];
5407 [alert release];
5408 return ret == NSAlertFirstButtonReturn;
5409 #endif
5410 }
5411
5412
5413 - (NSApplicationTerminateReply)applicationShouldTerminate: (id)sender
5414 {
5415 NSTRACE ("[EmacsApp applicationShouldTerminate:]");
5416
5417 bool ret;
5418
5419 if (NILP (ns_confirm_quit)) // || ns_shutdown_properly --> TO DO
5420 return NSTerminateNow;
5421
5422 ret = runAlertPanel(ns_app_name,
5423 @"Exit requested. Would you like to Save Buffers and Exit, or Cancel the request?",
5424 @"Save Buffers and Exit", @"Cancel");
5425
5426 if (ret)
5427 return NSTerminateNow;
5428 else
5429 return NSTerminateCancel;
5430 return NSTerminateNow; /* just in case */
5431 }
5432
5433 static int
5434 not_in_argv (NSString *arg)
5435 {
5436 int k;
5437 const char *a = [arg UTF8String];
5438 for (k = 1; k < initial_argc; ++k)
5439 if (strcmp (a, initial_argv[k]) == 0) return 0;
5440 return 1;
5441 }
5442
5443 /* Notification from the Workspace to open a file */
5444 - (BOOL)application: sender openFile: (NSString *)file
5445 {
5446 if (ns_do_open_file || not_in_argv (file))
5447 [ns_pending_files addObject: file];
5448 return YES;
5449 }
5450
5451
5452 /* Open a file as a temporary file */
5453 - (BOOL)application: sender openTempFile: (NSString *)file
5454 {
5455 if (ns_do_open_file || not_in_argv (file))
5456 [ns_pending_files addObject: file];
5457 return YES;
5458 }
5459
5460
5461 /* Notification from the Workspace to open a file noninteractively (?) */
5462 - (BOOL)application: sender openFileWithoutUI: (NSString *)file
5463 {
5464 if (ns_do_open_file || not_in_argv (file))
5465 [ns_pending_files addObject: file];
5466 return YES;
5467 }
5468
5469 /* Notification from the Workspace to open multiple files */
5470 - (void)application: sender openFiles: (NSArray *)fileList
5471 {
5472 NSEnumerator *files = [fileList objectEnumerator];
5473 NSString *file;
5474 /* Don't open files from the command line unconditionally,
5475 Cocoa parses the command line wrong, --option value tries to open value
5476 if --option is the last option. */
5477 while ((file = [files nextObject]) != nil)
5478 if (ns_do_open_file || not_in_argv (file))
5479 [ns_pending_files addObject: file];
5480
5481 [self replyToOpenOrPrint: NSApplicationDelegateReplySuccess];
5482
5483 }
5484
5485
5486 /* Handle dock menu requests. */
5487 - (NSMenu *)applicationDockMenu: (NSApplication *) sender
5488 {
5489 return dockMenu;
5490 }
5491
5492
5493 /* TODO: these may help w/IO switching btwn terminal and NSApp */
5494 - (void)applicationWillBecomeActive: (NSNotification *)notification
5495 {
5496 NSTRACE ("[EmacsApp applicationWillBecomeActive:]");
5497 //ns_app_active=YES;
5498 }
5499
5500 - (void)applicationDidBecomeActive: (NSNotification *)notification
5501 {
5502 NSTRACE ("[EmacsApp applicationDidBecomeActive:]");
5503
5504 #ifdef NS_IMPL_GNUSTEP
5505 if (! applicationDidFinishLaunchingCalled)
5506 [self applicationDidFinishLaunching:notification];
5507 #endif
5508 //ns_app_active=YES;
5509
5510 ns_update_auto_hide_menu_bar ();
5511 // No constraining takes place when the application is not active.
5512 ns_constrain_all_frames ();
5513 }
5514 - (void)applicationDidResignActive: (NSNotification *)notification
5515 {
5516 NSTRACE ("[EmacsApp applicationDidResignActive:]");
5517
5518 //ns_app_active=NO;
5519 ns_send_appdefined (-1);
5520 }
5521
5522
5523
5524 /* ==========================================================================
5525
5526 EmacsApp aux handlers for managing event loop
5527
5528 ========================================================================== */
5529
5530
5531 - (void)timeout_handler: (NSTimer *)timedEntry
5532 /* --------------------------------------------------------------------------
5533 The timeout specified to ns_select has passed.
5534 -------------------------------------------------------------------------- */
5535 {
5536 /*NSTRACE ("timeout_handler"); */
5537 ns_send_appdefined (-2);
5538 }
5539
5540 #ifdef NS_IMPL_GNUSTEP
5541 - (void)sendFromMainThread:(id)unused
5542 {
5543 ns_send_appdefined (nextappdefined);
5544 }
5545 #endif
5546
5547 - (void)fd_handler:(id)unused
5548 /* --------------------------------------------------------------------------
5549 Check data waiting on file descriptors and terminate if so
5550 -------------------------------------------------------------------------- */
5551 {
5552 int result;
5553 int waiting = 1, nfds;
5554 char c;
5555
5556 fd_set readfds, writefds, *wfds;
5557 struct timespec timeout, *tmo;
5558 NSAutoreleasePool *pool = nil;
5559
5560 /* NSTRACE ("fd_handler"); */
5561
5562 for (;;)
5563 {
5564 [pool release];
5565 pool = [[NSAutoreleasePool alloc] init];
5566
5567 if (waiting)
5568 {
5569 fd_set fds;
5570 FD_ZERO (&fds);
5571 FD_SET (selfds[0], &fds);
5572 result = select (selfds[0]+1, &fds, NULL, NULL, NULL);
5573 if (result > 0 && read (selfds[0], &c, 1) == 1 && c == 'g')
5574 waiting = 0;
5575 }
5576 else
5577 {
5578 pthread_mutex_lock (&select_mutex);
5579 nfds = select_nfds;
5580
5581 if (select_valid & SELECT_HAVE_READ)
5582 readfds = select_readfds;
5583 else
5584 FD_ZERO (&readfds);
5585
5586 if (select_valid & SELECT_HAVE_WRITE)
5587 {
5588 writefds = select_writefds;
5589 wfds = &writefds;
5590 }
5591 else
5592 wfds = NULL;
5593 if (select_valid & SELECT_HAVE_TMO)
5594 {
5595 timeout = select_timeout;
5596 tmo = &timeout;
5597 }
5598 else
5599 tmo = NULL;
5600
5601 pthread_mutex_unlock (&select_mutex);
5602
5603 FD_SET (selfds[0], &readfds);
5604 if (selfds[0] >= nfds) nfds = selfds[0]+1;
5605
5606 result = pselect (nfds, &readfds, wfds, NULL, tmo, NULL);
5607
5608 if (result == 0)
5609 ns_send_appdefined (-2);
5610 else if (result > 0)
5611 {
5612 if (FD_ISSET (selfds[0], &readfds))
5613 {
5614 if (read (selfds[0], &c, 1) == 1 && c == 's')
5615 waiting = 1;
5616 }
5617 else
5618 {
5619 pthread_mutex_lock (&select_mutex);
5620 if (select_valid & SELECT_HAVE_READ)
5621 select_readfds = readfds;
5622 if (select_valid & SELECT_HAVE_WRITE)
5623 select_writefds = writefds;
5624 if (select_valid & SELECT_HAVE_TMO)
5625 select_timeout = timeout;
5626 pthread_mutex_unlock (&select_mutex);
5627
5628 ns_send_appdefined (result);
5629 }
5630 }
5631 waiting = 1;
5632 }
5633 }
5634 }
5635
5636
5637
5638 /* ==========================================================================
5639
5640 Service provision
5641
5642 ========================================================================== */
5643
5644 /* called from system: queue for next pass through event loop */
5645 - (void)requestService: (NSPasteboard *)pboard
5646 userData: (NSString *)userData
5647 error: (NSString **)error
5648 {
5649 [ns_pending_service_names addObject: userData];
5650 [ns_pending_service_args addObject: [NSString stringWithUTF8String:
5651 SSDATA (ns_string_from_pasteboard (pboard))]];
5652 }
5653
5654
5655 /* called from ns_read_socket to clear queue */
5656 - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
5657 {
5658 struct frame *emacsframe = SELECTED_FRAME ();
5659 NSEvent *theEvent = [NSApp currentEvent];
5660
5661 NSTRACE ("[EmacsApp fulfillService:withArg:]");
5662
5663 if (!emacs_event)
5664 return NO;
5665
5666 emacs_event->kind = NS_NONKEY_EVENT;
5667 emacs_event->code = KEY_NS_SPI_SERVICE_CALL;
5668 ns_input_spi_name = build_string ([name UTF8String]);
5669 ns_input_spi_arg = build_string ([arg UTF8String]);
5670 emacs_event->modifiers = EV_MODIFIERS (theEvent);
5671 EV_TRAILER (theEvent);
5672
5673 return YES;
5674 }
5675
5676
5677 @end /* EmacsApp */
5678
5679
5680
5681 /* ==========================================================================
5682
5683 EmacsView implementation
5684
5685 ========================================================================== */
5686
5687
5688 @implementation EmacsView
5689
5690 /* needed to inform when window closed from LISP */
5691 - (void) setWindowClosing: (BOOL)closing
5692 {
5693 NSTRACE ("[EmacsView setWindowClosing:%d]", closing);
5694
5695 windowClosing = closing;
5696 }
5697
5698
5699 - (void)dealloc
5700 {
5701 NSTRACE ("[EmacsView dealloc]");
5702 [toolbar release];
5703 if (fs_state == FULLSCREEN_BOTH)
5704 [nonfs_window release];
5705 [super dealloc];
5706 }
5707
5708
5709 /* called on font panel selection */
5710 - (void)changeFont: (id)sender
5711 {
5712 NSEvent *e = [[self window] currentEvent];
5713 struct face *face = FRAME_DEFAULT_FACE (emacsframe);
5714 struct font *font = face->font;
5715 id newFont;
5716 CGFloat size;
5717 NSFont *nsfont;
5718
5719 NSTRACE ("[EmacsView changeFont:]");
5720
5721 if (!emacs_event)
5722 return;
5723
5724 #ifdef NS_IMPL_GNUSTEP
5725 nsfont = ((struct nsfont_info *)font)->nsfont;
5726 #endif
5727 #ifdef NS_IMPL_COCOA
5728 nsfont = (NSFont *) macfont_get_nsctfont (font);
5729 #endif
5730
5731 if ((newFont = [sender convertFont: nsfont]))
5732 {
5733 SET_FRAME_GARBAGED (emacsframe); /* now needed as of 2008/10 */
5734
5735 emacs_event->kind = NS_NONKEY_EVENT;
5736 emacs_event->modifiers = 0;
5737 emacs_event->code = KEY_NS_CHANGE_FONT;
5738
5739 size = [newFont pointSize];
5740 ns_input_fontsize = make_number (lrint (size));
5741 ns_input_font = build_string ([[newFont familyName] UTF8String]);
5742 EV_TRAILER (e);
5743 }
5744 }
5745
5746
5747 - (BOOL)acceptsFirstResponder
5748 {
5749 NSTRACE ("[EmacsView acceptsFirstResponder]");
5750 return YES;
5751 }
5752
5753
5754 - (void)resetCursorRects
5755 {
5756 NSRect visible = [self visibleRect];
5757 NSCursor *currentCursor = FRAME_POINTER_TYPE (emacsframe);
5758 NSTRACE ("[EmacsView resetCursorRects]");
5759
5760 if (currentCursor == nil)
5761 currentCursor = [NSCursor arrowCursor];
5762
5763 if (!NSIsEmptyRect (visible))
5764 [self addCursorRect: visible cursor: currentCursor];
5765 [currentCursor setOnMouseEntered: YES];
5766 }
5767
5768
5769
5770 /*****************************************************************************/
5771 /* Keyboard handling. */
5772 #define NS_KEYLOG 0
5773
5774 - (void)keyDown: (NSEvent *)theEvent
5775 {
5776 Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
5777 int code;
5778 unsigned fnKeysym = 0;
5779 static NSMutableArray *nsEvArray;
5780 int left_is_none;
5781 unsigned int flags = [theEvent modifierFlags];
5782
5783 NSTRACE ("[EmacsView keyDown:]");
5784
5785 /* Rhapsody and OS X give up and down events for the arrow keys */
5786 if (ns_fake_keydown == YES)
5787 ns_fake_keydown = NO;
5788 else if ([theEvent type] != NSKeyDown)
5789 return;
5790
5791 if (!emacs_event)
5792 return;
5793
5794 if (![[self window] isKeyWindow]
5795 && [[theEvent window] isKindOfClass: [EmacsWindow class]]
5796 /* we must avoid an infinite loop here. */
5797 && (EmacsView *)[[theEvent window] delegate] != self)
5798 {
5799 /* XXX: There is an occasional condition in which, when Emacs display
5800 updates a different frame from the current one, and temporarily
5801 selects it, then processes some interrupt-driven input
5802 (dispnew.c:3878), OS will send the event to the correct NSWindow, but
5803 for some reason that window has its first responder set to the NSView
5804 most recently updated (I guess), which is not the correct one. */
5805 [(EmacsView *)[[theEvent window] delegate] keyDown: theEvent];
5806 return;
5807 }
5808
5809 if (nsEvArray == nil)
5810 nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1];
5811
5812 [NSCursor setHiddenUntilMouseMoves: YES];
5813
5814 if (hlinfo->mouse_face_hidden && INTEGERP (Vmouse_highlight))
5815 {
5816 clear_mouse_face (hlinfo);
5817 hlinfo->mouse_face_hidden = 1;
5818 }
5819
5820 if (!processingCompose)
5821 {
5822 /* When using screen sharing, no left or right information is sent,
5823 so use Left key in those cases. */
5824 int is_left_key, is_right_key;
5825
5826 code = ([[theEvent charactersIgnoringModifiers] length] == 0) ?
5827 0 : [[theEvent charactersIgnoringModifiers] characterAtIndex: 0];
5828
5829 /* (Carbon way: [theEvent keyCode]) */
5830
5831 /* is it a "function key"? */
5832 /* Note: Sometimes a plain key will have the NSNumericPadKeyMask
5833 flag set (this is probably a bug in the OS).
5834 */
5835 if (code < 0x00ff && (flags&NSNumericPadKeyMask))
5836 {
5837 fnKeysym = ns_convert_key ([theEvent keyCode] | NSNumericPadKeyMask);
5838 }
5839 if (fnKeysym == 0)
5840 {
5841 fnKeysym = ns_convert_key (code);
5842 }
5843
5844 if (fnKeysym)
5845 {
5846 /* COUNTERHACK: map 'Delete' on upper-right main KB to 'Backspace',
5847 because Emacs treats Delete and KP-Delete same (in simple.el). */
5848 if ((fnKeysym == 0xFFFF && [theEvent keyCode] == 0x33)
5849 #ifdef NS_IMPL_GNUSTEP
5850 /* GNUstep uses incompatible keycodes, even for those that are
5851 supposed to be hardware independent. Just check for delete.
5852 Keypad delete does not have keysym 0xFFFF.
5853 See http://savannah.gnu.org/bugs/?25395
5854 */
5855 || (fnKeysym == 0xFFFF && code == 127)
5856 #endif
5857 )
5858 code = 0xFF08; /* backspace */
5859 else
5860 code = fnKeysym;
5861 }
5862
5863 /* are there modifiers? */
5864 emacs_event->modifiers = 0;
5865
5866 if (flags & NSHelpKeyMask)
5867 emacs_event->modifiers |= hyper_modifier;
5868
5869 if (flags & NSShiftKeyMask)
5870 emacs_event->modifiers |= shift_modifier;
5871
5872 is_right_key = (flags & NSRightCommandKeyMask) == NSRightCommandKeyMask;
5873 is_left_key = (flags & NSLeftCommandKeyMask) == NSLeftCommandKeyMask
5874 || (! is_right_key && (flags & NSCommandKeyMask) == NSCommandKeyMask);
5875
5876 if (is_right_key)
5877 emacs_event->modifiers |= parse_solitary_modifier
5878 (EQ (ns_right_command_modifier, Qleft)
5879 ? ns_command_modifier
5880 : ns_right_command_modifier);
5881
5882 if (is_left_key)
5883 {
5884 emacs_event->modifiers |= parse_solitary_modifier
5885 (ns_command_modifier);
5886
5887 /* if super (default), take input manager's word so things like
5888 dvorak / qwerty layout work */
5889 if (EQ (ns_command_modifier, Qsuper)
5890 && !fnKeysym
5891 && [[theEvent characters] length] != 0)
5892 {
5893 /* XXX: the code we get will be unshifted, so if we have
5894 a shift modifier, must convert ourselves */
5895 if (!(flags & NSShiftKeyMask))
5896 code = [[theEvent characters] characterAtIndex: 0];
5897 #if 0
5898 /* this is ugly and also requires linking w/Carbon framework
5899 (for LMGetKbdType) so for now leave this rare (?) case
5900 undealt with.. in future look into CGEvent methods */
5901 else
5902 {
5903 long smv = GetScriptManagerVariable (smKeyScript);
5904 Handle uchrHandle = GetResource
5905 ('uchr', GetScriptVariable (smv, smScriptKeys));
5906 UInt32 dummy = 0;
5907 UCKeyTranslate ((UCKeyboardLayout*)*uchrHandle,
5908 [[theEvent characters] characterAtIndex: 0],
5909 kUCKeyActionDisplay,
5910 (flags & ~NSCommandKeyMask) >> 8,
5911 LMGetKbdType (), kUCKeyTranslateNoDeadKeysMask,
5912 &dummy, 1, &dummy, &code);
5913 code &= 0xFF;
5914 }
5915 #endif
5916 }
5917 }
5918
5919 is_right_key = (flags & NSRightControlKeyMask) == NSRightControlKeyMask;
5920 is_left_key = (flags & NSLeftControlKeyMask) == NSLeftControlKeyMask
5921 || (! is_right_key && (flags & NSControlKeyMask) == NSControlKeyMask);
5922
5923 if (is_right_key)
5924 emacs_event->modifiers |= parse_solitary_modifier
5925 (EQ (ns_right_control_modifier, Qleft)
5926 ? ns_control_modifier
5927 : ns_right_control_modifier);
5928
5929 if (is_left_key)
5930 emacs_event->modifiers |= parse_solitary_modifier
5931 (ns_control_modifier);
5932
5933 if (flags & NS_FUNCTION_KEY_MASK && !fnKeysym)
5934 emacs_event->modifiers |=
5935 parse_solitary_modifier (ns_function_modifier);
5936
5937 left_is_none = NILP (ns_alternate_modifier)
5938 || EQ (ns_alternate_modifier, Qnone);
5939
5940 is_right_key = (flags & NSRightAlternateKeyMask)
5941 == NSRightAlternateKeyMask;
5942 is_left_key = (flags & NSLeftAlternateKeyMask) == NSLeftAlternateKeyMask
5943 || (! is_right_key
5944 && (flags & NSAlternateKeyMask) == NSAlternateKeyMask);
5945
5946 if (is_right_key)
5947 {
5948 if ((NILP (ns_right_alternate_modifier)
5949 || EQ (ns_right_alternate_modifier, Qnone)
5950 || (EQ (ns_right_alternate_modifier, Qleft) && left_is_none))
5951 && !fnKeysym)
5952 { /* accept pre-interp alt comb */
5953 if ([[theEvent characters] length] > 0)
5954 code = [[theEvent characters] characterAtIndex: 0];
5955 /*HACK: clear lone shift modifier to stop next if from firing */
5956 if (emacs_event->modifiers == shift_modifier)
5957 emacs_event->modifiers = 0;
5958 }
5959 else
5960 emacs_event->modifiers |= parse_solitary_modifier
5961 (EQ (ns_right_alternate_modifier, Qleft)
5962 ? ns_alternate_modifier
5963 : ns_right_alternate_modifier);
5964 }
5965
5966 if (is_left_key) /* default = meta */
5967 {
5968 if (left_is_none && !fnKeysym)
5969 { /* accept pre-interp alt comb */
5970 if ([[theEvent characters] length] > 0)
5971 code = [[theEvent characters] characterAtIndex: 0];
5972 /*HACK: clear lone shift modifier to stop next if from firing */
5973 if (emacs_event->modifiers == shift_modifier)
5974 emacs_event->modifiers = 0;
5975 }
5976 else
5977 emacs_event->modifiers |=
5978 parse_solitary_modifier (ns_alternate_modifier);
5979 }
5980
5981 if (NS_KEYLOG)
5982 fprintf (stderr, "keyDown: code =%x\tfnKey =%x\tflags = %x\tmods = %x\n",
5983 code, fnKeysym, flags, emacs_event->modifiers);
5984
5985 /* if it was a function key or had modifiers, pass it directly to emacs */
5986 if (fnKeysym || (emacs_event->modifiers
5987 && (emacs_event->modifiers != shift_modifier)
5988 && [[theEvent charactersIgnoringModifiers] length] > 0))
5989 /*[[theEvent characters] length] */
5990 {
5991 emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
5992 if (code < 0x20)
5993 code |= (1<<28)|(3<<16);
5994 else if (code == 0x7f)
5995 code |= (1<<28)|(3<<16);
5996 else if (!fnKeysym)
5997 emacs_event->kind = code > 0xFF
5998 ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT;
5999
6000 emacs_event->code = code;
6001 EV_TRAILER (theEvent);
6002 processingCompose = NO;
6003 return;
6004 }
6005 }
6006
6007
6008 if (NS_KEYLOG && !processingCompose)
6009 fprintf (stderr, "keyDown: Begin compose sequence.\n");
6010
6011 processingCompose = YES;
6012 [nsEvArray addObject: theEvent];
6013 [self interpretKeyEvents: nsEvArray];
6014 [nsEvArray removeObject: theEvent];
6015 }
6016
6017
6018 #ifdef NS_IMPL_COCOA
6019 /* Needed to pick up Ctrl-tab and possibly other events that OS X has
6020 decided not to send key-down for.
6021 See http://osdir.com/ml/editors.vim.mac/2007-10/msg00141.html
6022 This only applies on Tiger and earlier.
6023 If it matches one of these, send it on to keyDown. */
6024 -(void)keyUp: (NSEvent *)theEvent
6025 {
6026 int flags = [theEvent modifierFlags];
6027 int code = [theEvent keyCode];
6028
6029 NSTRACE ("[EmacsView keyUp:]");
6030
6031 if (floor (NSAppKitVersionNumber) <= 824 /*NSAppKitVersionNumber10_4*/ &&
6032 code == 0x30 && (flags & NSControlKeyMask) && !(flags & NSCommandKeyMask))
6033 {
6034 if (NS_KEYLOG)
6035 fprintf (stderr, "keyUp: passed test");
6036 ns_fake_keydown = YES;
6037 [self keyDown: theEvent];
6038 }
6039 }
6040 #endif
6041
6042
6043 /* <NSTextInput> implementation (called through super interpretKeyEvents:]). */
6044
6045
6046 /* <NSTextInput>: called when done composing;
6047 NOTE: also called when we delete over working text, followed immed.
6048 by doCommandBySelector: deleteBackward: */
6049 - (void)insertText: (id)aString
6050 {
6051 int code;
6052 int len = [(NSString *)aString length];
6053 int i;
6054
6055 NSTRACE ("[EmacsView insertText:]");
6056
6057 if (NS_KEYLOG)
6058 NSLog (@"insertText '%@'\tlen = %d", aString, len);
6059 processingCompose = NO;
6060
6061 if (!emacs_event)
6062 return;
6063
6064 /* first, clear any working text */
6065 if (workingText != nil)
6066 [self deleteWorkingText];
6067
6068 /* now insert the string as keystrokes */
6069 for (i =0; i<len; i++)
6070 {
6071 code = [aString characterAtIndex: i];
6072 /* TODO: still need this? */
6073 if (code == 0x2DC)
6074 code = '~'; /* 0x7E */
6075 if (code != 32) /* Space */
6076 emacs_event->modifiers = 0;
6077 emacs_event->kind
6078 = code > 0xFF ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT;
6079 emacs_event->code = code;
6080 EV_TRAILER ((id)nil);
6081 }
6082 }
6083
6084
6085 /* <NSTextInput>: inserts display of composing characters */
6086 - (void)setMarkedText: (id)aString selectedRange: (NSRange)selRange
6087 {
6088 NSString *str = [aString respondsToSelector: @selector (string)] ?
6089 [aString string] : aString;
6090
6091 NSTRACE ("[EmacsView setMarkedText:selectedRange:]");
6092
6093 if (NS_KEYLOG)
6094 NSLog (@"setMarkedText '%@' len =%lu range %lu from %lu",
6095 str, (unsigned long)[str length],
6096 (unsigned long)selRange.length,
6097 (unsigned long)selRange.location);
6098
6099 if (workingText != nil)
6100 [self deleteWorkingText];
6101 if ([str length] == 0)
6102 return;
6103
6104 if (!emacs_event)
6105 return;
6106
6107 processingCompose = YES;
6108 workingText = [str copy];
6109 ns_working_text = build_string ([workingText UTF8String]);
6110
6111 emacs_event->kind = NS_TEXT_EVENT;
6112 emacs_event->code = KEY_NS_PUT_WORKING_TEXT;
6113 EV_TRAILER ((id)nil);
6114 }
6115
6116
6117 /* delete display of composing characters [not in <NSTextInput>] */
6118 - (void)deleteWorkingText
6119 {
6120 NSTRACE ("[EmacsView deleteWorkingText]");
6121
6122 if (workingText == nil)
6123 return;
6124 if (NS_KEYLOG)
6125 NSLog(@"deleteWorkingText len =%lu\n", (unsigned long)[workingText length]);
6126 [workingText release];
6127 workingText = nil;
6128 processingCompose = NO;
6129
6130 if (!emacs_event)
6131 return;
6132
6133 emacs_event->kind = NS_TEXT_EVENT;
6134 emacs_event->code = KEY_NS_UNPUT_WORKING_TEXT;
6135 EV_TRAILER ((id)nil);
6136 }
6137
6138
6139 - (BOOL)hasMarkedText
6140 {
6141 NSTRACE ("[EmacsView hasMarkedText]");
6142
6143 return workingText != nil;
6144 }
6145
6146
6147 - (NSRange)markedRange
6148 {
6149 NSTRACE ("[EmacsView markedRange]");
6150
6151 NSRange rng = workingText != nil
6152 ? NSMakeRange (0, [workingText length]) : NSMakeRange (NSNotFound, 0);
6153 if (NS_KEYLOG)
6154 NSLog (@"markedRange request");
6155 return rng;
6156 }
6157
6158
6159 - (void)unmarkText
6160 {
6161 NSTRACE ("[EmacsView unmarkText]");
6162
6163 if (NS_KEYLOG)
6164 NSLog (@"unmark (accept) text");
6165 [self deleteWorkingText];
6166 processingCompose = NO;
6167 }
6168
6169
6170 /* used to position char selection windows, etc. */
6171 - (NSRect)firstRectForCharacterRange: (NSRange)theRange
6172 {
6173 NSRect rect;
6174 NSPoint pt;
6175 struct window *win = XWINDOW (FRAME_SELECTED_WINDOW (emacsframe));
6176
6177 NSTRACE ("[EmacsView firstRectForCharacterRange:]");
6178
6179 if (NS_KEYLOG)
6180 NSLog (@"firstRectForCharRange request");
6181
6182 rect.size.width = theRange.length * FRAME_COLUMN_WIDTH (emacsframe);
6183 rect.size.height = FRAME_LINE_HEIGHT (emacsframe);
6184 pt.x = WINDOW_TEXT_TO_FRAME_PIXEL_X (win, win->phys_cursor.x);
6185 pt.y = WINDOW_TO_FRAME_PIXEL_Y (win, win->phys_cursor.y
6186 +FRAME_LINE_HEIGHT (emacsframe));
6187
6188 pt = [self convertPoint: pt toView: nil];
6189 pt = [[self window] convertBaseToScreen: pt];
6190 rect.origin = pt;
6191 return rect;
6192 }
6193
6194
6195 - (NSInteger)conversationIdentifier
6196 {
6197 return (NSInteger)self;
6198 }
6199
6200
6201 - (void)doCommandBySelector: (SEL)aSelector
6202 {
6203 NSTRACE ("[EmacsView doCommandBySelector:]");
6204
6205 if (NS_KEYLOG)
6206 NSLog (@"doCommandBySelector: %@", NSStringFromSelector (aSelector));
6207
6208 processingCompose = NO;
6209 if (aSelector == @selector (deleteBackward:))
6210 {
6211 /* happens when user backspaces over an ongoing composition:
6212 throw a 'delete' into the event queue */
6213 if (!emacs_event)
6214 return;
6215 emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
6216 emacs_event->code = 0xFF08;
6217 EV_TRAILER ((id)nil);
6218 }
6219 }
6220
6221 - (NSArray *)validAttributesForMarkedText
6222 {
6223 static NSArray *arr = nil;
6224 if (arr == nil) arr = [NSArray new];
6225 /* [[NSArray arrayWithObject: NSUnderlineStyleAttributeName] retain]; */
6226 return arr;
6227 }
6228
6229 - (NSRange)selectedRange
6230 {
6231 if (NS_KEYLOG)
6232 NSLog (@"selectedRange request");
6233 return NSMakeRange (NSNotFound, 0);
6234 }
6235
6236 #if defined (NS_IMPL_COCOA) || GNUSTEP_GUI_MAJOR_VERSION > 0 || \
6237 GNUSTEP_GUI_MINOR_VERSION > 22
6238 - (NSUInteger)characterIndexForPoint: (NSPoint)thePoint
6239 #else
6240 - (unsigned int)characterIndexForPoint: (NSPoint)thePoint
6241 #endif
6242 {
6243 if (NS_KEYLOG)
6244 NSLog (@"characterIndexForPoint request");
6245 return 0;
6246 }
6247
6248 - (NSAttributedString *)attributedSubstringFromRange: (NSRange)theRange
6249 {
6250 static NSAttributedString *str = nil;
6251 if (str == nil) str = [NSAttributedString new];
6252 if (NS_KEYLOG)
6253 NSLog (@"attributedSubstringFromRange request");
6254 return str;
6255 }
6256
6257 /* End <NSTextInput> impl. */
6258 /*****************************************************************************/
6259
6260
6261 /* This is what happens when the user presses a mouse button. */
6262 - (void)mouseDown: (NSEvent *)theEvent
6263 {
6264 struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6265 NSPoint p = [self convertPoint: [theEvent locationInWindow] fromView: nil];
6266
6267 NSTRACE ("[EmacsView mouseDown:]");
6268
6269 [self deleteWorkingText];
6270
6271 if (!emacs_event)
6272 return;
6273
6274 dpyinfo->last_mouse_frame = emacsframe;
6275 /* appears to be needed to prevent spurious movement events generated on
6276 button clicks */
6277 emacsframe->mouse_moved = 0;
6278
6279 if ([theEvent type] == NSScrollWheel)
6280 {
6281 CGFloat delta = [theEvent deltaY];
6282 /* Mac notebooks send wheel events w/delta =0 when trackpad scrolling */
6283 if (delta == 0)
6284 {
6285 delta = [theEvent deltaX];
6286 if (delta == 0)
6287 {
6288 NSTRACE_MSG ("deltaIsZero");
6289 return;
6290 }
6291 emacs_event->kind = HORIZ_WHEEL_EVENT;
6292 }
6293 else
6294 emacs_event->kind = WHEEL_EVENT;
6295
6296 emacs_event->code = 0;
6297 emacs_event->modifiers = EV_MODIFIERS (theEvent) |
6298 ((delta > 0) ? up_modifier : down_modifier);
6299 }
6300 else
6301 {
6302 emacs_event->kind = MOUSE_CLICK_EVENT;
6303 emacs_event->code = EV_BUTTON (theEvent);
6304 emacs_event->modifiers = EV_MODIFIERS (theEvent)
6305 | EV_UDMODIFIERS (theEvent);
6306 }
6307 XSETINT (emacs_event->x, lrint (p.x));
6308 XSETINT (emacs_event->y, lrint (p.y));
6309 EV_TRAILER (theEvent);
6310 }
6311
6312
6313 - (void)rightMouseDown: (NSEvent *)theEvent
6314 {
6315 NSTRACE ("[EmacsView rightMouseDown:]");
6316 [self mouseDown: theEvent];
6317 }
6318
6319
6320 - (void)otherMouseDown: (NSEvent *)theEvent
6321 {
6322 NSTRACE ("[EmacsView otherMouseDown:]");
6323 [self mouseDown: theEvent];
6324 }
6325
6326
6327 - (void)mouseUp: (NSEvent *)theEvent
6328 {
6329 NSTRACE ("[EmacsView mouseUp:]");
6330 [self mouseDown: theEvent];
6331 }
6332
6333
6334 - (void)rightMouseUp: (NSEvent *)theEvent
6335 {
6336 NSTRACE ("[EmacsView rightMouseUp:]");
6337 [self mouseDown: theEvent];
6338 }
6339
6340
6341 - (void)otherMouseUp: (NSEvent *)theEvent
6342 {
6343 NSTRACE ("[EmacsView otherMouseUp:]");
6344 [self mouseDown: theEvent];
6345 }
6346
6347
6348 - (void) scrollWheel: (NSEvent *)theEvent
6349 {
6350 NSTRACE ("[EmacsView scrollWheel:]");
6351 [self mouseDown: theEvent];
6352 }
6353
6354
6355 /* Tell emacs the mouse has moved. */
6356 - (void)mouseMoved: (NSEvent *)e
6357 {
6358 Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
6359 struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6360 Lisp_Object frame;
6361 NSPoint pt;
6362
6363 NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsView mouseMoved:]");
6364
6365 dpyinfo->last_mouse_movement_time = EV_TIMESTAMP (e);
6366 pt = [self convertPoint: [e locationInWindow] fromView: nil];
6367 dpyinfo->last_mouse_motion_x = pt.x;
6368 dpyinfo->last_mouse_motion_y = pt.y;
6369
6370 /* update any mouse face */
6371 if (hlinfo->mouse_face_hidden)
6372 {
6373 hlinfo->mouse_face_hidden = 0;
6374 clear_mouse_face (hlinfo);
6375 }
6376
6377 /* tooltip handling */
6378 previous_help_echo_string = help_echo_string;
6379 help_echo_string = Qnil;
6380
6381 if (!NILP (Vmouse_autoselect_window))
6382 {
6383 NSTRACE_MSG ("mouse_autoselect_window");
6384 static Lisp_Object last_mouse_window;
6385 Lisp_Object window
6386 = window_from_coordinates (emacsframe, pt.x, pt.y, 0, 0);
6387
6388 if (WINDOWP (window)
6389 && !EQ (window, last_mouse_window)
6390 && !EQ (window, selected_window)
6391 && (focus_follows_mouse
6392 || (EQ (XWINDOW (window)->frame,
6393 XWINDOW (selected_window)->frame))))
6394 {
6395 NSTRACE_MSG ("in_window");
6396 emacs_event->kind = SELECT_WINDOW_EVENT;
6397 emacs_event->frame_or_window = window;
6398 EV_TRAILER2 (e);
6399 }
6400 /* Remember the last window where we saw the mouse. */
6401 last_mouse_window = window;
6402 }
6403
6404 if (!note_mouse_movement (emacsframe, pt.x, pt.y))
6405 help_echo_string = previous_help_echo_string;
6406
6407 XSETFRAME (frame, emacsframe);
6408 if (!NILP (help_echo_string) || !NILP (previous_help_echo_string))
6409 {
6410 /* NOTE: help_echo_{window,pos,object} are set in xdisp.c
6411 (note_mouse_highlight), which is called through the
6412 note_mouse_movement () call above */
6413 any_help_event_p = YES;
6414 gen_help_event (help_echo_string, frame, help_echo_window,
6415 help_echo_object, help_echo_pos);
6416 }
6417
6418 if (emacsframe->mouse_moved && send_appdefined)
6419 ns_send_appdefined (-1);
6420 }
6421
6422
6423 - (void)mouseDragged: (NSEvent *)e
6424 {
6425 NSTRACE ("[EmacsView mouseDragged:]");
6426 [self mouseMoved: e];
6427 }
6428
6429
6430 - (void)rightMouseDragged: (NSEvent *)e
6431 {
6432 NSTRACE ("[EmacsView rightMouseDragged:]");
6433 [self mouseMoved: e];
6434 }
6435
6436
6437 - (void)otherMouseDragged: (NSEvent *)e
6438 {
6439 NSTRACE ("[EmacsView otherMouseDragged:]");
6440 [self mouseMoved: e];
6441 }
6442
6443
6444 - (BOOL)windowShouldClose: (id)sender
6445 {
6446 NSEvent *e =[[self window] currentEvent];
6447
6448 NSTRACE ("[EmacsView windowShouldClose:]");
6449 windowClosing = YES;
6450 if (!emacs_event)
6451 return NO;
6452 emacs_event->kind = DELETE_WINDOW_EVENT;
6453 emacs_event->modifiers = 0;
6454 emacs_event->code = 0;
6455 EV_TRAILER (e);
6456 /* Don't close this window, let this be done from lisp code. */
6457 return NO;
6458 }
6459
6460 - (void) updateFrameSize: (BOOL) delay;
6461 {
6462 NSWindow *window = [self window];
6463 NSRect wr = [window frame];
6464 int extra = 0;
6465 int oldc = cols, oldr = rows;
6466 int oldw = FRAME_PIXEL_WIDTH (emacsframe);
6467 int oldh = FRAME_PIXEL_HEIGHT (emacsframe);
6468 int neww, newh;
6469
6470 NSTRACE ("[EmacsView updateFrameSize:]");
6471 NSTRACE_SIZE ("Original size", NSMakeSize (oldw, oldh));
6472 NSTRACE_RECT ("Original frame", wr);
6473 NSTRACE_MSG ("Original columns: %d", cols);
6474 NSTRACE_MSG ("Original rows: %d", rows);
6475
6476 if (! [self isFullscreen])
6477 {
6478 #ifdef NS_IMPL_GNUSTEP
6479 // GNUstep does not always update the tool bar height. Force it.
6480 if (toolbar && [toolbar isVisible])
6481 update_frame_tool_bar (emacsframe);
6482 #endif
6483
6484 extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
6485 + FRAME_TOOLBAR_HEIGHT (emacsframe);
6486 }
6487
6488 if (wait_for_tool_bar)
6489 {
6490 if (FRAME_TOOLBAR_HEIGHT (emacsframe) == 0)
6491 {
6492 NSTRACE_MSG ("Waiting for toolbar");
6493 return;
6494 }
6495 wait_for_tool_bar = NO;
6496 }
6497
6498 neww = (int)wr.size.width - emacsframe->border_width;
6499 newh = (int)wr.size.height - extra;
6500
6501 NSTRACE_SIZE ("New size", NSMakeSize (neww, newh));
6502 NSTRACE_MSG ("tool_bar_height: %d", emacsframe->tool_bar_height);
6503
6504 cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, neww);
6505 rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe, newh);
6506
6507 if (cols < MINWIDTH)
6508 cols = MINWIDTH;
6509
6510 if (rows < MINHEIGHT)
6511 rows = MINHEIGHT;
6512
6513 NSTRACE_MSG ("New columns: %d", cols);
6514 NSTRACE_MSG ("New rows: %d", rows);
6515
6516 if (oldr != rows || oldc != cols || neww != oldw || newh != oldh)
6517 {
6518 NSView *view = FRAME_NS_VIEW (emacsframe);
6519
6520 change_frame_size (emacsframe,
6521 FRAME_PIXEL_TO_TEXT_WIDTH (emacsframe, neww),
6522 FRAME_PIXEL_TO_TEXT_HEIGHT (emacsframe, newh),
6523 0, delay, 0, 1);
6524 SET_FRAME_GARBAGED (emacsframe);
6525 cancel_mouse_face (emacsframe);
6526
6527 wr = NSMakeRect (0, 0, neww, newh);
6528
6529 [view setFrame: wr];
6530
6531 // to do: consider using [NSNotificationCenter postNotificationName:].
6532 [self windowDidMove: // Update top/left.
6533 [NSNotification notificationWithName:NSWindowDidMoveNotification
6534 object:[view window]]];
6535 }
6536 else
6537 {
6538 NSTRACE_MSG ("No change");
6539 }
6540 }
6541
6542 - (NSSize)windowWillResize: (NSWindow *)sender toSize: (NSSize)frameSize
6543 /* normalize frame to gridded text size */
6544 {
6545 int extra = 0;
6546
6547 NSTRACE ("[EmacsView windowWillResize:toSize: " NSTRACE_FMT_SIZE "]",
6548 NSTRACE_ARG_SIZE (frameSize));
6549 NSTRACE_RECT ("[sender frame]", [sender frame]);
6550 NSTRACE_FSTYPE ("fs_state", fs_state);
6551
6552 if (fs_state == FULLSCREEN_MAXIMIZED
6553 && (maximized_width != (int)frameSize.width
6554 || maximized_height != (int)frameSize.height))
6555 [self setFSValue: FULLSCREEN_NONE];
6556 else if (fs_state == FULLSCREEN_WIDTH
6557 && maximized_width != (int)frameSize.width)
6558 [self setFSValue: FULLSCREEN_NONE];
6559 else if (fs_state == FULLSCREEN_HEIGHT
6560 && maximized_height != (int)frameSize.height)
6561 [self setFSValue: FULLSCREEN_NONE];
6562
6563 if (fs_state == FULLSCREEN_NONE)
6564 maximized_width = maximized_height = -1;
6565
6566 if (! [self isFullscreen])
6567 {
6568 extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
6569 + FRAME_TOOLBAR_HEIGHT (emacsframe);
6570 }
6571
6572 cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, frameSize.width);
6573 if (cols < MINWIDTH)
6574 cols = MINWIDTH;
6575
6576 rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe,
6577 frameSize.height - extra);
6578 if (rows < MINHEIGHT)
6579 rows = MINHEIGHT;
6580 #ifdef NS_IMPL_COCOA
6581 {
6582 /* this sets window title to have size in it; the wm does this under GS */
6583 NSRect r = [[self window] frame];
6584 if (r.size.height == frameSize.height && r.size.width == frameSize.width)
6585 {
6586 if (old_title != 0)
6587 {
6588 xfree (old_title);
6589 old_title = 0;
6590 }
6591 }
6592 else if (fs_state == FULLSCREEN_NONE && ! maximizing_resize)
6593 {
6594 char *size_title;
6595 NSWindow *window = [self window];
6596 if (old_title == 0)
6597 {
6598 char *t = strdup ([[[self window] title] UTF8String]);
6599 char *pos = strstr (t, " — ");
6600 if (pos)
6601 *pos = '\0';
6602 old_title = t;
6603 }
6604 size_title = xmalloc (strlen (old_title) + 40);
6605 esprintf (size_title, "%s — (%d x %d)", old_title, cols, rows);
6606 [window setTitle: [NSString stringWithUTF8String: size_title]];
6607 [window display];
6608 xfree (size_title);
6609 }
6610 }
6611 #endif /* NS_IMPL_COCOA */
6612
6613 NSTRACE_MSG ("cols: %d rows: %d", cols, rows);
6614
6615 /* Restrict the new size to the text gird.
6616
6617 Don't restrict the width if the user only adjusted the height, and
6618 vice versa. (Without this, the frame would shrink, and move
6619 slightly, if the window was resized by dragging one of its
6620 borders.) */
6621 if (!frame_resize_pixelwise)
6622 {
6623 NSRect r = [[self window] frame];
6624
6625 if (r.size.width != frameSize.width)
6626 {
6627 frameSize.width =
6628 FRAME_TEXT_COLS_TO_PIXEL_WIDTH (emacsframe, cols);
6629 }
6630
6631 if (r.size.height != frameSize.height)
6632 {
6633 frameSize.height =
6634 FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (emacsframe, rows) + extra;
6635 }
6636 }
6637
6638 NSTRACE_RETURN_SIZE (frameSize);
6639
6640 return frameSize;
6641 }
6642
6643
6644 - (void)windowDidResize: (NSNotification *)notification
6645 {
6646 NSTRACE ("[EmacsView windowDidResize:]");
6647 if (!FRAME_LIVE_P (emacsframe))
6648 {
6649 NSTRACE_MSG ("Ignored (frame dead)");
6650 return;
6651 }
6652 if (emacsframe->output_data.ns->in_animation)
6653 {
6654 NSTRACE_MSG ("Ignored (in animation)");
6655 return;
6656 }
6657
6658 if (! [self fsIsNative])
6659 {
6660 NSWindow *theWindow = [notification object];
6661 /* We can get notification on the non-FS window when in
6662 fullscreen mode. */
6663 if ([self window] != theWindow) return;
6664 }
6665
6666 NSTRACE_RECT ("frame", [[notification object] frame]);
6667
6668 #ifdef NS_IMPL_GNUSTEP
6669 NSWindow *theWindow = [notification object];
6670
6671 /* In GNUstep, at least currently, it's possible to get a didResize
6672 without getting a willResize.. therefore we need to act as if we got
6673 the willResize now */
6674 NSSize sz = [theWindow frame].size;
6675 sz = [self windowWillResize: theWindow toSize: sz];
6676 #endif /* NS_IMPL_GNUSTEP */
6677
6678 if (cols > 0 && rows > 0)
6679 {
6680 [self updateFrameSize: YES];
6681 }
6682
6683 ns_send_appdefined (-1);
6684 }
6685
6686 #ifdef NS_IMPL_COCOA
6687 - (void)viewDidEndLiveResize
6688 {
6689 NSTRACE ("[EmacsView viewDidEndLiveResize]");
6690
6691 [super viewDidEndLiveResize];
6692 if (old_title != 0)
6693 {
6694 [[self window] setTitle: [NSString stringWithUTF8String: old_title]];
6695 xfree (old_title);
6696 old_title = 0;
6697 }
6698 maximizing_resize = NO;
6699 }
6700 #endif /* NS_IMPL_COCOA */
6701
6702
6703 - (void)windowDidBecomeKey: (NSNotification *)notification
6704 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
6705 {
6706 [self windowDidBecomeKey];
6707 }
6708
6709
6710 - (void)windowDidBecomeKey /* for direct calls */
6711 {
6712 struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6713 struct frame *old_focus = dpyinfo->x_focus_frame;
6714
6715 NSTRACE ("[EmacsView windowDidBecomeKey]");
6716
6717 if (emacsframe != old_focus)
6718 dpyinfo->x_focus_frame = emacsframe;
6719
6720 ns_frame_rehighlight (emacsframe);
6721
6722 if (emacs_event)
6723 {
6724 emacs_event->kind = FOCUS_IN_EVENT;
6725 EV_TRAILER ((id)nil);
6726 }
6727 }
6728
6729
6730 - (void)windowDidResignKey: (NSNotification *)notification
6731 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
6732 {
6733 struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
6734 BOOL is_focus_frame = dpyinfo->x_focus_frame == emacsframe;
6735 NSTRACE ("[EmacsView windowDidResignKey:]");
6736
6737 if (is_focus_frame)
6738 dpyinfo->x_focus_frame = 0;
6739
6740 emacsframe->mouse_moved = 0;
6741 ns_frame_rehighlight (emacsframe);
6742
6743 /* FIXME: for some reason needed on second and subsequent clicks away
6744 from sole-frame Emacs to get hollow box to show */
6745 if (!windowClosing && [[self window] isVisible] == YES)
6746 {
6747 x_update_cursor (emacsframe, 1);
6748 x_set_frame_alpha (emacsframe);
6749 }
6750
6751 if (any_help_event_p)
6752 {
6753 Lisp_Object frame;
6754 XSETFRAME (frame, emacsframe);
6755 help_echo_string = Qnil;
6756 gen_help_event (Qnil, frame, Qnil, Qnil, 0);
6757 }
6758
6759 if (emacs_event && is_focus_frame)
6760 {
6761 [self deleteWorkingText];
6762 emacs_event->kind = FOCUS_OUT_EVENT;
6763 EV_TRAILER ((id)nil);
6764 }
6765 }
6766
6767
6768 - (void)windowWillMiniaturize: sender
6769 {
6770 NSTRACE ("[EmacsView windowWillMiniaturize:]");
6771 }
6772
6773
6774 - (void)setFrame:(NSRect)frameRect;
6775 {
6776 NSTRACE ("[EmacsView setFrame:" NSTRACE_FMT_RECT "]",
6777 NSTRACE_ARG_RECT (frameRect));
6778
6779 [super setFrame:(NSRect)frameRect];
6780 }
6781
6782
6783 - (BOOL)isFlipped
6784 {
6785 return YES;
6786 }
6787
6788
6789 - (BOOL)isOpaque
6790 {
6791 return NO;
6792 }
6793
6794
6795 - initFrameFromEmacs: (struct frame *)f
6796 {
6797 NSRect r, wr;
6798 Lisp_Object tem;
6799 NSWindow *win;
6800 NSColor *col;
6801 NSString *name;
6802
6803 NSTRACE ("[EmacsView initFrameFromEmacs:]");
6804 NSTRACE_MSG ("cols:%d lines:%d", f->text_cols, f->text_lines);
6805
6806 windowClosing = NO;
6807 processingCompose = NO;
6808 scrollbarsNeedingUpdate = 0;
6809 fs_state = FULLSCREEN_NONE;
6810 fs_before_fs = next_maximized = -1;
6811 #ifdef HAVE_NATIVE_FS
6812 fs_is_native = ns_use_native_fullscreen;
6813 #else
6814 fs_is_native = NO;
6815 #endif
6816 maximized_width = maximized_height = -1;
6817 nonfs_window = nil;
6818
6819 ns_userRect = NSMakeRect (0, 0, 0, 0);
6820 r = NSMakeRect (0, 0, FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, f->text_cols),
6821 FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, f->text_lines));
6822 [self initWithFrame: r];
6823 [self setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
6824
6825 FRAME_NS_VIEW (f) = self;
6826 emacsframe = f;
6827 #ifdef NS_IMPL_COCOA
6828 old_title = 0;
6829 maximizing_resize = NO;
6830 #endif
6831
6832 win = [[EmacsWindow alloc]
6833 initWithContentRect: r
6834 styleMask: (NSResizableWindowMask |
6835 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
6836 NSTitledWindowMask |
6837 #endif
6838 NSMiniaturizableWindowMask |
6839 NSClosableWindowMask)
6840 backing: NSBackingStoreBuffered
6841 defer: YES];
6842
6843 #ifdef HAVE_NATIVE_FS
6844 [win setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
6845 #endif
6846
6847 wr = [win frame];
6848 bwidth = f->border_width = wr.size.width - r.size.width;
6849 tibar_height = FRAME_NS_TITLEBAR_HEIGHT (f) = wr.size.height - r.size.height;
6850
6851 [win setAcceptsMouseMovedEvents: YES];
6852 [win setDelegate: self];
6853 #if !defined (NS_IMPL_COCOA) || \
6854 MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_9
6855 [win useOptimizedDrawing: YES];
6856 #endif
6857
6858 [[win contentView] addSubview: self];
6859
6860 if (ns_drag_types)
6861 [self registerForDraggedTypes: ns_drag_types];
6862
6863 tem = f->name;
6864 name = [NSString stringWithUTF8String:
6865 NILP (tem) ? "Emacs" : SSDATA (tem)];
6866 [win setTitle: name];
6867
6868 /* toolbar support */
6869 toolbar = [[EmacsToolbar alloc] initForView: self withIdentifier:
6870 [NSString stringWithFormat: @"Emacs Frame %d",
6871 ns_window_num]];
6872 [win setToolbar: toolbar];
6873 [toolbar setVisible: NO];
6874
6875 /* Don't set frame garbaged until tool bar is up to date?
6876 This avoids an extra clear and redraw (flicker) at frame creation. */
6877 if (FRAME_EXTERNAL_TOOL_BAR (f)) wait_for_tool_bar = YES;
6878 else wait_for_tool_bar = NO;
6879
6880
6881 #ifdef NS_IMPL_COCOA
6882 {
6883 NSButton *toggleButton;
6884 toggleButton = [win standardWindowButton: NSWindowToolbarButton];
6885 [toggleButton setTarget: self];
6886 [toggleButton setAction: @selector (toggleToolbar: )];
6887 }
6888 #endif
6889 FRAME_TOOLBAR_HEIGHT (f) = 0;
6890
6891 tem = f->icon_name;
6892 if (!NILP (tem))
6893 [win setMiniwindowTitle:
6894 [NSString stringWithUTF8String: SSDATA (tem)]];
6895
6896 {
6897 NSScreen *screen = [win screen];
6898
6899 if (screen != 0)
6900 {
6901 NSPoint pt = NSMakePoint
6902 (IN_BOUND (-SCREENMAX, f->left_pos, SCREENMAX),
6903 IN_BOUND (-SCREENMAX,
6904 [screen frame].size.height - NS_TOP_POS (f), SCREENMAX));
6905
6906 [win setFrameTopLeftPoint: pt];
6907
6908 NSTRACE_RECT ("new frame", [win frame]);
6909 }
6910 }
6911
6912 [win makeFirstResponder: self];
6913
6914 col = ns_lookup_indexed_color (NS_FACE_BACKGROUND
6915 (FRAME_DEFAULT_FACE (emacsframe)), emacsframe);
6916 [win setBackgroundColor: col];
6917 if ([col alphaComponent] != (EmacsCGFloat) 1.0)
6918 [win setOpaque: NO];
6919
6920 #if !defined (NS_IMPL_COCOA) || \
6921 MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_9
6922 [self allocateGState];
6923 #endif
6924 [NSApp registerServicesMenuSendTypes: ns_send_types
6925 returnTypes: nil];
6926
6927 ns_window_num++;
6928 return self;
6929 }
6930
6931
6932 - (void)windowDidMove: sender
6933 {
6934 NSWindow *win = [self window];
6935 NSRect r = [win frame];
6936 NSArray *screens = [NSScreen screens];
6937 NSScreen *screen = [screens objectAtIndex: 0];
6938
6939 NSTRACE ("[EmacsView windowDidMove:]");
6940
6941 if (!emacsframe->output_data.ns)
6942 return;
6943 if (screen != nil)
6944 {
6945 emacsframe->left_pos = r.origin.x;
6946 emacsframe->top_pos =
6947 [screen frame].size.height - (r.origin.y + r.size.height);
6948 }
6949 }
6950
6951
6952 /* Called AFTER method below, but before our windowWillResize call there leads
6953 to windowDidResize -> x_set_window_size. Update emacs' notion of frame
6954 location so set_window_size moves the frame. */
6955 - (BOOL)windowShouldZoom: (NSWindow *)sender toFrame: (NSRect)newFrame
6956 {
6957 NSTRACE (("[EmacsView windowShouldZoom:toFrame:" NSTRACE_FMT_RECT "]"
6958 NSTRACE_FMT_RETURN "YES"),
6959 NSTRACE_ARG_RECT (newFrame));
6960
6961 emacsframe->output_data.ns->zooming = 1;
6962 return YES;
6963 }
6964
6965
6966 /* Override to do something slightly nonstandard, but nice. First click on
6967 zoom button will zoom vertically. Second will zoom completely. Third
6968 returns to original. */
6969 - (NSRect)windowWillUseStandardFrame:(NSWindow *)sender
6970 defaultFrame:(NSRect)defaultFrame
6971 {
6972 // TODO: Rename to "currentFrame" and assign "result" properly in
6973 // all paths.
6974 NSRect result = [sender frame];
6975
6976 NSTRACE (("[EmacsView windowWillUseStandardFrame:defaultFrame:"
6977 NSTRACE_FMT_RECT "]"),
6978 NSTRACE_ARG_RECT (defaultFrame));
6979 NSTRACE_FSTYPE ("fs_state", fs_state);
6980 NSTRACE_FSTYPE ("fs_before_fs", fs_before_fs);
6981 NSTRACE_FSTYPE ("next_maximized", next_maximized);
6982 NSTRACE_RECT ("ns_userRect", ns_userRect);
6983 NSTRACE_RECT ("[sender frame]", [sender frame]);
6984
6985 if (fs_before_fs != -1) /* Entering fullscreen */
6986 {
6987 NSTRACE_MSG ("Entering fullscreen");
6988 result = defaultFrame;
6989 }
6990 else
6991 {
6992 // Save the window size and position (frame) before the resize.
6993 if (fs_state != FULLSCREEN_MAXIMIZED
6994 && fs_state != FULLSCREEN_WIDTH)
6995 {
6996 ns_userRect.size.width = result.size.width;
6997 ns_userRect.origin.x = result.origin.x;
6998 }
6999
7000 if (fs_state != FULLSCREEN_MAXIMIZED
7001 && fs_state != FULLSCREEN_HEIGHT)
7002 {
7003 ns_userRect.size.height = result.size.height;
7004 ns_userRect.origin.y = result.origin.y;
7005 }
7006
7007 NSTRACE_RECT ("ns_userRect (2)", ns_userRect);
7008
7009 if (next_maximized == FULLSCREEN_HEIGHT
7010 || (next_maximized == -1
7011 && abs ((int)(defaultFrame.size.height - result.size.height))
7012 > FRAME_LINE_HEIGHT (emacsframe)))
7013 {
7014 /* first click */
7015 NSTRACE_MSG ("FULLSCREEN_HEIGHT");
7016 maximized_height = result.size.height = defaultFrame.size.height;
7017 maximized_width = -1;
7018 result.origin.y = defaultFrame.origin.y;
7019 if (ns_userRect.size.height != 0)
7020 {
7021 result.origin.x = ns_userRect.origin.x;
7022 result.size.width = ns_userRect.size.width;
7023 }
7024 [self setFSValue: FULLSCREEN_HEIGHT];
7025 #ifdef NS_IMPL_COCOA
7026 maximizing_resize = YES;
7027 #endif
7028 }
7029 else if (next_maximized == FULLSCREEN_WIDTH)
7030 {
7031 NSTRACE_MSG ("FULLSCREEN_WIDTH");
7032 maximized_width = result.size.width = defaultFrame.size.width;
7033 maximized_height = -1;
7034 result.origin.x = defaultFrame.origin.x;
7035 if (ns_userRect.size.width != 0)
7036 {
7037 result.origin.y = ns_userRect.origin.y;
7038 result.size.height = ns_userRect.size.height;
7039 }
7040 [self setFSValue: FULLSCREEN_WIDTH];
7041 }
7042 else if (next_maximized == FULLSCREEN_MAXIMIZED
7043 || (next_maximized == -1
7044 && abs ((int)(defaultFrame.size.width - result.size.width))
7045 > FRAME_COLUMN_WIDTH (emacsframe)))
7046 {
7047 NSTRACE_MSG ("FULLSCREEN_MAXIMIZED");
7048
7049 result = defaultFrame; /* second click */
7050 maximized_width = result.size.width;
7051 maximized_height = result.size.height;
7052 [self setFSValue: FULLSCREEN_MAXIMIZED];
7053 #ifdef NS_IMPL_COCOA
7054 maximizing_resize = YES;
7055 #endif
7056 }
7057 else
7058 {
7059 /* restore */
7060 NSTRACE_MSG ("Restore");
7061 result = ns_userRect.size.height ? ns_userRect : result;
7062 NSTRACE_RECT ("restore (2)", result);
7063 ns_userRect = NSMakeRect (0, 0, 0, 0);
7064 #ifdef NS_IMPL_COCOA
7065 maximizing_resize = fs_state != FULLSCREEN_NONE;
7066 #endif
7067 [self setFSValue: FULLSCREEN_NONE];
7068 maximized_width = maximized_height = -1;
7069 }
7070 }
7071
7072 if (fs_before_fs == -1) next_maximized = -1;
7073
7074 NSTRACE_RECT ("Final ns_userRect", ns_userRect);
7075 NSTRACE_MSG ("Final maximized_width: %d", maximized_width);
7076 NSTRACE_MSG ("Final maximized_height: %d", maximized_height);
7077 NSTRACE_FSTYPE ("Final next_maximized", next_maximized);
7078
7079 [self windowWillResize: sender toSize: result.size];
7080
7081 NSTRACE_RETURN_RECT (result);
7082
7083 return result;
7084 }
7085
7086
7087 - (void)windowDidDeminiaturize: sender
7088 {
7089 NSTRACE ("[EmacsView windowDidDeminiaturize:]");
7090 if (!emacsframe->output_data.ns)
7091 return;
7092
7093 SET_FRAME_ICONIFIED (emacsframe, 0);
7094 SET_FRAME_VISIBLE (emacsframe, 1);
7095 windows_or_buffers_changed = 63;
7096
7097 if (emacs_event)
7098 {
7099 emacs_event->kind = DEICONIFY_EVENT;
7100 EV_TRAILER ((id)nil);
7101 }
7102 }
7103
7104
7105 - (void)windowDidExpose: sender
7106 {
7107 NSTRACE ("[EmacsView windowDidExpose:]");
7108 if (!emacsframe->output_data.ns)
7109 return;
7110
7111 SET_FRAME_VISIBLE (emacsframe, 1);
7112 SET_FRAME_GARBAGED (emacsframe);
7113
7114 if (send_appdefined)
7115 ns_send_appdefined (-1);
7116 }
7117
7118
7119 - (void)windowDidMiniaturize: sender
7120 {
7121 NSTRACE ("[EmacsView windowDidMiniaturize:]");
7122 if (!emacsframe->output_data.ns)
7123 return;
7124
7125 SET_FRAME_ICONIFIED (emacsframe, 1);
7126 SET_FRAME_VISIBLE (emacsframe, 0);
7127
7128 if (emacs_event)
7129 {
7130 emacs_event->kind = ICONIFY_EVENT;
7131 EV_TRAILER ((id)nil);
7132 }
7133 }
7134
7135 #ifdef HAVE_NATIVE_FS
7136 - (NSApplicationPresentationOptions)window:(NSWindow *)window
7137 willUseFullScreenPresentationOptions:
7138 (NSApplicationPresentationOptions)proposedOptions
7139 {
7140 return proposedOptions|NSApplicationPresentationAutoHideToolbar;
7141 }
7142 #endif
7143
7144 - (void)windowWillEnterFullScreen:(NSNotification *)notification
7145 {
7146 NSTRACE ("[EmacsView windowWillEnterFullScreen:]");
7147 [self windowWillEnterFullScreen];
7148 }
7149 - (void)windowWillEnterFullScreen /* provided for direct calls */
7150 {
7151 NSTRACE ("[EmacsView windowWillEnterFullScreen]");
7152 fs_before_fs = fs_state;
7153 }
7154
7155 - (void)windowDidEnterFullScreen:(NSNotification *)notification
7156 {
7157 NSTRACE ("[EmacsView windowDidEnterFullScreen:]");
7158 [self windowDidEnterFullScreen];
7159 }
7160
7161 - (void)windowDidEnterFullScreen /* provided for direct calls */
7162 {
7163 NSTRACE ("[EmacsView windowDidEnterFullScreen]");
7164 [self setFSValue: FULLSCREEN_BOTH];
7165 if (! [self fsIsNative])
7166 {
7167 [self windowDidBecomeKey];
7168 [nonfs_window orderOut:self];
7169 }
7170 else
7171 {
7172 BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (emacsframe) ? YES : NO;
7173 #ifdef NS_IMPL_COCOA
7174 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
7175 unsigned val = (unsigned)[NSApp presentationOptions];
7176
7177 // OSX 10.7 bug fix, the menu won't appear without this.
7178 // val is non-zero on other OSX versions.
7179 if (val == 0)
7180 {
7181 NSApplicationPresentationOptions options
7182 = NSApplicationPresentationAutoHideDock
7183 | NSApplicationPresentationAutoHideMenuBar
7184 | NSApplicationPresentationFullScreen
7185 | NSApplicationPresentationAutoHideToolbar;
7186
7187 [NSApp setPresentationOptions: options];
7188 }
7189 #endif
7190 #endif
7191 [toolbar setVisible:tbar_visible];
7192 }
7193 }
7194
7195 - (void)windowWillExitFullScreen:(NSNotification *)notification
7196 {
7197 NSTRACE ("[EmacsView windowWillExitFullScreen:]");
7198 [self windowWillExitFullScreen];
7199 }
7200
7201 - (void)windowWillExitFullScreen /* provided for direct calls */
7202 {
7203 NSTRACE ("[EmacsView windowWillExitFullScreen]");
7204 if (!FRAME_LIVE_P (emacsframe))
7205 {
7206 NSTRACE_MSG ("Ignored (frame dead)");
7207 return;
7208 }
7209 if (next_maximized != -1)
7210 fs_before_fs = next_maximized;
7211 }
7212
7213 - (void)windowDidExitFullScreen:(NSNotification *)notification
7214 {
7215 NSTRACE ("[EmacsView windowDidExitFullScreen:]");
7216 [self windowDidExitFullScreen];
7217 }
7218
7219 - (void)windowDidExitFullScreen /* provided for direct calls */
7220 {
7221 NSTRACE ("[EmacsView windowDidExitFullScreen]");
7222 if (!FRAME_LIVE_P (emacsframe))
7223 {
7224 NSTRACE_MSG ("Ignored (frame dead)");
7225 return;
7226 }
7227 [self setFSValue: fs_before_fs];
7228 fs_before_fs = -1;
7229 #ifdef HAVE_NATIVE_FS
7230 [self updateCollectionBehavior];
7231 #endif
7232 if (FRAME_EXTERNAL_TOOL_BAR (emacsframe))
7233 {
7234 [toolbar setVisible:YES];
7235 update_frame_tool_bar (emacsframe);
7236 [self updateFrameSize:YES];
7237 [[self window] display];
7238 }
7239 else
7240 [toolbar setVisible:NO];
7241
7242 if (next_maximized != -1)
7243 [[self window] performZoom:self];
7244 }
7245
7246 - (BOOL)fsIsNative
7247 {
7248 return fs_is_native;
7249 }
7250
7251 - (BOOL)isFullscreen
7252 {
7253 BOOL res;
7254
7255 if (! fs_is_native)
7256 {
7257 res = (nonfs_window != nil);
7258 }
7259 else
7260 {
7261 #ifdef HAVE_NATIVE_FS
7262 res = (([[self window] styleMask] & NSFullScreenWindowMask) != 0);
7263 #else
7264 res = NO;
7265 #endif
7266 }
7267
7268 NSTRACE ("[EmacsView isFullscreen] " NSTRACE_FMT_RETURN " %d",
7269 (int) res);
7270
7271 return res;
7272 }
7273
7274 #ifdef HAVE_NATIVE_FS
7275 - (void)updateCollectionBehavior
7276 {
7277 NSTRACE ("[EmacsView updateCollectionBehavior]");
7278
7279 if (! [self isFullscreen])
7280 {
7281 NSWindow *win = [self window];
7282 NSWindowCollectionBehavior b = [win collectionBehavior];
7283 if (ns_use_native_fullscreen)
7284 b |= NSWindowCollectionBehaviorFullScreenPrimary;
7285 else
7286 b &= ~NSWindowCollectionBehaviorFullScreenPrimary;
7287
7288 [win setCollectionBehavior: b];
7289 fs_is_native = ns_use_native_fullscreen;
7290 }
7291 }
7292 #endif
7293
7294 - (void)toggleFullScreen: (id)sender
7295 {
7296 NSWindow *w, *fw;
7297 BOOL onFirstScreen;
7298 struct frame *f;
7299 NSRect r, wr;
7300 NSColor *col;
7301
7302 NSTRACE ("[EmacsView toggleFullScreen:]");
7303
7304 if (fs_is_native)
7305 {
7306 #ifdef HAVE_NATIVE_FS
7307 [[self window] toggleFullScreen:sender];
7308 #endif
7309 return;
7310 }
7311
7312 w = [self window];
7313 onFirstScreen = [[w screen] isEqual:[[NSScreen screens] objectAtIndex:0]];
7314 f = emacsframe;
7315 wr = [w frame];
7316 col = ns_lookup_indexed_color (NS_FACE_BACKGROUND
7317 (FRAME_DEFAULT_FACE (f)),
7318 f);
7319
7320 if (fs_state != FULLSCREEN_BOTH)
7321 {
7322 NSScreen *screen = [w screen];
7323
7324 #if defined (NS_IMPL_COCOA) && \
7325 MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
7326 /* Hide ghost menu bar on secondary monitor? */
7327 if (! onFirstScreen)
7328 onFirstScreen = [NSScreen screensHaveSeparateSpaces];
7329 #endif
7330 /* Hide dock and menubar if we are on the primary screen. */
7331 if (onFirstScreen)
7332 {
7333 #ifdef NS_IMPL_COCOA
7334 NSApplicationPresentationOptions options
7335 = NSApplicationPresentationAutoHideDock
7336 | NSApplicationPresentationAutoHideMenuBar;
7337
7338 [NSApp setPresentationOptions: options];
7339 #else
7340 [NSMenu setMenuBarVisible:NO];
7341 #endif
7342 }
7343
7344 fw = [[EmacsFSWindow alloc]
7345 initWithContentRect:[w contentRectForFrameRect:wr]
7346 styleMask:NSBorderlessWindowMask
7347 backing:NSBackingStoreBuffered
7348 defer:YES
7349 screen:screen];
7350
7351 [fw setContentView:[w contentView]];
7352 [fw setTitle:[w title]];
7353 [fw setDelegate:self];
7354 [fw setAcceptsMouseMovedEvents: YES];
7355 #if !defined (NS_IMPL_COCOA) || \
7356 MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_9
7357 [fw useOptimizedDrawing: YES];
7358 #endif
7359 [fw setBackgroundColor: col];
7360 if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7361 [fw setOpaque: NO];
7362
7363 f->border_width = 0;
7364 FRAME_NS_TITLEBAR_HEIGHT (f) = 0;
7365 tobar_height = FRAME_TOOLBAR_HEIGHT (f);
7366 FRAME_TOOLBAR_HEIGHT (f) = 0;
7367
7368 nonfs_window = w;
7369
7370 [self windowWillEnterFullScreen];
7371 [fw makeKeyAndOrderFront:NSApp];
7372 [fw makeFirstResponder:self];
7373 [w orderOut:self];
7374 r = [fw frameRectForContentRect:[screen frame]];
7375 [fw setFrame: r display:YES animate:ns_use_fullscreen_animation];
7376 [self windowDidEnterFullScreen];
7377 [fw display];
7378 }
7379 else
7380 {
7381 fw = w;
7382 w = nonfs_window;
7383 nonfs_window = nil;
7384
7385 if (onFirstScreen)
7386 {
7387 #ifdef NS_IMPL_COCOA
7388 [NSApp setPresentationOptions: NSApplicationPresentationDefault];
7389 #else
7390 [NSMenu setMenuBarVisible:YES];
7391 #endif
7392 }
7393
7394 [w setContentView:[fw contentView]];
7395 [w setBackgroundColor: col];
7396 if ([col alphaComponent] != (EmacsCGFloat) 1.0)
7397 [w setOpaque: NO];
7398
7399 f->border_width = bwidth;
7400 FRAME_NS_TITLEBAR_HEIGHT (f) = tibar_height;
7401 if (FRAME_EXTERNAL_TOOL_BAR (f))
7402 FRAME_TOOLBAR_HEIGHT (f) = tobar_height;
7403
7404 // to do: consider using [NSNotificationCenter postNotificationName:] to send notifications.
7405
7406 [self windowWillExitFullScreen];
7407 [fw setFrame: [w frame] display:YES animate:ns_use_fullscreen_animation];
7408 [fw close];
7409 [w makeKeyAndOrderFront:NSApp];
7410 [self windowDidExitFullScreen];
7411 [self updateFrameSize:YES];
7412 }
7413 }
7414
7415 - (void)handleFS
7416 {
7417 NSTRACE ("[EmacsView handleFS]");
7418
7419 if (fs_state != emacsframe->want_fullscreen)
7420 {
7421 if (fs_state == FULLSCREEN_BOTH)
7422 {
7423 NSTRACE_MSG ("fs_state == FULLSCREEN_BOTH");
7424 [self toggleFullScreen:self];
7425 }
7426
7427 switch (emacsframe->want_fullscreen)
7428 {
7429 case FULLSCREEN_BOTH:
7430 NSTRACE_MSG ("FULLSCREEN_BOTH");
7431 [self toggleFullScreen:self];
7432 break;
7433 case FULLSCREEN_WIDTH:
7434 NSTRACE_MSG ("FULLSCREEN_WIDTH");
7435 next_maximized = FULLSCREEN_WIDTH;
7436 if (fs_state != FULLSCREEN_BOTH)
7437 [[self window] performZoom:self];
7438 break;
7439 case FULLSCREEN_HEIGHT:
7440 NSTRACE_MSG ("FULLSCREEN_HEIGHT");
7441 next_maximized = FULLSCREEN_HEIGHT;
7442 if (fs_state != FULLSCREEN_BOTH)
7443 [[self window] performZoom:self];
7444 break;
7445 case FULLSCREEN_MAXIMIZED:
7446 NSTRACE_MSG ("FULLSCREEN_MAXIMIZED");
7447 next_maximized = FULLSCREEN_MAXIMIZED;
7448 if (fs_state != FULLSCREEN_BOTH)
7449 [[self window] performZoom:self];
7450 break;
7451 case FULLSCREEN_NONE:
7452 NSTRACE_MSG ("FULLSCREEN_NONE");
7453 if (fs_state != FULLSCREEN_BOTH)
7454 {
7455 next_maximized = FULLSCREEN_NONE;
7456 [[self window] performZoom:self];
7457 }
7458 break;
7459 }
7460
7461 emacsframe->want_fullscreen = FULLSCREEN_NONE;
7462 }
7463
7464 }
7465
7466 - (void) setFSValue: (int)value
7467 {
7468 NSTRACE ("[EmacsView setFSValue:" NSTRACE_FMT_FSTYPE "]",
7469 NSTRACE_ARG_FSTYPE(value));
7470
7471 Lisp_Object lval = Qnil;
7472 switch (value)
7473 {
7474 case FULLSCREEN_BOTH:
7475 lval = Qfullboth;
7476 break;
7477 case FULLSCREEN_WIDTH:
7478 lval = Qfullwidth;
7479 break;
7480 case FULLSCREEN_HEIGHT:
7481 lval = Qfullheight;
7482 break;
7483 case FULLSCREEN_MAXIMIZED:
7484 lval = Qmaximized;
7485 break;
7486 }
7487 store_frame_param (emacsframe, Qfullscreen, lval);
7488 fs_state = value;
7489 }
7490
7491 - (void)mouseEntered: (NSEvent *)theEvent
7492 {
7493 NSTRACE ("[EmacsView mouseEntered:]");
7494 if (emacsframe)
7495 FRAME_DISPLAY_INFO (emacsframe)->last_mouse_movement_time
7496 = EV_TIMESTAMP (theEvent);
7497 }
7498
7499
7500 - (void)mouseExited: (NSEvent *)theEvent
7501 {
7502 Mouse_HLInfo *hlinfo = emacsframe ? MOUSE_HL_INFO (emacsframe) : NULL;
7503
7504 NSTRACE ("[EmacsView mouseExited:]");
7505
7506 if (!hlinfo)
7507 return;
7508
7509 FRAME_DISPLAY_INFO (emacsframe)->last_mouse_movement_time
7510 = EV_TIMESTAMP (theEvent);
7511
7512 if (emacsframe == hlinfo->mouse_face_mouse_frame)
7513 {
7514 clear_mouse_face (hlinfo);
7515 hlinfo->mouse_face_mouse_frame = 0;
7516 }
7517 }
7518
7519
7520 - menuDown: sender
7521 {
7522 NSTRACE ("[EmacsView menuDown:]");
7523 if (context_menu_value == -1)
7524 context_menu_value = [sender tag];
7525 else
7526 {
7527 NSInteger tag = [sender tag];
7528 find_and_call_menu_selection (emacsframe, emacsframe->menu_bar_items_used,
7529 emacsframe->menu_bar_vector,
7530 (void *)tag);
7531 }
7532
7533 ns_send_appdefined (-1);
7534 return self;
7535 }
7536
7537
7538 - (EmacsToolbar *)toolbar
7539 {
7540 return toolbar;
7541 }
7542
7543
7544 /* this gets called on toolbar button click */
7545 - toolbarClicked: (id)item
7546 {
7547 NSEvent *theEvent;
7548 int idx = [item tag] * TOOL_BAR_ITEM_NSLOTS;
7549
7550 NSTRACE ("[EmacsView toolbarClicked:]");
7551
7552 if (!emacs_event)
7553 return self;
7554
7555 /* send first event (for some reason two needed) */
7556 theEvent = [[self window] currentEvent];
7557 emacs_event->kind = TOOL_BAR_EVENT;
7558 XSETFRAME (emacs_event->arg, emacsframe);
7559 EV_TRAILER (theEvent);
7560
7561 emacs_event->kind = TOOL_BAR_EVENT;
7562 /* XSETINT (emacs_event->code, 0); */
7563 emacs_event->arg = AREF (emacsframe->tool_bar_items,
7564 idx + TOOL_BAR_ITEM_KEY);
7565 emacs_event->modifiers = EV_MODIFIERS (theEvent);
7566 EV_TRAILER (theEvent);
7567 return self;
7568 }
7569
7570
7571 - toggleToolbar: (id)sender
7572 {
7573 NSTRACE ("[EmacsView toggleToolbar:]");
7574
7575 if (!emacs_event)
7576 return self;
7577
7578 emacs_event->kind = NS_NONKEY_EVENT;
7579 emacs_event->code = KEY_NS_TOGGLE_TOOLBAR;
7580 EV_TRAILER ((id)nil);
7581 return self;
7582 }
7583
7584
7585 - (void)drawRect: (NSRect)rect
7586 {
7587 int x = NSMinX (rect), y = NSMinY (rect);
7588 int width = NSWidth (rect), height = NSHeight (rect);
7589
7590 NSTRACE ("[EmacsView drawRect:" NSTRACE_FMT_RECT "]",
7591 NSTRACE_ARG_RECT(rect));
7592
7593 if (!emacsframe || !emacsframe->output_data.ns)
7594 return;
7595
7596 ns_clear_frame_area (emacsframe, x, y, width, height);
7597 block_input ();
7598 expose_frame (emacsframe, x, y, width, height);
7599 unblock_input ();
7600
7601 /*
7602 drawRect: may be called (at least in OS X 10.5) for invisible
7603 views as well for some reason. Thus, do not infer visibility
7604 here.
7605
7606 emacsframe->async_visible = 1;
7607 emacsframe->async_iconified = 0;
7608 */
7609 }
7610
7611
7612 /* NSDraggingDestination protocol methods. Actually this is not really a
7613 protocol, but a category of Object. O well... */
7614
7615 -(NSDragOperation) draggingEntered: (id <NSDraggingInfo>) sender
7616 {
7617 NSTRACE ("[EmacsView draggingEntered:]");
7618 return NSDragOperationGeneric;
7619 }
7620
7621
7622 -(BOOL)prepareForDragOperation: (id <NSDraggingInfo>) sender
7623 {
7624 return YES;
7625 }
7626
7627
7628 -(BOOL)performDragOperation: (id <NSDraggingInfo>) sender
7629 {
7630 id pb;
7631 int x, y;
7632 NSString *type;
7633 NSEvent *theEvent = [[self window] currentEvent];
7634 NSPoint position;
7635 NSDragOperation op = [sender draggingSourceOperationMask];
7636 int modifiers = 0;
7637
7638 NSTRACE ("[EmacsView performDragOperation:]");
7639
7640 if (!emacs_event)
7641 return NO;
7642
7643 position = [self convertPoint: [sender draggingLocation] fromView: nil];
7644 x = lrint (position.x); y = lrint (position.y);
7645
7646 pb = [sender draggingPasteboard];
7647 type = [pb availableTypeFromArray: ns_drag_types];
7648
7649 if (! (op & (NSDragOperationMove|NSDragOperationDelete)) &&
7650 // URL drags contain all operations (0xf), don't allow all to be set.
7651 (op & 0xf) != 0xf)
7652 {
7653 if (op & NSDragOperationLink)
7654 modifiers |= NSControlKeyMask;
7655 if (op & NSDragOperationCopy)
7656 modifiers |= NSAlternateKeyMask;
7657 if (op & NSDragOperationGeneric)
7658 modifiers |= NSCommandKeyMask;
7659 }
7660
7661 modifiers = EV_MODIFIERS2 (modifiers);
7662 if (type == 0)
7663 {
7664 return NO;
7665 }
7666 else if ([type isEqualToString: NSFilenamesPboardType])
7667 {
7668 NSArray *files;
7669 NSEnumerator *fenum;
7670 NSString *file;
7671
7672 if (!(files = [pb propertyListForType: type]))
7673 return NO;
7674
7675 fenum = [files objectEnumerator];
7676 while ( (file = [fenum nextObject]) )
7677 {
7678 emacs_event->kind = DRAG_N_DROP_EVENT;
7679 XSETINT (emacs_event->x, x);
7680 XSETINT (emacs_event->y, y);
7681 ns_input_file = append2 (ns_input_file,
7682 build_string ([file UTF8String]));
7683 emacs_event->modifiers = modifiers;
7684 emacs_event->arg = list2 (Qfile, build_string ([file UTF8String]));
7685 EV_TRAILER (theEvent);
7686 }
7687 return YES;
7688 }
7689 else if ([type isEqualToString: NSURLPboardType])
7690 {
7691 NSURL *url = [NSURL URLFromPasteboard: pb];
7692 if (url == nil) return NO;
7693
7694 emacs_event->kind = DRAG_N_DROP_EVENT;
7695 XSETINT (emacs_event->x, x);
7696 XSETINT (emacs_event->y, y);
7697 emacs_event->modifiers = modifiers;
7698 emacs_event->arg = list2 (Qurl,
7699 build_string ([[url absoluteString]
7700 UTF8String]));
7701 EV_TRAILER (theEvent);
7702
7703 if ([url isFileURL] != NO)
7704 {
7705 NSString *file = [url path];
7706 ns_input_file = append2 (ns_input_file,
7707 build_string ([file UTF8String]));
7708 }
7709 return YES;
7710 }
7711 else if ([type isEqualToString: NSStringPboardType]
7712 || [type isEqualToString: NSTabularTextPboardType])
7713 {
7714 NSString *data;
7715
7716 if (! (data = [pb stringForType: type]))
7717 return NO;
7718
7719 emacs_event->kind = DRAG_N_DROP_EVENT;
7720 XSETINT (emacs_event->x, x);
7721 XSETINT (emacs_event->y, y);
7722 emacs_event->modifiers = modifiers;
7723 emacs_event->arg = list2 (Qnil, build_string ([data UTF8String]));
7724 EV_TRAILER (theEvent);
7725 return YES;
7726 }
7727 else
7728 {
7729 fprintf (stderr, "Invalid data type in dragging pasteboard");
7730 return NO;
7731 }
7732 }
7733
7734
7735 - (id) validRequestorForSendType: (NSString *)typeSent
7736 returnType: (NSString *)typeReturned
7737 {
7738 NSTRACE ("[EmacsView validRequestorForSendType:returnType:]");
7739 if (typeSent != nil && [ns_send_types indexOfObject: typeSent] != NSNotFound
7740 && typeReturned == nil)
7741 {
7742 if (! NILP (ns_get_local_selection (QPRIMARY, QUTF8_STRING)))
7743 return self;
7744 }
7745
7746 return [super validRequestorForSendType: typeSent
7747 returnType: typeReturned];
7748 }
7749
7750
7751 /* The next two methods are part of NSServicesRequests informal protocol,
7752 supposedly called when a services menu item is chosen from this app.
7753 But this should not happen because we override the services menu with our
7754 own entries which call ns-perform-service.
7755 Nonetheless, it appeared to happen (under strange circumstances): bug#1435.
7756 So let's at least stub them out until further investigation can be done. */
7757
7758 - (BOOL) readSelectionFromPasteboard: (NSPasteboard *)pb
7759 {
7760 /* we could call ns_string_from_pasteboard(pboard) here but then it should
7761 be written into the buffer in place of the existing selection..
7762 ordinary service calls go through functions defined in ns-win.el */
7763 return NO;
7764 }
7765
7766 - (BOOL) writeSelectionToPasteboard: (NSPasteboard *)pb types: (NSArray *)types
7767 {
7768 NSArray *typesDeclared;
7769 Lisp_Object val;
7770
7771 NSTRACE ("[EmacsView writeSelectionToPasteboard:types:]");
7772
7773 /* We only support NSStringPboardType */
7774 if ([types containsObject:NSStringPboardType] == NO) {
7775 return NO;
7776 }
7777
7778 val = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
7779 if (CONSP (val) && SYMBOLP (XCAR (val)))
7780 {
7781 val = XCDR (val);
7782 if (CONSP (val) && NILP (XCDR (val)))
7783 val = XCAR (val);
7784 }
7785 if (! STRINGP (val))
7786 return NO;
7787
7788 typesDeclared = [NSArray arrayWithObject:NSStringPboardType];
7789 [pb declareTypes:typesDeclared owner:nil];
7790 ns_string_to_pasteboard (pb, val);
7791 return YES;
7792 }
7793
7794
7795 /* setMini =YES means set from internal (gives a finder icon), NO means set nil
7796 (gives a miniaturized version of the window); currently we use the latter for
7797 frames whose active buffer doesn't correspond to any file
7798 (e.g., '*scratch*') */
7799 - setMiniwindowImage: (BOOL) setMini
7800 {
7801 id image = [[self window] miniwindowImage];
7802 NSTRACE ("[EmacsView setMiniwindowImage:%d]", setMini);
7803
7804 /* NOTE: under Cocoa miniwindowImage always returns nil, documentation
7805 about "AppleDockIconEnabled" notwithstanding, however the set message
7806 below has its effect nonetheless. */
7807 if (image != emacsframe->output_data.ns->miniimage)
7808 {
7809 if (image && [image isKindOfClass: [EmacsImage class]])
7810 [image release];
7811 [[self window] setMiniwindowImage:
7812 setMini ? emacsframe->output_data.ns->miniimage : nil];
7813 }
7814
7815 return self;
7816 }
7817
7818
7819 - (void) setRows: (int) r andColumns: (int) c
7820 {
7821 NSTRACE ("[EmacsView setRows:%d andColumns:%d]", r, c);
7822 rows = r;
7823 cols = c;
7824 }
7825
7826 - (int) fullscreenState
7827 {
7828 return fs_state;
7829 }
7830
7831 @end /* EmacsView */
7832
7833
7834
7835 /* ==========================================================================
7836
7837 EmacsWindow implementation
7838
7839 ========================================================================== */
7840
7841 @implementation EmacsWindow
7842
7843 #ifdef NS_IMPL_COCOA
7844 - (id)accessibilityAttributeValue:(NSString *)attribute
7845 {
7846 Lisp_Object str = Qnil;
7847 struct frame *f = SELECTED_FRAME ();
7848 struct buffer *curbuf = XBUFFER (XWINDOW (f->selected_window)->contents);
7849
7850 NSTRACE ("[EmacsWindow accessibilityAttributeValue:]");
7851
7852 if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
7853 return NSAccessibilityTextFieldRole;
7854
7855 if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]
7856 && curbuf && ! NILP (BVAR (curbuf, mark_active)))
7857 {
7858 str = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
7859 }
7860 else if (curbuf && [attribute isEqualToString:NSAccessibilityValueAttribute])
7861 {
7862 if (! NILP (BVAR (curbuf, mark_active)))
7863 str = ns_get_local_selection (QPRIMARY, QUTF8_STRING);
7864
7865 if (NILP (str))
7866 {
7867 ptrdiff_t start_byte = BUF_BEGV_BYTE (curbuf);
7868 ptrdiff_t byte_range = BUF_ZV_BYTE (curbuf) - start_byte;
7869 ptrdiff_t range = BUF_ZV (curbuf) - BUF_BEGV (curbuf);
7870
7871 if (! NILP (BVAR (curbuf, enable_multibyte_characters)))
7872 str = make_uninit_multibyte_string (range, byte_range);
7873 else
7874 str = make_uninit_string (range);
7875 /* To check: This returns emacs-utf-8, which is a superset of utf-8.
7876 Is this a problem? */
7877 memcpy (SDATA (str), BYTE_POS_ADDR (start_byte), byte_range);
7878 }
7879 }
7880
7881
7882 if (! NILP (str))
7883 {
7884 if (CONSP (str) && SYMBOLP (XCAR (str)))
7885 {
7886 str = XCDR (str);
7887 if (CONSP (str) && NILP (XCDR (str)))
7888 str = XCAR (str);
7889 }
7890 if (STRINGP (str))
7891 {
7892 const char *utfStr = SSDATA (str);
7893 NSString *nsStr = [NSString stringWithUTF8String: utfStr];
7894 return nsStr;
7895 }
7896 }
7897
7898 return [super accessibilityAttributeValue:attribute];
7899 }
7900 #endif /* NS_IMPL_COCOA */
7901
7902 /* Constrain size and placement of a frame.
7903
7904 By returning the original "frameRect", the frame is not
7905 constrained. This can lead to unwanted situations where, for
7906 example, the menu bar covers the frame.
7907
7908 The default implementation (accessed using "super") constrains the
7909 frame to the visible area of SCREEN, minus the menu bar (if
7910 present) and the Dock. Note that default implementation also calls
7911 windowWillResize, with the frame it thinks should have. (This can
7912 make the frame exit maximized mode.)
7913
7914 Note that this should work in situations where multiple monitors
7915 are present. Common configurations are side-by-side monitors and a
7916 monitor on top of another (e.g. when a laptop is placed under a
7917 large screen). */
7918 - (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
7919 {
7920 NSTRACE ("[EmacsWindow constrainFrameRect:" NSTRACE_FMT_RECT " toScreen:]",
7921 NSTRACE_ARG_RECT (frameRect));
7922
7923 #ifdef NS_IMPL_COCOA
7924 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
7925 // If separate spaces is on, it is like each screen is independent. There is
7926 // no spanning of frames across screens.
7927 if ([NSScreen screensHaveSeparateSpaces])
7928 {
7929 NSTRACE_MSG ("Screens have separate spaces");
7930 frameRect = [super constrainFrameRect:frameRect toScreen:screen];
7931 NSTRACE_RETURN_RECT (frameRect);
7932 return frameRect;
7933 }
7934 #endif
7935 #endif
7936
7937 return constrain_frame_rect(frameRect,
7938 [(EmacsView *)[self delegate] isFullscreen]);
7939 }
7940
7941
7942 - (void)performZoom:(id)sender
7943 {
7944 NSTRACE ("[EmacsWindow performZoom:]");
7945
7946 return [super performZoom:sender];
7947 }
7948
7949 - (void)zoom:(id)sender
7950 {
7951 NSTRACE ("[EmacsWindow zoom:]");
7952
7953 ns_update_auto_hide_menu_bar();
7954
7955 // Below are three zoom implementations. In the final commit, the
7956 // idea is that the last should be included.
7957
7958 #if 0
7959 // Native zoom done using the standard zoom animation. Size of the
7960 // resulting frame reduced to accommodate the Dock and, if present,
7961 // the menu-bar.
7962 [super zoom:sender];
7963
7964 #elif 0
7965 // Native zoom done using the standard zoom animation, plus an
7966 // explicit resize to cover the full screen, except the menu-bar and
7967 // dock, if present.
7968 [super zoom:sender];
7969
7970 // After the native zoom, resize the resulting frame to fill the
7971 // entire screen, except the menu-bar.
7972 //
7973 // This works for all practical purposes. (The only minor oddity is
7974 // when transiting from full-height frame to a maximized, the
7975 // animation reduces the height of the frame slightly (to the 4
7976 // pixels needed to accommodate the Doc) before it snaps back into
7977 // full height. The user would need a very trained eye to spot
7978 // this.)
7979 NSScreen * screen = [self screen];
7980 if (screen != nil)
7981 {
7982 int fs_state = [(EmacsView *)[self delegate] fullscreenState];
7983
7984 NSTRACE_FSTYPE ("fullscreenState", fs_state);
7985
7986 NSRect sr = [screen frame];
7987 struct EmacsMargins margins
7988 = ns_screen_margins_ignoring_hidden_dock(screen);
7989
7990 NSRect wr = [self frame];
7991 NSTRACE_RECT ("Rect after zoom", wr);
7992
7993 NSRect newWr = wr;
7994
7995 if (fs_state == FULLSCREEN_MAXIMIZED
7996 || fs_state == FULLSCREEN_HEIGHT)
7997 {
7998 newWr.origin.y = sr.origin.y + margins.bottom;
7999 newWr.size.height = sr.size.height - margins.top - margins.bottom;
8000 }
8001
8002 if (fs_state == FULLSCREEN_MAXIMIZED
8003 || fs_state == FULLSCREEN_WIDTH)
8004 {
8005 newWr.origin.x = sr.origin.x + margins.left;
8006 newWr.size.width = sr.size.width - margins.right - margins.left;
8007 }
8008
8009 if (newWr.size.width != wr.size.width
8010 || newWr.size.height != wr.size.height
8011 || newWr.origin.x != wr.origin.x
8012 || newWr.origin.y != wr.origin.y)
8013 {
8014 NSTRACE_MSG ("New frame different");
8015 [self setFrame: newWr display: NO];
8016 }
8017 }
8018 #else
8019 // Non-native zoom which is done instantaneously. The resulting
8020 // frame covers the entire screen, except the menu-bar and dock, if
8021 // present.
8022 NSScreen * screen = [self screen];
8023 if (screen != nil)
8024 {
8025 NSRect sr = [screen frame];
8026 struct EmacsMargins margins
8027 = ns_screen_margins_ignoring_hidden_dock(screen);
8028
8029 sr.size.height -= (margins.top + margins.bottom);
8030 sr.size.width -= (margins.left + margins.right);
8031 sr.origin.x += margins.left;
8032 sr.origin.y += margins.bottom;
8033
8034 sr = [[self delegate] windowWillUseStandardFrame:self
8035 defaultFrame:sr];
8036 [self setFrame: sr display: NO];
8037 }
8038 #endif
8039 }
8040
8041 - (void)setFrame:(NSRect)windowFrame
8042 display:(BOOL)displayViews
8043 {
8044 NSTRACE ("[EmacsWindow setFrame:" NSTRACE_FMT_RECT " display:%d]",
8045 NSTRACE_ARG_RECT (windowFrame), displayViews);
8046
8047 [super setFrame:windowFrame display:displayViews];
8048 }
8049
8050 - (void)setFrame:(NSRect)windowFrame
8051 display:(BOOL)displayViews
8052 animate:(BOOL)performAnimation
8053 {
8054 NSTRACE ("[EmacsWindow setFrame:" NSTRACE_FMT_RECT
8055 " display:%d performAnimation:%d]",
8056 NSTRACE_ARG_RECT (windowFrame), displayViews, performAnimation);
8057
8058 [super setFrame:windowFrame display:displayViews animate:performAnimation];
8059 }
8060
8061 - (void)setFrameTopLeftPoint:(NSPoint)point
8062 {
8063 NSTRACE ("[EmacsWindow setFrameTopLeftPoint:" NSTRACE_FMT_POINT "]",
8064 NSTRACE_ARG_POINT (point));
8065
8066 [super setFrameTopLeftPoint:point];
8067 }
8068 @end /* EmacsWindow */
8069
8070
8071 @implementation EmacsFSWindow
8072
8073 - (BOOL)canBecomeKeyWindow
8074 {
8075 return YES;
8076 }
8077
8078 - (BOOL)canBecomeMainWindow
8079 {
8080 return YES;
8081 }
8082
8083 @end
8084
8085 /* ==========================================================================
8086
8087 EmacsScroller implementation
8088
8089 ========================================================================== */
8090
8091
8092 @implementation EmacsScroller
8093
8094 /* for repeat button push */
8095 #define SCROLL_BAR_FIRST_DELAY 0.5
8096 #define SCROLL_BAR_CONTINUOUS_DELAY (1.0 / 15)
8097
8098 + (CGFloat) scrollerWidth
8099 {
8100 /* TODO: if we want to allow variable widths, this is the place to do it,
8101 however neither GNUstep nor Cocoa support it very well */
8102 CGFloat r;
8103 #if !defined (NS_IMPL_COCOA) || \
8104 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
8105 r = [NSScroller scrollerWidth];
8106 #else
8107 r = [NSScroller scrollerWidthForControlSize: NSRegularControlSize
8108 scrollerStyle: NSScrollerStyleLegacy];
8109 #endif
8110 return r;
8111 }
8112
8113
8114 - initFrame: (NSRect )r window: (Lisp_Object)nwin
8115 {
8116 NSTRACE ("[EmacsScroller initFrame: window:]");
8117
8118 r.size.width = [EmacsScroller scrollerWidth];
8119 [super initWithFrame: r/*NSMakeRect (0, 0, 0, 0)*/];
8120 [self setContinuous: YES];
8121 [self setEnabled: YES];
8122
8123 /* Ensure auto resizing of scrollbars occurs within the emacs frame's view
8124 locked against the top and bottom edges, and right edge on OS X, where
8125 scrollers are on right. */
8126 #ifdef NS_IMPL_GNUSTEP
8127 [self setAutoresizingMask: NSViewMaxXMargin | NSViewHeightSizable];
8128 #else
8129 [self setAutoresizingMask: NSViewMinXMargin | NSViewHeightSizable];
8130 #endif
8131
8132 window = XWINDOW (nwin);
8133 condemned = NO;
8134 pixel_height = NSHeight (r);
8135 if (pixel_height == 0) pixel_height = 1;
8136 min_portion = 20 / pixel_height;
8137
8138 frame = XFRAME (window->frame);
8139 if (FRAME_LIVE_P (frame))
8140 {
8141 int i;
8142 EmacsView *view = FRAME_NS_VIEW (frame);
8143 NSView *sview = [[view window] contentView];
8144 NSArray *subs = [sview subviews];
8145
8146 /* disable optimization stopping redraw of other scrollbars */
8147 view->scrollbarsNeedingUpdate = 0;
8148 for (i =[subs count]-1; i >= 0; i--)
8149 if ([[subs objectAtIndex: i] isKindOfClass: [EmacsScroller class]])
8150 view->scrollbarsNeedingUpdate++;
8151 [sview addSubview: self];
8152 }
8153
8154 /* [self setFrame: r]; */
8155
8156 return self;
8157 }
8158
8159
8160 - (void)setFrame: (NSRect)newRect
8161 {
8162 NSTRACE ("[EmacsScroller setFrame:]");
8163
8164 /* block_input (); */
8165 pixel_height = NSHeight (newRect);
8166 if (pixel_height == 0) pixel_height = 1;
8167 min_portion = 20 / pixel_height;
8168 [super setFrame: newRect];
8169 /* unblock_input (); */
8170 }
8171
8172
8173 - (void)dealloc
8174 {
8175 NSTRACE ("[EmacsScroller dealloc]");
8176 if (window)
8177 wset_vertical_scroll_bar (window, Qnil);
8178 window = 0;
8179 [super dealloc];
8180 }
8181
8182
8183 - condemn
8184 {
8185 NSTRACE ("[EmacsScroller condemn]");
8186 condemned =YES;
8187 return self;
8188 }
8189
8190
8191 - reprieve
8192 {
8193 NSTRACE ("[EmacsScroller reprieve]");
8194 condemned =NO;
8195 return self;
8196 }
8197
8198
8199 -(bool)judge
8200 {
8201 NSTRACE ("[EmacsScroller judge]");
8202 bool ret = condemned;
8203 if (condemned)
8204 {
8205 EmacsView *view;
8206 block_input ();
8207 /* ensure other scrollbar updates after deletion */
8208 view = (EmacsView *)FRAME_NS_VIEW (frame);
8209 if (view != nil)
8210 view->scrollbarsNeedingUpdate++;
8211 if (window)
8212 wset_vertical_scroll_bar (window, Qnil);
8213 window = 0;
8214 [self removeFromSuperview];
8215 [self release];
8216 unblock_input ();
8217 }
8218 return ret;
8219 }
8220
8221
8222 - (void)resetCursorRects
8223 {
8224 NSRect visible = [self visibleRect];
8225 NSTRACE ("[EmacsScroller resetCursorRects]");
8226
8227 if (!NSIsEmptyRect (visible))
8228 [self addCursorRect: visible cursor: [NSCursor arrowCursor]];
8229 [[NSCursor arrowCursor] setOnMouseEntered: YES];
8230 }
8231
8232
8233 - (int) checkSamePosition: (int) position portion: (int) portion
8234 whole: (int) whole
8235 {
8236 return em_position ==position && em_portion ==portion && em_whole ==whole
8237 && portion != whole; /* needed for resize empty buf */
8238 }
8239
8240
8241 - setPosition: (int)position portion: (int)portion whole: (int)whole
8242 {
8243 NSTRACE ("[EmacsScroller setPosition:portion:whole:]");
8244
8245 em_position = position;
8246 em_portion = portion;
8247 em_whole = whole;
8248
8249 if (portion >= whole)
8250 {
8251 #ifdef NS_IMPL_COCOA
8252 [self setKnobProportion: 1.0];
8253 [self setDoubleValue: 1.0];
8254 #else
8255 [self setFloatValue: 0.0 knobProportion: 1.0];
8256 #endif
8257 }
8258 else
8259 {
8260 float pos;
8261 CGFloat por;
8262 portion = max ((float)whole*min_portion/pixel_height, portion);
8263 pos = (float)position / (whole - portion);
8264 por = (CGFloat)portion/whole;
8265 #ifdef NS_IMPL_COCOA
8266 [self setKnobProportion: por];
8267 [self setDoubleValue: pos];
8268 #else
8269 [self setFloatValue: pos knobProportion: por];
8270 #endif
8271 }
8272
8273 return self;
8274 }
8275
8276 /* set up emacs_event */
8277 - (void) sendScrollEventAtLoc: (float)loc fromEvent: (NSEvent *)e
8278 {
8279 Lisp_Object win;
8280
8281 NSTRACE ("[EmacsScroller sendScrollEventAtLoc:fromEvent:]");
8282
8283 if (!emacs_event)
8284 return;
8285
8286 emacs_event->part = last_hit_part;
8287 emacs_event->code = 0;
8288 emacs_event->modifiers = EV_MODIFIERS (e) | down_modifier;
8289 XSETWINDOW (win, window);
8290 emacs_event->frame_or_window = win;
8291 emacs_event->timestamp = EV_TIMESTAMP (e);
8292 emacs_event->kind = SCROLL_BAR_CLICK_EVENT;
8293 emacs_event->arg = Qnil;
8294 XSETINT (emacs_event->x, loc * pixel_height);
8295 XSETINT (emacs_event->y, pixel_height-20);
8296
8297 if (q_event_ptr)
8298 {
8299 n_emacs_events_pending++;
8300 kbd_buffer_store_event_hold (emacs_event, q_event_ptr);
8301 }
8302 else
8303 hold_event (emacs_event);
8304 EVENT_INIT (*emacs_event);
8305 ns_send_appdefined (-1);
8306 }
8307
8308
8309 /* called manually thru timer to implement repeated button action w/hold-down */
8310 - repeatScroll: (NSTimer *)scrollEntry
8311 {
8312 NSEvent *e = [[self window] currentEvent];
8313 NSPoint p = [[self window] mouseLocationOutsideOfEventStream];
8314 BOOL inKnob = [self testPart: p] == NSScrollerKnob;
8315
8316 NSTRACE ("[EmacsScroller repeatScroll:]");
8317
8318 /* clear timer if need be */
8319 if (inKnob || [scroll_repeat_entry timeInterval] == SCROLL_BAR_FIRST_DELAY)
8320 {
8321 [scroll_repeat_entry invalidate];
8322 [scroll_repeat_entry release];
8323 scroll_repeat_entry = nil;
8324
8325 if (inKnob)
8326 return self;
8327
8328 scroll_repeat_entry
8329 = [[NSTimer scheduledTimerWithTimeInterval:
8330 SCROLL_BAR_CONTINUOUS_DELAY
8331 target: self
8332 selector: @selector (repeatScroll:)
8333 userInfo: 0
8334 repeats: YES]
8335 retain];
8336 }
8337
8338 [self sendScrollEventAtLoc: 0 fromEvent: e];
8339 return self;
8340 }
8341
8342
8343 /* Asynchronous mouse tracking for scroller. This allows us to dispatch
8344 mouseDragged events without going into a modal loop. */
8345 - (void)mouseDown: (NSEvent *)e
8346 {
8347 NSRect sr, kr;
8348 /* hitPart is only updated AFTER event is passed on */
8349 NSScrollerPart part = [self testPart: [e locationInWindow]];
8350 CGFloat inc = 0.0, loc, kloc, pos;
8351 int edge = 0;
8352
8353 NSTRACE ("[EmacsScroller mouseDown:]");
8354
8355 switch (part)
8356 {
8357 case NSScrollerDecrementPage:
8358 last_hit_part = scroll_bar_above_handle; inc = -1.0; break;
8359 case NSScrollerIncrementPage:
8360 last_hit_part = scroll_bar_below_handle; inc = 1.0; break;
8361 case NSScrollerDecrementLine:
8362 last_hit_part = scroll_bar_up_arrow; inc = -0.1; break;
8363 case NSScrollerIncrementLine:
8364 last_hit_part = scroll_bar_down_arrow; inc = 0.1; break;
8365 case NSScrollerKnob:
8366 last_hit_part = scroll_bar_handle; break;
8367 case NSScrollerKnobSlot: /* GNUstep-only */
8368 last_hit_part = scroll_bar_move_ratio; break;
8369 default: /* NSScrollerNoPart? */
8370 fprintf (stderr, "EmacsScoller-mouseDown: unexpected part %ld\n",
8371 (long) part);
8372 return;
8373 }
8374
8375 if (inc != 0.0)
8376 {
8377 pos = 0; /* ignored */
8378
8379 /* set a timer to repeat, as we can't let superclass do this modally */
8380 scroll_repeat_entry
8381 = [[NSTimer scheduledTimerWithTimeInterval: SCROLL_BAR_FIRST_DELAY
8382 target: self
8383 selector: @selector (repeatScroll:)
8384 userInfo: 0
8385 repeats: YES]
8386 retain];
8387 }
8388 else
8389 {
8390 /* handle, or on GNUstep possibly slot */
8391 NSEvent *fake_event;
8392
8393 /* compute float loc in slot and mouse offset on knob */
8394 sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot]
8395 toView: nil];
8396 loc = NSHeight (sr) - ([e locationInWindow].y - NSMinY (sr));
8397 if (loc <= 0.0)
8398 {
8399 loc = 0.0;
8400 edge = -1;
8401 }
8402 else if (loc >= NSHeight (sr))
8403 {
8404 loc = NSHeight (sr);
8405 edge = 1;
8406 }
8407
8408 if (edge)
8409 kloc = 0.5 * edge;
8410 else
8411 {
8412 kr = [self convertRect: [self rectForPart: NSScrollerKnob]
8413 toView: nil];
8414 kloc = NSHeight (kr) - ([e locationInWindow].y - NSMinY (kr));
8415 }
8416 last_mouse_offset = kloc;
8417
8418 /* if knob, tell emacs a location offset by knob pos
8419 (to indicate top of handle) */
8420 if (part == NSScrollerKnob)
8421 pos = (loc - last_mouse_offset) / NSHeight (sr);
8422 else
8423 /* else this is a slot click on GNUstep: go straight there */
8424 pos = loc / NSHeight (sr);
8425
8426 /* send a fake mouse-up to super to preempt modal -trackKnob: mode */
8427 fake_event = [NSEvent mouseEventWithType: NSLeftMouseUp
8428 location: [e locationInWindow]
8429 modifierFlags: [e modifierFlags]
8430 timestamp: [e timestamp]
8431 windowNumber: [e windowNumber]
8432 context: [e context]
8433 eventNumber: [e eventNumber]
8434 clickCount: [e clickCount]
8435 pressure: [e pressure]];
8436 [super mouseUp: fake_event];
8437 }
8438
8439 if (part != NSScrollerKnob)
8440 [self sendScrollEventAtLoc: pos fromEvent: e];
8441 }
8442
8443
8444 /* Called as we manually track scroller drags, rather than superclass. */
8445 - (void)mouseDragged: (NSEvent *)e
8446 {
8447 NSRect sr;
8448 double loc, pos;
8449
8450 NSTRACE ("[EmacsScroller mouseDragged:]");
8451
8452 sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot]
8453 toView: nil];
8454 loc = NSHeight (sr) - ([e locationInWindow].y - NSMinY (sr));
8455
8456 if (loc <= 0.0)
8457 {
8458 loc = 0.0;
8459 }
8460 else if (loc >= NSHeight (sr) + last_mouse_offset)
8461 {
8462 loc = NSHeight (sr) + last_mouse_offset;
8463 }
8464
8465 pos = (loc - last_mouse_offset) / NSHeight (sr);
8466 [self sendScrollEventAtLoc: pos fromEvent: e];
8467 }
8468
8469
8470 - (void)mouseUp: (NSEvent *)e
8471 {
8472 NSTRACE ("[EmacsScroller mouseUp:]");
8473
8474 if (scroll_repeat_entry)
8475 {
8476 [scroll_repeat_entry invalidate];
8477 [scroll_repeat_entry release];
8478 scroll_repeat_entry = nil;
8479 }
8480 last_hit_part = scroll_bar_above_handle;
8481 }
8482
8483
8484 /* treat scrollwheel events in the bar as though they were in the main window */
8485 - (void) scrollWheel: (NSEvent *)theEvent
8486 {
8487 NSTRACE ("[EmacsScroller scrollWheel:]");
8488
8489 EmacsView *view = (EmacsView *)FRAME_NS_VIEW (frame);
8490 [view mouseDown: theEvent];
8491 }
8492
8493 @end /* EmacsScroller */
8494
8495
8496 #ifdef NS_IMPL_GNUSTEP
8497 /* Dummy class to get rid of startup warnings. */
8498 @implementation EmacsDocument
8499
8500 @end
8501 #endif
8502
8503
8504 /* ==========================================================================
8505
8506 Font-related functions; these used to be in nsfaces.m
8507
8508 ========================================================================== */
8509
8510
8511 Lisp_Object
8512 x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
8513 {
8514 struct font *font = XFONT_OBJECT (font_object);
8515 EmacsView *view = FRAME_NS_VIEW (f);
8516 int font_ascent, font_descent;
8517
8518 if (fontset < 0)
8519 fontset = fontset_from_font (font_object);
8520 FRAME_FONTSET (f) = fontset;
8521
8522 if (FRAME_FONT (f) == font)
8523 /* This font is already set in frame F. There's nothing more to
8524 do. */
8525 return font_object;
8526
8527 FRAME_FONT (f) = font;
8528
8529 FRAME_BASELINE_OFFSET (f) = font->baseline_offset;
8530 FRAME_COLUMN_WIDTH (f) = font->average_width;
8531 get_font_ascent_descent (font, &font_ascent, &font_descent);
8532 FRAME_LINE_HEIGHT (f) = font_ascent + font_descent;
8533
8534 /* Compute the scroll bar width in character columns. */
8535 if (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) > 0)
8536 {
8537 int wid = FRAME_COLUMN_WIDTH (f);
8538 FRAME_CONFIG_SCROLL_BAR_COLS (f)
8539 = (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) + wid - 1) / wid;
8540 }
8541 else
8542 {
8543 int wid = FRAME_COLUMN_WIDTH (f);
8544 FRAME_CONFIG_SCROLL_BAR_COLS (f) = (14 + wid - 1) / wid;
8545 }
8546
8547 /* Compute the scroll bar height in character lines. */
8548 if (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) > 0)
8549 {
8550 int height = FRAME_LINE_HEIGHT (f);
8551 FRAME_CONFIG_SCROLL_BAR_LINES (f)
8552 = (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) + height - 1) / height;
8553 }
8554 else
8555 {
8556 int height = FRAME_LINE_HEIGHT (f);
8557 FRAME_CONFIG_SCROLL_BAR_LINES (f) = (14 + height - 1) / height;
8558 }
8559
8560 /* Now make the frame display the given font. */
8561 if (FRAME_NS_WINDOW (f) != 0 && ! [view isFullscreen])
8562 adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
8563 FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3,
8564 false, Qfont);
8565
8566 return font_object;
8567 }
8568
8569
8570 /* XLFD: -foundry-family-weight-slant-swidth-adstyle-pxlsz-ptSz-resx-resy-spc-avgWidth-rgstry-encoding */
8571 /* Note: ns_font_to_xlfd and ns_fontname_to_xlfd no longer needed, removed
8572 in 1.43. */
8573
8574 const char *
8575 ns_xlfd_to_fontname (const char *xlfd)
8576 /* --------------------------------------------------------------------------
8577 Convert an X font name (XLFD) to an NS font name.
8578 Only family is used.
8579 The string returned is temporarily allocated.
8580 -------------------------------------------------------------------------- */
8581 {
8582 char *name = xmalloc (180);
8583 int i, len;
8584 const char *ret;
8585
8586 if (!strncmp (xlfd, "--", 2))
8587 sscanf (xlfd, "--%*[^-]-%[^-]179-", name);
8588 else
8589 sscanf (xlfd, "-%*[^-]-%[^-]179-", name);
8590
8591 /* stopgap for malformed XLFD input */
8592 if (strlen (name) == 0)
8593 strcpy (name, "Monaco");
8594
8595 /* undo hack in ns_fontname_to_xlfd, converting '$' to '-', '_' to ' '
8596 also uppercase after '-' or ' ' */
8597 name[0] = c_toupper (name[0]);
8598 for (len =strlen (name), i =0; i<len; i++)
8599 {
8600 if (name[i] == '$')
8601 {
8602 name[i] = '-';
8603 if (i+1<len)
8604 name[i+1] = c_toupper (name[i+1]);
8605 }
8606 else if (name[i] == '_')
8607 {
8608 name[i] = ' ';
8609 if (i+1<len)
8610 name[i+1] = c_toupper (name[i+1]);
8611 }
8612 }
8613 /*fprintf (stderr, "converted '%s' to '%s'\n",xlfd,name); */
8614 ret = [[NSString stringWithUTF8String: name] UTF8String];
8615 xfree (name);
8616 return ret;
8617 }
8618
8619
8620 void
8621 syms_of_nsterm (void)
8622 {
8623 NSTRACE ("syms_of_nsterm");
8624
8625 ns_antialias_threshold = 10.0;
8626
8627 /* from 23+ we need to tell emacs what modifiers there are.. */
8628 DEFSYM (Qmodifier_value, "modifier-value");
8629 DEFSYM (Qalt, "alt");
8630 DEFSYM (Qhyper, "hyper");
8631 DEFSYM (Qmeta, "meta");
8632 DEFSYM (Qsuper, "super");
8633 DEFSYM (Qcontrol, "control");
8634 DEFSYM (QUTF8_STRING, "UTF8_STRING");
8635
8636 DEFSYM (Qfile, "file");
8637 DEFSYM (Qurl, "url");
8638
8639 Fput (Qalt, Qmodifier_value, make_number (alt_modifier));
8640 Fput (Qhyper, Qmodifier_value, make_number (hyper_modifier));
8641 Fput (Qmeta, Qmodifier_value, make_number (meta_modifier));
8642 Fput (Qsuper, Qmodifier_value, make_number (super_modifier));
8643 Fput (Qcontrol, Qmodifier_value, make_number (ctrl_modifier));
8644
8645 DEFVAR_LISP ("ns-input-file", ns_input_file,
8646 "The file specified in the last NS event.");
8647 ns_input_file =Qnil;
8648
8649 DEFVAR_LISP ("ns-working-text", ns_working_text,
8650 "String for visualizing working composition sequence.");
8651 ns_working_text =Qnil;
8652
8653 DEFVAR_LISP ("ns-input-font", ns_input_font,
8654 "The font specified in the last NS event.");
8655 ns_input_font =Qnil;
8656
8657 DEFVAR_LISP ("ns-input-fontsize", ns_input_fontsize,
8658 "The fontsize specified in the last NS event.");
8659 ns_input_fontsize =Qnil;
8660
8661 DEFVAR_LISP ("ns-input-line", ns_input_line,
8662 "The line specified in the last NS event.");
8663 ns_input_line =Qnil;
8664
8665 DEFVAR_LISP ("ns-input-spi-name", ns_input_spi_name,
8666 "The service name specified in the last NS event.");
8667 ns_input_spi_name =Qnil;
8668
8669 DEFVAR_LISP ("ns-input-spi-arg", ns_input_spi_arg,
8670 "The service argument specified in the last NS event.");
8671 ns_input_spi_arg =Qnil;
8672
8673 DEFVAR_LISP ("ns-alternate-modifier", ns_alternate_modifier,
8674 "This variable describes the behavior of the alternate or option key.\n\
8675 Set to control, meta, alt, super, or hyper means it is taken to be that key.\n\
8676 Set to none means that the alternate / option key is not interpreted by Emacs\n\
8677 at all, allowing it to be used at a lower level for accented character entry.");
8678 ns_alternate_modifier = Qmeta;
8679
8680 DEFVAR_LISP ("ns-right-alternate-modifier", ns_right_alternate_modifier,
8681 "This variable describes the behavior of the right alternate or option key.\n\
8682 Set to control, meta, alt, super, or hyper means it is taken to be that key.\n\
8683 Set to left means be the same key as `ns-alternate-modifier'.\n\
8684 Set to none means that the alternate / option key is not interpreted by Emacs\n\
8685 at all, allowing it to be used at a lower level for accented character entry.");
8686 ns_right_alternate_modifier = Qleft;
8687
8688 DEFVAR_LISP ("ns-command-modifier", ns_command_modifier,
8689 "This variable describes the behavior of the command key.\n\
8690 Set to control, meta, alt, super, or hyper means it is taken to be that key.");
8691 ns_command_modifier = Qsuper;
8692
8693 DEFVAR_LISP ("ns-right-command-modifier", ns_right_command_modifier,
8694 "This variable describes the behavior of the right command key.\n\
8695 Set to control, meta, alt, super, or hyper means it is taken to be that key.\n\
8696 Set to left means be the same key as `ns-command-modifier'.\n\
8697 Set to none means that the command / option key is not interpreted by Emacs\n\
8698 at all, allowing it to be used at a lower level for accented character entry.");
8699 ns_right_command_modifier = Qleft;
8700
8701 DEFVAR_LISP ("ns-control-modifier", ns_control_modifier,
8702 "This variable describes the behavior of the control key.\n\
8703 Set to control, meta, alt, super, or hyper means it is taken to be that key.");
8704 ns_control_modifier = Qcontrol;
8705
8706 DEFVAR_LISP ("ns-right-control-modifier", ns_right_control_modifier,
8707 "This variable describes the behavior of the right control key.\n\
8708 Set to control, meta, alt, super, or hyper means it is taken to be that key.\n\
8709 Set to left means be the same key as `ns-control-modifier'.\n\
8710 Set to none means that the control / option key is not interpreted by Emacs\n\
8711 at all, allowing it to be used at a lower level for accented character entry.");
8712 ns_right_control_modifier = Qleft;
8713
8714 DEFVAR_LISP ("ns-function-modifier", ns_function_modifier,
8715 "This variable describes the behavior of the function key (on laptops).\n\
8716 Set to control, meta, alt, super, or hyper means it is taken to be that key.\n\
8717 Set to none means that the function key is not interpreted by Emacs at all,\n\
8718 allowing it to be used at a lower level for accented character entry.");
8719 ns_function_modifier = Qnone;
8720
8721 DEFVAR_LISP ("ns-antialias-text", ns_antialias_text,
8722 "Non-nil (the default) means to render text antialiased.");
8723 ns_antialias_text = Qt;
8724
8725 DEFVAR_LISP ("ns-confirm-quit", ns_confirm_quit,
8726 "Whether to confirm application quit using dialog.");
8727 ns_confirm_quit = Qnil;
8728
8729 DEFVAR_LISP ("ns-auto-hide-menu-bar", ns_auto_hide_menu_bar,
8730 doc: /* Non-nil means that the menu bar is hidden, but appears when the mouse is near.
8731 Only works on OSX 10.6 or later. */);
8732 ns_auto_hide_menu_bar = Qnil;
8733
8734 DEFVAR_BOOL ("ns-use-native-fullscreen", ns_use_native_fullscreen,
8735 doc: /*Non-nil means to use native fullscreen on OSX >= 10.7.
8736 Nil means use fullscreen the old (< 10.7) way. The old way works better with
8737 multiple monitors, but lacks tool bar. This variable is ignored on OSX < 10.7.
8738 Default is t for OSX >= 10.7, nil otherwise. */);
8739 #ifdef HAVE_NATIVE_FS
8740 ns_use_native_fullscreen = YES;
8741 #else
8742 ns_use_native_fullscreen = NO;
8743 #endif
8744 ns_last_use_native_fullscreen = ns_use_native_fullscreen;
8745
8746 DEFVAR_BOOL ("ns-use-fullscreen-animation", ns_use_fullscreen_animation,
8747 doc: /*Non-nil means use animation on non-native fullscreen.
8748 For native fullscreen, this does nothing.
8749 Default is nil. */);
8750 ns_use_fullscreen_animation = NO;
8751
8752 DEFVAR_BOOL ("ns-use-srgb-colorspace", ns_use_srgb_colorspace,
8753 doc: /*Non-nil means to use sRGB colorspace on OSX >= 10.7.
8754 Note that this does not apply to images.
8755 This variable is ignored on OSX < 10.7 and GNUstep. */);
8756 ns_use_srgb_colorspace = YES;
8757
8758 /* TODO: move to common code */
8759 DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars,
8760 doc: /* Which toolkit scroll bars Emacs uses, if any.
8761 A value of nil means Emacs doesn't use toolkit scroll bars.
8762 With the X Window system, the value is a symbol describing the
8763 X toolkit. Possible values are: gtk, motif, xaw, or xaw3d.
8764 With MS Windows or Nextstep, the value is t. */);
8765 Vx_toolkit_scroll_bars = Qt;
8766
8767 DEFVAR_BOOL ("x-use-underline-position-properties",
8768 x_use_underline_position_properties,
8769 doc: /*Non-nil means make use of UNDERLINE_POSITION font properties.
8770 A value of nil means ignore them. If you encounter fonts with bogus
8771 UNDERLINE_POSITION font properties, for example 7x13 on XFree prior
8772 to 4.1, set this to nil. */);
8773 x_use_underline_position_properties = 0;
8774
8775 DEFVAR_BOOL ("x-underline-at-descent-line",
8776 x_underline_at_descent_line,
8777 doc: /* Non-nil means to draw the underline at the same place as the descent line.
8778 A value of nil means to draw the underline according to the value of the
8779 variable `x-use-underline-position-properties', which is usually at the
8780 baseline level. The default value is nil. */);
8781 x_underline_at_descent_line = 0;
8782
8783 /* Tell Emacs about this window system. */
8784 Fprovide (Qns, Qnil);
8785
8786 DEFSYM (Qcocoa, "cocoa");
8787 DEFSYM (Qgnustep, "gnustep");
8788
8789 #ifdef NS_IMPL_COCOA
8790 Fprovide (Qcocoa, Qnil);
8791 syms_of_macfont ();
8792 #else
8793 Fprovide (Qgnustep, Qnil);
8794 syms_of_nsfont ();
8795 #endif
8796
8797 }