First import
[xorg_rtime.git] / xorg-server-1.4 / hw / darwin / apple / X11Controller.m
blob3dc965b699f4f261c6d60409529baabaaf28b0b1
1 /* X11Controller.m -- connect the IB ui, also the NSApp delegate
2    $Id: X11Controller.m,v 1.40 2006/09/06 21:19:32 jharper Exp $
3  
4    Copyright (c) 2002-2007 Apple Inc. All rights reserved.
5  
6    Permission is hereby granted, free of charge, to any person
7    obtaining a copy of this software and associated documentation files
8    (the "Software"), to deal in the Software without restriction,
9    including without limitation the rights to use, copy, modify, merge,
10    publish, distribute, sublicense, and/or sell copies of the Software,
11    and to permit persons to whom the Software is furnished to do so,
12    subject to the following conditions:
14    The above copyright notice and this permission notice shall be
15    included in all copies or substantial portions of the Software.
17    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20    NONINFRINGEMENT.  IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
21    HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24    DEALINGS IN THE SOFTWARE.
26    Except as contained in this notice, the name(s) of the above
27    copyright holders shall not be used in advertising or otherwise to
28    promote the sale, use or other dealings in this Software without
29    prior written authorization. */
31 #define DEFAULT_PATH "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/X11/bin"
33 #include "../quartz/quartzCommon.h"
35 #import "X11Controller.h"
36 #import "X11Application.h"
37 #import <Carbon/Carbon.h>
39 /* ouch! */
40 #define BOOL X_BOOL
41 //# include "Xproto.h"
42 #include "opaque.h"
43 # include "darwin.h"
44 # include "../quartz/quartz.h"
45 # define _APPLEWM_SERVER_
46 # include "X11/extensions/applewm.h"
47 # include "../quartz/applewmExt.h"
48 //# include "X.h"
49 #undef BOOL
51 #include <stdio.h>
52 #include <unistd.h>
53 #include <fcntl.h>
54 #include <sys/types.h>
55 #include <sys/wait.h>
57 #define TRACE() fprintf (stderr, "%s\n", __FUNCTION__)
59 @implementation X11Controller
61 - (void) awakeFromNib
63   X11Application *xapp = NSApp;
64   NSArray *array;
65         
66   /* Point X11Application at ourself. */
67   [xapp set_controller:self];
68         
69   array = [xapp prefs_get_array:@PREFS_APPSMENU];
70   if (array != nil)
71     {
72       int count;
73                 
74       /* convert from [TITLE1 COMMAND1 TITLE2 COMMAND2 ...]
75          to [[TITLE1 COMMAND1] [TITLE2 COMMAND2] ...] format. */
76                 
77       count = [array count];
78       if (count > 0
79           && ![[array objectAtIndex:0] isKindOfClass:[NSArray class]])
80         {
81           int i;
82           NSMutableArray *copy, *sub;
83                         
84           copy = [NSMutableArray arrayWithCapacity:(count / 2)];
85                         
86           for (i = 0; i < count / 2; i++)
87             {
88               sub = [[NSMutableArray alloc] initWithCapacity:3];
89               [sub addObject:[array objectAtIndex:i*2]];
90               [sub addObject:[array objectAtIndex:i*2+1]];
91               [sub addObject:@""];
92               [copy addObject:sub];
93               [sub release];
94             }
95                         
96           array = copy;
97         }
98                 
99       [self set_apps_menu:array];
100     }
103 - (void) item_selected:sender
105   [NSApp activateIgnoringOtherApps:YES];
106         
107   QuartzMessageServerThread (kXDarwinControllerNotify, 2,
108                              AppleWMWindowMenuItem, [sender tag]);
111 - (void) remove_window_menu
113   NSMenu *menu;
114   int first, count, i;
115         
116   /* Work backwards so we don't mess up the indices */
117   menu = [window_separator menu];
118   first = [menu indexOfItem:window_separator] + 1;
119   count = [menu numberOfItems];
120   for (i = count - 1; i >= first; i--)
121     [menu removeItemAtIndex:i];
122         
123   menu = [dock_window_separator menu];
124   count = [menu indexOfItem:dock_window_separator];
125   for (i = 0; i < count; i++)
126     [dock_menu removeItemAtIndex:0];
129 - (void) install_window_menu:(NSArray *)list
131   NSMenu *menu;
132   NSMenuItem *item;
133   int first, count, i;
134         
135   menu = [window_separator menu];
136   first = [menu indexOfItem:window_separator] + 1;
137   count = [list count];
138   for (i = 0; i < count; i++)
139     {
140       NSString *name, *shortcut;
141                 
142       name = [[list objectAtIndex:i] objectAtIndex:0];
143       shortcut = [[list objectAtIndex:i] objectAtIndex:1];
144                 
145       item = (NSMenuItem *) [menu addItemWithTitle:name action:@selector
146                                   (item_selected:) keyEquivalent:shortcut];
147       [item setTarget:self];
148       [item setTag:i];
149       [item setEnabled:YES];
150                 
151       item = (NSMenuItem *) [dock_menu insertItemWithTitle:name
152                                        action:@selector
153                                        (item_selected:) keyEquivalent:shortcut
154                                        atIndex:i];
155       [item setTarget:self];
156       [item setTag:i];
157       [item setEnabled:YES];
158     }
159         
160   if (checked_window_item >= 0 && checked_window_item < count)
161     {
162       item = (NSMenuItem *) [menu itemAtIndex:first + checked_window_item];
163       [item setState:NSOnState];
164       item = (NSMenuItem *) [dock_menu itemAtIndex:checked_window_item];
165       [item setState:NSOnState];
166     }
169 - (void) remove_apps_menu
171   NSMenu *menu;
172   NSMenuItem *item;
173   int i;
174         
175   if (apps == nil || apps_separator == nil) return;
176         
177   menu = [apps_separator menu];
178         
179   if (menu != nil)
180     {
181       for (i = [menu numberOfItems] - 1; i >= 0; i--)
182         {
183           item = (NSMenuItem *) [menu itemAtIndex:i];
184           if ([item tag] != 0)
185             [menu removeItemAtIndex:i];
186         }
187     }
188     
189   if (dock_apps_menu != nil)
190     {
191       for (i = [dock_apps_menu numberOfItems] - 1; i >= 0; i--)
192         {
193           item = (NSMenuItem *) [dock_apps_menu itemAtIndex:i];
194           if ([item tag] != 0)
195             [dock_apps_menu removeItemAtIndex:i];
196         }
197     }
198     
199   [apps release];
200   apps = nil;
203 - (void) prepend_apps_item:(NSArray *)list index:(int)i menu:(NSMenu *)menu
205   NSString *title, *shortcut = @"";
206   NSArray *group;
207   NSMenuItem *item;
208         
209   group = [list objectAtIndex:i];
210   title = [group objectAtIndex:0];
211   if ([group count] >= 3)
212     shortcut = [group objectAtIndex:2];
213         
214   if ([title length] != 0)
215     {
216       item = (NSMenuItem *) [menu insertItemWithTitle:title
217                                   action:@selector (app_selected:)
218                                   keyEquivalent:shortcut atIndex:0];
219       [item setTarget:self];
220       [item setEnabled:YES];
221     }
222   else
223     {
224       item = (NSMenuItem *) [NSMenuItem separatorItem];
225       [menu insertItem:item atIndex:0];
226     }
227         
228   [item setTag:i+1];                    /* can't be zero, so add one */
231 - (void) install_apps_menu:(NSArray *)list
233   NSMenu *menu;
234   int i, count;
235         
236   count = [list count];
237         
238   if (count == 0 || apps_separator == nil) return;
239         
240   menu = [apps_separator menu];
241         
242   for (i = count - 1; i >= 0; i--)
243     {
244       if (menu != nil)
245         [self prepend_apps_item:list index:i menu:menu];
246       if (dock_apps_menu != nil)
247         [self prepend_apps_item:list index:i menu:dock_apps_menu];
248     }
249         
250   apps = [list retain];
253 - (void) set_window_menu:(NSArray *)list
255   [self remove_window_menu];
256   [self install_window_menu:list];
257         
258   QuartzMessageServerThread (kXDarwinControllerNotify, 1,
259                              AppleWMWindowMenuNotify);
262 - (void) set_window_menu_check:(NSNumber *)nn
264   NSMenu *menu;
265   NSMenuItem *item;
266   int first, count;
267   int n = [nn intValue];
269   menu = [window_separator menu];
270   first = [menu indexOfItem:window_separator] + 1;
271   count = [menu numberOfItems] - first;
272         
273   if (checked_window_item >= 0 && checked_window_item < count)
274     {
275       item = (NSMenuItem *) [menu itemAtIndex:first + checked_window_item];
276       [item setState:NSOffState];
277       item = (NSMenuItem *) [dock_menu itemAtIndex:checked_window_item];
278       [item setState:NSOffState];
279     }
280   if (n >= 0 && n < count)
281     {
282       item = (NSMenuItem *) [menu itemAtIndex:first + n];
283       [item setState:NSOnState];
284       item = (NSMenuItem *) [dock_menu itemAtIndex:n];
285       [item setState:NSOnState];
286     }
287   checked_window_item = n;
290 - (void) set_apps_menu:(NSArray *)list
292   [self remove_apps_menu];
293   [self install_apps_menu:list];
296 - (void) launch_client:(NSString *)filename
298   const char *command = [filename UTF8String];
299   const char *shell;
300   const char *argv[5];
301   int child1, child2 = 0;
302   int status;
303         
304   shell = getenv("SHELL");
305   if (shell == NULL) shell = "/bin/bash";
306     
307   argv[0] = shell;
308   argv[1] = "-l";
309   argv[2] = "-c";
310   argv[3] = command;
311   argv[4] = NULL;
312     
313   /* Do the fork-twice trick to avoid having to reap zombies */
314     
315   child1 = fork();
316     
317   switch (child1) {
318   case -1:                                /* error */
319     break;
320       
321   case 0:                                 /* child1 */
322     child2 = fork();
323       
324     switch (child2) {
325       int max_files, i;
326       char buf[1024], *temp;
327         
328     case -1:                            /* error */
329       _exit(1);
330         
331     case 0:                             /* child2 */
332       /* close all open files except for standard streams */
333       max_files = sysconf(_SC_OPEN_MAX);
334       for (i = 3; i < max_files; i++)   close(i);
335         
336       /* ensure stdin is on /dev/null */
337       close(0);
338       open("/dev/null", O_RDONLY);
339         
340       /* Setup environment */
341       temp = getenv("DISPLAY");
342       if (temp == NULL || temp[0] == 0) {
343         snprintf(buf, sizeof(buf), ":%s", display);
344         setenv("DISPLAY", buf, TRUE);
345       }
346         
347       temp = getenv("PATH");
348       if (temp == NULL || temp[0] == 0) 
349         setenv ("PATH", DEFAULT_PATH, TRUE);
350       else if (strnstr(temp, "/usr/X11/bin", sizeof(temp)) == NULL) {
351         snprintf(buf, sizeof(buf), "%s:/usr/X11/bin", temp);            
352         setenv("PATH", buf, TRUE);      
353       }
354       /* cd $HOME */
355       temp = getenv("HOME");
356       if (temp != NULL && temp[0]!=0) chdir(temp);
357         
358       execvp(argv[0], (char **const) argv);
359         
360       _exit(2);
361         
362     default:                            /* parent (child1) */
363       _exit(0);
364     }
365     break;
366       
367   default:                                /* parent */
368     waitpid(child1, &status, 0);
369   }
372 - (void) app_selected:sender
374   int tag;
375   NSString *item;
376   
377   tag = [sender tag] - 1;
378   if (apps == nil || tag < 0 || tag >= [apps count])
379     return;
380   
381   item = [[apps objectAtIndex:tag] objectAtIndex:1];
382   
383   [self launch_client:item];
386 - (IBAction) apps_table_show:sender
388   NSArray *columns;
389         
390   if (table_apps == nil) {
391     table_apps = [[NSMutableArray alloc] initWithCapacity:1];
392       
393     if (apps != nil)[table_apps addObjectsFromArray:apps];
394   }
395         
396   columns = [apps_table tableColumns];
397   [[columns objectAtIndex:0] setIdentifier:@"0"];
398   [[columns objectAtIndex:1] setIdentifier:@"1"];
399   [[columns objectAtIndex:2] setIdentifier:@"2"];
400         
401   [apps_table setDataSource:self];
402   [apps_table selectRow:0 byExtendingSelection:NO];
403         
404   [[apps_table window] makeKeyAndOrderFront:sender];
407 - (IBAction) apps_table_cancel:sender
409   [[apps_table window] orderOut:sender];
410   [apps_table reloadData];
411         
412   [table_apps release];
413   table_apps = nil;
416 - (IBAction) apps_table_done:sender
418   [apps_table deselectAll:sender];      /* flush edits? */
419         
420   [self remove_apps_menu];
421   [self install_apps_menu:table_apps];
422         
423   [NSApp prefs_set_array:@PREFS_APPSMENU value:table_apps];
424   [NSApp prefs_synchronize];
425         
426   [[apps_table window] orderOut:sender];
427         
428   [table_apps release];
429   table_apps = nil;
432 - (IBAction) apps_table_new:sender
434   NSMutableArray *item;
435         
436   int row = [apps_table selectedRow], i;
437         
438   if (row < 0) row = 0;
439   else row = row + 1;
440         
441   i = row;
442   if (i > [table_apps count])
443     return;                             /* avoid exceptions */
444         
445   [apps_table deselectAll:sender];
446         
447   item = [[NSMutableArray alloc] initWithCapacity:3];
448   [item addObject:@""];
449   [item addObject:@""];
450   [item addObject:@""];
451         
452   [table_apps insertObject:item atIndex:i];
453   [item release];
454         
455   [apps_table reloadData];
456   [apps_table selectRow:row byExtendingSelection:NO];
459 - (IBAction) apps_table_duplicate:sender
461   int row = [apps_table selectedRow], i;
462   NSObject *item;
463         
464   if (row < 0) {
465     [self apps_table_new:sender];
466     return;
467   }
468         
469   i = row;
470   if (i > [table_apps count] - 1) return;                               /* avoid exceptions */
471     
472   [apps_table deselectAll:sender];
473         
474   item = [[table_apps objectAtIndex:i] mutableCopy];
475   [table_apps insertObject:item atIndex:i];
476   [item release];
477         
478   [apps_table reloadData];
479   [apps_table selectRow:row+1 byExtendingSelection:NO];
482 - (IBAction) apps_table_delete:sender
484   int row = [apps_table selectedRow];
485         
486   if (row >= 0)
487     {
488       int i = row;
489       
490       if (i > [table_apps count] - 1) return;                   /* avoid exceptions */
491       
492       [apps_table deselectAll:sender];
493       
494       [table_apps removeObjectAtIndex:i];
495     }
496         
497   [apps_table reloadData];
498         
499   row = MIN (row, [table_apps count] - 1);
500   if (row >= 0)
501     [apps_table selectRow:row byExtendingSelection:NO];
504 - (int) numberOfRowsInTableView:(NSTableView *)tableView
506   if (table_apps == nil) return 0;
507   
508   return [table_apps count];
511 - (id) tableView:(NSTableView *)tableView
512 objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row
514   NSArray *item;
515   int col;
516         
517   if (table_apps == nil) return nil;
518         
519   col = [[tableColumn identifier] intValue];
520         
521   item = [table_apps objectAtIndex:row];
522   if ([item count] > col)
523     return [item objectAtIndex:col];
524   else
525     return @"";
528 - (void) tableView:(NSTableView *)tableView setObjectValue:(id)object
529     forTableColumn:(NSTableColumn *)tableColumn row:(int)row
531   NSMutableArray *item;
532   int col;
533         
534   if (table_apps == nil) return;
535         
536   col = [[tableColumn identifier] intValue];
537         
538   item = [table_apps objectAtIndex:row];
539   [item replaceObjectAtIndex:col withObject:object];
542 - (void) hide_window:sender
544   if ([X11App x_active])
545     QuartzMessageServerThread (kXDarwinControllerNotify, 1, AppleWMHideWindow);
546   else
547     NSBeep ();                  /* FIXME: something here */
550 - (IBAction)bring_to_front:sender
552   QuartzMessageServerThread(kXDarwinControllerNotify, 1, AppleWMBringAllToFront);
555 - (IBAction)close_window:sender
557   if ([X11App x_active])
558     QuartzMessageServerThread (kXDarwinControllerNotify, 1, AppleWMCloseWindow);
559   else
560     [[NSApp keyWindow] performClose:sender];
563 - (IBAction)minimize_window:sender
565   if ([X11App x_active])
566     QuartzMessageServerThread (kXDarwinControllerNotify, 1, AppleWMMinimizeWindow);
567   else
568     [[NSApp keyWindow] performMiniaturize:sender];
571 - (IBAction)zoom_window:sender
573   if ([X11App x_active])
574     QuartzMessageServerThread (kXDarwinControllerNotify, 1, AppleWMZoomWindow);
575   else
576     [[NSApp keyWindow] performZoom:sender];
579 - (IBAction) next_window:sender
581   QuartzMessageServerThread (kXDarwinControllerNotify, 1, AppleWMNextWindow);
584 - (IBAction) previous_window:sender
586   QuartzMessageServerThread (kXDarwinControllerNotify, 1, AppleWMPreviousWindow);
589 - (IBAction) enable_fullscreen_changed:sender
591   int value = ![enable_fullscreen intValue];
592         
593 #ifdef DARWIN_DDX_MISSING
594   QuartzMessageServerThread (kXDarwinSetRootless, 1, value);
595 #endif
596         
597   [NSApp prefs_set_boolean:@PREFS_ROOTLESS value:value];
598   [NSApp prefs_synchronize];
601 - (IBAction) toggle_fullscreen:sender
603 #ifdef DARWIN_DDX_MISSING
604   QuartzMessageServerThread (kXDarwinToggleFullscreen, 0);
605 #endif
608 - (void) set_can_quit:(BOOL)state
610   can_quit = state;
613 - (IBAction)prefs_changed:sender
615   darwinFakeButtons = [fake_buttons intValue];
616   quartzUseSysBeep = [use_sysbeep intValue];
617   X11EnableKeyEquivalents = [enable_keyequivs intValue];
618   darwinSyncKeymap = [sync_keymap intValue];
619         
620   /* after adding prefs here, also add to [X11Application read_defaults]
621      and below */
622         
623   [NSApp prefs_set_boolean:@PREFS_FAKEBUTTONS value:darwinFakeButtons];
624   [NSApp prefs_set_boolean:@PREFS_SYSBEEP value:quartzUseSysBeep];
625   [NSApp prefs_set_boolean:@PREFS_KEYEQUIVS value:X11EnableKeyEquivalents];
626   [NSApp prefs_set_boolean:@PREFS_SYNC_KEYMAP value:darwinSyncKeymap];
627   [NSApp prefs_set_boolean:@PREFS_NO_AUTH value:![enable_auth intValue]];
628   [NSApp prefs_set_boolean:@PREFS_NO_TCP value:![enable_tcp intValue]];
629   [NSApp prefs_set_integer:@PREFS_DEPTH value:[depth selectedTag]];
630         
631   [NSApp prefs_synchronize];
634 - (IBAction) prefs_show:sender
636   [fake_buttons setIntValue:darwinFakeButtons];
637   [use_sysbeep setIntValue:quartzUseSysBeep];
638   [enable_keyequivs setIntValue:X11EnableKeyEquivalents];
639   [sync_keymap setIntValue:darwinSyncKeymap];
640   [sync_keymap setEnabled:darwinKeymapFile == NULL];
641         
642   [enable_auth setIntValue:![NSApp prefs_get_boolean:@PREFS_NO_AUTH default:NO]];
643   [enable_tcp setIntValue:![NSApp prefs_get_boolean:@PREFS_NO_TCP default:NO]];
644   [depth selectItemAtIndex:[depth indexOfItemWithTag:[NSApp prefs_get_integer:@PREFS_DEPTH default:-1]]];
645         
646   [enable_fullscreen setIntValue:!quartzEnableRootless];
647         
648   [prefs_panel makeKeyAndOrderFront:sender];
651 - (IBAction) quit:sender
653   QuartzMessageServerThread (kXDarwinQuit, 0);
656 - (IBAction) x11_help:sender
658   AHLookupAnchor (CFSTR ("Mac Help"), CFSTR ("mchlp2276"));
661 - (BOOL) validateMenuItem:(NSMenuItem *)item
663   NSMenu *menu = [item menu];
664         
665   if (item == toggle_fullscreen_item)
666     return !quartzEnableRootless;
667   else if (menu == [window_separator menu] || menu == dock_menu
668            || (menu == [x11_about_item menu] && [item tag] == 42))
669     return (AppleWMSelectedEvents () & AppleWMControllerNotifyMask) != 0;
670   else
671     return TRUE;
674 - (void) applicationDidHide:(NSNotification *)notify
676   QuartzMessageServerThread (kXDarwinControllerNotify, 1, AppleWMHideAll);
679 - (void) applicationDidUnhide:(NSNotification *)notify
681   QuartzMessageServerThread (kXDarwinControllerNotify, 1, AppleWMShowAll);
684 - (NSApplicationTerminateReply) applicationShouldTerminate:sender
686   NSString *msg;
687         
688   if (can_quit || [X11App prefs_get_boolean:@PREFS_NO_QUIT_ALERT default:NO])
689     return NSTerminateNow;
690         
691   /* Make sure we're frontmost. */
692   [NSApp activateIgnoringOtherApps:YES];
693         
694   msg = NSLocalizedString (@"Are you sure you want to quit X11?\n\nIf you quit X11, any X11 applications you are running will stop immediately and you will lose any changes you have not saved.", @"Dialog when quitting");
695         
696   /* FIXME: safe to run the alert in here? Or should we return Later
697      and then run the alert on a timer? It seems to work here, so.. */
698         
699   return (NSRunAlertPanel (nil, msg, NSLocalizedString (@"Quit", @""),
700                            NSLocalizedString (@"Cancel", @""), nil)
701           == NSAlertDefaultReturn) ? NSTerminateNow : NSTerminateCancel;
704 - (void) applicationWillTerminate:(NSNotification *)aNotification
706   [X11App prefs_synchronize];
707         
708   /* shutdown the X server, it will exit () for us. */
709   QuartzMessageServerThread (kXDarwinQuit, 0);
710         
711   /* In case it doesn't, exit anyway after a while. */
712   while (sleep (10) != 0) ;
713   exit (1);
716 - (void) server_ready
718   x_list *node;
719         
720   finished_launching = YES;
721         
722   for (node = pending_apps; node != NULL; node = node->next)
723     {
724       NSString *filename = node->data;
725       [self launch_client:filename];
726       [filename release];
727     }
728         
729   x_list_free (pending_apps);
730   pending_apps = NULL;
733 - (BOOL) application:(NSApplication *)app openFile:(NSString *)filename
735   const char *name = [filename UTF8String];
736         
737   if (finished_launching)
738     [self launch_client:filename];
739   else if (name[0] != ':')              /* ignore display names */
740     pending_apps = x_list_prepend (pending_apps, [filename retain]);
741         
742   /* FIXME: report failures. */
743   return YES;
746 @end
748 void X11ControllerMain (int argc, const char *argv[],
749                         void (*server_thread) (void *), void *server_arg)
751   X11ApplicationMain (argc, argv, server_thread, server_arg);