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