/* Copyright (c) 2008 Stefan Endrullis, All Rights Reserved
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* <p/>
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package jnacontrib.x11.api;
import com.sun.jna.Native;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import com.sun.jna.Memory;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.NativeLongByReference;
import com.sun.jna.ptr.PointerByReference;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.ArrayList;
import java.awt.Rectangle;
import com.sun.jna.platform.unix.X11;
import com.sun.jna.platform.unix.X11.Atom;
import com.sun.jna.platform.unix.X11.WindowByReference;
/**
* Object oriented X window system.
* <p/>
* Some code segments in this class are based on the code of the program
* wmctrl (licensed under GPLv2) written by Tomas Styblo <tripie@cpan.org>.
* Thanks a lot, Tomas!
*
* @author Stefan Endrullis
*/
public class X {
/** Remove/unset property. */
public static final int _NET_WM_STATE_REMOVE = 0;
/** Add/set property. */
public static final int _NET_WM_STATE_ADD = 1;
/** Toggle property. */
public static final int _NET_WM_STATE_TOGGLE = 2;
/** Maximal property value length. */
public static final int MAX_PROPERTY_VALUE_LEN = 4096;
private static final X11 x11 = X11.INSTANCE;
private static int bytesToInt(byte[] prop) {
return ((prop[3] & 0xff) << 24)
| ((prop[2] & 0xff) << 16)
| ((prop[1] & 0xff) << 8)
| ((prop[0] & 0xff));
}
private static int bytesToInt(byte[] prop, int offset) {
return ((prop[3 + offset] & 0xff) << 24)
| ((prop[2 + offset] & 0xff) << 16)
| ((prop[1 + offset] & 0xff) << 8)
| ((prop[offset] & 0xff));
}
/**
* X Display.
*/
public static class Display {
/**
* Open display.
*/
private X11.Display x11Display;
/**
* HashMap<String,X11.Atom>.
*/
private HashMap<String, Atom> atomsHash = new HashMap<String, Atom>();
/**
* Creates the OOWindowUtils using the default display.
*/
public Display() {
x11Display = x11.XOpenDisplay(null);
if (x11Display == null) {
throw new Error("Can't open X Display");
}
}
/**
* Creates the OOWindowUtils using a given display.
*
* @param x11Display open display
*/
public Display(X11.Display x11Display) {
this.x11Display = x11Display;
if (x11Display == null) {
throw new Error("X Display is null");
}
}
/**
* Closes the display.
*/
public void close() {
x11.XCloseDisplay(x11Display);
}
/**
* Flushes the output buffer / event queue.
*/
public void flush() {
x11.XFlush(x11Display);
}
/**
* Returns the X11 display.
*
* @return X11 display
*/
public X11.Display getX11Display() {
return x11Display;
}
/**
* Get internal atoms by name.
*
* @param name name of the atom
* @return atom
*/
public X11.Atom getAtom(String name) {
X11.Atom atom = atomsHash.get(name);
if (atom == null) {
atom = x11.XInternAtom(x11Display, name, false);
atomsHash.put(name, atom);
}
return atom;
}
/**
* Returns the window manager information as an window.
*
* @return window manager information as an window
* @throws X11Exception thrown if X11 window errors occurred
*/
public Window getWindowManagerInfo() throws X11Exception {
Window rootWindow = getRootWindow();
try {
return rootWindow.getWindowProperty(X11.XA_WINDOW, "_NET_SUPPORTING_WM_CHECK");
} catch (X11Exception e) {
try {
return rootWindow.getWindowProperty(X11.XA_CARDINAL, "_WIN_SUPPORTING_WM_CHECK");
} catch (X11Exception e1) {
throw new X11Exception("Cannot get window manager info properties. (_NET_SUPPORTING_WM_CHECK or _WIN_SUPPORTING_WM_CHECK)");
}
}
}
/**
* Returns the root window.
*
* @return root window
*/
public Window getRootWindow() {
return new Window(this, x11.XDefaultRootWindow(x11Display));
}
/**
* Returns the current active window.
*
* @return current active window
* @throws X11Exception thrown if X11 window errors occurred
*/
public Window getActiveWindow() throws X11Exception {
return getRootWindow().getWindowProperty(X11.XA_WINDOW, "_NET_ACTIVE_WINDOW");
}
/**
* Returns all windows managed by the window manager.
*
* @return all windows managed by the window manager
* @throws X11Exception thrown if X11 window errors occurred
*/
public Window[] getWindows() throws X11Exception {
byte[] bytes;
Window rootWindow = getRootWindow();
try {
bytes = rootWindow.getProperty(X11.XA_WINDOW, "_NET_CLIENT_LIST");
} catch (X11Exception e) {
try {
bytes = rootWindow.getProperty(X11.XA_CARDINAL, "_WIN_CLIENT_LIST");
} catch (X11Exception e1) {
throw new X11Exception("Cannot get client list properties (_NET_CLIENT_LIST or _WIN_CLIENT_LIST)");
}
}
Window[] windowList = new Window[bytes.length / X11.Window.SIZE];
for (int i = 0; i < windowList.length; i++) {
windowList[i] = new Window(this, new X11.Window(bytesToInt(bytes, X11.XID.SIZE * i)));
}
return windowList;
}
/**
* Returns the number of desktops.
*
* @return number of desktops
* @throws X11Exception thrown if X11 window errors occurred
*/
public int getDesktopCount() throws X11Exception {
Window root = getRootWindow();
try {
return root.getIntProperty(X11.XA_CARDINAL, "_NET_NUMBER_OF_DESKTOPS");
} catch (X11Exception e) {
try {
return root.getIntProperty(X11.XA_CARDINAL, "_WIN_WORKSPACE_COUNT");
} catch (X11Exception e1) {
throw new X11Exception("Cannot get number of desktops properties (_NET_NUMBER_OF_DESKTOPS or _WIN_WORKSPACE_COUNT)");
}
}
}
/**
* Returns the number of the active desktop.
*
* @return number of the active desktop
* @throws X11Exception thrown if X11 window errors occurred
*/
public int getActiveDesktopNumber() throws X11Exception {
Window root = getRootWindow();
int cur_desktop;
try {
cur_desktop = root.getIntProperty(X11.XA_CARDINAL, "_NET_CURRENT_DESKTOP");
} catch (X11Exception e) {
try {
cur_desktop = root.getIntProperty(X11.XA_CARDINAL, "_WIN_WORKSPACE");
} catch (X11Exception e1) {
throw new X11Exception("Cannot get current desktop properties (_NET_CURRENT_DESKTOP or _WIN_WORKSPACE property)");
}
}
return cur_desktop;
}
/**
* Returns the available desktops.
*
* @return available desktops
* @throws X11Exception thrown if X11 window errors occurred
*/
public Desktop[] getDesktops() throws X11Exception {
Window root = getRootWindow();
String[] desktopNames;
try {
desktopNames = root.getUtf8ListProperty(getAtom("UTF8_STRING"), "_NET_DESKTOP_NAMES");
} catch (X11Exception e) {
try {
desktopNames = root.getStringListProperty(X11.XA_STRING, "_WIN_WORKSPACE_NAMES");
} catch (X11Exception e1) {
throw new X11Exception("Cannot get desktop names properties (_NET_DESKTOP_NAMES or _WIN_WORKSPACE_NAMES)");
}
}
Desktop[] desktops = new Desktop[getDesktopCount()];
for (int i = 0; i < desktops.length; i++) {
desktops[i] = new Desktop(this, i, desktopNames[i]);
}
return desktops;
}
/**
* Switches to the given desktop.
*
* @param nr desktop number
* @throws X11Exception thrown if X11 window errors occurred
*/
public void switchDesktop(int nr) throws X11Exception {
getRootWindow().clientMsg("_NET_CURRENT_DESKTOP", nr, 0, 0, 0, 0);
}
/**
* Sets the "showing the desktop" state.
*
* @param state true if the desktop should be shown
* @throws X11Exception thrown if X11 window errors occurred
*/
public void showingDesktop(boolean state) throws X11Exception {
getRootWindow().clientMsg("_NET_SHOWING_DESKTOP", state ? 1 : 0, 0, 0, 0, 0);
}
/**
* Enables / disables the auto-repeat of pressed keys.
*
* @param on true if auto-repeat shall be enabled
*/
public void setKeyAutoRepeat(boolean on) {
if (on) {
x11.XAutoRepeatOn(x11Display);
} else {
x11.XAutoRepeatOff(x11Display);
}
}
/**
* Returns the key symbol corresponding to the the key name.
*
* @param keyName name of the key
* @return key symbol
*/
public X11.KeySym getKeySym(String keyName) {
return x11.XStringToKeysym(keyName);
}
/**
* Returns the key symbol corresponding to the keycode.
*
* @param keyCode keycode
* @param index element of the keycode vector
* @return key symbol
*/
public X11.KeySym getKeySym(byte keyCode, int index) {
return x11.XKeycodeToKeysym(x11Display, keyCode, index);
}
/**
* Returns the keycode corresponding to the key symbol.
*
* @param keySym key symbol
* @return keycode
*/
public byte getKeyCode(X11.KeySym keySym) {
return x11.XKeysymToKeycode(x11Display, keySym);
}
/**
* Returns the keycode corresponding to the key name.
*
* @param keyName name of the key
* @return keycode
*/
public byte getKeyCode(String keyName) {
return x11.XKeysymToKeycode(x11Display, getKeySym(keyName));
}
/**
* Returns the key name corresponding to the key symbol.
*
* @param keySym key symbol
* @return name of the key
*/
public String getKeyName(X11.KeySym keySym) {
return x11.XKeysymToString(keySym);
}
/**
* Returns the key name corresponding to the keycode and the index in the keycode vector.
*
* @param keyCode keycode
* @param index index in the keycode vector
* @return name of the key
*/
public String getKeyName(byte keyCode, int index) {
return getKeyName(getKeySym(keyCode, index));
}
/**
* Returns the modifier keymap.
*
* @return modifier keymap
*/
public ModifierKeymap getModifierKeymap() {
X11.XModifierKeymapRef xModifierKeymapRef = x11.XGetModifierMapping(x11Display);
ModifierKeymap modifierKeymap = new ModifierKeymap(xModifierKeymapRef);
x11.XFreeModifiermap(xModifierKeymapRef);
return modifierKeymap;
}
/**
* Sets the modifier keymap.
*
* @param modifierKeymap modifier keymap
*/
public void setModifierKeymap(ModifierKeymap modifierKeymap) {
X11.XModifierKeymapRef xModifierKeymapRef = modifierKeymap.toXModifierKeyamp();
x11.XSetModifierMapping(x11Display,xModifierKeymapRef);
}
}
/**
* Modifier keymap. The lists shift, lock, control, mod1, mod1, mod1, mod1, mod1
* contain the keycodes as Byte objects. You can directly access these lists to
* read, replace, remove or insert new keycodes to these modifiers.
* To apply a new modifier keymap call
* {@link X.Display#setModifierKeymap(ModifierKeymap)}.
*/
public static class ModifierKeymap {
/** Shift modifier as an ArrayList<Byte>. */
public ArrayList<Byte> shift = new ArrayList<Byte>(4);
/** Lock modifier as an ArrayList<Byte>. */
public ArrayList<Byte> lock = new ArrayList<Byte>(4);
/** Control modifier as an ArrayList<Byte>. */
public ArrayList<Byte> control = new ArrayList<Byte>(4);
/** Mod1 modifier as an ArrayList<Byte>. */
public ArrayList<Byte> mod1 = new ArrayList<Byte>(4);
/** Mod2 modifier as an ArrayList<Byte>. */
public ArrayList<Byte> mod2 = new ArrayList<Byte>(4);
/** Mod3 modifier as an ArrayList<Byte>. */
public ArrayList<Byte> mod3 = new ArrayList<Byte>(4);
/** Mod4 modifier as an ArrayList<Byte>. */
public ArrayList<Byte> mod4 = new ArrayList<Byte>(4);
/** Mod5 modifier as an ArrayList<Byte>. */
public ArrayList<Byte> mod5 = new ArrayList<Byte>(4);
/**
* Creates an empty modifier keymap.
*/
public ModifierKeymap() {
}
/**
* Creates a modifier keymap and reads the modifiers from the XModifierKeymap.
*
* @param xModifierKeymapRef XModifierKeymap
*/
public ModifierKeymap(X11.XModifierKeymapRef xModifierKeymapRef) {
fromXModifierKeymap(xModifierKeymapRef);
}
/**
* Reads all modifiers from the XModifierKeymap.
*
* @param xModifierKeymapRef XModifierKeymap
*/
public void fromXModifierKeymap(X11.XModifierKeymapRef xModifierKeymapRef) {
int count = xModifierKeymapRef.max_keypermod;
byte[] keys = xModifierKeymapRef.modifiermap.getByteArray(0, 8*count);
ArrayList<Byte>[] allModifiers = getAllModifiers();
for (int modNr = 0; modNr < 8; modNr++) {
ArrayList<Byte> modifier = allModifiers[modNr];
modifier.clear();
for (int keyNr = 0; keyNr < count; keyNr++) {
byte key = keys[modNr*count + keyNr];
if (key != 0) {
modifier.add(new Byte(key));
}
}
}
}
/**
* Returns an XModifierKeymap corresponding to this object.
*
* @return XModifierKeymap
*/
public X11.XModifierKeymapRef toXModifierKeyamp() {
ArrayList<Byte>[] allModifiers = getAllModifiers();
// determine max list size
int count = 0;
for (int i = 0; i < allModifiers.length; i++) {
count = Math.max(count, allModifiers[i].size());
}
byte[] keys = new byte[8*count];
for (int modNr = 0; modNr < 8; modNr++) {
ArrayList<Byte> modifier = allModifiers[modNr];
for (int keyNr = 0; keyNr < modifier.size(); keyNr++) {
keys[modNr*count + keyNr] = modifier.get(keyNr).byteValue();
}
}
X11.XModifierKeymapRef xModifierKeymapRef = new X11.XModifierKeymapRef();
xModifierKeymapRef.max_keypermod = count;
xModifierKeymapRef.modifiermap = new Memory(keys.length);
xModifierKeymapRef.modifiermap.write(0, keys, 0, keys.length);
return xModifierKeymapRef;
}
/**
* Returns all modifiers as an array.
*
* @return array of modifier lists
*/
public ArrayList<Byte>[] getAllModifiers() {
return new ArrayList[] {
shift, lock, control, mod1, mod2, mod3, mod4, mod5
};
}
}
/**
* X Desktop.
*/
public static class Desktop {
public X.Display display;
public int number;
public String name;
public Desktop(Display display, int number, String name) {
this.display = display;
this.number = number;
this.name = name;
}
}
/**
* X Window.
*/
public static class Window {
private X.Display display;
private X11.Window x11Window;
/**
* Returns the X11 window object.
*
* @return X11 window
*/
public X11.Window getX11Window() {
return x11Window;
}
/**
* Returns the ID of the window.
*
* @return window ID
*/
public int getID() {
return x11Window.intValue();
}
/**
* Creates the window.
*
* @param display display where the window is allocated
* @param x11Window X11 window
*/
public Window(X.Display display, X11.Window x11Window) {
this.display = display;
this.x11Window = x11Window;
}
/**
* Returns the title of the window.
*
* @return title of the window
* @throws X11Exception thrown if X11 window errors occurred
*/
public String getTitle() throws X11Exception {
try {
return getUtf8Property(display.getAtom("UTF8_STRING"), "_NET_WM_NAME");
} catch (X11Exception e) {
return getUtf8Property(X11.XA_STRING, X11.XA_WM_NAME);
}
}
/**
* Returns the window class.
*
* @return window class
* @throws X11Exception thrown if X11 window errors occurred
*/
public String getWindowClass() throws X11Exception {
return getUtf8Property(X11.XA_STRING, X11.XA_WM_CLASS);
}
/**
* Returns the PID of the window.
*
* @return PID of the window
* @throws X11Exception thrown if X11 window errors occurred
*/
public Integer getPID() throws X11Exception {
return getIntProperty(X11.XA_CARDINAL, "_NET_WM_PID");
}
/**
* Returns the desktop ID of the window.
*
* @return desktop ID of the window
* @throws X11Exception thrown if X11 window errors occurred
*/
public int getDesktop() throws X11Exception {
try {
return getIntProperty(X11.XA_CARDINAL, "_NET_WM_DESKTOP");
} catch (X11Exception e) {
return getIntProperty(X11.XA_CARDINAL, "_WIN_WORKSPACE");
}
}
/**
* Returns the client machine name of the window.
*
* @return client machine name of the window
* @throws X11Exception thrown if X11 window errors occurred
*/
public String getMachine() throws X11Exception {
return getStringProperty(X11.XA_STRING, "WM_CLIENT_MACHINE");
}
/**
* Returns the XWindowAttributes of the window.
*
* @return XWindowAttributes of the window
*/
public X11.XWindowAttributes getXWindowAttributes() {
X11.XWindowAttributes xwa = new X11.XWindowAttributes();
x11.XGetWindowAttributes(display.x11Display, x11Window, xwa);
return xwa;
}
/**
* Returns the geometry of the window.
*
* @return geometry of the window
*/
public Geometry getGeometry() {
X11.WindowByReference junkRoot = new X11.WindowByReference();
IntByReference junkX = new IntByReference();
IntByReference junkY = new IntByReference();
IntByReference x = new IntByReference();
IntByReference y = new IntByReference();
IntByReference width = new IntByReference();
IntByReference height = new IntByReference();
IntByReference borderWidth = new IntByReference();
IntByReference depth = new IntByReference();
x11.XGetGeometry(display.x11Display, x11Window, junkRoot, junkX, junkY, width, height, borderWidth, depth);
x11.XTranslateCoordinates(display.x11Display, x11Window, junkRoot.getValue(), junkX.getValue(),
junkY.getValue(), x, y, junkRoot);
return new Geometry(x.getValue(), y.getValue(), width.getValue(), height.getValue(),
borderWidth.getValue(), depth.getValue());
}
/**
* Returns the bounding box of the window.
*
* @return bounding box of the window
*/
public Rectangle getBounds() {
X11.WindowByReference junkRoot = new X11.WindowByReference();
IntByReference junkX = new IntByReference();
IntByReference junkY = new IntByReference();
IntByReference x = new IntByReference();
IntByReference y = new IntByReference();
IntByReference width = new IntByReference();
IntByReference height = new IntByReference();
IntByReference border_width = new IntByReference();
IntByReference depth = new IntByReference();
x11.XGetGeometry(display.x11Display, x11Window, junkRoot, junkX, junkY, width, height, border_width, depth);
x11.XTranslateCoordinates(display.x11Display, x11Window, junkRoot.getValue(), junkX.getValue(),
junkY.getValue(), x, y, junkRoot);
int xVal = x.getValue();
int yVal = y.getValue();
return new Rectangle(xVal, yVal, xVal + width.getValue(), yVal + height.getValue());
}
/**
* Activates the window.
*
* @throws X11Exception thrown if X11 window errors occurred
*/
public void activate() throws X11Exception {
clientMsg("_NET_ACTIVE_WINDOW", 0, 0, 0, 0, 0);
x11.XMapRaised(display.x11Display, x11Window);
}
/**
* Moves the window to the specified desktop.
*
* @param desktopNr desktop
* @return X11.SUCCESS if closing was successful
* @throws X11Exception thrown if X11 window errors occurred
*/
public int moveToDesktop(int desktopNr) throws X11Exception {
return clientMsg("_NET_WM_DESKTOP", desktopNr, 0, 0, 0, 0);
}
/**
* Selects the input events to listen for.
*
* @param eventMask event mask representing the events to listen for
*/
public void selectInput(int eventMask) {
x11.XSelectInput(display.x11Display, x11Window, new NativeLong(eventMask));
}
public int nextEvent(X11.XEvent event) {
return x11.XNextEvent(display.x11Display, event);
}
public void sendEvent(int eventMask, X11.XEvent event) {
x11.XSendEvent(display.x11Display, x11Window, 1, new NativeLong(eventMask), event);
}
/**
* Closes the window gracefully.
*
* @return X11.SUCCESS if closing was successful
* @throws X11Exception thrown if X11 window errors occurred
*/
public int close() throws X11Exception {
return clientMsg("_NET_CLOSE_WINDOW", 0, 0, 0, 0, 0);
}
/**
* Returns the property value as integer.
*
* @param xa_prop_type property type
* @param xa_prop_name property name
* @return property value as integer or null if not found
* @throws X11Exception thrown if X11 window errors occurred
*/
public Integer getIntProperty(X11.Atom xa_prop_type, X11.Atom xa_prop_name) throws X11Exception {
byte[] property = getProperty(xa_prop_type, xa_prop_name);
if( property == null ){
return null;
}
return bytesToInt(property);
}
/**
* Returns the property value as integer.
*
* @param xa_prop_type property type
* @param xa_prop_name property name
* @return property value as integer
* @throws X11Exception thrown if X11 window errors occurred
*/
public Integer getIntProperty(X11.Atom xa_prop_type, String xa_prop_name) throws X11Exception {
return getIntProperty(xa_prop_type, display.getAtom(xa_prop_name));
}
/**
* Returns the property value as window.
*
* @param xa_prop_type property type
* @param xa_prop_name property name
* @return property value as window, or null if not found
* @throws X11Exception thrown if X11 window errors occurred
*/
public Window getWindowProperty(X11.Atom xa_prop_type, X11.Atom xa_prop_name) throws X11Exception {
Integer windowId = getIntProperty(xa_prop_type, xa_prop_name);
if( windowId == null ){
return null;
}
X11.Window x11Window = new X11.Window(windowId);
return new Window(display, x11Window);
}
/**
* Returns the property value as window.
*
* @param xa_prop_type property type
* @param xa_prop_name property name
* @return property value as window
* @throws X11Exception thrown if X11 window errors occurred
*/
public Window getWindowProperty(X11.Atom xa_prop_type, String xa_prop_name) throws X11Exception {
return getWindowProperty(xa_prop_type, display.getAtom(xa_prop_name));
}
/**
* Returns the property value as a null terminated byte array.
*
* @param xa_prop_type property type
* @param xa_prop_name property name
* @return property value as a null terminated byte array, or null if not found
* @throws X11Exception thrown if X11 window errors occurred
*/
public byte[] getNullTerminatedProperty(X11.Atom xa_prop_type, X11.Atom xa_prop_name) throws X11Exception {
byte[] bytesOrig = getProperty(xa_prop_type, xa_prop_name);
byte[] bytesDest;
if( bytesOrig == null ){
return null;
}
// search for '\0'
int i;
for (i = 0; i < bytesOrig.length; i++) {
if (bytesOrig[i] == '\0') break;
}
if (i < bytesOrig.length - 1) {
bytesDest = new byte[i + 1];
System.arraycopy(bytesOrig, 0, bytesDest, 0, i + 1);
} else {
bytesDest = bytesOrig;
}
return bytesDest;
}
/**
* Returns the property value as a null terminated byte array.
*
* @param xa_prop_type property type
* @param xa_prop_name property name
* @return property value as a null terminated byte array
* @throws X11Exception thrown if X11 window errors occurred
*/
public byte[] getNullTerminatedProperty(X11.Atom xa_prop_type, String xa_prop_name) throws X11Exception {
return getNullTerminatedProperty(xa_prop_type, display.getAtom(xa_prop_name));
}
/**
* Returns the property value as byte array where every '\0' character is replaced by '.'.
*
* @param xa_prop_type property type
* @param xa_prop_name property name
* @return property value as byte array where every '\0' character is replaced by '.'. null if the property was not found
* @throws X11Exception thrown if X11 window errors occurred
*/
public byte[] getNullReplacedStringProperty(X11.Atom xa_prop_type, X11.Atom xa_prop_name) throws X11Exception {
byte[] bytes = getProperty(xa_prop_type, xa_prop_name);
if( bytes == null ){
return null;
}
// search for '\0'
int i;
for (i = 0; i < bytes.length; i++) {
if (bytes[i] == '\0') {
bytes[i] = '.';
}
}
return bytes;
}
/**
* Returns the property value as byte array where every '\0' character is replaced by '.'.
*
* @param xa_prop_type property type
* @param xa_prop_name property name
* @return property value as byte array where every '\0' character is replaced by '.'
* @throws X11Exception thrown if X11 window errors occurred
*/
public byte[] getNullReplacedStringProperty(X11.Atom xa_prop_type, String xa_prop_name) throws X11Exception {
return getNullReplacedStringProperty(xa_prop_type, display.getAtom(xa_prop_name));
}
/**
* Returns the property value as string where every '\0' character is replaced by '.'.
*
* @param xa_prop_type property type
* @param xa_prop_name property name
* @return property value as string where every '\0' character is replaced by '.'
* @throws X11Exception thrown if X11 window errors occurred
*/
public String getStringProperty(X11.Atom xa_prop_type, X11.Atom xa_prop_name) throws X11Exception {
return new String(getNullReplacedStringProperty(xa_prop_type, xa_prop_name));
}
/**
* Returns the property value as string where every '\0' character is replaced by '.'.
*
* @param xa_prop_type property type
* @param xa_prop_name property name
* @return property value as string where every '\0' character is replaced by '.'
* @throws X11Exception thrown if X11 window errors occurred
*/
public String getStringProperty(X11.Atom xa_prop_type, String xa_prop_name) throws X11Exception {
return new String(getNullReplacedStringProperty(xa_prop_type, xa_prop_name));
}
/**
* Returns the property value as string list.
*
* @param xa_prop_type property type
* @param xa_prop_name property name
* @return property value as string list
* @throws X11Exception thrown if X11 window errors occurred
*/
public String[] getStringListProperty(X11.Atom xa_prop_type, X11.Atom xa_prop_name) throws X11Exception {
return new String(getProperty(xa_prop_type, xa_prop_name)).split("\0");
}
/**
* Returns the property value as string list.
*
* @param xa_prop_type property type
* @param xa_prop_name property name
* @return property value as string list, or null if the property value does not exist
* @throws X11Exception thrown if X11 window errors occurred
*/
public String[] getStringListProperty(X11.Atom xa_prop_type, String xa_prop_name) throws X11Exception {
byte[] property = getProperty(xa_prop_type, xa_prop_name);
if( property == null ){
return null;
}
return new String(property).split("\0");
}
/**
* Returns the property value as UTF8 string where every '\0' character is replaced by '.'.
*
* @param xa_prop_type property type
* @param xa_prop_name property name
* @return property value as UTF8 string where every '\0' character is replaced by '.'
* @throws X11Exception thrown if X11 window errors occurred
*/
public String getUtf8Property(X11.Atom xa_prop_type, X11.Atom xa_prop_name) throws X11Exception {
try {
byte[] property = getNullReplacedStringProperty(xa_prop_type, xa_prop_name);
if( property == null ){
return null;
}
return new String(property, "UTF8");
} catch (UnsupportedEncodingException e) {
throw new X11Exception(e);
}
}
/**
* Returns the property value as UTF8 string where every '\0' character is replaced by '.'.
*
* @param xa_prop_type property type
* @param xa_prop_name property name
* @return property value as UTF8 string where every '\0' character is replaced by '.'
* @throws X11Exception thrown if X11 window errors occurred
*/
public String getUtf8Property(X11.Atom xa_prop_type, String xa_prop_name) throws X11Exception {
return getUtf8Property(xa_prop_type, display.getAtom(xa_prop_name));
}
/**
* Returns the property value as UTF8 string list
*
* @param xa_prop_type property type
* @param xa_prop_name property name
* @return property value as UTF8 string list
* @throws X11Exception thrown if X11 window errors occurred
*/
public String[] getUtf8ListProperty(X11.Atom xa_prop_type, X11.Atom xa_prop_name) throws X11Exception {
try {
return new String(getProperty(xa_prop_type, xa_prop_name), "UTF8").split("\0");
} catch (UnsupportedEncodingException e) {
throw new X11Exception(e);
}
}
/**
* Returns the property value as UTF8 string list
*
* @param xa_prop_type property type
* @param xa_prop_name property name
* @return property value as UTF8 string list
* @throws X11Exception thrown if X11 window errors occurred
*/
public String[] getUtf8ListProperty(X11.Atom xa_prop_type, String xa_prop_name) throws X11Exception {
return getUtf8ListProperty(xa_prop_type, display.getAtom(xa_prop_name));
}
/**
* Returns the property value as a byte array.
*
* @param xa_prop_type property type
* @param xa_prop_name property name
* @return property value as a byte array
* @throws X11Exception thrown if X11 window errors occurred
*/
public byte[] getProperty(X11.Atom xa_prop_type, X11.Atom xa_prop_name) throws X11Exception {
X11.AtomByReference xa_ret_type_ref = new X11.AtomByReference();
IntByReference ret_format_ref = new IntByReference();
NativeLongByReference ret_nitems_ref = new NativeLongByReference();
NativeLongByReference ret_bytes_after_ref = new NativeLongByReference();
PointerByReference ret_prop_ref = new PointerByReference();
NativeLong long_offset = new NativeLong(0);
NativeLong long_length = new NativeLong(MAX_PROPERTY_VALUE_LEN / 4);
/* MAX_PROPERTY_VALUE_LEN / 4 explanation (XGetWindowProperty manpage):
*
* long_length = Specifies the length in 32-bit multiples of the
* data to be retrieved.
*/
if (x11.XGetWindowProperty(display.x11Display, x11Window, xa_prop_name, long_offset, long_length, false,
xa_prop_type, xa_ret_type_ref, ret_format_ref,
ret_nitems_ref, ret_bytes_after_ref, ret_prop_ref) != X11.Success) {
String prop_name = x11.XGetAtomName(display.x11Display, xa_prop_name);
throw new X11Exception("Cannot get " + prop_name + " property.");
}
X11.Atom xa_ret_type = xa_ret_type_ref.getValue();
Pointer ret_prop = ret_prop_ref.getValue();
if( xa_ret_type == null ){
//the specified property does not exist for the specified window
return null;
}
if( xa_ret_type == null ){
//the specified property does not exist for the specified window
return null;
}
if( xa_ret_type == null ){
//the specified property does not exist for the specified window
return null;
}
if (xa_ret_type == null || xa_prop_type == null ||
!xa_ret_type.toNative().equals(xa_prop_type.toNative())) {
x11.XFree(ret_prop);
String prop_name = x11.XGetAtomName(display.x11Display, xa_prop_name);
throw new X11Exception("Invalid type of " + prop_name + " property");
}
int ret_format = ret_format_ref.getValue();
long ret_nitems = ret_nitems_ref.getValue().longValue();
// null terminate the result to make string handling easier
int nbytes;
if (ret_format == 32)
nbytes = Native.LONG_SIZE;
else if (ret_format == 16)
nbytes = Native.LONG_SIZE / 2;
else if (ret_format == 8)
nbytes = 1;
else if (ret_format == 0)
nbytes = 0;
else
throw new X11Exception("Invalid return format");
int length = Math.min((int) ret_nitems * nbytes, MAX_PROPERTY_VALUE_LEN);
byte[] ret = ret_prop.getByteArray(0, length);
x11.XFree(ret_prop);
return ret;
}
/**
* Returns the property value as a byte array.
*
* @param xa_prop_type property type
* @param xa_prop_name property name
* @return property value as a byte array
* @throws X11Exception thrown if X11 window errors occurred
*/
public byte[] getProperty(X11.Atom xa_prop_type, String xa_prop_name) throws X11Exception {
return getProperty(xa_prop_type, display.getAtom(xa_prop_name));
}
public int clientMsg(String msg, int data0, int data1, int data2, int data3, int data4) throws X11Exception {
return clientMsg(
msg,
new NativeLong(data0),
new NativeLong(data1),
new NativeLong(data2),
new NativeLong(data3),
new NativeLong(data4)
);
}
public int clientMsg(String msg, NativeLong data0, NativeLong data1, NativeLong data2, NativeLong data3, NativeLong data4) throws X11Exception {
X11.XClientMessageEvent event;
NativeLong mask = new NativeLong(X11.SubstructureRedirectMask | X11.SubstructureNotifyMask);
event = new X11.XClientMessageEvent();
event.type = X11.ClientMessage;
event.serial = new NativeLong(0);
event.send_event = 1;
event.message_type = display.getAtom(msg);
event.window = x11Window;
event.format = 32;
event.data.setType(NativeLong[].class);
event.data.l[0] = data0;
event.data.l[1] = data1;
event.data.l[2] = data2;
event.data.l[3] = data3;
event.data.l[4] = data4;
X11.XEvent e = new X11.XEvent();
e.setTypedValue(event);
if (x11.XSendEvent(display.x11Display, display.getRootWindow().x11Window, 0, mask, e) != 0) {
return X11.Success;
} else {
throw new X11Exception("Cannot send " + msg + " event.");
}
}
public Window[] getSubwindows() throws X11Exception {
WindowByReference root = new WindowByReference();
WindowByReference parent = new WindowByReference();
PointerByReference children = new PointerByReference();
IntByReference childCount = new IntByReference();
if (x11.XQueryTree(display.x11Display, x11Window, root, parent, children, childCount) == 0){
throw new X11Exception("Can't query subwindows");
}
if( childCount.getValue() == 0 ){
return null;
}
Window[] retVal = new Window[ childCount.getValue() ];
//Depending on if we're running on 64-bit or 32-bit systems,
//the Window ID size may be different; we need to make sure that
//we get the data properly no matter what
if (X11.XID.SIZE == 4) {
int[] windows = children.getValue().getIntArray( 0, childCount.getValue() );
for( int x = 0; x < retVal.length; x++ ){
X11.Window win = new X11.Window( windows [ x ] );
retVal[ x ] = new Window( display, win );
}
}
else {
long[] windows = children.getValue().getLongArray( 0, childCount.getValue() );
for( int x = 0; x < retVal.length; x++ ){
X11.Window win = new X11.Window( windows [ x ] );
retVal[ x ] = new Window( display, win );
}
}
x11.XFree(children.getValue());
return retVal;
}
public String toString() {
return x11Window.toString();
}
public static class Geometry {
public int x, y, width, height, borderWidth, depth;
public Geometry(int x, int y, int width, int height, int border_width, int depth) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.borderWidth = border_width;
this.depth = depth;
}
}
}
/**
* General exception which is thrown when an X11 window error occurred.
*/
public static class X11Exception extends Exception {
private static final long serialVersionUID = 1L;
public X11Exception() {
}
public X11Exception(String message) {
super(message);
}
public X11Exception(String message, Throwable cause) {
super(message, cause);
}
public X11Exception(Throwable cause) {
super(cause);
}
}
}