diff --git a/ChangeLog b/ChangeLog index 7d4e0a2..46e52df 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2004-11-10 18:41 Alexander Malmberg + + * Source/x11/XWindowBuffer.m (test_xshm): New function. + (test_xshm_error_handler): New function. + (+windowBufferForWindow:depthInfo:): Move XShm detection code + to test_xshm and use it to decide whether to use XShm or not. + (+initialize): Move use_xshm setting code to test_xshm. + (+_gotShmCompletion:): Don't warn if we can't find an XWindowBuffer + for the event. + 2004-11-10 Matt Rice * Source/x11/XGServerWindow.m (-iconSize): Implement new method. diff --git a/Source/x11/XWindowBuffer.m b/Source/x11/XWindowBuffer.m index acec82d..e0fe858 100644 --- a/Source/x11/XWindowBuffer.m +++ b/Source/x11/XWindowBuffer.m @@ -41,8 +41,121 @@ static int num_window_buffers; static int use_shape_hack = 0; /* this is an ugly hack :) */ -static int use_xshm = 1; +static int did_test_xshm = 0; +static int use_xshm = 1; +static int num_xshm_test_errors = 0; + +static NSString *xshm_warning + = @"Falling back to normal XImage:s (will be slower)."; + +static int test_xshm_error_handler(Display *d, XErrorEvent *ev) +{ + num_xshm_test_errors++; + return 0; +} + +static void test_xshm(Display *display, int drawing_depth) +{ + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + + did_test_xshm = 1; + + if ([ud objectForKey: @"XWindowBufferUseXShm"]) + use_xshm = [ud boolForKey: @"XWindowBufferUseXShm"]; + + /* If the user doesn't want XShm, there's no point in doing any further + testing. */ + if (!use_xshm) + return; + + /* This seems to return success if the X server understands the XShm + protocol, regardless of whether XShm can actually be used or not... */ + if (!XShmQueryExtension(display)) + { + NSLog(@"XShm not supported by X server."); + NSLog(xshm_warning); + use_xshm = 0; + return; + } + + /* ... so we check that it actually works here. To do this, we need to + set up our own error handler (because the xlib calls never fail, they + just cause error events to be sent to us), explicitly synchronize + (or we wouldn't get to handle, and thus detect, the errors until much + later), and try actually creating a XShm image. Yuck. */ + { + XImage *ximage; + XShmSegmentInfo shminfo; + int (*old_error_handler)(); + + old_error_handler = XSetErrorHandler(test_xshm_error_handler); + + ximage = XShmCreateImage(display, + DefaultVisual(display, DefaultScreen(display)), + drawing_depth, ZPixmap, NULL, &shminfo, + 1, 1); + if (!ximage || num_xshm_test_errors) + { + NSLog(@"XShm not supported, XShmCreateImage failed."); + goto no_xshm; + } + + shminfo.shmid = shmget(IPC_PRIVATE, + 64, /* We don't have exact bytes per row values here, but this + should be safe, and we'll probably get a full page anyway. + (And if it turns out not to be enough, the X server will notice + and give us errors, causing us to think that XShm isn't + supported.) */ + IPC_CREAT | 0700); + + if (shminfo.shmid == -1 || num_xshm_test_errors) + { + NSLog(@"XShm not supported, shmget() failed: %m."); + XDestroyImage(ximage); + goto no_xshm; + } + + shminfo.shmaddr = shmat(shminfo.shmid, 0, 0); + if ((int)shminfo.shmaddr == -1 || num_xshm_test_errors) + { + NSLog(@"XShm not supported, shmat() failed: %m."); + XDestroyImage(ximage); + shmctl(shminfo.shmid, IPC_RMID, 0); + goto no_xshm; + } + + shminfo.readOnly = 0; + if (!XShmAttach(display, &shminfo) || num_xshm_test_errors) + { + NSLog(@"XShm not supported, XShmAttach() failed."); + XDestroyImage(ximage); + shmdt(shminfo.shmaddr); + shmctl(shminfo.shmid, IPC_RMID, 0); + goto no_xshm; + } + + XShmDetach(display, &shminfo); + XDestroyImage(ximage); + shmdt(shminfo.shmaddr); + + /* Most of the time, all the above calls will return success despite + actually failing. To catch all errors generated by the calls before + returning, we synchronize here. */ + XSync(display, False); + + shmctl(shminfo.shmid, IPC_RMID, 0); + + if (num_xshm_test_errors) + { + NSLog(@"XShm not supported."); +no_xshm: + NSLog(xshm_warning); + use_xshm = 0; + } + XSetErrorHandler(old_error_handler); + } +} @implementation XWindowBuffer @@ -149,20 +262,13 @@ static int use_xshm = 1; if (wi->window->xframe.size.width * wi->window->xframe.size.height < 4096) goto no_xshm; -#define WARN @" Falling back to normal XImage:s (will be slower)." + if (!did_test_xshm) + test_xshm(wi->display, aDI->drawing_depth); + if (!use_xshm) goto no_xshm; /* Use XShm if possible, else fall back to normal XImage:s */ - if (!XShmQueryExtension(wi->display)) - { -static BOOL xshm_warned = NO; - if (!xshm_warned) - NSLog(@"XShm not supported." WARN); - xshm_warned = YES; - goto no_xshm; - } - wi->use_shm = 1; wi->ximage = XShmCreateImage(wi->display, DefaultVisual(wi->display, DefaultScreen(wi->display)), @@ -171,7 +277,8 @@ static BOOL xshm_warned = NO; wi->window->xframe.size.height); if (!wi->ximage) { - NSLog(@"Warning: XShmCreateImage failed!" WARN); + NSLog(@"Warning: XShmCreateImage failed!"); + NSLog(xshm_warning); goto no_xshm; } wi->shminfo.shmid = shmget(IPC_PRIVATE, @@ -180,7 +287,8 @@ static BOOL xshm_warned = NO; if (wi->shminfo.shmid == -1) { - NSLog(@"Warning: shmget() failed: %m." WARN); + NSLog(@"Warning: shmget() failed: %m."); + NSLog(xshm_warning); XDestroyImage(wi->ximage); goto no_xshm; } @@ -188,7 +296,8 @@ static BOOL xshm_warned = NO; wi->shminfo.shmaddr = wi->ximage->data = shmat(wi->shminfo.shmid, 0, 0); if ((int)wi->shminfo.shmaddr == -1) { - NSLog(@"Warning: shmat() failed: %m." WARN); + NSLog(@"Warning: shmat() failed: %m."); + NSLog(xshm_warning); XDestroyImage(wi->ximage); shmctl(wi->shminfo.shmid, IPC_RMID, 0); goto no_xshm; @@ -197,7 +306,8 @@ static BOOL xshm_warned = NO; wi->shminfo.readOnly = 0; if (!XShmAttach(wi->display, &wi->shminfo)) { - NSLog(@"Warning: XShmAttach() failed." WARN); + NSLog(@"Warning: XShmAttach() failed."); + NSLog(xshm_warning); XDestroyImage(wi->ximage); shmdt(wi->shminfo.shmaddr); shmctl(wi->shminfo.shmid, IPC_RMID, 0); @@ -210,20 +320,21 @@ static BOOL xshm_warned = NO; dragged across a window. */ /* TODO: we still get and handle expose events, although we don't need to. */ - wi->pixmap=XShmCreatePixmap(wi->display,wi->drawable, - wi->ximage->data,&wi->shminfo, - wi->window->xframe.size.width, - wi->window->xframe.size.height, - aDI->drawing_depth); + wi->pixmap = XShmCreatePixmap(wi->display, wi->drawable, + wi->ximage->data, &wi->shminfo, + wi->window->xframe.size.width, + wi->window->xframe.size.height, + aDI->drawing_depth); if (wi->pixmap) /* TODO: this doesn't work */ { - XSetWindowBackgroundPixmap(wi->display,wi->window->ident,wi->pixmap); + XSetWindowBackgroundPixmap(wi->display, wi->window->ident, + wi->pixmap); } /* On some systems (eg. freebsd), X can't attach to the shared segment if it's marked for destruction, so we make sure it's attached before marking it. */ - XSync(wi->display,False); + XSync(wi->display, False); /* Mark the segment as destroyed now. Since we're attached, it won't actually be destroyed, but if we crashed before doing this, it wouldn't @@ -541,9 +652,6 @@ static int warn = 0; { NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; use_shape_hack = [ud boolForKey: @"XWindowBuffer-shape-hack"]; - - if ([ud objectForKey: @"XWindowBufferUseXShm"]) - use_xshm = [ud boolForKey: @"XWindowBufferUseXShm"]; } @@ -558,7 +666,6 @@ static int warn = 0; return; } } - NSLog(@"Warning: gotShmCompletion: couldn't find XWindowBuffer for drawable"); } @end