diff options
Diffstat (limited to 'hw/darwin/quartz/XServer.m')
-rw-r--r-- | hw/darwin/quartz/XServer.m | 537 |
1 files changed, 497 insertions, 40 deletions
diff --git a/hw/darwin/quartz/XServer.m b/hw/darwin/quartz/XServer.m index c57defd53..1be22f82c 100644 --- a/hw/darwin/quartz/XServer.m +++ b/hw/darwin/quartz/XServer.m @@ -34,7 +34,7 @@ * sale, use or other dealings in this Software without prior written * authorization. */ -/* $XFree86: xc/programs/Xserver/hw/darwin/quartz/XServer.m,v 1.8 2003/01/23 00:34:26 torrey Exp $ */ +/* $XFree86: xc/programs/Xserver/hw/darwin/quartz/XServer.m,v 1.17 2003/11/14 20:27:58 torrey Exp $ */ #include "quartzCommon.h" @@ -42,7 +42,12 @@ #include "X.h" #include "Xproto.h" #include "os.h" +#include "opaque.h" #include "darwin.h" +#include "quartz.h" +#define _APPLEWM_SERVER_ +#include "applewm.h" +#include "applewmExt.h" #undef BOOL #import "XServer.h" @@ -64,14 +69,6 @@ #import <IOKit/pwr_mgt/IOPMLib.h> #import <IOKit/IOMessage.h> -#define ENQUEUE(xe) \ -{ \ - char byte = 0; \ - DarwinEQEnqueue(xe); \ - /* signal there is an event ready to handle */ \ - write(eventWriteFD, &byte, 1); \ -} - // Types of shells enum { shell_Unknown, @@ -90,7 +87,7 @@ static shellList_t const shellList[] = { { "sh", shell_Bourne }, // standard Bourne shell { "zsh", shell_Bourne }, // Z shell { "bash", shell_Bourne }, // GNU Bourne again shell - { NULL, shell_Unknown } + { NULL, shell_Unknown } }; extern int argcGlobal; @@ -99,7 +96,6 @@ extern char **envpGlobal; extern int main(int argc, char *argv[], char *envp[]); extern void HideMenuBar(void); extern void ShowMenuBar(void); -extern void QuartzReallySetCursor(); static void childDone(int sig); static void powerDidChange(void *x, io_service_t y, natural_t messageType, void *messageArgument); @@ -122,15 +118,15 @@ static io_connect_t root_port; serverState = server_NotStarted; serverLock = [[NSRecursiveLock alloc] init]; + pendingClients = nil; clientPID = 0; sendServerEvents = NO; + x11Active = YES; serverVisible = NO; rootlessMenuBarVisible = YES; queueShowServer = YES; quartzServerQuitting = NO; mouseState = 0; - eventWriteFD = quartzEventWriteFD; - windowClass = [NSWindow class]; // set up a port to safely send messages to main thread from server thread signalPort = [[NSPort port] retain]; @@ -145,6 +141,11 @@ static io_connect_t root_port; [[NSRunLoop currentRunLoop] addPort:signalPort forMode:NSModalPanelRunLoopMode]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(windowBecameKey:) + name:NSWindowDidBecomeKeyNotification + object:nil]; + return self; } @@ -164,7 +165,7 @@ static io_connect_t root_port; [self showServer:NO]; sendServerEvents = NO; - if (clientPID != 0 || !quartzStartClients) { + if (!quitWithoutQuery && (clientPID != 0 || !quartzStartClients)) { int but; but = NSRunAlertPanel(NSLocalizedString(@"Quit X server?",@""), @@ -270,11 +271,18 @@ static io_connect_t root_port; break; case NSLeftMouseDown: [self getMousePosition:&xe fromEvent:anEvent]; - if (quartzRootless && - ! ([anEvent window] && - [[anEvent window] isKindOfClass:windowClass])) { - // Click in non X window - ignore - return NO; + if (quartzRootless) { + // Check that event is in X11 window + if (!quartzProcs->IsX11Window([anEvent window], + [anEvent windowNumber])) + { + if (x11Active) + [self activateX11:NO]; + return NO; + } else { + if (!x11Active) + [self activateX11:YES]; + } } mouse1Pressed = YES; xe.u.u.type = ButtonPress; @@ -310,6 +318,8 @@ static io_connect_t root_port; break; case NSKeyDown: case NSKeyUp: + if (!x11Active) + return NO; // If the mouse is not on the valid X display area, // we don't send the X server key events. if (![self getMousePosition:&xe fromEvent:nil]) @@ -321,6 +331,8 @@ static io_connect_t root_port; xe.u.u.detail = [anEvent keyCode]; break; case NSFlagsChanged: + if (!x11Active) + return NO; [self getMousePosition:&xe fromEvent:nil]; xe.u.u.type = kXDarwinUpdateModifiers; xe.u.clientMessage.u.l.longs0 = flags; @@ -335,14 +347,13 @@ static io_connect_t root_port; [self sendXEvent:&xe]; - // Rootless: Send first NSLeftMouseDown to windows and views so window - // ordering can be suppressed. + // Rootless: Send first NSLeftMouseDown to Cocoa windows and views so + // window ordering can be suppressed. // Don't pass further events - they (incorrectly?) bring the window // forward no matter what. if (quartzRootless && (type == NSLeftMouseDown || type == NSLeftMouseUp) && - [anEvent clickCount] == 1 && - [[anEvent window] isKindOfClass:windowClass]) + [anEvent clickCount] == 1 && [anEvent window]) { return NO; } @@ -429,6 +440,27 @@ static io_connect_t root_port; } } + +// Load the appropriate display mode bundle +- (BOOL)loadDisplayBundle +{ + if (quartzRootless) { + NSEnumerator *enumerator = [[Preferences displayModeBundles] + objectEnumerator]; + NSString *bundleName; + + while ((bundleName = [enumerator nextObject])) { + if (QuartzLoadDisplayBundle([bundleName cString])) + return YES; + } + + return NO; + } else { + return QuartzLoadDisplayBundle("fullscreen.bundle"); + } +} + + // Start the X server thread and the client process - (void)startX { @@ -455,6 +487,9 @@ static io_connect_t root_port; else NSLog(@"No version"); + if (![self loadDisplayBundle]) + [NSApp terminate:nil]; + // Start the X server thread serverState = server_Starting; [NSThread detachNewThreadSelector:@selector(run) toTarget:self @@ -517,6 +552,19 @@ static io_connect_t root_port; if (quartzServerQuitting) { [self quitServer]; [NSApp replyToApplicationShouldTerminate:YES]; + return; + } + + if (pendingClients) { + NSEnumerator *enumerator = [pendingClients objectEnumerator]; + NSString *filename; + + while ((filename = [enumerator nextObject])) { + [self runClient:filename]; + } + + [pendingClients release]; + pendingClients = nil; } } @@ -666,6 +714,87 @@ static io_connect_t root_port; return YES; } +// Start the specified client in its own task +// FIXME: This should be unified with startXClients +- (void)runClient:(NSString *)filename +{ + const char *command = [filename UTF8String]; + const char *shell; + const char *argv[5]; + int child1, child2 = 0; + int status; + + shell = getenv("SHELL"); + if (shell == NULL) + shell = "/bin/bash"; + + /* At least [ba]sh, [t]csh and zsh all work with this syntax. We + need to use an interactive shell to force it to load the user's + environment. */ + + argv[0] = shell; + argv[1] = "-i"; + argv[2] = "-c"; + argv[3] = command; + argv[4] = NULL; + + /* Do the fork-twice trick to avoid having to reap zombies */ + + child1 = fork(); + + switch (child1) { + case -1: /* error */ + break; + + case 0: /* child1 */ + child2 = fork(); + + switch (child2) { + int max_files, i; + char buf[1024], *tem; + + case -1: /* error */ + _exit(1); + + case 0: /* child2 */ + /* close all open files except for standard streams */ + max_files = sysconf(_SC_OPEN_MAX); + for (i = 3; i < max_files; i++) + close(i); + + /* ensure stdin is on /dev/null */ + close(0); + open("/dev/null", O_RDONLY); + + /* cd $HOME */ + tem = getenv("HOME"); + if (tem != NULL) + chdir(tem); + + /* Setup environment */ + snprintf(buf, sizeof(buf), ":%s", display); + setenv("DISPLAY", buf, TRUE); + tem = getenv("PATH"); + if (tem != NULL && tem[0] != NULL) + snprintf(buf, sizeof(buf), "%s:/usr/X11R6/bin", tem); + else + snprintf(buf, sizeof(buf), "/bin:/usr/bin:/usr/X11R6/bin"); + setenv("PATH", buf, TRUE); + + execvp(argv[0], (char **const) argv); + + _exit(2); + + default: /* parent (child1) */ + _exit(0); + } + break; + + default: /* parent */ + waitpid(child1, &status, 0); + } +} + // Run the X server thread - (void)run { @@ -712,6 +841,12 @@ static io_connect_t root_port; [NSApp activateIgnoringOtherApps:YES]; } +// Show the Aqua-X11 switch panel useful for fullscreen mode +- (IBAction)showSwitchPanel:(id)sender +{ + [switchWindow orderFront:nil]; +} + // Show the X server when sent message from GUI - (IBAction)showAction:(id)sender { @@ -787,11 +922,10 @@ static io_connect_t root_port; if (show) { if (!quartzRootless) { - QuartzFSCapture(); + quartzProcs->CaptureScreens(); HideMenuBar(); } - xe.u.u.type = kXDarwinShow; - [self sendXEvent:&xe]; + [self activateX11:YES]; // the mouse location will have moved; track it xe.u.u.type = MotionNotify; @@ -802,14 +936,19 @@ static io_connect_t root_port; xe.u.clientMessage.u.l.longs0 = [[NSApp currentEvent] modifierFlags]; [self sendXEvent:&xe]; - // put the pasteboard into the X cut buffer - [self readPasteboard]; + // If there is no AppleWM-aware cut and paste manager, do what we can. + if ((AppleWMSelectedEvents() & AppleWMPasteboardNotifyMask) == 0) { + // put the pasteboard into the X cut buffer + [self readPasteboard]; + } } else { - // put the X cut buffer on the pasteboard - [self writePasteboard]; + // If there is no AppleWM-aware cut and paste manager, do what we can. + if ((AppleWMSelectedEvents() & AppleWMPasteboardNotifyMask) == 0) { + // put the X cut buffer on the pasteboard + [self writePasteboard]; + } - xe.u.u.type = kXDarwinHide; - [self sendXEvent:&xe]; + [self activateX11:NO]; } serverVisible = show; @@ -877,7 +1016,7 @@ static io_connect_t root_port; } #endif - ENQUEUE(xe); + DarwinEQEnqueue(xe); } // Handle messages from the X server thread @@ -885,12 +1024,12 @@ static io_connect_t root_port; { unsigned msg = [portMessage msgid]; - switch(msg) { + switch (msg) { case kQuartzServerHidden: // Make sure the X server wasn't queued to be shown again while // the hide was pending. if (!quartzRootless && !serverVisible) { - QuartzFSRelease(); + quartzProcs->ReleaseScreens(); ShowMenuBar(); } break; @@ -908,13 +1047,43 @@ static io_connect_t root_port; break; case kQuartzCursorUpdate: - QuartzReallySetCursor(); + if (quartzProcs->CursorUpdate) + quartzProcs->CursorUpdate(); break; case kQuartzPostEvent: { const xEvent *xe = [[[portMessage components] lastObject] bytes]; - ENQUEUE(xe); + DarwinEQEnqueue(xe); + break; + } + + case kQuartzSetWindowMenu: + { + NSArray *list; + [[[portMessage components] lastObject] getBytes:&list]; + [self setX11WindowList:list]; + [list release]; + break; + } + + case kQuartzSetWindowMenuCheck: + { + int n; + [[[portMessage components] lastObject] getBytes:&n]; + [self setX11WindowCheck:[NSNumber numberWithInt:n]]; + break; + } + + case kQuartzSetFrontProcess: + [NSApp activateIgnoringOtherApps:YES]; + break; + + case kQuartzSetCanQuit: + { + int n; + [[[portMessage components] lastObject] getBytes:&n]; + quitWithoutQuery = (BOOL) n; break; } @@ -939,6 +1108,234 @@ static io_connect_t root_port; } } +// User selected an X11 window from a menu +- (IBAction)itemSelected:(id)sender +{ + xEvent xe; + + [NSApp activateIgnoringOtherApps:YES]; + + // Notify the client of the change through the X server thread + xe.u.u.type = kXDarwinControllerNotify; + xe.u.clientMessage.u.l.longs0 = AppleWMWindowMenuItem; + xe.u.clientMessage.u.l.longs1 = [sender tag]; + [self sendXEvent:&xe]; +} + +// User selected Next from window menu +- (IBAction)nextWindow:(id)sender +{ + QuartzMessageServerThread(kXDarwinControllerNotify, 1, + AppleWMNextWindow); +} + +// User selected Previous from window menu +- (IBAction)previousWindow:(id)sender +{ + QuartzMessageServerThread(kXDarwinControllerNotify, 1, + AppleWMPreviousWindow); +} + +/* + * The XPR implementation handles close, minimize, and zoom actions for X11 + * windows here, while CR handles these in the NSWindow class. + */ + +// Handle Close from window menu for X11 window in XPR implementation +- (IBAction)performClose:(id)sender +{ + QuartzMessageServerThread(kXDarwinControllerNotify, 1, + AppleWMCloseWindow); +} + +// Handle Minimize from window menu for X11 window in XPR implementation +- (IBAction)performMiniaturize:(id)sender +{ + QuartzMessageServerThread(kXDarwinControllerNotify, 1, + AppleWMMinimizeWindow); +} + +// Handle Zoom from window menu for X11 window in XPR implementation +- (IBAction)performZoom:(id)sender +{ + QuartzMessageServerThread(kXDarwinControllerNotify, 1, + AppleWMZoomWindow); +} + +// Handle "Bring All to Front" from window menu +- (IBAction)bringAllToFront:(id)sender +{ + if ((AppleWMSelectedEvents() & AppleWMControllerNotifyMask) != 0) { + QuartzMessageServerThread(kXDarwinControllerNotify, 1, + AppleWMBringAllToFront); + } else { + [NSApp arrangeInFront:nil]; + } +} + +// This ends up at the end of the responder chain. +- (IBAction)copy:(id)sender +{ + QuartzMessageServerThread(kXDarwinPasteboardNotify, 1, + AppleWMCopyToPasteboard); +} + +// Set whether or not X11 is active and should receive all key events +- (void)activateX11:(BOOL)state +{ + if (state) { + QuartzMessageServerThread(kXDarwinActivate, 0); + } + else { + QuartzMessageServerThread(kXDarwinDeactivate, 0); + } + + x11Active = state; +} + +// Some NSWindow became the key window +- (void)windowBecameKey:(NSWindow *)window +{ + if (quartzProcs->IsX11Window(window, [window windowNumber])) { + if (!x11Active) + [self activateX11:YES]; + } else { + if (x11Active) + [self activateX11:NO]; + } +} + +// Set the Apple-WM specifiable part of the window menu +- (void)setX11WindowList:(NSArray *)list +{ + NSMenuItem *item; + int first, count, i; + xEvent xe; + + /* Work backwards so we don't mess up the indices */ + first = [windowMenu indexOfItem:windowSeparator] + 1; + if (first > 0) { + count = [windowMenu numberOfItems]; + for (i = count - 1; i >= first; i--) + [windowMenu removeItemAtIndex:i]; + } else { + windowSeparator = (NSMenuItem *)[windowMenu addItemWithTitle:@"" + action:nil + keyEquivalent:@""]; + } + + count = [dockMenu numberOfItems]; + for (i = 0; i < count; i++) + [dockMenu removeItemAtIndex:0]; + + count = [list count]; + + for (i = 0; i < count; i++) + { + NSString *name, *shortcut; + + name = [[list objectAtIndex:i] objectAtIndex:0]; + shortcut = [[list objectAtIndex:i] objectAtIndex:1]; + + item = (NSMenuItem *)[windowMenu addItemWithTitle:name + action:@selector(itemSelected:) + keyEquivalent:shortcut]; + [item setTarget:self]; + [item setTag:i]; + [item setEnabled:YES]; + + item = (NSMenuItem *)[dockMenu insertItemWithTitle:name + action:@selector(itemSelected:) + keyEquivalent:shortcut atIndex:i]; + [item setTarget:self]; + [item setTag:i]; + [item setEnabled:YES]; + } + + if (checkedWindowItem >= 0 && checkedWindowItem < count) + { + item = (NSMenuItem *)[windowMenu itemAtIndex:first + checkedWindowItem]; + [item setState:NSOnState]; + item = (NSMenuItem *)[dockMenu itemAtIndex:checkedWindowItem]; + [item setState:NSOnState]; + } + + // Notify the client of the change through the X server thread + xe.u.u.type = kXDarwinControllerNotify; + xe.u.clientMessage.u.l.longs0 = AppleWMWindowMenuNotify; + [self sendXEvent:&xe]; +} + +// Set the checked item on the Apple-WM specifiable window menu +- (void)setX11WindowCheck:(NSNumber *)nn +{ + NSMenuItem *item; + int first, count; + int n = [nn intValue]; + + first = [windowMenu indexOfItem:windowSeparator] + 1; + count = [windowMenu numberOfItems] - first; + + if (checkedWindowItem >= 0 && checkedWindowItem < count) + { + item = (NSMenuItem *)[windowMenu itemAtIndex:first + checkedWindowItem]; + [item setState:NSOffState]; + item = (NSMenuItem *)[dockMenu itemAtIndex:checkedWindowItem]; + [item setState:NSOffState]; + } + if (n >= 0 && n < count) + { + item = (NSMenuItem *)[windowMenu itemAtIndex:first + n]; + [item setState:NSOnState]; + item = (NSMenuItem *)[dockMenu itemAtIndex:n]; + [item setState:NSOnState]; + } + checkedWindowItem = n; +} + +// Return whether or not a menu item should be enabled +- (BOOL)validateMenuItem:(NSMenuItem *)item +{ + NSMenu *menu = [item menu]; + + if (menu == windowMenu && [item tag] == 30) { + // Mode switch panel is for fullscreen only + return !quartzRootless; + } + else if ((menu == windowMenu && [item tag] != 40) || menu == dockMenu) { + // The special window and dock menu items should not be active unless + // there is an AppleWM-aware window manager running. + return (AppleWMSelectedEvents() & AppleWMControllerNotifyMask) != 0; + } + else { + return TRUE; + } +} + +/* + * Application Delegate Methods + */ + +- (void)applicationDidHide:(NSNotification *)aNotification +{ + if ((AppleWMSelectedEvents() & AppleWMControllerNotifyMask) != 0) { + QuartzMessageServerThread(kXDarwinControllerNotify, 1, + AppleWMHideAll); + } else { + // FIXME: We need to hide Xplugin windows here + } +} + +- (void)applicationDidUnhide:(NSNotification *)aNotification +{ + if ((AppleWMSelectedEvents() & AppleWMControllerNotifyMask) != 0) { + QuartzMessageServerThread(kXDarwinControllerNotify, 1, + AppleWMShowAll); + } else { + [NSApp arrangeInFront:nil]; + } +} + // Called when the user clicks the application icon, // but not when Cmd-Tab is used. // Rootless: Don't switch until applicationWillBecomeActive. @@ -958,8 +1355,38 @@ static io_connect_t root_port; - (void)applicationWillBecomeActive:(NSNotification *)aNotification { - if (quartzRootless) + if (quartzRootless) { [self showServer:YES]; + + // If there is no AppleWM-aware window manager, we can't allow + // interleaving of Aqua and X11 windows. + if ((AppleWMSelectedEvents() & AppleWMControllerNotifyMask) == 0) { + [NSApp arrangeInFront:nil]; + } + } +} + +// Called when the user opens a document type that we claim (ie. an X11 executable). +- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename +{ + if (serverState == server_Running) { + [self runClient:filename]; + return YES; + } + else if (serverState == server_NotStarted || serverState == server_Starting) { + if ([filename UTF8String][0] != ':') { // Ignore display names + if (!pendingClients) { + pendingClients = [[NSMutableArray alloc] initWithCapacity:1]; + } + [pendingClients addObject:filename]; + return YES; // Assume it will launch successfully + } + return NO; + } + + // If the server is quitting or done, + // its too late to launch new clients this time. + return NO; } @end @@ -970,7 +1397,7 @@ static io_connect_t root_port; // NSPort is not thread safe. void QuartzMessageMainThread(unsigned msg, void *data, unsigned length) { - if (msg == kQuartzPostEvent) { + if (length > 0) { NSData *eventData = [NSData dataWithBytes:data length:length]; NSArray *eventArray = [NSArray arrayWithObject:eventData]; NSPortMessage *newMessage = @@ -986,6 +1413,36 @@ void QuartzMessageMainThread(unsigned msg, void *data, unsigned length) } } +void +QuartzSetWindowMenu(int nitems, const char **items, + const char *shortcuts) +{ + NSMutableArray *array; + int i; + + array = [[NSMutableArray alloc] initWithCapacity:nitems]; + + for (i = 0; i < nitems; i++) { + NSMutableArray *subarray = [NSMutableArray arrayWithCapacity:2]; + NSString *string = [NSString stringWithUTF8String:items[i]]; + + [subarray addObject:string]; + + if (shortcuts[i] != 0) { + NSString *number = [NSString stringWithFormat:@"%d", + shortcuts[i]]; + [subarray addObject:number]; + } else + [subarray addObject:@""]; + + [array addObject:subarray]; + } + + /* Send the array of strings over to the main thread. */ + /* Will be released in main thread. */ + QuartzMessageMainThread(kQuartzSetWindowMenu, &array, sizeof(NSArray *)); +} + // Handle SIGCHLD signals static void childDone(int sig) { @@ -1025,5 +1482,5 @@ static void powerDidChange( } break; } - + } |