001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Dmitry A. Durnev, Pavel Dolgov
019: * @version $Revision$
020: */package org.apache.harmony.awt.wtk.linux;
021:
022: import java.awt.Frame;
023: import java.awt.Insets;
024:
025: import org.apache.harmony.awt.wtk.CreationParams;
026:
027: import org.apache.harmony.awt.nativebridge.CLongPointer;
028: import org.apache.harmony.awt.nativebridge.Int32Pointer;
029: import org.apache.harmony.awt.nativebridge.Int8Pointer;
030: import org.apache.harmony.awt.nativebridge.NativeBridge;
031: import org.apache.harmony.awt.nativebridge.PointerPointer;
032: import org.apache.harmony.awt.nativebridge.VoidPointer;
033: import org.apache.harmony.awt.nativebridge.linux.X11;
034: import org.apache.harmony.awt.nativebridge.linux.X11Defs;
035:
036: /**
037: * Window manager specific actions
038: *
039: */
040: class WindowManager {
041:
042: private static final X11 x11 = X11.getInstance();
043: private static final NativeBridge bridge = NativeBridge
044: .getInstance();
045:
046: private final LinuxWindowFactory factory;
047: private final long display;
048:
049: //atom which defines window property type "ATOM"
050: private final long XA_ATOM;
051: private final long XA_CARDINAL;
052:
053: // WM hints
054: public final long WM_PROTOCOLS;
055: public final long WM_DELETE_WINDOW;
056: public final long WM_TAKE_FOCUS;
057:
058: // _NET extended hints
059: private final long NET_WM_WINDOW_TYPE;
060: private final long NET_WM_WINDOW_TYPE_DIALOG;
061: private final long NET_WM_WINDOW_TYPE_NORMAL;
062: private final long NET_REQUEST_FRAME_EXTENTS;
063: public final long NET_WM_STATE;
064: public final long NET_WM_STATE_HIDDEN;
065: public final long NET_WM_STATE_MAXIMIZED_VERT;
066: public final long NET_WM_STATE_MAXIMIZED_HORZ;
067: public final long NET_WM_STATE_ABOVE;
068: public final long NET_WM_STATE_STAYS_ON_TOP; // same as ABOVE, only for KDE
069: public final long NET_FRAME_EXTENTS;
070:
071: // KDE specific hints
072: private final long KDE_NET_WM_WINDOW_TYPE_OVERRIDE;
073: public final long KDE_NET_WM_FRAME_STRUT;
074:
075: // Motif hints (for Gnome)
076: private final long XA_MOTIF_WM_HINTS;
077:
078: private long focusedWindow;
079:
080: WindowManager(LinuxWindowFactory factory) {
081: this .factory = factory;
082: display = factory.getDisplay();
083:
084: XA_ATOM = internAtom("ATOM"); //$NON-NLS-1$
085: XA_CARDINAL = internAtom("CARDINAL"); //$NON-NLS-1$
086:
087: // WM hints
088: WM_PROTOCOLS = internAtom("WM_PROTOCOLS"); //$NON-NLS-1$
089: WM_DELETE_WINDOW = internAtom("WM_DELETE_WINDOW"); //$NON-NLS-1$
090: WM_TAKE_FOCUS = internAtom("WM_TAKE_FOCUS"); //$NON-NLS-1$
091:
092: // _NET extended hints
093: NET_WM_WINDOW_TYPE = internAtom("_NET_WM_WINDOW_TYPE"); //$NON-NLS-1$
094: NET_WM_WINDOW_TYPE_DIALOG = internAtom("_NET_WM_WINDOW_TYPE_DIALOG"); //$NON-NLS-1$
095: NET_WM_WINDOW_TYPE_NORMAL = internAtom("_NET_WM_WINDOW_TYPE_NORMAL"); //$NON-NLS-1$
096: NET_REQUEST_FRAME_EXTENTS = internAtom("_NET_REQUEST_FRAME_EXTENTS"); //$NON-NLS-1$
097: NET_WM_STATE = internAtom("_NET_WM_STATE"); //$NON-NLS-1$
098: NET_WM_STATE_HIDDEN = internAtom("_NET_WM_STATE_HIDDEN"); //$NON-NLS-1$
099: NET_WM_STATE_MAXIMIZED_VERT = internAtom("_NET_WM_STATE_MAXIMIZED_VERT"); //$NON-NLS-1$
100: NET_WM_STATE_MAXIMIZED_HORZ = internAtom("_NET_WM_STATE_MAXIMIZED_HORZ"); //$NON-NLS-1$
101: NET_WM_STATE_ABOVE = internAtom("_NET_WM_STATE_ABOVE"); //$NON-NLS-1$
102: NET_WM_STATE_STAYS_ON_TOP = internAtom("_NET_WM_STATE_STAYS_ON_TOP"); //$NON-NLS-1$
103: NET_FRAME_EXTENTS = internAtom("_NET_FRAME_EXTENTS"); //$NON-NLS-1$
104:
105: // KDE specific hints
106: KDE_NET_WM_WINDOW_TYPE_OVERRIDE = internAtom("_KDE_NET_WM_WINDOW_TYPE_OVERRIDE"); //$NON-NLS-1$
107: KDE_NET_WM_FRAME_STRUT = internAtom("_KDE_NET_WM_FRAME_STRUT"); //$NON-NLS-1$
108:
109: // Motif hints (for Gnome)
110: XA_MOTIF_WM_HINTS = internAtom("_MOTIF_WM_HINTS"); //$NON-NLS-1$
111: }
112:
113: long internAtom(String atomName) {
114: return factory.internAtom(atomName);
115: }
116:
117: String getAtomName(int atom) {
118: return factory.getAtomName(atom);
119: }
120:
121: void changeWindowProperty(long winId, long property, long type,
122: long values[]) {
123: int count = values.length;
124: Int8Pointer dataPtr = null;
125:
126: if (count > 0) {
127: CLongPointer data = bridge.createCLongPointer(count, false);
128: data.set(values, 0, count);
129: dataPtr = bridge.createInt8Pointer(data);
130: }
131:
132: x11.XChangeProperty(display, winId, property, type, 32,
133: X11Defs.PropModeReplace, dataPtr, count);
134: }
135:
136: void setDecorType(long winId, int decorType, boolean undecorated) {
137:
138: long styles[] = null;
139: if (undecorated
140: || decorType == CreationParams.DECOR_TYPE_UNDECOR) {
141: styles = new long[] { KDE_NET_WM_WINDOW_TYPE_OVERRIDE,
142: NET_WM_WINDOW_TYPE_NORMAL };
143:
144: } else if (decorType == CreationParams.DECOR_TYPE_FRAME) {
145: styles = new long[] { NET_WM_WINDOW_TYPE_NORMAL };
146:
147: } else if (decorType == CreationParams.DECOR_TYPE_DIALOG) {
148: styles = new long[] { NET_WM_WINDOW_TYPE_DIALOG };
149: }
150:
151: if (styles != null) {
152: changeWindowProperty(winId, NET_WM_WINDOW_TYPE, XA_ATOM,
153: styles);
154: }
155:
156: if (undecorated
157: || decorType == CreationParams.DECOR_TYPE_UNDECOR) {
158: long hint[] = new long[] { 2, 0, 0, 0, 0 };
159: changeWindowProperty(winId, XA_MOTIF_WM_HINTS,
160: XA_MOTIF_WM_HINTS, hint);
161: }
162: }
163:
164: /**
165: * Set non-geometry WM Hints, such as keyboard input,
166: * window group leader, icon, etc.
167: */
168: void setWMHints(int winId, int ownerID) {
169: long wmHintsPtr = x11.XAllocWMHints();
170: X11.XWMHints wmHints = x11.createXWMHints(wmHintsPtr);
171: int flags = X11Defs.InputHint;
172: wmHints.set_input(1);
173: if (ownerID != 0) {
174: flags |= X11Defs.WindowGroupHint;
175: wmHints.set_window_group(ownerID);//set group leader ID to owner
176: }
177: wmHints.set_flags(flags);
178: x11.XSetWMHints(display, winId, wmHintsPtr);
179: x11.XFree(wmHintsPtr);
180: }
181:
182: void setWMProtocols(long windowID) {
183: CLongPointer protocols = bridge.createCLongPointer(2, false);
184: protocols.set(0, WM_DELETE_WINDOW);
185: protocols.set(1, WM_TAKE_FOCUS);
186: x11.XSetWMProtocols(display, windowID, protocols, 2);
187: }
188:
189: void setInputAllowed(long winId, boolean allow) {
190: long wmHintsPtr = x11.XGetWMHints(display, winId);
191: if (wmHintsPtr == 0) {
192: wmHintsPtr = x11.XAllocWMHints();
193: }
194: X11.XWMHints wmHints = x11.createXWMHints(wmHintsPtr);
195: wmHints.set_input(allow ? 1 : 0);
196: wmHints.set_flags(wmHints.get_flags() | X11Defs.InputHint);
197: x11.XSetWMHints(display, winId, wmHintsPtr);
198:
199: x11.XFree(wmHintsPtr);
200: x11.XFlush(display);
201: }
202:
203: boolean isInputAllowed(long winId) {
204: long wmHintsPtr = x11.XGetWMHints(display, winId);
205: if (wmHintsPtr == 0) {
206: return true;
207: }
208: X11.XWMHints wmHints = x11.createXWMHints(wmHintsPtr);
209: long flags = wmHints.get_flags();
210: int input = wmHints.get_input();
211: x11.XFree(wmHintsPtr);
212: if ((flags & X11Defs.InputHint) == 0) {
213: return true;
214: }
215: return input != 0;
216: }
217:
218: /**
219: * @return
220: */
221: long getInputFocus() {
222: CLongPointer window = bridge.createCLongPointer(1, false);
223: Int32Pointer revertStatus = bridge.createInt32Pointer(1, false);
224: x11.XGetInputFocus(display, window, revertStatus);
225: return window.get(0);
226: }
227:
228: long getFocusedWindow() {
229: return focusedWindow;
230: }
231:
232: void setFocusedWindow(long windowId) {
233: focusedWindow = windowId;
234: }
235:
236: /**
237: *
238: * @param winId - the window ID
239: * @param resizable - the window should be resizable
240: * @param width - width to set if the window isn't resizable
241: * @param height - height to set if the window isn't resizable
242: */
243: void setResizableHint(long winId, boolean resizable, int x, int y,
244: int width, int height) {
245: setResizableHint(winId, resizable, false, x, y, width, height);
246: }
247:
248: void setResizableHint(long winId, boolean resizable,
249: boolean zoomed, int x, int y, int width, int height) {
250: final int MAX = Integer.MAX_VALUE;
251: boolean setMaxSize = (!resizable || (zoomed && (width < MAX) && (height < MAX)));
252: int flags = X11Defs.PMinSize | X11Defs.PSize
253: | X11Defs.PPosition | X11Defs.USSize
254: | X11Defs.USPosition;
255: long sizeHintsPtr = x11.XAllocSizeHints();
256: X11.XSizeHints sizeHints = x11.createXSizeHints(sizeHintsPtr);
257: sizeHints.set_flags(flags);
258:
259: //save old hints settings
260: x11.XGetWMNormalHints(display, winId, sizeHintsPtr,
261: sizeHintsPtr);
262: //sizeHints = x11.new XSizeHints(sizeHintsPtr);
263: if (!zoomed) {
264: sizeHints.set_min_height(!resizable ? height
265: : LinuxWindow.MIN_WINDOW_HEIGHT);
266: sizeHints.set_min_width(!resizable ? width
267: : LinuxWindow.MIN_WINDOW_WIDTH);
268:
269: // Dummy values for obsolete fields
270: sizeHints.set_x(x);
271: sizeHints.set_y(y);
272: sizeHints.set_width(width);
273: sizeHints.set_height(height);
274: }
275:
276: if (setMaxSize) {
277: flags |= X11Defs.PMaxSize;
278: sizeHints.set_max_height(height);
279: sizeHints.set_max_width(width);
280: }
281:
282: sizeHints.set_flags(flags);
283: x11.XSetWMNormalHints(display, winId, sizeHintsPtr);
284: x11.XFree(sizeHintsPtr);
285:
286: }
287:
288: /** register a window to receive notifications when WM frame extents
289: * (i. e. native insets) change
290: * @param winId - id of a window which wants to receive WM notifications
291: */
292: void requestFrameExtents(long winId) {
293: X11.XEvent event = createClientMessage(winId,
294: NET_REQUEST_FRAME_EXTENTS);
295: sendClientMessage(event);
296: }
297:
298: int sendClientMessage(X11.XEvent event) {
299: int mask = (X11Defs.SubstructureNotifyMask | X11Defs.SubstructureRedirectMask);
300: int status = x11.XSendEvent(display, factory.getRootWindow(),
301: X11Defs.False, mask, event);
302: return status;
303: }
304:
305: X11.XEvent createClientMessage(long winId, long msgType) {
306: X11.XEvent retEvent = x11.createXEvent(false);
307: X11.XClientMessageEvent cme = retEvent.get_xclient();
308: cme.set_display(display);
309: cme.set_window(winId);
310: cme.set_message_type(msgType);
311: cme.set_type(X11Defs.ClientMessage);
312: cme.set_format(32);
313: return retEvent;
314: }
315:
316: int changeWindowState(LinuxWindow wnd, int action, long[] properties) {
317: int mapState = wnd.getMapState();
318: long winId = wnd.getId();
319: if (mapState == X11Defs.IsUnmapped) {
320:
321: long[] props = getStateProps(wnd);
322: if (props != null) {
323: changeWindowProperty(winId, NET_WM_STATE, XA_ATOM,
324: props);
325: }
326: return 0;
327: }
328: X11.XEvent clientEvent = createClientMessage(winId,
329: NET_WM_STATE);
330: CLongPointer data = clientEvent.get_xclient().get_l();
331: data.set(0, action); // add/remove/toggle
332: for (int i = 0; i < properties.length; i++) {
333: data.set(i + 1, properties[i]);
334: }
335: return sendClientMessage(clientEvent);
336: }
337:
338: /**
339: * @param wnd
340: * @return
341: */
342: private long[] getStateProps(LinuxWindow wnd) {
343: long props[] = new long[] { 0l, 0l, 0l, 0l };
344: int state = wnd.getCurrentState();
345: int k = 0;
346: if ((state & Frame.MAXIMIZED_HORIZ) != 0) {
347: props[k++] = NET_WM_STATE_MAXIMIZED_HORZ;
348: }
349: if ((state & Frame.MAXIMIZED_VERT) != 0) {
350: props[k++] = NET_WM_STATE_MAXIMIZED_VERT;
351: }
352: if ((state & Frame.ICONIFIED) != 0) {
353: props[k++] = NET_WM_STATE_HIDDEN;
354: }
355: if (wnd.alwaysOnTop) {
356: props[k++] = NET_WM_STATE_ABOVE;
357: }
358: long[] ret = new long[k];
359: System.arraycopy(props, 0, ret, 0, k);
360: return ret;
361: }
362:
363: long[] getSupportedHints() {
364: final long NET_SUPPORTED = internAtom("_NET_SUPPORTED"); //$NON-NLS-1$
365: long root = factory.getRootWindow();
366: long[] supportedHints = getWindowProperty(root, NET_SUPPORTED);
367: return supportedHints;
368: }
369:
370: private long[] getWindowProperty(long winId, final long propertyAtom) {
371: CLongPointer type = bridge.createCLongPointer(1, false);
372: Int32Pointer formatPtr = bridge.createInt32Pointer(1, false);
373: CLongPointer nItemsPtr = bridge.createCLongPointer(1, false);
374: CLongPointer bytesRemaining = bridge.createCLongPointer(1,
375: false);
376: PointerPointer data = bridge.createPointerPointer(1, false);
377:
378: final int anyType = X11Defs.AnyPropertyType;
379: x11.XGetWindowProperty(display, winId, propertyAtom, 0, 1,
380: anyType, X11Defs.False, type, formatPtr, nItemsPtr,
381: bytesRemaining, data);
382: VoidPointer dataPtr = data.get(0);
383: if (dataPtr == null) {
384: return null;
385: }
386: x11.XFree(dataPtr);
387: long nBytes = bytesRemaining.get(0);
388: long typeAtom = type.get(0);
389: if (typeAtom == X11Defs.None) {
390: // the property doesn't exist
391: return null;
392: }
393: int bitFormat = formatPtr.get(0);
394: long nItems = (nBytes + 4) * 8 / bitFormat;
395: long n32bitItems = nItems / (32 / bitFormat);
396: x11.XGetWindowProperty(display, winId, propertyAtom, 0,
397: n32bitItems, anyType, X11Defs.False, type, formatPtr,
398: nItemsPtr, bytesRemaining, data);
399:
400: nBytes = bytesRemaining.get(0);
401: assert nBytes == 0;
402: //read the data:
403: int itemSize = bitFormat / 8;
404: CLongPointer dataArray = bridge.createCLongPointer(data.get(0));
405: long[] props = new long[(int) nItems];
406: for (int i = 0; i < nItems; i++) {
407: int item = 0;
408: if (itemSize == 4) {
409: item = (int) dataArray.get(i);
410: } else if (itemSize == 2) {
411: item = (short) dataArray.get(i);
412: } else if (itemSize == 1) {
413: item = (byte) dataArray.get(i);
414: }
415: props[i] = item;
416: }
417: x11.XFree(dataPtr);
418:
419: return props;
420: }
421:
422: /**
423: * @return array of child window id's
424: * (for a top-level this maybe a WM frame window)
425: */
426: long[] getChildrenIDs(long windowID) {
427: if (windowID == 0) {
428: return new long[0];
429: }
430:
431: CLongPointer root = bridge.createCLongPointer(1, false);
432: CLongPointer parent = bridge.createCLongPointer(1, false);
433: PointerPointer childrenArray = bridge.createPointerPointer(1,
434: false);
435: Int32Pointer childrenCount = bridge
436: .createInt32Pointer(1, false);
437: ;
438: x11.XQueryTree(display, windowID, root, parent, childrenArray,
439: childrenCount);
440:
441: int count = childrenCount.get(0);
442: CLongPointer children = bridge.createCLongPointer(childrenArray
443: .get(0));
444: if (children == null) {
445: return new long[0];
446: }
447: long[] result = new long[count];
448: children.get(result, 0, count);
449: x11.XFree(children);
450: return result;
451: }
452:
453: int setIcon(long windowID, long pixmap, int mask) {
454: // save old WM hints
455: long wmHintsPtr = x11.XGetWMHints(display, windowID);
456: if (wmHintsPtr == 0) {
457: wmHintsPtr = x11.XAllocWMHints();
458: }
459: X11.XWMHints wmHints = x11.createXWMHints(wmHintsPtr);
460: int flags = (int) wmHints.get_flags() | X11Defs.IconPixmapHint;
461: wmHints.set_icon_pixmap(pixmap);
462: if (mask != 0) {
463: flags |= X11Defs.IconMaskHint;
464: wmHints.set_icon_mask(mask);
465: }
466: wmHints.set_flags(flags);
467: int result = x11.XSetWMHints(display, windowID, wmHintsPtr);
468: x11.XFree(wmHintsPtr);
469: return result;
470: }
471:
472: /**
473: *
474: * @param windowId - native window ID
475: * @param property - could be NET_FRAME_EXTENTS or KDE_NET_WM_FRAME_STRUT
476: * @return native insets set by Window Manager, or null if Window manager
477: * doesn't support this property or property has unexpected formats
478: */
479: Insets getNativeInsets(long windowId, long property) {
480: Insets insets = null;
481:
482: CLongPointer actualTypeReturn = bridge.createCLongPointer(1,
483: false);
484: Int32Pointer actualFormatReturn = bridge.createInt32Pointer(1,
485: false);
486: CLongPointer nitemsReturn = bridge.createCLongPointer(1, false);
487: CLongPointer bytesAfterReturn = bridge.createCLongPointer(1,
488: false);
489: PointerPointer propReturn = bridge.createPointerPointer(1,
490: false);
491:
492: int result = x11.XGetWindowProperty(factory.getDisplay(),
493: windowId, property, 0, 4, X11Defs.FALSE,
494: X11Defs.AnyPropertyType, actualTypeReturn,
495: actualFormatReturn, nitemsReturn, bytesAfterReturn,
496: propReturn);
497:
498: if (result == X11Defs.Success) {
499: long nItems = nitemsReturn.get(0);
500: long actualType = actualTypeReturn.get(0);
501: int actualFormat = actualFormatReturn.get(0);
502: CLongPointer ptrData = bridge.createCLongPointer(propReturn
503: .get(0));
504: if (ptrData == null) {
505: return insets;
506: }
507:
508: if ((nItems == 4) && (actualType == XA_CARDINAL)
509: && (actualFormat == 32)) {
510:
511: insets = new Insets(0, 0, 0, 0);
512: insets.left = (int) ptrData.get(0);
513: insets.right = (int) ptrData.get(1);
514: insets.top = (int) ptrData.get(2);
515: insets.bottom = (int) ptrData.get(3);
516: }
517: x11.XFree(ptrData);
518: }
519:
520: return insets;
521: }
522: }
|