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