From 32d116c7b28b280532931c6b4fc1f55e7f9d36f7 Mon Sep 17 00:00:00 2001 From: Fred Kiefer Date: Tue, 14 Aug 2007 22:44:40 +0000 Subject: [PATCH] Add more EWMH methods and helper methods. Simplify dragging support code. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/back/trunk@25389 72102866-910b-0410-8b05-ffd578937521 --- ChangeLog | 6 + Source/x11/XGServerWindow.m | 1354 ++++++++++++++++++++++------------- Tools/xpbs.m | 621 ++++++++-------- 3 files changed, 1198 insertions(+), 783 deletions(-) diff --git a/ChangeLog b/ChangeLog index d177aca..1740896 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2007-08-15 Fred Kiefer + + * Source/x11/XGServerWindow.m: Add more EWMH methods and helper + methods for them. + * Tools/xpbs.m: Simplify dragging support code. + 2007-08-13 Fred Kiefer * Source/art/ftfont.m (-coveredCharacterSet): Correct code. diff --git a/Source/x11/XGServerWindow.m b/Source/x11/XGServerWindow.m index 4865317..17ed1d9 100644 --- a/Source/x11/XGServerWindow.m +++ b/Source/x11/XGServerWindow.m @@ -533,6 +533,138 @@ static void setWindowHintsForStyle (Display *dpy, Window window, return x; } +- (void)_sendRoot: (Window)root + type: (Atom)type + window: (Window)window + data0: (long)data0 + data1: (long)data1 + data2: (long)data2 + data3: (long)data3 +{ + XEvent event; + + memset(&event, 0, sizeof(event)); + event.xclient.type = ClientMessage; + event.xclient.message_type = type; + event.xclient.format = 32; + event.xclient.display = dpy; + event.xclient.window = window; + event.xclient.data.l[0] = data0; + event.xclient.data.l[1] = data1; + event.xclient.data.l[2] = data2; + event.xclient.data.l[3] = data3; + XSendEvent(dpy, root, False, + (SubstructureNotifyMask|SubstructureRedirectMask), &event); + XFlush(dpy); +} + +/* + * Check if the window manager supports a feature. + */ +- (BOOL) _checkWMSupports: (Atom)feature +{ + Window root; + int count; + Atom *data; + Atom supported; + + if ((generic.wm & XGWM_EWMH) != 0) + { + return NO; + } + + supported = XInternAtom(dpy, "_NET_SUPPORTED", False); + root = DefaultRootWindow(dpy); + data = (Atom*)PropGetCheckProperty(dpy, root, supported, XA_ATOM, 32, -1, &count); + if (data != 0) + { + int i = 0; + + while (i < count && data[i] != feature) + { + i++; + } + XFree(data); + + if (i < count) + { + return YES; + } + } + return NO; +} + +Bool +_get_next_prop_new_event(Display *display, XEvent *event, char *arg) +{ + XID *data = (XID*)arg; + + if (event->type == PropertyNotify && + event->xproperty.window == data[0] && + event->xproperty.atom == data[1] && + event->xproperty.state == PropertyNewValue) + { + return True; + } + else + { + return False; + } +} + +- (BOOL) _tryRequestFrameExtents: (gswindow_device_t *)window +{ + static Atom _net_request_frame_extents = None; + XEvent xEvent; + XID event_data[2]; + NSDate *limit; + + if (_net_request_frame_extents == None) + { + _net_request_frame_extents = XInternAtom(dpy, "_NET_REQUEST_FRAME_EXTENTS", + False); + } + + if (![self _checkWMSupports: _net_request_frame_extents]) + { + return NO; + } + + event_data[0] = window->ident; + event_data[1] = _net_request_frame_extents; + + [self _sendRoot: window->root + type: _net_request_frame_extents + window: window->ident + data0: 0 + data1: 0 + data2: 0 + data3: 0]; + + limit = [NSDate dateWithTimeIntervalSinceNow: 1.0]; + while ([limit timeIntervalSinceNow] > 0.0) + { + if (XCheckTypedWindowEvent(dpy, window->ident, DestroyNotify, &xEvent)) + { + return NO; + } + else if (XCheckIfEvent(dpy, &xEvent, _get_next_prop_new_event, + (char*)(&event_data))) + { + return YES; + } + else + { + CREATE_AUTORELEASE_POOL(pool); + + [NSThread sleepUntilDate: + [NSDate dateWithTimeIntervalSinceNow: 0.01]]; + DESTROY(pool); + } + } + + return NO; +} - (BOOL) _checkStyle: (unsigned)style { @@ -668,12 +800,14 @@ static void setWindowHintsForStyle (Display *dpy, Window window, window->numProtocols = 0; window->protocols[window->numProtocols++] = generic.take_focus_atom; window->protocols[window->numProtocols++] = generic.delete_win_atom; - window->protocols[window->numProtocols++] = generic.net_wm_ping_atom; + if ((generic.wm & XGWM_EWMH) != 0) + { + window->protocols[window->numProtocols++] = generic.net_wm_ping_atom; + } if ((generic.wm & XGWM_WINDOWMAKER) != 0) { window->protocols[window->numProtocols++] = generic.miniaturize_atom; } - // FIXME Add ping protocol for EWMH XSetWMProtocols(dpy, window->ident, window->protocols, window->numProtocols); window->exposedRects = [NSMutableArray new]; @@ -700,60 +834,66 @@ static void setWindowHintsForStyle (Display *dpy, Window window, NSMapInsert(windowmaps, (void*)(uintptr_t)window->ident, window); NSMapInsert(windowtags, (void*)(uintptr_t)window->number, window); [self _setWindowOwnedByServer: window->number]; - [self orderwindow: NSWindowAbove : 0 : window->number]; - XSync(dpy, False); - while (XPending(dpy) > 0 || window->visibility > 1) + if (![self _tryRequestFrameExtents: window]) { - if (XPending(dpy) == 0) + // Only display the window, if the window manager does not support + // _NET_REQUEST_FRAME_EXTENTS + [self orderwindow: NSWindowAbove : 0 : window->number]; + + XSync(dpy, False); + while (XPending(dpy) > 0 || window->visibility > 1) { - NSDate *until; - - /* In theory, after executing XSync() all events resulting from - * our window creation and ordering front should be available in - * the X event queue. However, it's possible that a window manager - * could send soime events after the XSync() has been satisfied, - * so if we have not received a visibility notification we can wait - * for up to a second for more events. - */ - until = [NSDate dateWithTimeIntervalSinceNow: 1.0]; - while (XPending(dpy) == 0 && [until timeIntervalSinceNow] > 0.0) - { - CREATE_AUTORELEASE_POOL(pool); - - [NSThread sleepUntilDate: - [NSDate dateWithTimeIntervalSinceNow: 0.01]]; - DESTROY(pool); - } - if (XPending(dpy) == 0) - { - NSLog(@"Waited for a second, but the X system never" - @" made the window visible"); - break; - } - } - XNextEvent(dpy, &xEvent); - NSDebugLLog(@"Offset", @"Testing ... event %d window %d\n", - xEvent.type, xEvent.xany.window); - if (xEvent.xany.window != window->ident) - { - continue; - } - switch (xEvent.type) - { - case VisibilityNotify: - window->visibility = xEvent.xvisibility.state; - break; - - case ReparentNotify: - NSDebugLLog(@"Offset", @"%d ReparentNotify - offset %d %d\n", - xEvent.xreparent.window, xEvent.xreparent.x, - xEvent.xreparent.y); - repp = xEvent.xreparent.parent; - repx = xEvent.xreparent.x; - repy = xEvent.xreparent.y; - break; - } + if (XPending(dpy) == 0) + { + NSDate *until; + + /* In theory, after executing XSync() all events resulting from + * our window creation and ordering front should be available in + * the X event queue. However, it's possible that a window manager + * could send some events after the XSync() has been satisfied, + * so if we have not received a visibility notification we can wait + * for up to a second for more events. + */ + until = [NSDate dateWithTimeIntervalSinceNow: 1.0]; + while (XPending(dpy) == 0 && [until timeIntervalSinceNow] > 0.0) + { + CREATE_AUTORELEASE_POOL(pool); + + [NSThread sleepUntilDate: + [NSDate dateWithTimeIntervalSinceNow: 0.01]]; + DESTROY(pool); + } + if (XPending(dpy) == 0) + { + NSLog(@"Waited for a second, but the X system never" + @" made the window visible"); + break; + } + } + XNextEvent(dpy, &xEvent); + NSDebugLLog(@"Offset", @"Testing ... event %d window %d\n", + xEvent.type, xEvent.xany.window); + if (xEvent.xany.window != window->ident) + { + continue; + } + switch (xEvent.type) + { + case VisibilityNotify: + window->visibility = xEvent.xvisibility.state; + break; + + case ReparentNotify: + NSDebugLLog(@"Offset", @"%d ReparentNotify - offset %d %d\n", + xEvent.xreparent.window, xEvent.xreparent.x, + xEvent.xreparent.y); + repp = xEvent.xreparent.parent; + repx = xEvent.xreparent.x; + repy = xEvent.xreparent.y; + break; + } + } } /* If our window manager supports _NET_FRAME_EXTENTS we trust that as @@ -761,15 +901,13 @@ static void setWindowHintsForStyle (Display *dpy, Window window, */ if (_net_frame_extents == None) { - _net_frame_extents = XInternAtom(dpy, - "_NET_FRAME_EXTENTS", False); + _net_frame_extents = XInternAtom(dpy, "_NET_FRAME_EXTENTS", False); } extents = (unsigned long *)PropGetCheckProperty(dpy, window->ident, _net_frame_extents, XA_CARDINAL, 32, 4, &count); if (extents != 0) { - NSDebugLLog(@"Offset", - @"Offsets retrieved from _NET_FRAME_EXTENTS"); + NSDebugLLog(@"Offset", @"Offsets retrieved from _NET_FRAME_EXTENTS"); } if (extents == 0) { @@ -777,17 +915,17 @@ static void setWindowHintsForStyle (Display *dpy, Window window, * its as reliable as _NET_FRAME_EXTENTS */ if (_kde_frame_strut == None) - { - _kde_frame_strut = XInternAtom(dpy, - "_KDE_NET_WM_FRAME_STRUT", False); - } + { + _kde_frame_strut = XInternAtom(dpy, + "_KDE_NET_WM_FRAME_STRUT", False); + } extents = (unsigned long *)PropGetCheckProperty(dpy, - window->ident, _kde_frame_strut, XA_CARDINAL, 32, 4, &count); + window->ident, _kde_frame_strut, XA_CARDINAL, 32, 4, &count); if (extents!= 0) { - NSDebugLLog(@"Offset", - @"Offsets retrieved from _KDE_NET_WM_FRAME_STRUT"); - } + NSDebugLLog(@"Offset", + @"Offsets retrieved from _KDE_NET_WM_FRAME_STRUT"); + } } if (extents != 0) @@ -798,106 +936,105 @@ static void setWindowHintsForStyle (Display *dpy, Window window, o->b = extents[3]; o->known = YES; NSDebugLLog(@"Offset", @"Extents left %d, right %d, top %d, bottom %d", - extents[0], extents[1], extents[2], extents[3]); + extents[0], extents[1], extents[2], extents[3]); XFree(extents); } else if (repp != 0) { - NSDebugLLog(@"Offset", - @"Offsets retrieved from ReparentNotify"); + NSDebugLLog(@"Offset", @"Offsets retrieved from ReparentNotify"); window->parent = repp; if (repp != window->root) - { - Window parent = repp; - XWindowAttributes wattr; - float l; - float r; - float t; - float b; + { + Window parent = repp; + XWindowAttributes wattr; + float l; + float r; + float t; + float b; - /* Get the WM offset info which we hope is the same - * for all parented windows with the same style. - * The coordinates in the event are insufficient to determine - * the offsets as the new parent window may have a border, - * so we must get the attributes of that window and use them - * to determine our offsets. - */ - XGetWindowAttributes(dpy, parent, &wattr); - NSDebugLLog(@"Offset", @"Parent border,width,height %d,%d,%d\n", - wattr.border_width, wattr.width, wattr.height); - l = repx + wattr.border_width; - t = repy + wattr.border_width; + /* Get the WM offset info which we hope is the same + * for all parented windows with the same style. + * The coordinates in the event are insufficient to determine + * the offsets as the new parent window may have a border, + * so we must get the attributes of that window and use them + * to determine our offsets. + */ + XGetWindowAttributes(dpy, parent, &wattr); + NSDebugLLog(@"Offset", @"Parent border,width,height %d,%d,%d\n", + wattr.border_width, wattr.width, wattr.height); + l = repx + wattr.border_width; + t = repy + wattr.border_width; + + /* Find total parent size and subtract window size and + * top-left-corner offset to determine bottom-right-corner + * offset. + */ + r = wattr.width + wattr.border_width * 2; + r -= (window->xframe.size.width + l); + b = wattr.height + wattr.border_width * 2; + b -= (window->xframe.size.height + t); + + // Some window manager e.g. KDE2 put in multiple windows, + // so we have to find the right parent, closest to root + /* FIXME: This section of code has caused problems with + certain users. An X error occurs in XQueryTree and + later a seg fault in XFree. It's 'commented' out for + now unless you set the default 'GSDoubleParentWindows' + or we are reparented to 0,0 (which presumably must mean + that we have a double parent). + */ + if (generic.flags.doubleParentWindow == YES + || (repx == 0 && repy == 0)) + { + Window new_parent = parent; - /* Find total parent size and subtract window size and - * top-left-corner offset to determine bottom-right-corner - * offset. - */ - r = wattr.width + wattr.border_width * 2; - r -= (window->xframe.size.width + l); - b = wattr.height + wattr.border_width * 2; - b -= (window->xframe.size.height + t); - - // Some window manager e.g. KDE2 put in multiple windows, - // so we have to find the right parent, closest to root - /* FIXME: This section of code has caused problems with - certain users. An X error occurs in XQueryTree and - later a seg fault in XFree. It's 'commented' out for - now unless you set the default 'GSDoubleParentWindows' - or we are reparented to 0,0 (which presumably must mean - that we have a double parent). - */ - if (generic.flags.doubleParentWindow == YES - || (repx == 0 && repy == 0)) - { - Window new_parent = parent; - - r = wattr.width + wattr.border_width * 2; - b = wattr.height + wattr.border_width * 2; - while (new_parent && (new_parent != window->root)) - { - Window root; - Window *children = 0; - unsigned int nchildren; - - parent = new_parent; - NSLog(@"QueryTree window is %d (root %d cwin root %d)", - parent, root, window->root); - if (!XQueryTree(dpy, parent, &root, &new_parent, - &children, &nchildren)) - { - new_parent = None; - if (children) - { - NSLog(@"Bad pointer from failed X call?"); - children = 0; - } - } - if (children) - { - XFree(children); - } - if (new_parent && new_parent != window->root) - { - XGetWindowAttributes(dpy, parent, &wattr); - l += wattr.x + wattr.border_width; - t += wattr.y + wattr.border_width; - r = wattr.width + wattr.border_width * 2; - b = wattr.height + wattr.border_width * 2; - } - } /* while */ - r -= (window->xframe.size.width + l); - b -= (window->xframe.size.height + t); - } /* generic.flags.doubleParentWindow */ - - o->l = l; - o->r = r; - o->t = t; - o->b = b; - o->known = YES; - NSDebugLLog(@"Offset", - @"Style %d lrtb set to %d,%d,%d,%d\n", - style, (int)o->l, (int)o->r, (int)o->t, (int)o->b); - } + r = wattr.width + wattr.border_width * 2; + b = wattr.height + wattr.border_width * 2; + while (new_parent && (new_parent != window->root)) + { + Window root; + Window *children = 0; + unsigned int nchildren; + + parent = new_parent; + NSLog(@"QueryTree window is %d (root %d cwin root %d)", + parent, root, window->root); + if (!XQueryTree(dpy, parent, &root, &new_parent, + &children, &nchildren)) + { + new_parent = None; + if (children) + { + NSLog(@"Bad pointer from failed X call?"); + children = 0; + } + } + if (children) + { + XFree(children); + } + if (new_parent && new_parent != window->root) + { + XGetWindowAttributes(dpy, parent, &wattr); + l += wattr.x + wattr.border_width; + t += wattr.y + wattr.border_width; + r = wattr.width + wattr.border_width * 2; + b = wattr.height + wattr.border_width * 2; + } + } /* while */ + r -= (window->xframe.size.width + l); + b -= (window->xframe.size.height + t); + } /* generic.flags.doubleParentWindow */ + + o->l = l; + o->r = r; + o->t = t; + o->b = b; + o->known = YES; + NSDebugLLog(@"Offset", + @"Style %d lrtb set to %d,%d,%d,%d\n", + style, (int)o->l, (int)o->r, (int)o->t, (int)o->b); + } } [self termwindow: window->number]; @@ -906,11 +1043,11 @@ static void setWindowHintsForStyle (Display *dpy, Window window, { XNextEvent(dpy, &xEvent); NSDebugLLog(@"Offset", @"Destroying ... event %d window %d\n", - xEvent.type, xEvent.xany.window); + xEvent.type, xEvent.xany.window); if (xEvent.xany.window != window->ident) { - continue; - } + continue; + } } if (o->known == NO) { @@ -942,35 +1079,35 @@ static void setWindowHintsForStyle (Display *dpy, Window window, noticeboard = XInternAtom(dpy, "_WINDOWMAKER_NOTICEBOARD", False); while (i < count && data[i] != noticeboard) - { - i++; - } + { + i++; + } XFree(data); if (i < count) - { - Window *win; - void *d; - - win = (Window*)PropGetCheckProperty(dpy, root, - noticeboard, XA_WINDOW, 32, -1, &count); - - if (win != 0) - { - d = PropGetCheckProperty(dpy, *win, noticeboard, - XA_WINDOW, 32, 1, NULL); - if (d != 0) - { - XFree(d); - wmflags |= XGWM_WINDOWMAKER; - } - XFree(win); - } - } + { + Window *win; + void *d; + + win = (Window*)PropGetCheckProperty(dpy, root, + noticeboard, XA_WINDOW, 32, -1, &count); + + if (win != 0) + { + d = PropGetCheckProperty(dpy, *win, noticeboard, + XA_WINDOW, 32, 1, NULL); + if (d != 0) + { + XFree(d); + wmflags |= XGWM_WINDOWMAKER; + } + XFree(win); + } + } else - { - wmflags |= XGWM_WINDOWMAKER; - } + { + wmflags |= XGWM_WINDOWMAKER; + } } /* Now check for Gnome */ @@ -987,15 +1124,15 @@ static void setWindowHintsForStyle (Display *dpy, Window window, // a left over from an old window manager. if (win1 && *win1 == *win) { - wmflags |= XGWM_GNOME; + wmflags |= XGWM_GNOME; - generic.wintypes.win_type_atom = - XInternAtom(dpy, "_WIN_LAYER", False); - } + generic.wintypes.win_type_atom = + XInternAtom(dpy, "_WIN_LAYER", False); + } if (win1) { - XFree(win1); - } + XFree(win1); + } XFree(win); } @@ -1014,44 +1151,44 @@ static void setWindowHintsForStyle (Display *dpy, Window window, // a left over from an old window manager. if (win1 && *win1 == *win) { - wmflags |= XGWM_EWMH; - - // Store window type Atoms for this WM - generic.wintypes.win_type_atom = - XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); - generic.wintypes.win_desktop_atom = - XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", False); - generic.wintypes.win_dock_atom = - XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); - generic.wintypes.win_floating_atom = - XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_TOOLBAR", False); - generic.wintypes.win_menu_atom = - XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_MENU", False); - generic.wintypes.win_modal_atom = - XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); - generic.wintypes.win_normal_atom = - XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False); - // New in wmspec 1.2 - generic.wintypes.win_utility_atom = - XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_UTILITY", False); - generic.wintypes.win_splash_atom = - XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_SPLASH", False); - //KDE extensions - generic.wintypes.win_override_atom = - XInternAtom(dpy, "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE", False); - generic.wintypes.win_topmenu_atom = - XInternAtom(dpy, "_KDE_NET_WM_WINDOW_TYPE_TOPMENU", False); - - // Window state - generic.netstates.net_wm_state_atom = - XInternAtom(dpy, "_NET_WM_STATE", False); - generic.netstates.net_wm_state_skip_taskbar_atom = - XInternAtom(dpy, "_NET_WM_STATE_SKIP_TASKBAR", False); - } + wmflags |= XGWM_EWMH; + + // Store window type Atoms for this WM + generic.wintypes.win_type_atom = + XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); + generic.wintypes.win_desktop_atom = + XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", False); + generic.wintypes.win_dock_atom = + XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); + generic.wintypes.win_floating_atom = + XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_TOOLBAR", False); + generic.wintypes.win_menu_atom = + XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_MENU", False); + generic.wintypes.win_modal_atom = + XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); + generic.wintypes.win_normal_atom = + XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False); + // New in wmspec 1.2 + generic.wintypes.win_utility_atom = + XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_UTILITY", False); + generic.wintypes.win_splash_atom = + XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_SPLASH", False); + //KDE extensions + generic.wintypes.win_override_atom = + XInternAtom(dpy, "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE", False); + generic.wintypes.win_topmenu_atom = + XInternAtom(dpy, "_KDE_NET_WM_WINDOW_TYPE_TOPMENU", False); + + // Window state + generic.netstates.net_wm_state_atom = + XInternAtom(dpy, "_NET_WM_STATE", False); + generic.netstates.net_wm_state_skip_taskbar_atom = + XInternAtom(dpy, "_NET_WM_STATE_SKIP_TASKBAR", False); + } if (win1) { - XFree(win1); - } + XFree(win1); + } XFree(win); } @@ -1352,9 +1489,9 @@ static void setWindowHintsForStyle (Display *dpy, Window window, * hold 64bit values. */ XChangeProperty(dpy, ROOT, - generic.win_decor_atom, generic.win_decor_atom, - 32, PropModeReplace, (unsigned char *)&win_attrs, - sizeof(GNUstepWMAttributes)/sizeof(CARD32)); + generic.win_decor_atom, generic.win_decor_atom, + 32, PropModeReplace, (unsigned char *)&win_attrs, + sizeof(GNUstepWMAttributes)/sizeof(CARD32)); } if ((generic.wm & XGWM_EWMH) != 0) @@ -1400,70 +1537,70 @@ static void setWindowHintsForStyle (Display *dpy, Window window, */ if (_offsets_name == None) { - _offsets_name = XInternAtom(dpy, "_GNUSTEP_FRAME_OFFSETS", False); - } + _offsets_name = XInternAtom(dpy, "_GNUSTEP_FRAME_OFFSETS", False); + } if ([defs boolForKey: @"GSIgnoreRootOffsets"] == YES) { - offsets = 0; - } + offsets = 0; + } else { - offsets = (uint16_t *)PropGetCheckProperty(dpy, - DefaultRootWindow(dpy), _offsets_name, XA_CARDINAL, 16, 60, &count); - } + offsets = (uint16_t *)PropGetCheckProperty(dpy, + DefaultRootWindow(dpy), _offsets_name, XA_CARDINAL, 16, 60, &count); + } if (offsets == 0) { - BOOL ok = YES; + BOOL ok = YES; - /* No offsets available on the root window ... so we test each - * style of window to determine its offsets. - */ - for (i = 1; i < 16; i++) - { - if ([self _checkStyle: i] == NO) - { - ok = NO; // test failed for this style - } - } + /* No offsets available on the root window ... so we test each + * style of window to determine its offsets. + */ + for (i = 1; i < 16; i++) + { + if ([self _checkStyle: i] == NO) + { + ok = NO; // test failed for this style + } + } - if (ok == YES) - { - uint16_t off[60]; + if (ok == YES) + { + uint16_t off[60]; - /* We have obtained all the offsets, so we store them to - * the root window so that other GNUstep applications don't - * need to test to determine offsets. - */ - count = 0; - for (i = 1; i < 16; i++) - { - off[count++] = (uint16_t)generic.offsets[i].l; - off[count++] = (uint16_t)generic.offsets[i].r; - off[count++] = (uint16_t)generic.offsets[i].t; - off[count++] = (uint16_t)generic.offsets[i].b; - } - XChangeProperty(dpy, DefaultRootWindow(dpy), - _offsets_name, XA_CARDINAL, 16, PropModeReplace, - (unsigned char *)off, 60); - } - } + /* We have obtained all the offsets, so we store them to + * the root window so that other GNUstep applications don't + * need to test to determine offsets. + */ + count = 0; + for (i = 1; i < 16; i++) + { + off[count++] = (uint16_t)generic.offsets[i].l; + off[count++] = (uint16_t)generic.offsets[i].r; + off[count++] = (uint16_t)generic.offsets[i].t; + off[count++] = (uint16_t)generic.offsets[i].b; + } + XChangeProperty(dpy, DefaultRootWindow(dpy), + _offsets_name, XA_CARDINAL, 16, PropModeReplace, + (unsigned char *)off, 60); + } + } else { - /* Got offsets from the root window. - * Let's copy them into our local table. - */ - count = 0; - for (i = 1; i < 16; i++) - { - generic.offsets[i].l = (float)(offsets[count++]); - generic.offsets[i].r = (float)(offsets[count++]); - generic.offsets[i].t = (float)(offsets[count++]); - generic.offsets[i].b = (float)(offsets[count++]); - generic.offsets[i].known = YES; - } - XFree(offsets); - } + /* Got offsets from the root window. + * Let's copy them into our local table. + */ + count = 0; + for (i = 1; i < 16; i++) + { + generic.offsets[i].l = (float)(offsets[count++]); + generic.offsets[i].r = (float)(offsets[count++]); + generic.offsets[i].t = (float)(offsets[count++]); + generic.offsets[i].b = (float)(offsets[count++]); + generic.offsets[i].known = YES; + } + XFree(offsets); + } } } @@ -1692,7 +1829,7 @@ static void setWindowHintsForStyle (Display *dpy, Window window, else { window->win_attrs.window_style - = style & (NSIconWindowMask | NSMiniWindowMask); + = style & (NSIconWindowMask | NSMiniWindowMask); } frame = [self _OSFrameToXFrame: frame for: window]; @@ -2045,37 +2182,36 @@ static void setWindowHintsForStyle (Display *dpy, Window window, if (_net_frame_extents == None) { - _net_frame_extents = XInternAtom(dpy, - "_NET_FRAME_EXTENTS", False); - } + _net_frame_extents = XInternAtom(dpy, + "_NET_FRAME_EXTENTS", False); + } extents = (unsigned long *)PropGetCheckProperty(dpy, - win, _net_frame_extents, XA_CARDINAL, 32, 4, &count); - + win, _net_frame_extents, XA_CARDINAL, 32, 4, &count); if (!extents) // && (generic.wm & XGWM_KDE)) { - if (_kde_frame_strut == None) - { - _kde_frame_strut = XInternAtom(dpy, - "_KDE_NET_WM_FRAME_STRUT", False); - } - extents = (unsigned long *)PropGetCheckProperty(dpy, - win, _kde_frame_strut, XA_CARDINAL, 32, 4, &count); - } + if (_kde_frame_strut == None) + { + _kde_frame_strut = XInternAtom(dpy, + "_KDE_NET_WM_FRAME_STRUT", False); + } + extents = (unsigned long *)PropGetCheckProperty(dpy, + win, _kde_frame_strut, XA_CARDINAL, 32, 4, &count); + } if (extents) { - NSDebugLLog(@"Frame", - @"Window %d, left %d, right %d, top %d, bottom %d", - win, extents[0], extents[1], extents[2], extents[3]); - *l = extents[0]; - *r = extents[1]; - *t = extents[2]; - *b = extents[3]; - XFree(extents); - return; - } + NSDebugLLog(@"Frame", + @"Window %d, left %d, right %d, top %d, bottom %d", + win, extents[0], extents[1], extents[2], extents[3]); + *l = extents[0]; + *r = extents[1]; + *t = extents[2]; + *b = extents[3]; + XFree(extents); + return; + } } if ((style & NSIconWindowMask) || (style & NSMiniWindowMask)) @@ -2096,8 +2232,8 @@ static void setWindowHintsForStyle (Display *dpy, Window window, *b = o->b; *t = o->t; NSDebugLLog(@"Frame", - @"Window %d, offsets %f, %f, %f, %f", - win, *l, *r, *t, *b); + @"Window %d, offsets %f, %f, %f, %f", + win, *l, *r, *t, *b); return; } @@ -2107,33 +2243,33 @@ NSLog(@"styleoffsets ... guessing offsets\n"); { *l = *r = *t = *b = 1.0; if (NSResizableWindowMask & style) - { - *b = 9.0; - } + { + *b = 9.0; + } if ((style & NSTitledWindowMask) || (style & NSClosableWindowMask) - || (style & NSMiniaturizableWindowMask)) - { - *t = 25.0; - } + || (style & NSMiniaturizableWindowMask)) + { + *t = 25.0; + } NSDebugLLog(@"Frame", - @"Window %d, windowmaker %f, %f, %f, %f", - win, *l, *r, *t, *b); + @"Window %d, windowmaker %f, %f, %f, %f", + win, *l, *r, *t, *b); } else if ((generic.wm & XGWM_EWMH) != 0) { *l = *r = *t = *b = 4; if (NSResizableWindowMask & style) - { - *b = 7; - } + { + *b = 7; + } if ((style & NSTitledWindowMask) || (style & NSClosableWindowMask) - || (style & NSMiniaturizableWindowMask)) - { - *t = 20; - } + || (style & NSMiniaturizableWindowMask)) + { + *t = 20; + } NSDebugLLog(@"Frame", - @"Window %d, EWMH %f, %f, %f, %f", - win, *l, *r, *t, *b); + @"Window %d, EWMH %f, %f, %f, %f", + win, *l, *r, *t, *b); } else { @@ -2144,8 +2280,8 @@ NSLog(@"styleoffsets ... guessing offsets\n"); */ *l = *r = *t = *b = 0.0; NSDebugLLog(@"Frame", - @"Window %d, unknown %f, %f, %f, %f", - win, *l, *r, *t, *b); + @"Window %d, unknown %f, %f, %f, %f", + win, *l, *r, *t, *b); } } @@ -2878,6 +3014,7 @@ static BOOL didCreatePixmaps; - (void) setwindowlevel: (int)level : (int)win { + static Atom net_wm_state_skip_pager = None; gswindow_device_t *window; window = WINDOW_WITH_TAG(win); @@ -2893,166 +3030,177 @@ static BOOL didCreatePixmaps; // send WindowMaker WM window style hints // Always send GNUstepWMAttributes - { - XEvent event; - - /* - * First change the window properties so that, if the window - * is not mapped, we have stored the required info for when - * the WM maps it. - */ + /* + * First change the window properties so that, if the window + * is not mapped, we have stored the required info for when + * the WM maps it. + */ /* Warning ... X-bug .. when we specify 32bit data X actually expects data * of type 'long' or 'unsigned long' even on machines where those types * hold 64bit values. */ - XChangeProperty(dpy, window->ident, - generic.win_decor_atom, generic.win_decor_atom, - 32, PropModeReplace, (unsigned char *)&window->win_attrs, - sizeof(GNUstepWMAttributes)/sizeof(CARD32)); - /* - * Now send a message for rapid handling. - */ - event.xclient.type = ClientMessage; - event.xclient.message_type = generic.win_decor_atom; - event.xclient.format = 32; - event.xclient.display = dpy; - event.xclient.window = window->ident; - event.xclient.data.l[0] = GSWindowLevelAttr; - event.xclient.data.l[1] = window->win_attrs.window_level; - event.xclient.data.l[2] = 0; - event.xclient.data.l[3] = 0; - XSendEvent(dpy, DefaultRootWindow(dpy), False, - SubstructureRedirectMask, &event); - } + XChangeProperty(dpy, window->ident, + generic.win_decor_atom, generic.win_decor_atom, + 32, PropModeReplace, (unsigned char *)&window->win_attrs, + sizeof(GNUstepWMAttributes)/sizeof(CARD32)); + /* + * Now send a message for rapid handling. + */ + [self _sendRoot: window->root + type: generic.win_decor_atom + window: window->ident + data0: GSWindowLevelAttr + data1: window->win_attrs.window_level + data2: 0 + data3: 0]; if ((generic.wm & XGWM_EWMH) != 0) - { - int len; - long data[2]; - BOOL skipTaskbar = NO; + { + int len; + long data[2]; + BOOL skipTaskbar = NO; + + data[0] = generic.wintypes.win_normal_atom; + data[1] = None; + len = 1; - data[0] = generic.wintypes.win_normal_atom; - data[1] = None; - len = 1; - - if (level == NSModalPanelWindowLevel) - { - data[0] = generic.wintypes.win_modal_atom; - skipTaskbar = YES; - } - else if (level == NSMainMenuWindowLevel) - { - // For strange reasons menu level does not - // work out for the main menu - //data[0] = generic.wintypes.win_topmenu_atom; - data[0] = generic.wintypes.win_dock_atom; - //len = 2; - skipTaskbar = YES; - } - else if (level == NSSubmenuWindowLevel - || level == NSFloatingWindowLevel - || level == NSTornOffMenuWindowLevel) - { + if (level == NSModalPanelWindowLevel) + { + data[0] = generic.wintypes.win_modal_atom; + skipTaskbar = YES; + } + else if (level == NSMainMenuWindowLevel) + { + // For strange reasons menu level does not + // work out for the main menu + //data[0] = generic.wintypes.win_topmenu_atom; + data[0] = generic.wintypes.win_dock_atom; + //len = 2; + skipTaskbar = YES; + } + else if (level == NSSubmenuWindowLevel + || level == NSFloatingWindowLevel + || level == NSTornOffMenuWindowLevel) + { #ifdef USE_KDE_OVERRIDE - data[0] = generic.wintypes.win_override_atom; - //data[0] = generic.wintypes.win_utility_atom; - data[1] = generic.wintypes.win_menu_atom; - len = 2; + data[0] = generic.wintypes.win_override_atom; + //data[0] = generic.wintypes.win_utility_atom; + data[1] = generic.wintypes.win_menu_atom; + len = 2; #else - data[0] = generic.wintypes.win_menu_atom; - len = 1; + data[0] = generic.wintypes.win_menu_atom; + len = 1; #endif - skipTaskbar = YES; - } - else if (level == NSDockWindowLevel - || level == NSStatusWindowLevel) - { - data[0] =generic.wintypes.win_dock_atom; - skipTaskbar = YES; - } - // Does this belong into a different category? - else if (level == NSPopUpMenuWindowLevel) - { + skipTaskbar = YES; + } + else if (level == NSDockWindowLevel + || level == NSStatusWindowLevel) + { + data[0] =generic.wintypes.win_dock_atom; + skipTaskbar = YES; + } + // Does this belong into a different category? + else if (level == NSPopUpMenuWindowLevel) + { #ifdef USE_KDE_OVERRIDE - data[0] = generic.wintypes.win_override_atom; - data[1] = generic.wintypes.win_floating_atom; - len = 2; + data[0] = generic.wintypes.win_override_atom; + data[1] = generic.wintypes.win_floating_atom; + len = 2; #else - data[0] = generic.wintypes.win_modal_atom; - len = 1; + data[0] = generic.wintypes.win_modal_atom; + len = 1; #endif - skipTaskbar = YES; - } - else if (level == NSDesktopWindowLevel) - { - data[0] = generic.wintypes.win_desktop_atom; - skipTaskbar = YES; - } + skipTaskbar = YES; + } + else if (level == NSDesktopWindowLevel) + { + data[0] = generic.wintypes.win_desktop_atom; + skipTaskbar = YES; + } /* Warning ... X-bug .. when we specify 32bit data X actually expects data * of type 'long' or 'unsigned long' even on machines where those types * hold 64bit values. */ - XChangeProperty(dpy, window->ident, generic.wintypes.win_type_atom, - XA_ATOM, 32, PropModeReplace, - (unsigned char *)&data, len); - + XChangeProperty(dpy, window->ident, generic.wintypes.win_type_atom, + XA_ATOM, 32, PropModeReplace, + (unsigned char *)&data, len); + /* * Change _NET_WM_STATE based on window level. * This should be based on real window type (NSMenu, NSPanel, etc). * This feature is only needed for window managers that cannot properly * handle the window type set above. */ - if (skipTaskbar) - { - len = 1; - data[0] = generic.netstates.net_wm_state_skip_taskbar_atom; - XChangeProperty(dpy, window->ident, - generic.netstates.net_wm_state_atom, - XA_ATOM, 32, PropModeReplace, - (unsigned char *)&data, len); - } - else - { - XDeleteProperty(dpy, window->ident, - generic.netstates.net_wm_state_atom); - } - } + if (skipTaskbar) + { + static Atom net_wm_state_add = None; + + if (net_wm_state_add == None) + net_wm_state_add = XInternAtom(dpy, "_NET_WM_STATE_ADD", False); + if (net_wm_state_skip_pager == None) + net_wm_state_skip_pager = XInternAtom(dpy, "_NET_WM_STATE_SKIP_PAGER", + False); + [self _sendRoot: window->root + type: generic.netstates.net_wm_state_atom + window: window->ident + data0: net_wm_state_add + data1: generic.netstates.net_wm_state_skip_taskbar_atom + data2: net_wm_state_skip_pager + data3: 1]; + } + else + { + static Atom net_wm_state_remove = None; + + if (net_wm_state_remove == None) + net_wm_state_remove = XInternAtom(dpy, "_NET_WM_STATE_REMOVE", False); + if (net_wm_state_skip_pager == None) + net_wm_state_skip_pager = XInternAtom(dpy, "_NET_WM_STATE_SKIP_PAGER", + False); + + [self _sendRoot: window->root + type: generic.netstates.net_wm_state_atom + window: window->ident + data0: net_wm_state_remove + data1: generic.netstates.net_wm_state_skip_taskbar_atom + data2: net_wm_state_skip_pager + data3: 1]; + } + } else if ((generic.wm & XGWM_GNOME) != 0) - { - XEvent event; - long flag = WIN_LAYER_NORMAL; + { + long flag = WIN_LAYER_NORMAL; - if (level == NSDesktopWindowLevel) - flag = WIN_LAYER_DESKTOP; - else if (level == NSSubmenuWindowLevel - || level == NSFloatingWindowLevel - || level == NSTornOffMenuWindowLevel) - flag = WIN_LAYER_ONTOP; - else if (level == NSMainMenuWindowLevel) - flag = WIN_LAYER_MENU; - else if (level == NSDockWindowLevel - || level == NSStatusWindowLevel) - flag = WIN_LAYER_DOCK; - else if (level == NSModalPanelWindowLevel - || level == NSPopUpMenuWindowLevel) - flag = WIN_LAYER_ONTOP; - else if (level == NSScreenSaverWindowLevel) - flag = WIN_LAYER_ABOVE_DOCK; - - XChangeProperty(dpy, window->ident, generic.wintypes.win_type_atom, - XA_CARDINAL, 32, PropModeReplace, - (unsigned char *)&flag, 1); - - event.xclient.type = ClientMessage; - event.xclient.window = window->ident; - event.xclient.display = dpy; - event.xclient.message_type = generic.wintypes.win_type_atom; - event.xclient.format = 32; - event.xclient.data.l[0] = flag; - XSendEvent(dpy, window->root, False, - SubstructureNotifyMask, &event); - } + if (level == NSDesktopWindowLevel) + flag = WIN_LAYER_DESKTOP; + else if (level == NSSubmenuWindowLevel + || level == NSFloatingWindowLevel + || level == NSTornOffMenuWindowLevel) + flag = WIN_LAYER_ONTOP; + else if (level == NSMainMenuWindowLevel) + flag = WIN_LAYER_MENU; + else if (level == NSDockWindowLevel + || level == NSStatusWindowLevel) + flag = WIN_LAYER_DOCK; + else if (level == NSModalPanelWindowLevel + || level == NSPopUpMenuWindowLevel) + flag = WIN_LAYER_ONTOP; + else if (level == NSScreenSaverWindowLevel) + flag = WIN_LAYER_ABOVE_DOCK; + + XChangeProperty(dpy, window->ident, generic.wintypes.win_type_atom, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *)&flag, 1); + + [self _sendRoot: window->root + type: generic.wintypes.win_type_atom + window: window->ident + data0: flag + data1: 0 + data2: 0 + data3: 0]; + } } } @@ -3100,7 +3248,7 @@ static BOOL didCreatePixmaps; * interested in the ones which are ours. */ if (tmp) { - [ret addObject:[NSNumber numberWithInt:tmp->number]]; + [ret addObject:[NSNumber numberWithInt:tmp->number]]; } } @@ -3415,24 +3563,19 @@ static BOOL didCreatePixmaps; if ((generic.wm & XGWM_WINDOWMAKER) != 0) { gswindow_device_t *window = WINDOW_WITH_TAG(win); - XEvent event; if (win == 0 || window == 0) - { - return; - } + { + return; + } - event.xclient.type = ClientMessage; - event.xclient.message_type = generic.titlebar_state_atom; - event.xclient.format = 32; - event.xclient.display = dpy; - event.xclient.window = window->ident; - event.xclient.data.l[0] = st; - event.xclient.data.l[1] = 0; - event.xclient.data.l[2] = 0; - event.xclient.data.l[3] = 0; - XSendEvent(dpy, DefaultRootWindow(dpy), False, - SubstructureRedirectMask, &event); + [self _sendRoot: window->root + type: generic.titlebar_state_atom + window: window->ident + data0: st + data1: 0 + data2: 0 + data3: 0]; } } @@ -3460,21 +3603,55 @@ static BOOL didCreatePixmaps; } else { - unsigned long opacity; + unsigned int opacity; - opacity = (unsigned int)(alpha * 0xffffffff); + opacity = (unsigned int)(alpha * 0xffffffffU); XChangeProperty(window->display, window->ident, opacity_atom, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&opacity, 1L); if (window->parent != window->root) { - XChangeProperty(window->display, window->parent, opacity_atom, - XA_CARDINAL, 32, PropModeReplace, - (unsigned char*)&opacity, 1); - } + XChangeProperty(window->display, window->parent, opacity_atom, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char*)&opacity, 1); + } + + // GDK uses an event to set opacity, but most window manager still wait + // for property changes. What is the official stanard? } } +- (float) getAlpha: (int)win +{ + gswindow_device_t *window = WINDOW_WITH_TAG(win); + static Atom opacity_atom = None; + int c; + unsigned int *num; + float alpha = 0.0; + + if (win == 0 || window == 0) + { + NSDebugLLog(@"XGTrace", @"Setting alpha to unknown win %d", win); + return alpha; + } + + /* Initialize the atom if needed */ + if (opacity_atom == None) + opacity_atom = XInternAtom (window->display, "_NET_WM_WINDOW_OPACITY", False); + + num = (unsigned int*)PropGetCheckProperty(dpy, window->ident, + opacity_atom, XA_CARDINAL, + 32, 1, &c); + + if (num) + { + if (*num) + alpha = (float)*num / 0xffffffffU; + XFree(num); + } + + return alpha; +} - (void *) serverDevice { @@ -4067,5 +4244,212 @@ _computeDepth(int class, int bpp) return [super iconSize]; } -@end +- (unsigned int) numberOfDesktops: (int)screen +{ + static Atom number_of_desktops = None; + int c; + unsigned int *num; + unsigned int number = 0; + if (number_of_desktops == None) + number_of_desktops = XInternAtom(dpy, "_NET_NUMBER_OF_DESKTOPS", False); + + num = (unsigned int*)PropGetCheckProperty(dpy, RootWindow(dpy, screen), + number_of_desktops, XA_CARDINAL, + 32, 1, &c); + + if (num) + { + number = *num; + XFree(num); + } + return number; +} + +- (NSArray *) namesOfDesktops: (int)screen +{ + static Atom utf8_string = None; + static Atom desktop_names = None; + int c; + char *names; + + if (utf8_string == None) + { + utf8_string = XInternAtom(dpy, "UTF8_STRING", False); + desktop_names = XInternAtom(dpy, "_NET_DESKTOP_NAMES", False); + } + + names = (char *)PropGetCheckProperty(dpy, RootWindow(dpy, screen), + desktop_names, utf8_string, + 0, 0, &c); + if (names) + { + NSMutableArray *array = [[NSMutableArray alloc] init]; + char *p = names; + + while (p < names + c - 1) + { + [array addObject: [NSString stringWithUTF8String: p]]; + p += strlen(p) + 1; + } + XFree(names); + return AUTORELEASE(array); + } + + return nil; +} + +- (unsigned int) desktopNumberForScreen: (int)screen +{ + static Atom current_desktop = None; + int c; + unsigned int *num; + unsigned int number = 0; + + if (current_desktop == None) + current_desktop = XInternAtom(dpy, "_NET_CURRENT_DESKTOP", False); + + num = (unsigned int*)PropGetCheckProperty(dpy, RootWindow(dpy, screen), + current_desktop, XA_CARDINAL, + 32, 1, &c); + + if (num) + { + number = *num; + XFree(num); + } + return number; +} + +- (void) setDesktopNumber: (unsigned int)workspace forScreen: (int)screen +{ + static Atom current_desktop = None; + Window root = RootWindow(dpy, screen); + + if (current_desktop == None) + current_desktop = XInternAtom(dpy, "_NET_CURRENT_DESKTOP", False); + + [self _sendRoot: root + type: current_desktop + window: root + data0: workspace + data1: generic.lastTime + data2: 0 + data3: 0]; +} + +- (unsigned int) desktopNumberForWindow: (int)win +{ + gswindow_device_t *window; + static Atom wm_desktop = None; + int c; + unsigned int *num; + unsigned int number = 0; + + window = WINDOW_WITH_TAG(win); + if (!window) + return 0; + + if (wm_desktop == None) + wm_desktop = XInternAtom(dpy, "_NET_WM_DESKTOP", False); + + num = (unsigned int*)PropGetCheckProperty(dpy, window->ident, + wm_desktop, XA_CARDINAL, + 32, 1, &c); + + if (num) + { + number = *num; + XFree(num); + } + return number; +} + +- (void) setDesktopNumber: (unsigned int)workspace forWindow: (int)win +{ + gswindow_device_t *window; + static Atom wm_desktop = None; + + window = WINDOW_WITH_TAG(win); + if (!window) + return; + + if (wm_desktop == None) + wm_desktop = XInternAtom(dpy, "_NET_WM_DESKTOP", False); + + [self _sendRoot: window->root + type: wm_desktop + window: window->ident + data0: workspace + data1: 1 + data2: 0 + data3: 0]; +} + +- (void) setShadow: (BOOL)hasShadow : (int)win +{ + gswindow_device_t *window; + static Atom wm_window_shadow = None; + unsigned long shadow; + + window = WINDOW_WITH_TAG(win); + if (!window) + return; + + if (wm_window_shadow == None) + wm_window_shadow = XInternAtom(dpy, "_NET_WM_WINDOW_SHADOW", False); + + if (hasShadow) + { + // FIXME: What size? + shadow = (unsigned int)(0.1 * 0xffffffff); + + XChangeProperty(window->display, window->ident, wm_window_shadow, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char*)&shadow, 1L); + if (window->parent != window->root) + { + XChangeProperty(window->display, window->parent, wm_window_shadow, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char*)&shadow, 1); + } + } + else + { + XDeleteProperty(window->display, window->ident, wm_window_shadow); + if (window->parent != window->root) + { + XDeleteProperty(window->display, window->parent, wm_window_shadow); + } + } +} + +- (BOOL) hasShadow: (int)win +{ + gswindow_device_t *window; + static Atom wm_window_shadow = None; + int c; + unsigned int *num; + BOOL hasShadow = NO; + + window = WINDOW_WITH_TAG(win); + if (!window) + return hasShadow; + + if (wm_window_shadow == None) + wm_window_shadow = XInternAtom(dpy, "_NET_WM_WINDOW_SHADOW", False); + + num = (unsigned int*)PropGetCheckProperty(dpy, window->ident, + wm_window_shadow, XA_CARDINAL, + 32, 1, &c); + + if (num) + { + if (*num) + hasShadow = YES; + XFree(num); + } + return hasShadow; +} + +@end diff --git a/Tools/xpbs.m b/Tools/xpbs.m index 28d7c89..887b33d 100644 --- a/Tools/xpbs.m +++ b/Tools/xpbs.m @@ -34,7 +34,7 @@ /* * Non-predefined atoms that are used in the X selection mechanism */ -static char* atom_names[] = { +static char *atom_names[] = { "CHARACTER_POSITION", "CLIENT_WINDOW", "HOST_NAME", @@ -54,7 +54,17 @@ static char* atom_names[] = { "UTF8_STRING", "MULTIPLE", "COMPOUND_TEXT", - "INCR" + "INCR", + + // MIME types + "text/plain", + "text/uri-list", + "application/postscript", + "text/tab-separated-values", + "text/richtext", + "image/tiff", + "application/octet-stream", + "application/x-rootwindow-drop" }; static Atom atoms[sizeof(atom_names)/sizeof(char*)]; @@ -82,6 +92,8 @@ static Atom atoms[sizeof(atom_names)/sizeof(char*)]; #define XG_MULTIPLE atoms[17] #define XG_COMPOUND_TEXT atoms[18] #define XG_INCR atoms[19] +#define XG_MIME_PLAIN atoms[20] +#define XG_MIME_URI atoms[21] @@ -274,38 +286,38 @@ static NSString *xWaitMode = @"XPasteboardWaitMode"; while ((count = XPending(xDisplay)) > 0) { while (count-- > 0) - { - XEvent xEvent; + { + XEvent xEvent; - XNextEvent(xDisplay, &xEvent); + XNextEvent(xDisplay, &xEvent); + + switch (xEvent.type) + { + case PropertyNotify: + [self xPropertyNotify: (XPropertyEvent*)&xEvent]; + NSDebugLLog(@"Pbs", @"PropertyNotify."); + break; + + case SelectionNotify: + [self xSelectionNotify: (XSelectionEvent*)&xEvent]; + NSDebugLLog(@"Pbs", @"SelectionNotify."); + break; - switch (xEvent.type) - { - case PropertyNotify: - [self xPropertyNotify: (XPropertyEvent*)&xEvent]; - NSDebugLLog(@"Pbs", @"PropertyNotify."); - break; + case SelectionClear: + [self xSelectionClear: (XSelectionClearEvent*)&xEvent]; + NSDebugLLog(@"Pbs", @"SelectionClear."); + break; - case SelectionNotify: - [self xSelectionNotify: (XSelectionEvent*)&xEvent]; - NSDebugLLog(@"Pbs", @"SelectionNotify."); - break; + case SelectionRequest: + [self xSelectionRequest: (XSelectionRequestEvent*)&xEvent]; + NSDebugLLog(@"Pbs", @"SelectionRequest."); + break; - case SelectionClear: - [self xSelectionClear: (XSelectionClearEvent*)&xEvent]; - NSDebugLLog(@"Pbs", @"SelectionClear."); - break; - - case SelectionRequest: - [self xSelectionRequest: (XSelectionRequestEvent*)&xEvent]; - NSDebugLLog(@"Pbs", @"SelectionRequest."); - break; - - default: - NSDebugLLog(@"Pbs", @"Unexpected X event."); - break; - } - } + default: + NSDebugLLog(@"Pbs", @"Unexpected X event."); + break; + } + } } } @@ -332,7 +344,7 @@ static NSString *xWaitMode = @"XPasteboardWaitMode"; char *name = XGetAtomName(xDisplay, xEvent->selection); NSDebugLLog(@"Pbs", @"Selection clear for unknown selection - '%s'.", - name); + name); XFree(name); return; } @@ -355,7 +367,7 @@ static NSString *xWaitMode = @"XPasteboardWaitMode"; { char *name = XGetAtomName(xDisplay, xEvent->atom); NSDebugLLog(@"Pbs", @"Property notify for unknown property - '%s'.", - name); + name); XFree(name); return; } @@ -374,14 +386,14 @@ static NSString *xWaitMode = @"XPasteboardWaitMode"; + (void) xSelectionNotify: (XSelectionEvent*)xEvent { - XPbOwner *o; + XPbOwner *o; o = [self ownerByXPb: xEvent->selection]; if (o == nil) { char *name = XGetAtomName(xDisplay, xEvent->selection); NSDebugLLog(@"Pbs", @"Selection notify for unknown selection - '%s'.", - name); + name); XFree(name); return; } @@ -393,19 +405,19 @@ static NSString *xWaitMode = @"XPasteboardWaitMode"; } else { - char *sel_name = XGetAtomName(xDisplay, xEvent->selection); - char *pro_name; + char *sel_name = XGetAtomName(xDisplay, xEvent->selection); + char *pro_name; + + if (xEvent->property == None) + pro_name = NULL; + else + pro_name = XGetAtomName(xDisplay, xEvent->property); - if (xEvent->property == None) - pro_name = NULL; - else - pro_name = XGetAtomName(xDisplay, xEvent->property); - - NSDebugLLog(@"Pbs", @"Selection (%s) notify - '%s'.", sel_name, - pro_name? pro_name : "None"); - XFree(sel_name); - if (pro_name) - XFree(pro_name); + NSDebugLLog(@"Pbs", @"Selection (%s) notify - '%s'.", sel_name, + pro_name? pro_name : "None"); + XFree(sel_name); + if (pro_name) + XFree(pro_name); } [o xSelectionNotify: xEvent]; @@ -420,7 +432,7 @@ static NSString *xWaitMode = @"XPasteboardWaitMode"; { char *name = XGetAtomName(xDisplay, xEvent->selection); NSDebugLLog(@"Pbs", @"Selection request for unknown selection - '%s'.", - name); + name); XFree(name); return; } @@ -505,7 +517,7 @@ static NSString *xWaitMode = @"XPasteboardWaitMode"; - (void) requestData: (Atom)xType { - Time whenRequested; + Time whenRequested; /* * Do a nul append to a property to get a timestamp, if it returns the @@ -514,7 +526,7 @@ static NSString *xWaitMode = @"XPasteboardWaitMode"; whenRequested = [self xTimeByAppending]; if (whenRequested != CurrentTime) { - NSDate *limit; + NSDate *limit; /* * Ok - we got a timestamp, so we can ask the selection system for @@ -523,7 +535,7 @@ static NSString *xWaitMode = @"XPasteboardWaitMode"; * appropriate property of our application root window. */ XConvertSelection(xDisplay, [self xPb], xType, - [self xPb], xAppWin, whenRequested); + [self xPb], xAppWin, whenRequested); XFlush(xDisplay); /* @@ -534,19 +546,19 @@ static NSString *xWaitMode = @"XPasteboardWaitMode"; [self setWaitingForSelection: whenRequested]; while ([self waitingForSelection] == whenRequested) { - [[NSRunLoop currentRunLoop] runMode: xWaitMode - beforeDate: limit]; - if ([limit timeIntervalSinceNow] <= 0.0) - break; /* Timeout */ - } + [[NSRunLoop currentRunLoop] runMode: xWaitMode + beforeDate: limit]; + if ([limit timeIntervalSinceNow] <= 0.0) + break; /* Timeout */ + } if ([self waitingForSelection] != 0) { - char *name = XGetAtomName(xDisplay, xType); + char *name = XGetAtomName(xDisplay, xType); - [self setWaitingForSelection: 0]; - NSLog(@"Timed out waiting for X selection '%s'", name); - XFree(name); - } + [self setWaitingForSelection: 0]; + NSLog(@"Timed out waiting for X selection '%s'", name); + XFree(name); + } } } @@ -563,8 +575,13 @@ static NSString *xWaitMode = @"XPasteboardWaitMode"; { [self requestData: XG_UTF8_STRING]; if ([self data] == nil) - [self requestData: XA_STRING]; + [self requestData: XA_STRING]; } + else if ([type isEqual: NSFilenamesPboardType]) + { + [self requestData: XG_FILE_NAME]; + } + // FIXME: Support more types else { NSLog(@"Request for non-string info from X pasteboard: %@", type); @@ -630,7 +647,7 @@ xErrorHandler(Display *d, XErrorEvent *e) } - (NSMutableData*) getSelectionData: (XSelectionEvent*)xEvent - type: (Atom*)type + type: (Atom*)type { int status; unsigned char *data; @@ -649,51 +666,51 @@ xErrorHandler(Display *d, XErrorEvent *e) do { status = XGetWindowProperty(xDisplay, - xEvent->requestor, - xEvent->property, - long_offset, // offset - long_length, - True, // Delete prop when read. - req_type, - &actual_type, - &actual_format, - &number_items, - &bytes_remaining, - &data); + xEvent->requestor, + xEvent->property, + long_offset, // offset + long_length, + True, // Delete prop when read. + req_type, + &actual_type, + &actual_format, + &number_items, + &bytes_remaining, + &data); if ((status == Success) && (number_items > 0)) { - long count = number_items * actual_format / 8; - - if (md == nil) - { - md = [[NSMutableData alloc] initWithBytes: (void *)data - length: count]; - req_type = actual_type; - } - else - { - if (req_type != actual_type) - { - char *req_name = XGetAtomName(xDisplay, req_type); - char *act_name = XGetAtomName(xDisplay, actual_type); - - NSLog(@"Selection changed type from %s to %s.", - req_name, act_name); - XFree(req_name); - XFree(act_name); - RELEASE(md); - return nil; - } - [md appendBytes: (void *)data length: count]; - } - - long_offset += count / 4; - if (data) - { - XFree(data); - } - } + long count = number_items * actual_format / 8; + + if (md == nil) + { + md = [[NSMutableData alloc] initWithBytes: (void *)data + length: count]; + req_type = actual_type; + } + else + { + if (req_type != actual_type) + { + char *req_name = XGetAtomName(xDisplay, req_type); + char *act_name = XGetAtomName(xDisplay, actual_type); + + NSLog(@"Selection changed type from %s to %s.", + req_name, act_name); + XFree(req_name); + XFree(act_name); + RELEASE(md); + return nil; + } + [md appendBytes: (void *)data length: count]; + } + + long_offset += count / 4; + if (data) + { + XFree(data); + } + } } while ((status == Success) && (bytes_remaining > 0)); @@ -733,79 +750,99 @@ xErrorHandler(Display *d, XErrorEvent *e) if (md != nil) { if (actual_type == XG_INCR) - { - XEvent event; - NSMutableData *imd = nil; - BOOL wait = YES; - - md = nil; - while (wait) - { - XNextEvent(xDisplay, &event); - - if (event.type == PropertyNotify) - { - if (event.xproperty.state != PropertyNewValue) continue; - - imd = [self getSelectionData: xEvent type: &actual_type]; - if (imd != nil) - { - if (md == nil) - { - md = imd; - } - else - { - [md appendData: imd]; - } - } - else - { - wait = NO; - } - } - } - } + { + XEvent event; + NSMutableData *imd = nil; + BOOL wait = YES; + + md = nil; + while (wait) + { + XNextEvent(xDisplay, &event); + + if (event.type == PropertyNotify) + { + if (event.xproperty.state != PropertyNewValue) continue; + + imd = [self getSelectionData: xEvent type: &actual_type]; + if (imd != nil) + { + if (md == nil) + { + md = imd; + } + else + { + [md appendData: imd]; + } + } + else + { + wait = NO; + } + } + } + } } - + if (md != nil) { // Convert data to text string. if (actual_type == XG_UTF8_STRING) - { - NSString *s; - NSData *d; - - s = [[NSString alloc] initWithData: md - encoding: NSUTF8StringEncoding]; - if (s != nil) - { - d = [NSSerializer serializePropertyList: s]; - RELEASE(s); - [self setData: d]; - } - } + { + NSString *s; + NSData *d; + + s = [[NSString alloc] initWithData: md + encoding: NSUTF8StringEncoding]; + if (s != nil) + { + d = [NSSerializer serializePropertyList: s]; + RELEASE(s); + [self setData: d]; + } + } else if (actual_type == XA_STRING) - { - NSString *s; - NSData *d; + { + NSString *s; + NSData *d; + + s = [[NSString alloc] initWithData: md + encoding: NSISOLatin1StringEncoding]; + if (s != nil) + { + d = [NSSerializer serializePropertyList: s]; + RELEASE(s); + [self setData: d]; + } + } + else if (actual_type == XG_FILE_NAME) + { + NSArray *names; + NSData *d; + NSString *s; + NSURL *url; - s = [[NSString alloc] initWithData: md - encoding: NSISOLatin1StringEncoding]; - if (s != nil) - { - d = [NSSerializer serializePropertyList: s]; - RELEASE(s); - [self setData: d]; - } - } + s = [[NSString alloc] initWithData: md + encoding: NSUTF8StringEncoding]; + url = [[NSURL alloc] initWithString: s]; + RELEASE(s); + if ([url isFileURL]) + { + s = [url path]; + names = [NSArray arrayWithObject: s]; + d = [NSSerializer serializePropertyList: names]; + [self setData: d]; + } + RELEASE(url); + } else - { - char *name = XGetAtomName(xDisplay, actual_type); - - NSLog(@"Unsupported data type '%s' from X selection.", name); - XFree(name); - } + { + char *name = XGetAtomName(xDisplay, actual_type); + + NSLog(@"Unsupported data type '%s' from X selection.", name); + XFree(name); + } } } @@ -873,16 +910,16 @@ xErrorHandler(Display *d, XErrorEvent *e) if ([types containsObject: NSStringPboardType]) { - xTypes[numTypes++] = XG_UTF8_STRING; - xTypes[numTypes++] = XG_COMPOUND_TEXT; - xTypes[numTypes++] = XA_STRING; - xTypes[numTypes++] = XG_TEXT; - } + xTypes[numTypes++] = XG_UTF8_STRING; + xTypes[numTypes++] = XG_COMPOUND_TEXT; + xTypes[numTypes++] = XA_STRING; + xTypes[numTypes++] = XG_TEXT; + } if ([types containsObject: NSFilenamesPboardType]) { - xTypes[numTypes++] = XG_FILE_NAME; - } + xTypes[numTypes++] = XG_FILE_NAME; + } xType = XA_ATOM; format = 32; @@ -906,59 +943,59 @@ xErrorHandler(Display *d, XErrorEvent *e) */ if ([types containsObject: NSStringPboardType]) { - xEvent->target = XG_UTF8_STRING; - [self xProvideSelection: xEvent]; - } + xEvent->target = XG_UTF8_STRING; + [self xProvideSelection: xEvent]; + } else if ([types containsObject: NSFilenamesPboardType]) { - xEvent->target = XG_FILE_NAME; - [self xProvideSelection: xEvent]; - } + xEvent->target = XG_FILE_NAME; + [self xProvideSelection: xEvent]; + } } else if (xEvent->target == XG_MULTIPLE) { if (xEvent->property != None) { - Atom *multipleAtoms= NULL; - int actual_format; - Atom actual_type; - unsigned long number_items, bytes_remaining; - int status; + Atom *multipleAtoms= NULL; + int actual_format; + Atom actual_type; + unsigned long number_items, bytes_remaining; + int status; - status = XGetWindowProperty(xDisplay, - xEvent->requestor, - xEvent->property, - 0, - 100, - False, - AnyPropertyType, - &actual_type, - &actual_format, - &number_items, - &bytes_remaining, - (unsigned char **)&multipleAtoms); - if ((status == Success) && (bytes_remaining == 0) && - (actual_format == 32) && (actual_type == XA_ATOM)) - { - int i; - XSelectionRequestEvent requestEvent; - - memcpy(&requestEvent, xEvent, sizeof(XSelectionRequestEvent)); - for (i = 0; i < number_items; i += 2) - { - requestEvent.target= multipleAtoms[i]; - requestEvent.property= multipleAtoms[i+1]; - if (requestEvent.target != None) - { - // Recursive call to this method for each pair. - if (![self xProvideSelection: &requestEvent]) - { - multipleAtoms[i+1]= None; - } - } - } - // FIXME: Should we call XChangeProperty to set the invalid types? - } + status = XGetWindowProperty(xDisplay, + xEvent->requestor, + xEvent->property, + 0, + 100, + False, + AnyPropertyType, + &actual_type, + &actual_format, + &number_items, + &bytes_remaining, + (unsigned char **)&multipleAtoms); + if ((status == Success) && (bytes_remaining == 0) && + (actual_format == 32) && (actual_type == XA_ATOM)) + { + int i; + XSelectionRequestEvent requestEvent; + + memcpy(&requestEvent, xEvent, sizeof(XSelectionRequestEvent)); + for (i = 0; i < number_items; i += 2) + { + requestEvent.target= multipleAtoms[i]; + requestEvent.property= multipleAtoms[i+1]; + if (requestEvent.target != None) + { + // Recursive call to this method for each pair. + if (![self xProvideSelection: &requestEvent]) + { + multipleAtoms[i+1]= None; + } + } + } + // FIXME: Should we call XChangeProperty to set the invalid types? + } } } else if ((xEvent->target == XG_COMPOUND_TEXT) && @@ -973,25 +1010,25 @@ xErrorHandler(Display *d, XErrorEvent *e) d = [s cString]; if (d) { - char *list[]= {(char *)d, NULL}; - XTextProperty textProperty; - - status = XmbTextListToTextProperty(xEvent->display, list, 1, - XCompoundTextStyle, &textProperty); - if (status == Success) - { - NSAssert(textProperty.format == 8, @"textProperty.format == 8"); - numItems = textProperty.nitems; - data = malloc(numItems + 1); - memcpy(data, textProperty.value, numItems + 1); - XFree((void *)textProperty.value); - } - } + char *list[]= {(char *)d, NULL}; + XTextProperty textProperty; + + status = XmbTextListToTextProperty(xEvent->display, list, 1, + XCompoundTextStyle, &textProperty); + if (status == Success) + { + NSAssert(textProperty.format == 8, @"textProperty.format == 8"); + numItems = textProperty.nitems; + data = malloc(numItems + 1); + memcpy(data, textProperty.value, numItems + 1); + XFree((void *)textProperty.value); + } + } } else if (((xEvent->target == XG_UTF8_STRING) || - (xEvent->target == XA_STRING) || - (xEvent->target == XG_TEXT)) && - [types containsObject: NSStringPboardType]) + (xEvent->target == XA_STRING) || + (xEvent->target == XG_TEXT)) && + [types containsObject: NSStringPboardType]) { NSString *s = [_pb stringForType: NSStringPboardType]; NSData *d = nil; @@ -1005,24 +1042,43 @@ xErrorHandler(Display *d, XErrorEvent *e) */ if (xType == XG_UTF8_STRING) { - d = [s dataUsingEncoding: NSUTF8StringEncoding]; - } + d = [s dataUsingEncoding: NSUTF8StringEncoding]; + } else if ((xType == XA_STRING) || (xType == XG_TEXT)) { - d = [s dataUsingEncoding: NSISOLatin1StringEncoding]; - } + d = [s dataUsingEncoding: NSISOLatin1StringEncoding]; + } if (d != nil) { - numItems = [d length]; - data = malloc(numItems + 1); - if (data) - [d getBytes: data]; - } + numItems = [d length]; + data = malloc(numItems + 1); + if (data) + [d getBytes: data]; + } } else if ((xEvent->target == XG_FILE_NAME) && [types containsObject: NSFilenamesPboardType]) { + NSArray *names = [_pb propertyListForType: NSFilenamesPboardType]; + NSString *file = [[names lastObject] stringByStandardizingPath]; + NSURL *url = [[NSURL alloc] initWithScheme: NSURLFileScheme + host: @"localhost" + path: file]; + NSString *s = [url absoluteString]; + NSData *d; + + xType = xEvent->target; + format = 8; + RELEASE(url); + d = [s dataUsingEncoding: NSISOLatin1StringEncoding]; + if (d != nil) + { + numItems = [d length]; + data = malloc(numItems + 1); + if (data) + [d getBytes: data]; + } } else { @@ -1034,12 +1090,12 @@ xErrorHandler(Display *d, XErrorEvent *e) } return [self xSendData: data format: format items: numItems type: xType - to: xEvent->requestor property: xEvent->property]; + to: xEvent->requestor property: xEvent->property]; } - (BOOL) xSendData: (unsigned char*) data format: (int) format - items: (int) numItems type: (Atom) xType - to: (Window) window property: (Atom) property + items: (int) numItems type: (Atom) xType + to: (Window) window property: (Atom) property { BOOL status = NO; @@ -1061,23 +1117,23 @@ xErrorHandler(Display *d, XErrorEvent *e) oldHandler = XSetErrorHandler(xErrorHandler); while (appendFailure == NO && pos < numItems) - { - if (pos + maxItems > numItems) - { - maxItems = numItems - pos; - } - XChangeProperty(xDisplay, window, property, - xType, format, mode, &data[pos*format/8], maxItems); - mode = PropModeAppend; - pos += maxItems; - XSync(xDisplay, False); - } + { + if (pos + maxItems > numItems) + { + maxItems = numItems - pos; + } + XChangeProperty(xDisplay, window, property, + xType, format, mode, &data[pos*format/8], maxItems); + mode = PropModeAppend; + pos += maxItems; + XSync(xDisplay, False); + } free(data); XSetErrorHandler(oldHandler); if (appendFailure == NO) - { - status = YES; - } + { + status = YES; + } } return status; } @@ -1125,7 +1181,7 @@ xErrorHandler(Display *d, XErrorEvent *e) [[NSRunLoop currentRunLoop] runMode: xWaitMode beforeDate: limit]; if ([limit timeIntervalSinceNow] <= 0.0) - break; /* Timeout */ + break; /* Timeout */ } if ((whenRequested = [self timeOfLastAppend]) == 0) { @@ -1204,12 +1260,7 @@ static DndClass dnd; NSArray *types; Atom *typelist; - // Some GNUstep application did grap the drag pasteboard. Report this to X. - if (xdnd_set_selection_owner(&dnd, xAppWin, None)) - { - NSLog(@"Failed to set X drag selection owner to the pasteboard server."); - } - [self setOwnedByOpenStep: YES]; + [super pasteboardChangedOwner: sender]; // We also have to set the supported types for our window types = [_pb types]; @@ -1237,35 +1288,9 @@ static DndClass dnd; { NSString *mime = [NSPasteboard mimeTypeForPasteboardType: type]; Atom mType = XInternAtom(xDisplay, [mime cString], False); - Window window; - Time whenRequested = CurrentTime; - NSDate *limit; [self setData: nil]; - // How can we get the selection owner? - window = XGetSelectionOwner(xDisplay, dnd.XdndSelection); - - xdnd_convert_selection(&dnd, window, xAppWin, mType); - XFlush(xDisplay); - - /* - * Run an event loop to read X events until we have aquired the - * pasteboard data we need. - */ - limit = [NSDate dateWithTimeIntervalSinceNow: 20.0]; - [self setWaitingForSelection: whenRequested]; - while ([self waitingForSelection] == whenRequested) - { - [[NSRunLoop currentRunLoop] runMode: xWaitMode - beforeDate: limit]; - if ([limit timeIntervalSinceNow] <= 0.0) - break; /* Timeout */ - } - if ([self waitingForSelection] != 0) - { - [self setWaitingForSelection: 0]; - NSLog(@"Timed out waiting for X selection"); - } + [self requestData: mType]; [pb setData: [self data] forType: type]; }