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