1/* X11Controller.m -- connect the IB ui, also the NSApp delegate
2 
3   Copyright (c) 2002-2008 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#include "sanitizedCarbon.h"
31#include <AvailabilityMacros.h>
32
33#ifdef HAVE_DIX_CONFIG_H
34#include <dix-config.h>
35#endif
36
37#include "quartzCommon.h"
38
39#import "X11Controller.h"
40#import "X11Application.h"
41
42#include "opaque.h"
43#include "darwin.h"
44#include "darwinEvents.h"
45#include "quartz.h"
46#include "quartzKeyboard.h"
47#include <X11/extensions/applewmconst.h>
48#include "applewmExt.h"
49
50#include <stdio.h>
51#include <unistd.h>
52#include <fcntl.h>
53#include <sys/types.h>
54#include <sys/wait.h>
55
56@implementation X11Controller
57
58- (void) awakeFromNib
59{
60  X11Application *xapp = NSApp;
61  NSArray *array;
62	
63  /* Point X11Application at ourself. */
64  [xapp set_controller:self];
65	
66  array = [xapp prefs_get_array:@PREFS_APPSMENU];
67  if (array != nil)
68    {
69      int count;
70		
71      /* convert from [TITLE1 COMMAND1 TITLE2 COMMAND2 ...]
72	 to [[TITLE1 COMMAND1] [TITLE2 COMMAND2] ...] format. */
73		
74      count = [array count];
75      if (count > 0
76	  && ![[array objectAtIndex:0] isKindOfClass:[NSArray class]])
77	{
78	  int i;
79	  NSMutableArray *copy, *sub;
80			
81	  copy = [NSMutableArray arrayWithCapacity:(count / 2)];
82			
83	  for (i = 0; i < count / 2; i++)
84	    {
85	      sub = [[NSMutableArray alloc] initWithCapacity:3];
86	      [sub addObject:[array objectAtIndex:i*2]];
87	      [sub addObject:[array objectAtIndex:i*2+1]];
88	      [sub addObject:@""];
89	      [copy addObject:sub];
90	      [sub release];
91	    }
92			
93	  array = copy;
94	}
95		
96      [self set_apps_menu:array];
97    }
98    
99    [[NSNotificationCenter defaultCenter]
100     addObserver: self
101     selector: @selector(apps_table_done:)
102     name: NSWindowWillCloseNotification
103     object: [apps_table window]];
104
105    // Setup data about our Windows menu
106    if(window_separator) {
107        [[window_separator menu] removeItem:window_separator];
108        window_separator = nil;
109    }
110    
111    windows_menu_start = [[X11App windowsMenu] numberOfItems];
112}
113
114- (void) item_selected:sender
115{
116  [NSApp activateIgnoringOtherApps:YES];
117	
118  DarwinSendDDXEvent(kXquartzControllerNotify, 2,
119			     AppleWMWindowMenuItem, [sender tag]);
120}
121
122- (void) remove_window_menu
123{
124  NSMenu *menu;
125  int count, i;
126
127  /* Work backwards so we don't mess up the indices */
128  menu = [X11App windowsMenu];
129  count = [menu numberOfItems];
130  for (i = count - 1; i >= windows_menu_start; i--)
131    [menu removeItemAtIndex:i];
132	
133  count = [dock_menu indexOfItem:dock_window_separator];
134  for (i = 0; i < count; i++)
135    [dock_menu removeItemAtIndex:0];
136}
137
138- (void) install_window_menu:(NSArray *)list
139{
140  NSMenu *menu;
141  NSMenuItem *item;
142  int first, count, i;
143
144  menu = [X11App windowsMenu];
145  first = windows_menu_start + 1;
146  count = [list count];
147  
148  // Push a Separator
149  if(count) {
150      [menu addItem:[NSMenuItem separatorItem]];
151  }
152
153  for (i = 0; i < count; i++)
154    {
155      NSString *name, *shortcut;
156		
157      name = [[list objectAtIndex:i] objectAtIndex:0];
158      shortcut = [[list objectAtIndex:i] objectAtIndex:1];
159        
160      if(windowItemModMask == 0 || windowItemModMask == -1)
161          shortcut = @"";
162
163      item = (NSMenuItem *) [menu addItemWithTitle:name action:@selector
164				  (item_selected:) keyEquivalent:shortcut];
165      [item setKeyEquivalentModifierMask:(NSUInteger) windowItemModMask];
166      [item setTarget:self];
167      [item setTag:i];
168      [item setEnabled:YES];
169
170      item = (NSMenuItem *) [dock_menu insertItemWithTitle:name
171				       action:@selector
172				       (item_selected:) keyEquivalent:shortcut
173				       atIndex:i];
174      [item setKeyEquivalentModifierMask:(NSUInteger) windowItemModMask];
175      [item setTarget:self];
176      [item setTag:i];
177      [item setEnabled:YES];
178    }
179	
180  if (checked_window_item >= 0 && checked_window_item < count)
181    {
182      item = (NSMenuItem *) [menu itemAtIndex:first + checked_window_item];
183      [item setState:NSOnState];
184      item = (NSMenuItem *) [dock_menu itemAtIndex:checked_window_item];
185      [item setState:NSOnState];
186    }
187}
188
189- (void) remove_apps_menu
190{
191  NSMenu *menu;
192  NSMenuItem *item;
193  int i;
194	
195  if (apps == nil || apps_separator == nil) return;
196	
197  menu = [apps_separator menu];
198	
199  if (menu != nil)
200    {
201      for (i = [menu numberOfItems] - 1; i >= 0; i--)
202	{
203	  item = (NSMenuItem *) [menu itemAtIndex:i];
204	  if ([item tag] != 0)
205	    [menu removeItemAtIndex:i];
206	}
207    }
208    
209  if (dock_apps_menu != nil)
210    {
211      for (i = [dock_apps_menu numberOfItems] - 1; i >= 0; i--)
212	{
213	  item = (NSMenuItem *) [dock_apps_menu itemAtIndex:i];
214	  if ([item tag] != 0)
215	    [dock_apps_menu removeItemAtIndex:i];
216	}
217    }
218    
219  [apps release];
220  apps = nil;
221}
222
223- (void) prepend_apps_item:(NSArray *)list index:(int)i menu:(NSMenu *)menu
224{
225  NSString *title, *shortcut = @"";
226  NSArray *group;
227  NSMenuItem *item;
228	
229  group = [list objectAtIndex:i];
230  title = [group objectAtIndex:0];
231  if ([group count] >= 3)
232    shortcut = [group objectAtIndex:2];
233	
234  if ([title length] != 0)
235    {
236      item = (NSMenuItem *) [menu insertItemWithTitle:title
237				  action:@selector (app_selected:)
238				  keyEquivalent:shortcut atIndex:0];
239      [item setTarget:self];
240      [item setEnabled:YES];
241    }
242  else
243    {
244      item = (NSMenuItem *) [NSMenuItem separatorItem];
245      [menu insertItem:item atIndex:0];
246    }
247	
248  [item setTag:i+1];			/* can't be zero, so add one */
249}
250
251- (void) install_apps_menu:(NSArray *)list
252{
253  NSMenu *menu;
254  int i, count;
255	
256  count = [list count];
257	
258  if (count == 0 || apps_separator == nil) return;
259	
260  menu = [apps_separator menu];
261	
262  for (i = count - 1; i >= 0; i--)
263    {
264      if (menu != nil)
265	[self prepend_apps_item:list index:i menu:menu];
266      if (dock_apps_menu != nil)
267	[self prepend_apps_item:list index:i menu:dock_apps_menu];
268    }
269	
270  apps = [list retain];
271}
272
273- (void) set_window_menu:(NSArray *)list
274{
275  [self remove_window_menu];
276  [self install_window_menu:list];
277	
278  DarwinSendDDXEvent(kXquartzControllerNotify, 1,
279			     AppleWMWindowMenuNotify);
280}
281
282- (void) set_window_menu_check:(NSNumber *)nn
283{
284  NSMenu *menu;
285  NSMenuItem *item;
286  int first, count;
287  int n = [nn intValue];
288
289  menu = [X11App windowsMenu];
290  first = windows_menu_start + 1;
291  count = [menu numberOfItems] - first;
292	
293  if (checked_window_item >= 0 && checked_window_item < count)
294    {
295      item = (NSMenuItem *) [menu itemAtIndex:first + checked_window_item];
296      [item setState:NSOffState];
297      item = (NSMenuItem *) [dock_menu itemAtIndex:checked_window_item];
298      [item setState:NSOffState];
299    }
300  if (n >= 0 && n < count)
301    {
302      item = (NSMenuItem *) [menu itemAtIndex:first + n];
303      [item setState:NSOnState];
304      item = (NSMenuItem *) [dock_menu itemAtIndex:n];
305      [item setState:NSOnState];
306    }
307  checked_window_item = n;
308}
309
310- (void) set_apps_menu:(NSArray *)list
311{
312  [self remove_apps_menu];
313  [self install_apps_menu:list];
314}
315
316#ifdef XQUARTZ_SPARKLE
317- (void) setup_sparkle {
318    if(check_for_updates_item)
319        return; // already did it...
320
321    NSMenu *menu = [x11_about_item menu];
322
323    check_for_updates_item = [menu insertItemWithTitle:NSLocalizedString(@"Check for X11 Updates...", @"Check for X11 Updates...")
324                                               action:@selector (checkForUpdates:)
325                                        keyEquivalent:@""
326                                              atIndex:1];
327    [check_for_updates_item setTarget:[SUUpdater sharedUpdater]];
328    [check_for_updates_item setEnabled:YES];
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    //[self set_can_quit:YES];
337}
338
339#endif
340
341- (void) launch_client:(NSString *)filename
342{
343    int child1, child2 = 0;
344    int status;
345    const char *newargv[4];
346    char buf[128];
347    char *s;
348    
349    newargv[0] = [X11App prefs_get_string:@PREFS_LOGIN_SHELL default:"/bin/sh"];
350    newargv[1] = "-c";
351    newargv[2] = [filename UTF8String];
352    newargv[3] = NULL;
353    
354    s = getenv("DISPLAY");
355    if (s == NULL || s[0] == 0) {
356        snprintf(buf, sizeof(buf), ":%s", display);
357        setenv("DISPLAY", buf, TRUE);
358    }
359
360    /* Do the fork-twice trick to avoid having to reap zombies */
361    child1 = fork();
362    switch (child1) {
363        case -1:                                /* error */
364            break;
365      
366        case 0:                                 /* child1 */
367            child2 = fork();
368      
369            switch (child2) {
370                int max_files, i;
371	
372                case -1:                            /* error */
373                    _exit(1);
374	 
375                case 0:                             /* child2 */
376                /* close all open files except for standard streams */
377                max_files = sysconf(_SC_OPEN_MAX);
378                for(i = 3; i < max_files; i++)
379                    close(i);
380	
381                /* ensure stdin is on /dev/null */
382                close(0);
383                open("/dev/null", O_RDONLY);
384	
385                execvp(newargv[0], (char **const) newargv);
386                _exit(2);
387	
388                default:                            /* parent (child1) */
389                _exit(0);
390            }
391            break;
392      
393        default:                                /* parent */
394            waitpid(child1, &status, 0);
395    }
396}
397
398- (void) app_selected:sender
399{
400  int tag;
401  NSString *item;
402  
403  tag = [sender tag] - 1;
404  if (apps == nil || tag < 0 || tag >= [apps count])
405    return;
406  
407  item = [[apps objectAtIndex:tag] objectAtIndex:1];
408  
409  [self launch_client:item];
410}
411
412- (IBAction) apps_table_show:sender
413{
414  NSArray *columns;
415  NSMutableArray *oldapps = nil;
416	
417  if (table_apps != nil)
418    oldapps = table_apps;
419
420  table_apps = [[NSMutableArray alloc] initWithCapacity:1];
421  if(apps != nil)
422      [table_apps addObjectsFromArray:apps];
423	
424  columns = [apps_table tableColumns];
425  [[columns objectAtIndex:0] setIdentifier:@"0"];
426  [[columns objectAtIndex:1] setIdentifier:@"1"];
427  [[columns objectAtIndex:2] setIdentifier:@"2"];
428	
429  [apps_table setDataSource:self];
430  [apps_table selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO];
431
432  [[apps_table window] makeKeyAndOrderFront:sender];
433  [apps_table reloadData];
434  if(oldapps != nil)
435    [oldapps release];
436}
437
438- (IBAction) apps_table_done:sender
439{
440  [apps_table deselectAll:sender];	/* flush edits? */
441	
442  [self remove_apps_menu];
443  [self install_apps_menu:table_apps];
444	
445  [NSApp prefs_set_array:@PREFS_APPSMENU value:table_apps];
446  [NSApp prefs_synchronize];
447	
448  [[apps_table window] orderOut:sender];
449	
450  [table_apps release];
451  table_apps = nil;
452}
453
454- (IBAction) apps_table_new:sender
455{
456  NSMutableArray *item;
457	
458  int row = [apps_table selectedRow], i;
459	
460  if (row < 0) row = 0;
461  else row = row + 1;
462	
463  i = row;
464  if (i > [table_apps count])
465    return;				/* avoid exceptions */
466	
467  [apps_table deselectAll:sender];
468	
469  item = [[NSMutableArray alloc] initWithCapacity:3];
470  [item addObject:@""];
471  [item addObject:@""];
472  [item addObject:@""];
473	
474  [table_apps insertObject:item atIndex:i];
475  [item release];
476	
477  [apps_table reloadData];
478  [apps_table selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
479}
480
481- (IBAction) apps_table_duplicate:sender
482{
483  int row = [apps_table selectedRow], i;
484  NSObject *item;
485	
486  if (row < 0) {
487    [self apps_table_new:sender];
488    return;
489  }
490	
491  i = row;
492  if (i > [table_apps count] - 1) return;				/* avoid exceptions */
493    
494  [apps_table deselectAll:sender];
495	
496  item = [[table_apps objectAtIndex:i] mutableCopy];
497  [table_apps insertObject:item atIndex:i];
498  [item release];
499	
500  [apps_table reloadData];
501  [apps_table selectRowIndexes:[NSIndexSet indexSetWithIndex:row+1] byExtendingSelection:NO];
502}
503
504- (IBAction) apps_table_delete:sender
505{
506  int row = [apps_table selectedRow];
507	
508  if (row >= 0)
509    {
510      int i = row;
511      
512      if (i > [table_apps count] - 1) return;			/* avoid exceptions */
513      
514      [apps_table deselectAll:sender];
515      
516      [table_apps removeObjectAtIndex:i];
517    }
518	
519  [apps_table reloadData];
520	
521  row = MIN (row, [table_apps count] - 1);
522  if (row >= 0)
523    [apps_table selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
524}
525
526- (NSInteger) numberOfRowsInTableView:(NSTableView *)tableView
527{
528  if (table_apps == nil) return 0;
529  
530  return [table_apps count];
531}
532
533- (id) tableView:(NSTableView *)tableView
534objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
535{
536  NSArray *item;
537  int col;
538	
539  if (table_apps == nil) return nil;
540	
541  col = [[tableColumn identifier] intValue];
542	
543  item = [table_apps objectAtIndex:row];
544  if ([item count] > col)
545    return [item objectAtIndex:col];
546  else
547    return @"";
548}
549
550- (void) tableView:(NSTableView *)tableView setObjectValue:(id)object
551    forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
552{
553  NSMutableArray *item;
554  int col;
555	
556  if (table_apps == nil) return;
557	
558  col = [[tableColumn identifier] intValue];
559	
560  item = [table_apps objectAtIndex:row];
561  [item replaceObjectAtIndex:col withObject:object];
562}
563
564- (void) hide_window:sender
565{
566  if ([X11App x_active])
567    DarwinSendDDXEvent(kXquartzControllerNotify, 1, AppleWMHideWindow);
568  else
569    NSBeep ();			/* FIXME: something here */
570}
571
572- (IBAction)bring_to_front:sender
573{
574  DarwinSendDDXEvent(kXquartzControllerNotify, 1, AppleWMBringAllToFront);
575}
576
577- (IBAction)close_window:sender
578{
579  if ([X11App x_active])
580    DarwinSendDDXEvent(kXquartzControllerNotify, 1, AppleWMCloseWindow);
581  else
582    [[NSApp keyWindow] performClose:sender];
583}
584
585- (IBAction)minimize_window:sender
586{
587  if ([X11App x_active])
588    DarwinSendDDXEvent(kXquartzControllerNotify, 1, AppleWMMinimizeWindow);
589  else
590    [[NSApp keyWindow] performMiniaturize:sender];
591}
592
593- (IBAction)zoom_window:sender
594{
595  if ([X11App x_active])
596    DarwinSendDDXEvent(kXquartzControllerNotify, 1, AppleWMZoomWindow);
597  else
598    [[NSApp keyWindow] performZoom:sender];
599}
600
601- (IBAction) next_window:sender
602{
603  DarwinSendDDXEvent(kXquartzControllerNotify, 1, AppleWMNextWindow);
604}
605
606- (IBAction) previous_window:sender
607{
608  DarwinSendDDXEvent(kXquartzControllerNotify, 1, AppleWMPreviousWindow);
609}
610
611- (IBAction) enable_fullscreen_changed:sender {
612    XQuartzRootlessDefault = ![enable_fullscreen intValue];
613
614    [enable_fullscreen_menu setEnabled:!XQuartzRootlessDefault];
615    [enable_fullscreen_menu_text setTextColor:XQuartzRootlessDefault ? [NSColor disabledControlTextColor] : [NSColor controlTextColor]];
616
617    DarwinSendDDXEvent(kXquartzSetRootless, 1, XQuartzRootlessDefault);
618
619    [NSApp prefs_set_boolean:@PREFS_ROOTLESS value:XQuartzRootlessDefault];
620    [NSApp prefs_synchronize];
621}
622
623- (IBAction) toggle_fullscreen:sender
624{
625  DarwinSendDDXEvent(kXquartzToggleFullscreen, 0);
626}
627
628- (void) set_can_quit:(OSX_BOOL)state
629{
630  can_quit = state;
631}
632
633- (IBAction)prefs_changed:sender
634{
635    if(!sender)
636        return;
637    
638    if(sender == fake_buttons) {
639        darwinFakeButtons = [fake_buttons intValue];
640        [NSApp prefs_set_boolean:@PREFS_FAKEBUTTONS value:darwinFakeButtons];
641    } else if(sender == enable_keyequivs) {
642        XQuartzEnableKeyEquivalents =  [enable_keyequivs intValue];
643        [NSApp prefs_set_boolean:@PREFS_KEYEQUIVS value:XQuartzEnableKeyEquivalents];
644    } else if(sender == sync_keymap) {
645        darwinSyncKeymap = [sync_keymap intValue];
646        [NSApp prefs_set_boolean:@PREFS_SYNC_KEYMAP value:darwinSyncKeymap];
647    } else if(sender == enable_fullscreen_menu) {
648        XQuartzFullscreenMenu = [enable_fullscreen_menu intValue];
649        [NSApp prefs_set_boolean:@PREFS_FULLSCREEN_MENU value:XQuartzFullscreenMenu];
650    } else if(sender == option_sends_alt) {
651        BOOL prev_opt_sends_alt = XQuartzOptionSendsAlt;
652        
653        XQuartzOptionSendsAlt = [option_sends_alt intValue];
654        [NSApp prefs_set_boolean:@PREFS_OPTION_SENDS_ALT value:XQuartzOptionSendsAlt];
655
656        if(prev_opt_sends_alt != XQuartzOptionSendsAlt)
657            QuartsResyncKeymap(TRUE);
658    } else if(sender == click_through) {
659        [NSApp prefs_set_boolean:@PREFS_CLICK_THROUGH value:[click_through intValue]];
660    } else if(sender == focus_follows_mouse) {
661        [NSApp prefs_set_boolean:@PREFS_FFM value:[focus_follows_mouse intValue]];
662    } else if(sender == focus_on_new_window) {
663        [NSApp prefs_set_boolean:@PREFS_FOCUS_ON_NEW_WINDOW value:[focus_on_new_window intValue]];
664    } else if(sender == enable_auth) {
665        [NSApp prefs_set_boolean:@PREFS_NO_AUTH value:![enable_auth intValue]];
666    } else if(sender == enable_tcp) {
667        [NSApp prefs_set_boolean:@PREFS_NO_TCP value:![enable_tcp intValue]];
668    } else if(sender == depth) {
669        [NSApp prefs_set_integer:@PREFS_DEPTH value:[depth selectedTag]];
670    } else if(sender == sync_pasteboard) {
671        BOOL pbproxy_active = [sync_pasteboard intValue];
672        [NSApp prefs_set_boolean:@PREFS_SYNC_PB value:pbproxy_active];
673
674        [sync_pasteboard_to_clipboard setEnabled:pbproxy_active];
675        [sync_pasteboard_to_primary setEnabled:pbproxy_active];
676        [sync_clipboard_to_pasteboard setEnabled:pbproxy_active];
677        [sync_primary_immediately setEnabled:pbproxy_active];
678
679        // setEnabled doesn't do this...
680        [sync_text1 setTextColor:pbproxy_active ? [NSColor controlTextColor] : [NSColor disabledControlTextColor]];
681        [sync_text2 setTextColor:pbproxy_active ? [NSColor controlTextColor] : [NSColor disabledControlTextColor]];
682    } else if(sender == sync_pasteboard_to_clipboard) {
683        [NSApp prefs_set_boolean:@PREFS_SYNC_PB_TO_CLIPBOARD value:[sync_pasteboard_to_clipboard intValue]];
684    } else if(sender == sync_pasteboard_to_primary) {
685        [NSApp prefs_set_boolean:@PREFS_SYNC_PB_TO_PRIMARY value:[sync_pasteboard_to_primary intValue]];
686    } else if(sender == sync_clipboard_to_pasteboard) {
687        [NSApp prefs_set_boolean:@PREFS_SYNC_CLIPBOARD_TO_PB value:[sync_clipboard_to_pasteboard intValue]];
688    } else if(sender == sync_primary_immediately) {
689        [NSApp prefs_set_boolean:@PREFS_SYNC_PRIMARY_ON_SELECT value:[sync_primary_immediately intValue]];
690    } else if(sender == scroll_in_device_direction) {
691        XQuartzScrollInDeviceDirection = [scroll_in_device_direction intValue];
692        [NSApp prefs_set_boolean:@PREFS_SCROLL_IN_DEV_DIRECTION value:XQuartzScrollInDeviceDirection];
693    }
694
695    [NSApp prefs_synchronize];
696    
697    DarwinSendDDXEvent(kXquartzReloadPreferences, 0);
698}
699
700- (IBAction) prefs_show:sender
701{
702    BOOL pbproxy_active = [NSApp prefs_get_boolean:@PREFS_SYNC_PB default:YES];
703
704    // Remove preferences from the GUI which are not supported
705    // TODO: Change 1117 to NSAppKitVersionNumber10_7 when it is defined
706    if(scroll_in_device_direction && NSAppKitVersionNumber < 1117) {
707        [scroll_in_device_direction removeFromSuperview];
708        scroll_in_device_direction = nil;
709    } else {
710        [scroll_in_device_direction setIntValue:XQuartzScrollInDeviceDirection];
711    }
712    
713    [fake_buttons setIntValue:darwinFakeButtons];
714    [enable_keyequivs setIntValue:XQuartzEnableKeyEquivalents];
715    [sync_keymap setIntValue:darwinSyncKeymap];
716    [option_sends_alt setIntValue:XQuartzOptionSendsAlt];
717    [click_through setIntValue:[NSApp prefs_get_boolean:@PREFS_CLICK_THROUGH default:NO]];
718    [focus_follows_mouse setIntValue:[NSApp prefs_get_boolean:@PREFS_FFM default:NO]];
719    [focus_on_new_window setIntValue:[NSApp prefs_get_boolean:@PREFS_FOCUS_ON_NEW_WINDOW default:YES]];
720    
721    [enable_auth setIntValue:![NSApp prefs_get_boolean:@PREFS_NO_AUTH default:NO]];
722    [enable_tcp setIntValue:![NSApp prefs_get_boolean:@PREFS_NO_TCP default:NO]];
723
724    [depth selectItemAtIndex:[depth indexOfItemWithTag:[NSApp prefs_get_integer:@PREFS_DEPTH default:-1]]];
725
726    [sync_pasteboard setIntValue:pbproxy_active];
727    [sync_pasteboard_to_clipboard setIntValue:[NSApp prefs_get_boolean:@PREFS_SYNC_PB_TO_CLIPBOARD default:YES]];
728    [sync_pasteboard_to_primary setIntValue:[NSApp prefs_get_boolean:@PREFS_SYNC_PB_TO_PRIMARY default:YES]];
729    [sync_clipboard_to_pasteboard setIntValue:[NSApp prefs_get_boolean:@PREFS_SYNC_CLIPBOARD_TO_PB default:YES]];
730    [sync_primary_immediately setIntValue:[NSApp prefs_get_boolean:@PREFS_SYNC_PRIMARY_ON_SELECT default:NO]];
731
732    [sync_pasteboard_to_clipboard setEnabled:pbproxy_active];
733    [sync_pasteboard_to_primary setEnabled:pbproxy_active];
734    [sync_clipboard_to_pasteboard setEnabled:pbproxy_active];
735    [sync_primary_immediately setEnabled:pbproxy_active];
736
737    // setEnabled doesn't do this...
738    [sync_text1 setTextColor:pbproxy_active ? [NSColor controlTextColor] : [NSColor disabledControlTextColor]];
739    [sync_text2 setTextColor:pbproxy_active ? [NSColor controlTextColor] : [NSColor disabledControlTextColor]];
740	
741    [enable_fullscreen setIntValue:!XQuartzRootlessDefault];
742    [enable_fullscreen_menu setIntValue:XQuartzFullscreenMenu];
743    [enable_fullscreen_menu setEnabled:!XQuartzRootlessDefault];
744    [enable_fullscreen_menu_text setTextColor:XQuartzRootlessDefault ? [NSColor disabledControlTextColor] : [NSColor controlTextColor]];
745
746    [prefs_panel makeKeyAndOrderFront:sender];
747}
748
749- (IBAction) quit:sender {
750    DarwinSendDDXEvent(kXquartzQuit, 0);
751}
752
753- (IBAction) x11_help:sender {
754#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
755    AHLookupAnchor((CFStringRef)NSLocalizedString(@"Mac Help", no comment), CFSTR("mchlp2276"));
756#else
757    AHLookupAnchor(CFSTR("com.apple.machelp"), CFSTR("mchlp2276"));
758#endif
759}
760
761- (OSX_BOOL) validateMenuItem:(NSMenuItem *)item {
762  NSMenu *menu = [item menu];
763    
764  if (item == toggle_fullscreen_item)
765    return !XQuartzIsRootless;
766  else if (menu == [X11App windowsMenu] || menu == dock_menu
767	   || (menu == [x11_about_item menu] && [item tag] == 42))
768    return (AppleWMSelectedEvents () & AppleWMControllerNotifyMask) != 0;
769  else
770    return TRUE;
771}
772
773- (void) applicationDidHide:(NSNotification *)notify
774{
775  DarwinSendDDXEvent(kXquartzControllerNotify, 1, AppleWMHideAll);
776}
777
778- (void) applicationDidUnhide:(NSNotification *)notify
779{
780  DarwinSendDDXEvent(kXquartzControllerNotify, 1, AppleWMShowAll);
781}
782
783- (NSApplicationTerminateReply) applicationShouldTerminate:sender {
784    NSString *msg;
785    NSString *title;
786	
787    if (can_quit || [X11App prefs_get_boolean:@PREFS_NO_QUIT_ALERT default:NO])
788        return NSTerminateNow;
789	
790    /* Make sure we're frontmost. */
791    [NSApp activateIgnoringOtherApps:YES];
792	
793    title = NSLocalizedString(@"Do you really want to quit X11?", @"Dialog title when quitting");
794    msg = NSLocalizedString(@"Any open X11 applications will stop immediately, and you will lose any unsaved changes.", @"Dialog when quitting");
795
796    /* FIXME: safe to run the alert in here? Or should we return Later
797     *        and then run the alert on a timer? It seems to work here, so..
798     */
799	
800    return (NSRunAlertPanel (title, msg, NSLocalizedString (@"Quit", @""),
801                             NSLocalizedString (@"Cancel", @""), nil)
802            == NSAlertDefaultReturn) ? NSTerminateNow : NSTerminateCancel;
803}
804
805- (void) applicationWillTerminate:(NSNotification *)aNotification
806{
807  unsigned remain;
808  [X11App prefs_synchronize];
809	
810  /* shutdown the X server, it will exit () for us. */
811  DarwinSendDDXEvent(kXquartzQuit, 0);
812	
813  /* In case it doesn't, exit anyway after a while. */
814  remain = 10000000;
815  while((remain = usleep(remain)) > 0);
816
817  exit (1);
818}
819
820- (void) server_ready
821{
822  x_list *node;
823	
824  finished_launching = YES;
825	
826  for (node = pending_apps; node != NULL; node = node->next)
827    {
828      NSString *filename = node->data;
829      [self launch_client:filename];
830      [filename release];
831    }
832	
833  x_list_free (pending_apps);
834  pending_apps = NULL;
835}
836
837- (OSX_BOOL) application:(NSApplication *)app openFile:(NSString *)filename
838{
839    const char *name = [filename UTF8String];
840    
841    if (finished_launching)
842        [self launch_client:filename];
843    else if (name[0] != ':')		/* ignore display names */
844        pending_apps = x_list_prepend (pending_apps, [filename retain]);
845    
846    /* FIXME: report failures. */
847    return YES;
848}
849
850@end
851
852void X11ControllerMain(int argc, char **argv, char **envp) {
853    X11ApplicationMain (argc, argv, envp);
854}
855