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
This commit is contained in:
Fred Kiefer 2007-08-14 22:44:40 +00:00
parent 0dd2da6751
commit 32d116c7b2
3 changed files with 1198 additions and 783 deletions

View file

@ -1,3 +1,9 @@
2007-08-15 Fred Kiefer <FredKiefer@gmx.de>
* 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 <FredKiefer@gmx.de>
* Source/art/ftfont.m (-coveredCharacterSet): Correct code.

View file

@ -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;
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,6 +834,11 @@ 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];
if (![self _tryRequestFrameExtents: window])
{
// Only display the window, if the window manager does not support
// _NET_REQUEST_FRAME_EXTENTS
[self orderwindow: NSWindowAbove : 0 : window->number];
XSync(dpy, False);
@ -712,7 +851,7 @@ static void setWindowHintsForStyle (Display *dpy, Window window,
/* 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,
* 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.
*/
@ -755,21 +894,20 @@ static void setWindowHintsForStyle (Display *dpy, Window window,
break;
}
}
}
/* If our window manager supports _NET_FRAME_EXTENTS we trust that as
* definitive information.
*/
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)
{
@ -803,8 +941,7 @@ static void setWindowHintsForStyle (Display *dpy, Window window,
}
else if (repp != 0)
{
NSDebugLLog(@"Offset",
@"Offsets retrieved from ReparentNotify");
NSDebugLLog(@"Offset", @"Offsets retrieved from ReparentNotify");
window->parent = repp;
if (repp != window->root)
{
@ -2052,7 +2189,6 @@ static void setWindowHintsForStyle (Display *dpy, Window window,
extents = (unsigned long *)PropGetCheckProperty(dpy,
win, _net_frame_extents, XA_CARDINAL, 32, 4, &count);
if (!extents) // && (generic.wm & XGWM_KDE))
{
if (_kde_frame_strut == None)
@ -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,9 +3030,6 @@ 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
@ -2912,18 +3046,13 @@ static BOOL didCreatePixmaps;
/*
* 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);
}
[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)
{
@ -3005,22 +3134,42 @@ static BOOL didCreatePixmaps;
*/
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);
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
{
XDeleteProperty(dpy, window->ident,
generic.netstates.net_wm_state_atom);
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;
if (level == NSDesktopWindowLevel)
@ -3044,14 +3193,13 @@ static BOOL didCreatePixmaps;
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);
[self _sendRoot: window->root
type: generic.wintypes.win_type_atom
window: window->ident
data0: flag
data1: 0
data2: 0
data3: 0];
}
}
}
@ -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;
}
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,9 +3603,9 @@ 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);
@ -3472,9 +3615,43 @@ static BOOL didCreatePixmaps;
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

View file

@ -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]
@ -565,6 +577,11 @@ static NSString *xWaitMode = @"XPasteboardWaitMode";
if ([self data] == nil)
[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);
@ -799,6 +816,26 @@ xErrorHandler(Display *d, XErrorEvent *e)
[self setData: d];
}
}
else if (actual_type == XG_FILE_NAME)
{
NSArray *names;
NSData *d;
NSString *s;
NSURL *url;
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);
@ -1023,6 +1060,25 @@ xErrorHandler(Display *d, XErrorEvent *e)
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
{
@ -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];
}