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