1/* X11Controller.m -- connect the IB ui, also the NSApp delegate 2 * 3 * Copyright (c) 2002-2012 Apple Inc. All rights reserved. 4 * 5 * Permission is hereby granted, free of charge, to any person 6 * obtaining a copy of this software and associated documentation files 7 * (the "Software"), to deal in the Software without restriction, 8 * including without limitation the rights to use, copy, modify, merge, 9 * publish, distribute, sublicense, and/or sell copies of the Software, 10 * and to permit persons to whom the Software is furnished to do so, 11 * subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be 14 * included in all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 * NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT 20 * HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 * DEALINGS IN THE SOFTWARE. 24 * 25 * Except as contained in this notice, the name(s) of the above 26 * copyright holders shall not be used in advertising or otherwise to 27 * promote the sale, use or other dealings in this Software without 28 * prior written authorization. 29 */ 30 31#include "sanitizedCarbon.h" 32 33#ifdef HAVE_DIX_CONFIG_H 34#include <dix-config.h> 35#endif 36 37#import "X11Controller.h" 38#import "X11Application.h" 39 40#include "opaque.h" 41#include "darwin.h" 42#include "darwinEvents.h" 43#include "quartz.h" 44#include "quartzKeyboard.h" 45#include <X11/extensions/applewmconst.h> 46#include "applewmExt.h" 47 48#include <stdio.h> 49#include <unistd.h> 50#include <fcntl.h> 51#include <sys/types.h> 52#include <sys/wait.h> 53#include <asl.h> 54#include <stdlib.h> 55 56extern aslclient aslc; 57extern char *bundle_id_prefix; 58 59@interface X11Controller () 60#ifdef XQUARTZ_SPARKLE 61@property (nonatomic, readwrite, strong) NSMenuItem *check_for_updates_item; // Programatically enabled 62#endif 63 64@property (nonatomic, readwrite, strong) NSArray *apps; 65@property (nonatomic, readwrite, strong) NSMutableArray *table_apps; 66@property (nonatomic, readwrite, assign) NSInteger windows_menu_nitems; 67@property (nonatomic, readwrite, assign) int checked_window_item; 68@property (nonatomic, readwrite, assign) x_list *pending_apps; 69@property (nonatomic, readwrite, assign) OSX_BOOL finished_launching; 70@end 71 72@implementation X11Controller 73 74- (void) awakeFromNib 75{ 76 X11Application *xapp = NSApp; 77 NSArray *array; 78 79 /* Point X11Application at ourself. */ 80 xapp.controller = self; 81 82 array = [xapp prefs_get_array:@PREFS_APPSMENU]; 83 if (array != nil) { 84 int count; 85 86 /* convert from [TITLE1 COMMAND1 TITLE2 COMMAND2 ...] 87 to [[TITLE1 COMMAND1] [TITLE2 COMMAND2] ...] format. */ 88 89 count = [array count]; 90 if (count > 0 91 && ![[array objectAtIndex:0] isKindOfClass:[NSArray class]]) { 92 int i; 93 NSMutableArray *copy, *sub; 94 95 copy = [NSMutableArray arrayWithCapacity:(count / 2)]; 96 97 for (i = 0; i < count / 2; i++) { 98 sub = [[NSMutableArray alloc] initWithCapacity:3]; 99 [sub addObject:[array objectAtIndex:i * 2]]; 100 [sub addObject:[array objectAtIndex:i * 2 + 1]]; 101 [sub addObject:@""]; 102 [copy addObject:sub]; 103 [sub release]; 104 } 105 106 array = copy; 107 } 108 109 [self set_apps_menu:array]; 110 } 111 112 [[NSNotificationCenter defaultCenter] 113 addObserver: self 114 selector: @selector(apps_table_done:) 115 name: NSWindowWillCloseNotification 116 object: self.apps_table.window]; 117} 118 119- (void) item_selected:sender 120{ 121 [NSApp activateIgnoringOtherApps:YES]; 122 123 DarwinSendDDXEvent(kXquartzControllerNotify, 2, 124 AppleWMWindowMenuItem, [sender tag]); 125} 126 127- (void) remove_apps_menu 128{ 129 NSMenu *menu; 130 NSMenuItem *item; 131 int i; 132 133 NSMenuItem * const apps_separator = self.apps_separator; 134 NSMenu * const dock_apps_menu = self.dock_apps_menu; 135 136 if (self.apps == nil || apps_separator == nil) return; 137 138 menu = [apps_separator menu]; 139 140 if (menu != nil) { 141 for (i = [menu numberOfItems] - 1; i >= 0; i--) { 142 item = (NSMenuItem *)[menu itemAtIndex:i]; 143 if ([item tag] != 0) 144 [menu removeItemAtIndex:i]; 145 } 146 } 147 148 if (dock_apps_menu != nil) { 149 for (i = [dock_apps_menu numberOfItems] - 1; i >= 0; i--) { 150 item = (NSMenuItem *)[dock_apps_menu itemAtIndex:i]; 151 if ([item tag] != 0) 152 [dock_apps_menu removeItemAtIndex:i]; 153 } 154 } 155 156 self.apps = nil; 157} 158 159- (void) prepend_apps_item:(NSArray *)list index:(int)i menu:(NSMenu *)menu 160{ 161 NSString *title, *shortcut = @""; 162 NSArray *group; 163 NSMenuItem *item; 164 165 group = [list objectAtIndex:i]; 166 title = [group objectAtIndex:0]; 167 if ([group count] >= 3) 168 shortcut = [group objectAtIndex:2]; 169 170 if ([title length] != 0) { 171 item = (NSMenuItem *)[menu insertItemWithTitle:title 172 action:@selector ( 173 app_selected:) 174 keyEquivalent:shortcut atIndex:0]; 175 [item setTarget:self]; 176 [item setEnabled:YES]; 177 } 178 else { 179 item = (NSMenuItem *)[NSMenuItem separatorItem]; 180 [menu insertItem:item atIndex:0]; 181 } 182 183 [item setTag:i + 1]; /* can't be zero, so add one */ 184} 185 186- (void) install_apps_menu:(NSArray *)list 187{ 188 NSMenu *menu; 189 int i, count; 190 191 count = [list count]; 192 193 NSMenuItem * const apps_separator = self.apps_separator; 194 NSMenu * const dock_apps_menu = self.dock_apps_menu; 195 196 if (count == 0 || apps_separator == nil) return; 197 198 menu = [apps_separator menu]; 199 200 for (i = count - 1; i >= 0; i--) { 201 if (menu != nil) 202 [self prepend_apps_item:list index:i menu:menu]; 203 if (dock_apps_menu != nil) 204 [self prepend_apps_item:list index:i menu:dock_apps_menu]; 205 } 206 207 self.apps = list; 208} 209 210- (void) set_window_menu:(NSArray *)list 211{ 212 NSMenu * const menu = X11App.windowsMenu; 213 NSMenu * const dock_menu = self.dock_menu; 214 215 /* First, remove the existing items from the Window Menu */ 216 NSInteger itemsToRemove = self.windows_menu_nitems; 217 if (itemsToRemove > 0) { 218 NSInteger indexForRemoval = menu.numberOfItems - itemsToRemove - 1; /* we also want to remove the separator */ 219 220 for (NSInteger i = 0 ; i < itemsToRemove + 1 ; i++) { 221 [menu removeItemAtIndex:indexForRemoval]; 222 } 223 224 for (NSInteger i = 0 ; i < itemsToRemove; i++) { 225 [dock_menu removeItemAtIndex:0]; 226 } 227 } 228 229 NSInteger const itemsToAdd = list.count; 230 self.windows_menu_nitems = itemsToAdd; 231 232 if (itemsToAdd > 0) { 233 NSMenuItem *item; 234 235 // Push a Separator 236 [menu addItem:[NSMenuItem separatorItem]]; 237 238 for (NSInteger i = 0; i < itemsToAdd; i++) { 239 NSString *name, *shortcut; 240 241 name = list[i][0]; 242 shortcut = list[i][1]; 243 244 if (windowItemModMask == 0 || windowItemModMask == -1) 245 shortcut = @""; 246 247 item = (NSMenuItem *)[menu addItemWithTitle:name 248 action:@selector(item_selected:) 249 keyEquivalent:shortcut]; 250 [item setKeyEquivalentModifierMask:(NSUInteger)windowItemModMask]; 251 [item setTarget:self]; 252 [item setTag:i]; 253 [item setEnabled:YES]; 254 255 item = (NSMenuItem *)[dock_menu insertItemWithTitle:name 256 action:@selector(item_selected:) 257 keyEquivalent:shortcut 258 atIndex:i]; 259 [item setKeyEquivalentModifierMask:(NSUInteger)windowItemModMask]; 260 [item setTarget:self]; 261 [item setTag:i]; 262 [item setEnabled:YES]; 263 } 264 265 int const checked_window_item = self.checked_window_item; 266 if (checked_window_item >= 0 && checked_window_item < itemsToAdd) { 267 NSInteger first = menu.numberOfItems - itemsToAdd; 268 item = (NSMenuItem *)[menu itemAtIndex:first + checked_window_item]; 269 [item setState:NSOnState]; 270 271 item = (NSMenuItem *)[dock_menu itemAtIndex:checked_window_item]; 272 [item setState:NSOnState]; 273 } 274 } 275 276 DarwinSendDDXEvent(kXquartzControllerNotify, 1, AppleWMWindowMenuNotify); 277} 278 279- (void) set_window_menu_check:(NSNumber *)nn 280{ 281 NSMenu * const menu = X11App.windowsMenu; 282 NSMenu * const dock_menu = self.dock_menu; 283 NSMenuItem *item; 284 int n = nn.intValue; 285 286 NSInteger const count = self.windows_menu_nitems; 287 NSInteger const first = menu.numberOfItems - count; 288 289 int const checked_window_item = self.checked_window_item; 290 291 if (checked_window_item >= 0 && checked_window_item < count) { 292 item = (NSMenuItem *)[menu itemAtIndex:first + checked_window_item]; 293 [item setState:NSOffState]; 294 item = (NSMenuItem *)[dock_menu itemAtIndex:checked_window_item]; 295 [item setState:NSOffState]; 296 } 297 if (n >= 0 && n < count) { 298 item = (NSMenuItem *)[menu itemAtIndex:first + n]; 299 [item setState:NSOnState]; 300 item = (NSMenuItem *)[dock_menu itemAtIndex:n]; 301 [item setState:NSOnState]; 302 } 303 self.checked_window_item = n; 304} 305 306- (void) set_apps_menu:(NSArray *)list 307{ 308 [self remove_apps_menu]; 309 [self install_apps_menu:list]; 310} 311 312#ifdef XQUARTZ_SPARKLE 313- (void) setup_sparkle 314{ 315 if (self.check_for_updates_item) 316 return; // already did it... 317 318 NSMenu *menu = [self.x11_about_item menu]; 319 320 NSMenuItem * const check_for_updates_item = 321 [menu insertItemWithTitle:NSLocalizedString(@"Check for X11 Updates...", @"Check for X11 Updates...") 322 action:@selector(checkForUpdates:) 323 keyEquivalent:@"" 324 atIndex:1]; 325 [check_for_updates_item setTarget:[SUUpdater sharedUpdater]]; 326 [check_for_updates_item setEnabled:YES]; 327 328 self.check_for_updates_item = check_for_updates_item; 329 330 // Set X11Controller as the delegate for the updater. 331 [[SUUpdater sharedUpdater] setDelegate:self]; 332} 333 334// Sent immediately before installing the specified update. 335- (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)update 336{ 337 //self.can_quit = YES; 338} 339 340#endif 341 342- (void) launch_client:(NSString *)filename 343{ 344 int child1, child2 = 0; 345 int status; 346 const char *newargv[4]; 347 char buf[128]; 348 char *s; 349 int stdout_pipe[2]; 350 int stderr_pipe[2]; 351 352 newargv[0] = [X11App prefs_get_string:@PREFS_LOGIN_SHELL default:"/bin/sh"]; 353 newargv[1] = "-c"; 354 newargv[2] = [filename UTF8String]; 355 newargv[3] = NULL; 356 357 s = getenv("DISPLAY"); 358 if (s == NULL || s[0] == 0) { 359 snprintf(buf, sizeof(buf), ":%s", display); 360 setenv("DISPLAY", buf, TRUE); 361 } 362 363 if (&asl_log_descriptor) { 364 char *asl_sender; 365 aslmsg amsg = asl_new(ASL_TYPE_MSG); 366 assert(amsg); 367 368 asprintf(&asl_sender, "%s.%s", bundle_id_prefix, newargv[2]); 369 assert(asl_sender); 370 for(s = asl_sender + strlen(bundle_id_prefix) + 1; *s; s++) { 371 if(! ((*s >= 'a' && *s <= 'z') || 372 (*s >= 'A' && *s <= 'Z') || 373 (*s >= '0' && *s <= '9'))) { 374 *s = '_'; 375 } 376 } 377 378 (void)asl_set(amsg, ASL_KEY_SENDER, asl_sender); 379 free(asl_sender); 380 381 assert(0 == pipe(stdout_pipe)); 382 fcntl(stdout_pipe[0], F_SETFD, FD_CLOEXEC); 383 fcntl(stdout_pipe[0], F_SETFL, O_NONBLOCK); 384 385 assert(0 == pipe(stderr_pipe)); 386 fcntl(stderr_pipe[0], F_SETFD, FD_CLOEXEC); 387 fcntl(stderr_pipe[0], F_SETFL, O_NONBLOCK); 388 389 asl_log_descriptor(aslc, amsg, ASL_LEVEL_INFO, stdout_pipe[0], ASL_LOG_DESCRIPTOR_READ); 390 asl_log_descriptor(aslc, amsg, ASL_LEVEL_NOTICE, stderr_pipe[0], ASL_LOG_DESCRIPTOR_READ); 391 392 asl_free(amsg); 393 } 394 395 /* Do the fork-twice trick to avoid having to reap zombies */ 396 child1 = fork(); 397 switch (child1) { 398 case -1: /* error */ 399 break; 400 401 case 0: /* child1 */ 402 child2 = fork(); 403 404 switch (child2) { 405 int max_files, i; 406 407 case -1: /* error */ 408 _exit(1); 409 410 case 0: /* child2 */ 411 if (&asl_log_descriptor) { 412 /* Replace our stdout/stderr */ 413 dup2(stdout_pipe[1], STDOUT_FILENO); 414 dup2(stderr_pipe[1], STDERR_FILENO); 415 } 416 417 /* close all open files except for standard streams */ 418 max_files = sysconf(_SC_OPEN_MAX); 419 for (i = 3; i < max_files; i++) 420 close(i); 421 422 /* ensure stdin is on /dev/null */ 423 close(0); 424 open("/dev/null", O_RDONLY); 425 426 execvp(newargv[0], (char * *const)newargv); 427 _exit(2); 428 429 default: /* parent (child1) */ 430 _exit(0); 431 } 432 break; 433 434 default: /* parent */ 435 waitpid(child1, &status, 0); 436 } 437 438 if (&asl_log_descriptor) { 439 /* Close the write ends of the pipe */ 440 close(stdout_pipe[1]); 441 close(stderr_pipe[1]); 442 } 443} 444 445- (void) app_selected:sender 446{ 447 int tag; 448 NSString *item; 449 NSArray * const apps = self.apps; 450 451 tag = [sender tag] - 1; 452 if (apps == nil || tag < 0 || tag >= [apps count]) 453 return; 454 455 item = [[apps objectAtIndex:tag] objectAtIndex:1]; 456 457 [self launch_client:item]; 458} 459 460- (IBAction) apps_table_show:sender 461{ 462 NSArray *columns; 463 NSMutableArray *oldapps = self.table_apps; 464 NSTableView * const apps_table = self.apps_table; 465 466 NSMutableArray * const table_apps = [[NSMutableArray alloc] initWithCapacity:1]; 467 self.table_apps = table_apps; 468 469 NSArray * const apps = self.apps; 470 if (apps != nil) 471 [table_apps addObjectsFromArray:apps]; 472 473 columns = [apps_table tableColumns]; 474 [[columns objectAtIndex:0] setIdentifier:@"0"]; 475 [[columns objectAtIndex:1] setIdentifier:@"1"]; 476 [[columns objectAtIndex:2] setIdentifier:@"2"]; 477 478 [apps_table setDataSource:self]; 479 [apps_table selectRowIndexes:[NSIndexSet indexSetWithIndex:0] 480 byExtendingSelection:NO]; 481 482 [[apps_table window] makeKeyAndOrderFront:sender]; 483 [apps_table reloadData]; 484 if (oldapps != nil) 485 [oldapps release]; 486} 487 488- (IBAction) apps_table_done:sender 489{ 490 NSMutableArray * const table_apps = self.table_apps; 491 NSTableView * const apps_table = self.apps_table; 492 [apps_table deselectAll:sender]; /* flush edits? */ 493 494 [self remove_apps_menu]; 495 [self install_apps_menu:table_apps]; 496 497 [NSApp prefs_set_array:@PREFS_APPSMENU value:table_apps]; 498 [NSApp prefs_synchronize]; 499 500 [[apps_table window] orderOut:sender]; 501 502 self.table_apps = nil; 503} 504 505- (IBAction) apps_table_new:sender 506{ 507 NSMutableArray *item; 508 NSMutableArray * const table_apps = self.table_apps; 509 NSTableView * const apps_table = self.apps_table; 510 511 int row = [apps_table selectedRow], i; 512 513 if (row < 0) row = 0; 514 else row = row + 1; 515 516 i = row; 517 if (i > [table_apps count]) 518 return; /* avoid exceptions */ 519 520 [apps_table deselectAll:sender]; 521 522 item = [[NSMutableArray alloc] initWithCapacity:3]; 523 [item addObject:@""]; 524 [item addObject:@""]; 525 [item addObject:@""]; 526 527 [table_apps insertObject:item atIndex:i]; 528 [item release]; 529 530 [apps_table reloadData]; 531 [apps_table selectRowIndexes:[NSIndexSet indexSetWithIndex:row] 532 byExtendingSelection:NO]; 533} 534 535- (IBAction) apps_table_duplicate:sender 536{ 537 NSMutableArray * const table_apps = self.table_apps; 538 NSTableView * const apps_table = self.apps_table; 539 int row = [apps_table selectedRow], i; 540 NSObject *item; 541 542 if (row < 0) { 543 [self apps_table_new:sender]; 544 return; 545 } 546 547 i = row; 548 if (i > [table_apps count] - 1) return; /* avoid exceptions */ 549 550 [apps_table deselectAll:sender]; 551 552 item = [[table_apps objectAtIndex:i] mutableCopy]; 553 [table_apps insertObject:item atIndex:i]; 554 [item release]; 555 556 [apps_table reloadData]; 557 [apps_table selectRowIndexes:[NSIndexSet indexSetWithIndex:row + 558 1] byExtendingSelection:NO]; 559} 560 561- (IBAction) apps_table_delete:sender 562{ 563 NSMutableArray * const table_apps = self.table_apps; 564 NSTableView * const apps_table = self.apps_table; 565 int row = [apps_table selectedRow]; 566 567 if (row >= 0) { 568 int i = row; 569 570 if (i > [table_apps count] - 1) return; /* avoid exceptions */ 571 572 [apps_table deselectAll:sender]; 573 574 [table_apps removeObjectAtIndex:i]; 575 } 576 577 [apps_table reloadData]; 578 579 row = MIN(row, [table_apps count] - 1); 580 if (row >= 0) 581 [apps_table selectRowIndexes:[NSIndexSet indexSetWithIndex:row] 582 byExtendingSelection:NO]; 583} 584 585- (NSInteger) numberOfRowsInTableView:(NSTableView *)tableView 586{ 587 NSMutableArray * const table_apps = self.table_apps; 588 if (table_apps == nil) return 0; 589 590 return [table_apps count]; 591} 592 593- (id) tableView:(NSTableView *)tableView 594 objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row 595{ 596 NSMutableArray * const table_apps = self.table_apps; 597 NSArray *item; 598 int col; 599 600 if (table_apps == nil) return nil; 601 602 col = [[tableColumn identifier] intValue]; 603 604 item = [table_apps objectAtIndex:row]; 605 if ([item count] > col) 606 return [item objectAtIndex:col]; 607 else 608 return @""; 609} 610 611- (void) tableView:(NSTableView *)tableView setObjectValue:(id)object 612 forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row 613{ 614 NSMutableArray * const table_apps = self.table_apps; 615 NSMutableArray *item; 616 int col; 617 618 if (table_apps == nil) return; 619 620 col = [[tableColumn identifier] intValue]; 621 622 item = [table_apps objectAtIndex:row]; 623 [item replaceObjectAtIndex:col withObject:object]; 624} 625 626- (void) hide_window:sender 627{ 628 if ([X11App x_active]) 629 DarwinSendDDXEvent(kXquartzControllerNotify, 1, AppleWMHideWindow); 630 else 631 NSBeep(); /* FIXME: something here */ 632} 633 634- (IBAction)bring_to_front:sender 635{ 636 DarwinSendDDXEvent(kXquartzControllerNotify, 1, AppleWMBringAllToFront); 637} 638 639- (IBAction)close_window:sender 640{ 641 if ([X11App x_active]) 642 DarwinSendDDXEvent(kXquartzControllerNotify, 1, AppleWMCloseWindow); 643 else 644 [[NSApp keyWindow] performClose:sender]; 645} 646 647- (IBAction)minimize_window:sender 648{ 649 if ([X11App x_active]) 650 DarwinSendDDXEvent(kXquartzControllerNotify, 1, AppleWMMinimizeWindow); 651 else 652 [[NSApp keyWindow] performMiniaturize:sender]; 653} 654 655- (IBAction)zoom_window:sender 656{ 657 if ([X11App x_active]) 658 DarwinSendDDXEvent(kXquartzControllerNotify, 1, AppleWMZoomWindow); 659 else 660 [[NSApp keyWindow] performZoom:sender]; 661} 662 663- (IBAction) next_window:sender 664{ 665 DarwinSendDDXEvent(kXquartzControllerNotify, 1, AppleWMNextWindow); 666} 667 668- (IBAction) previous_window:sender 669{ 670 DarwinSendDDXEvent(kXquartzControllerNotify, 1, AppleWMPreviousWindow); 671} 672 673- (IBAction) enable_fullscreen_changed:sender 674{ 675 XQuartzRootlessDefault = !self.enable_fullscreen.intValue; 676 677 [self.enable_fullscreen_menu setEnabled:!XQuartzRootlessDefault]; 678 [self.enable_fullscreen_menu_text setTextColor:XQuartzRootlessDefault ? NSColor.disabledControlTextColor : NSColor.controlTextColor]; 679 680 DarwinSendDDXEvent(kXquartzSetRootless, 1, XQuartzRootlessDefault); 681 682 [NSApp prefs_set_boolean:@PREFS_ROOTLESS value:XQuartzRootlessDefault]; 683 [NSApp prefs_synchronize]; 684} 685 686- (IBAction) toggle_fullscreen:sender 687{ 688 DarwinSendDDXEvent(kXquartzToggleFullscreen, 0); 689} 690 691- (IBAction)prefs_changed:sender 692{ 693 if (!sender) 694 return; 695 696 if (sender == self.fake_buttons) { 697 darwinFakeButtons = self.fake_buttons.intValue; 698 [NSApp prefs_set_boolean:@PREFS_FAKEBUTTONS value:darwinFakeButtons]; 699 } 700 else if (sender == self.enable_keyequivs) { 701 XQuartzEnableKeyEquivalents = self.enable_keyequivs.intValue; 702 [NSApp prefs_set_boolean:@PREFS_KEYEQUIVS value: 703 XQuartzEnableKeyEquivalents]; 704 } 705 else if (sender == self.sync_keymap) { 706 darwinSyncKeymap = self.sync_keymap.intValue; 707 [NSApp prefs_set_boolean:@PREFS_SYNC_KEYMAP value:darwinSyncKeymap]; 708 } 709 else if (sender == self.enable_fullscreen_menu) { 710 XQuartzFullscreenMenu = self.enable_fullscreen_menu.intValue; 711 [NSApp prefs_set_boolean:@PREFS_FULLSCREEN_MENU value: 712 XQuartzFullscreenMenu]; 713 } 714 else if (sender == self.option_sends_alt) { 715 BOOL prev_opt_sends_alt = XQuartzOptionSendsAlt; 716 717 XQuartzOptionSendsAlt = self.option_sends_alt.intValue; 718 [NSApp prefs_set_boolean:@PREFS_OPTION_SENDS_ALT value: 719 XQuartzOptionSendsAlt]; 720 721 if (prev_opt_sends_alt != XQuartzOptionSendsAlt) 722 QuartsResyncKeymap(TRUE); 723 } 724 else if (sender == self.click_through) { 725 [NSApp prefs_set_boolean:@PREFS_CLICK_THROUGH value:self.click_through.intValue]; 726 } 727 else if (sender == self.focus_follows_mouse) { 728 [NSApp prefs_set_boolean:@PREFS_FFM value:self.focus_follows_mouse.intValue]; 729 } 730 else if (sender == self.focus_on_new_window) { 731 [NSApp prefs_set_boolean:@PREFS_FOCUS_ON_NEW_WINDOW value:self.focus_on_new_window.intValue]; 732 } 733 else if (sender == self.enable_auth) { 734 [NSApp prefs_set_boolean:@PREFS_NO_AUTH value:!self.enable_auth.intValue]; 735 } 736 else if (sender == self.enable_tcp) { 737 [NSApp prefs_set_boolean:@PREFS_NO_TCP value:!self.enable_tcp.intValue]; 738 } 739 else if (sender == self.depth) { 740 [NSApp prefs_set_integer:@PREFS_DEPTH value:self.depth.selectedTag]; 741 } 742 else if (sender == self.sync_pasteboard) { 743 BOOL pbproxy_active = self.sync_pasteboard.intValue; 744 [NSApp prefs_set_boolean:@PREFS_SYNC_PB value:pbproxy_active]; 745 746 [self.sync_pasteboard_to_clipboard setEnabled:pbproxy_active]; 747 [self.sync_pasteboard_to_primary setEnabled:pbproxy_active]; 748 [self.sync_clipboard_to_pasteboard setEnabled:pbproxy_active]; 749 [self.sync_primary_immediately setEnabled:pbproxy_active]; 750 751 // setEnabled doesn't do this... 752 [self.sync_text1 setTextColor:pbproxy_active ? NSColor.controlTextColor : NSColor.disabledControlTextColor]; 753 [self.sync_text2 setTextColor:pbproxy_active ? NSColor.controlTextColor : NSColor.disabledControlTextColor]; 754 } 755 else if (sender == self.sync_pasteboard_to_clipboard) { 756 [NSApp prefs_set_boolean:@PREFS_SYNC_PB_TO_CLIPBOARD value:self.sync_pasteboard_to_clipboard.intValue]; 757 } 758 else if (sender == self.sync_pasteboard_to_primary) { 759 [NSApp prefs_set_boolean:@PREFS_SYNC_PB_TO_PRIMARY value:self.sync_pasteboard_to_primary.intValue]; 760 } 761 else if (sender == self.sync_clipboard_to_pasteboard) { 762 [NSApp prefs_set_boolean:@PREFS_SYNC_CLIPBOARD_TO_PB value:self.sync_clipboard_to_pasteboard.intValue]; 763 } 764 else if (sender == self.sync_primary_immediately) { 765 [NSApp prefs_set_boolean:@PREFS_SYNC_PRIMARY_ON_SELECT value:self.sync_primary_immediately.intValue]; 766 } 767 else if (sender == self.scroll_in_device_direction) { 768 XQuartzScrollInDeviceDirection = self.scroll_in_device_direction.intValue; 769 [NSApp prefs_set_boolean:@PREFS_SCROLL_IN_DEV_DIRECTION value:XQuartzScrollInDeviceDirection]; 770 } 771 772 [NSApp prefs_synchronize]; 773 774 DarwinSendDDXEvent(kXquartzReloadPreferences, 0); 775} 776 777- (IBAction) prefs_show:sender 778{ 779 BOOL pbproxy_active = 780 [NSApp prefs_get_boolean:@PREFS_SYNC_PB default:YES]; 781 782 [self.scroll_in_device_direction setIntValue:XQuartzScrollInDeviceDirection]; 783 784 [self.fake_buttons setIntValue:darwinFakeButtons]; 785 [self.enable_keyequivs setIntValue:XQuartzEnableKeyEquivalents]; 786 [self.sync_keymap setIntValue:darwinSyncKeymap]; 787 [self.option_sends_alt setIntValue:XQuartzOptionSendsAlt]; 788 [self.click_through setIntValue:[NSApp prefs_get_boolean:@PREFS_CLICK_THROUGH default:NO]]; 789 [self.focus_follows_mouse setIntValue:[NSApp prefs_get_boolean:@PREFS_FFM default:NO]]; 790 [self.focus_on_new_window setIntValue:[NSApp prefs_get_boolean:@PREFS_FOCUS_ON_NEW_WINDOW default:YES]]; 791 792 [self.enable_auth setIntValue:![NSApp prefs_get_boolean:@PREFS_NO_AUTH default:NO]]; 793 [self.enable_tcp setIntValue:![NSApp prefs_get_boolean:@PREFS_NO_TCP default:NO]]; 794 795 [self.depth selectItemAtIndex:[self.depth indexOfItemWithTag:[NSApp prefs_get_integer:@PREFS_DEPTH default:-1]]]; 796 797 [self.sync_pasteboard setIntValue:pbproxy_active]; 798 [self.sync_pasteboard_to_clipboard setIntValue:[NSApp prefs_get_boolean:@PREFS_SYNC_PB_TO_CLIPBOARD default:YES]]; 799 [self.sync_pasteboard_to_primary setIntValue:[NSApp prefs_get_boolean:@PREFS_SYNC_PB_TO_PRIMARY default:YES]]; 800 [self.sync_clipboard_to_pasteboard setIntValue:[NSApp prefs_get_boolean:@PREFS_SYNC_CLIPBOARD_TO_PB default:YES]]; 801 [self.sync_primary_immediately setIntValue:[NSApp prefs_get_boolean:@PREFS_SYNC_PRIMARY_ON_SELECT default:NO]]; 802 803 [self.sync_pasteboard_to_clipboard setEnabled:pbproxy_active]; 804 [self.sync_pasteboard_to_primary setEnabled:pbproxy_active]; 805 [self.sync_clipboard_to_pasteboard setEnabled:pbproxy_active]; 806 [self.sync_primary_immediately setEnabled:pbproxy_active]; 807 808 // setEnabled doesn't do this... 809 [self.sync_text1 setTextColor:pbproxy_active ? NSColor.controlTextColor : NSColor.disabledControlTextColor]; 810 [self.sync_text2 setTextColor:pbproxy_active ? NSColor.controlTextColor : NSColor.disabledControlTextColor]; 811 812 [self.enable_fullscreen setIntValue:!XQuartzRootlessDefault]; 813 [self.enable_fullscreen_menu setIntValue:XQuartzFullscreenMenu]; 814 [self.enable_fullscreen_menu setEnabled:!XQuartzRootlessDefault]; 815 [self.enable_fullscreen_menu_text setTextColor:XQuartzRootlessDefault ? NSColor.disabledControlTextColor : NSColor.controlTextColor]; 816 817 [self.prefs_panel makeKeyAndOrderFront:sender]; 818} 819 820- (IBAction) quit:sender 821{ 822 DarwinSendDDXEvent(kXquartzQuit, 0); 823} 824 825- (IBAction) x11_help:sender 826{ 827 AHLookupAnchor(CFSTR("com.apple.machelp"), CFSTR("mchlp2276")); 828} 829 830- (OSX_BOOL) validateMenuItem:(NSMenuItem *)item 831{ 832 NSMenu *menu = [item menu]; 833 NSMenu * const dock_menu = self.dock_menu; 834 835 if (item == self.toggle_fullscreen_item) 836 return !XQuartzIsRootless; 837 else if (menu == [X11App windowsMenu] || menu == dock_menu 838 || (menu == [self.x11_about_item menu] && [item tag] == 42)) 839 return (AppleWMSelectedEvents() & AppleWMControllerNotifyMask) != 0; 840 else 841 return TRUE; 842} 843 844- (void) applicationDidHide:(NSNotification *)notify 845{ 846 DarwinSendDDXEvent(kXquartzControllerNotify, 1, AppleWMHideAll); 847 848 /* Toggle off fullscreen mode to leave our non-default video 849 * mode and hide our guard window. 850 */ 851 if (!XQuartzIsRootless && XQuartzFullscreenVisible) { 852 DarwinSendDDXEvent(kXquartzToggleFullscreen, 0); 853 } 854} 855 856- (void) applicationDidUnhide:(NSNotification *)notify 857{ 858 DarwinSendDDXEvent(kXquartzControllerNotify, 1, AppleWMShowAll); 859} 860 861- (NSApplicationTerminateReply) applicationShouldTerminate:sender 862{ 863 NSString *msg; 864 NSString *title; 865 866 if (self.can_quit || 867 [X11App prefs_get_boolean:@PREFS_NO_QUIT_ALERT default:NO]) 868 return NSTerminateNow; 869 870 /* Make sure we're frontmost. */ 871 [NSApp activateIgnoringOtherApps:YES]; 872 873 title = NSLocalizedString(@"Do you really want to quit X11?", 874 @"Dialog title when quitting"); 875 msg = NSLocalizedString( 876 @"Any open X11 applications will stop immediately, and you will lose any unsaved changes.", 877 @"Dialog when quitting"); 878 879 /* FIXME: safe to run the alert in here? Or should we return Later 880 * and then run the alert on a timer? It seems to work here, so.. 881 */ 882 883 NSInteger result = NSRunAlertPanel(title, @"%@", NSLocalizedString(@"Quit", @""), 884 NSLocalizedString(@"Cancel", @""), nil, msg); 885 return (result == NSAlertDefaultReturn) ? NSTerminateNow : NSTerminateCancel; 886} 887 888- (void) applicationWillTerminate:(NSNotification *)aNotification _X_NORETURN 889{ 890 [X11App prefs_synchronize]; 891 892 /* shutdown the X server, it will exit () for us. */ 893 DarwinSendDDXEvent(kXquartzQuit, 0); 894 895 /* In case it doesn't, exit anyway after 5s. */ 896 [NSThread sleepForTimeInterval:5.0]; 897 898 exit(1); 899} 900 901- (void) server_ready 902{ 903 x_list *node; 904 905 self.finished_launching = YES; 906 907 for (node = self.pending_apps; node != NULL; node = node->next) { 908 NSString *filename = node->data; 909 [self launch_client:filename]; 910 [filename release]; 911 } 912 913 x_list_free(self.pending_apps); 914 self.pending_apps = NULL; 915} 916 917- (OSX_BOOL) application:(NSApplication *)app openFile:(NSString *)filename 918{ 919 const char *name = [filename UTF8String]; 920 921 if (self.finished_launching) 922 [self launch_client:filename]; 923 else if (name[0] != ':') /* ignore display names */ 924 self.pending_apps = x_list_prepend(self.pending_apps, [filename retain]); 925 926 /* FIXME: report failures. */ 927 return YES; 928} 929 930@end 931 932void 933X11ControllerMain(int argc, char **argv, char **envp) 934{ 935 X11ApplicationMain(argc, argv, envp); 936} 937