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