xserver

xserver with xephyr scale patch
git clone https://git.neptards.moe/u3shit/xserver.git
Log | Files | Refs | README | LICENSE

X11Controller.m (30012B)


      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 
     56 extern aslclient aslc;
     57 extern 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 
    932 void
    933 X11ControllerMain(int argc, char **argv, char **envp)
    934 {
    935     X11ApplicationMain(argc, argv, envp);
    936 }