Linux: Rewrote focus handling to cope with the weird focus behaviour when running in an XEmbed enabled jvm (applet mode)

This commit is contained in:
Elias Naur 2008-04-12 20:07:23 +00:00
parent c8d4eb6a86
commit a015dc4558
4 changed files with 180 additions and 30 deletions

View file

@ -57,6 +57,18 @@ final class LinuxDisplay implements DisplayImplementation {
public final static int AutoRepeatModeDefault = 2;
public final static int None = 0;
private final static int KeyPressMask = 1 << 0;
private final static int KeyReleaseMask = 1 << 1;
private final static int ButtonPressMask = 1 << 2;
private final static int ButtonReleaseMask = 1 << 3;
private final static int NotifyAncestor = 0;
private final static int NotifyNonlinear = 3;
private final static int NotifyPointer = 5;
private final static int NotifyPointerRoot = 6;
private final static int NotifyDetailNone = 7;
/** Window mode enum */
private static final int FULLSCREEN_LEGACY = 1;
private static final int FULLSCREEN_NETWM = 2;
@ -78,6 +90,7 @@ final class LinuxDisplay implements DisplayImplementation {
/** Event buffer */
private final LinuxEvent event_buffer = new LinuxEvent();
private final LinuxEvent tmp_event_buffer = new LinuxEvent();
/** Current mode swithcing API */
private int current_displaymode_extension = NONE;
@ -103,10 +116,13 @@ final class LinuxDisplay implements DisplayImplementation {
private boolean minimized;
private boolean dirty;
private boolean close_requested;
private boolean focused_at_least_once;
private long current_cursor;
private long blank_cursor;
private Canvas parent;
private long parent_focus_window;
private boolean parent_focus_window_valid;
private long parent_window;
private boolean xembedded;
private LinuxKeyboard keyboard;
private LinuxMouse mouse;
@ -371,16 +387,17 @@ final class LinuxDisplay implements DisplayImplementation {
current_window_mode = getWindowMode(fullscreen);
boolean undecorated = Display.getPrivilegedBoolean("org.lwjgl.opengl.Window.undecorated") || current_window_mode != WINDOWED;
this.parent = parent;
long parent_window = parent != null ? getHandle(parent) : getRootWindow(getDisplay(), getDefaultScreen());
parent_window = parent != null ? getHandle(parent) : getRootWindow(getDisplay(), getDefaultScreen());
current_window = nCreateWindow(getDisplay(), getDefaultScreen(), handle, mode, current_window_mode, x, y, undecorated, parent_window);
xembedded = parent != null && isAncestorXEmbedded(parent_window);
blank_cursor = createBlankCursor();
parent_focus_window_valid = false;
current_cursor = None;
focused = true;
focused = false;
input_released = false;
pointer_grabbed = false;
keyboard_grabbed = false;
close_requested = false;
focused_at_least_once = false;
grab = false;
minimized = false;
dirty = true;
@ -397,6 +414,21 @@ final class LinuxDisplay implements DisplayImplementation {
}
private static native long nCreateWindow(long display, int screen, ByteBuffer peer_info_handle, DisplayMode mode, int window_mode, int x, int y, boolean undecorated, long parent_handle) throws LWJGLException;
private static native long getRootWindow(long display, int screen);
private static native boolean hasProperty(long display, long window, long property);
private static native long getParentWindow(long display, long window) throws LWJGLException;
private boolean isAncestorXEmbedded(long window) throws LWJGLException {
long xembed_atom = internAtom("_XEMBED_INFO", true);
if (xembed_atom != None) {
long w = parent_window;
while (w != None) {
if (hasProperty(getDisplay(), w, xembed_atom))
return true;
w = getParentWindow(getDisplay(), w);
}
}
return false;
}
private static long getHandle(Canvas parent) throws LWJGLException {
AWTCanvasImplementation awt_impl = AWTGLCanvas.createImplementation();
@ -623,17 +655,49 @@ final class LinuxDisplay implements DisplayImplementation {
static native void setInputFocus(long display, long window, long time);
private void relayEventToParent(LinuxEvent event_buffer, int event_mask) {
tmp_event_buffer.copyFrom(event_buffer);
tmp_event_buffer.setWindow(parent_window);
tmp_event_buffer.sendEvent(getDisplay(), parent_window, true, event_mask);
}
private void relayEventToParent(LinuxEvent event_buffer) {
if (parent == null)
return;
switch (event_buffer.getType()) {
case LinuxEvent.KeyPress:
relayEventToParent(event_buffer, KeyPressMask);
break;
case LinuxEvent.KeyRelease:
relayEventToParent(event_buffer, KeyPressMask);
break;
case LinuxEvent.ButtonPress:
relayEventToParent(event_buffer, KeyPressMask);
break;
case LinuxEvent.ButtonRelease:
relayEventToParent(event_buffer, KeyPressMask);
break;
default:
break;
}
}
private void processEvents() {
while (LinuxEvent.getPending(getDisplay()) > 0) {
event_buffer.nextEvent(getDisplay());
long event_window = event_buffer.getWindow();
if (event_buffer.getType() == LinuxEvent.ButtonPress && parent != null)
setInputFocus(getDisplay(), getWindow(), event_buffer.getButtonTime());
relayEventToParent(event_buffer);
if (event_window != getWindow() || event_buffer.filterEvent(event_window) ||
(mouse != null && mouse.filterEvent(grab, shouldWarpPointer(), event_buffer)) ||
(keyboard != null && keyboard.filterEvent(event_buffer)))
continue;
switch (event_buffer.getType()) {
case LinuxEvent.FocusIn:
setFocused(true, event_buffer.getFocusDetail());
break;
case LinuxEvent.FocusOut:
setFocused(false, event_buffer.getFocusDetail());
break;
case LinuxEvent.ClientMessage:
if ((event_buffer.getClientFormat() == 32) && (event_buffer.getClientData(0) == delete_atom))
close_requested = true;
@ -641,7 +705,6 @@ final class LinuxDisplay implements DisplayImplementation {
case LinuxEvent.MapNotify:
dirty = true;
minimized = false;
updateInputGrab();
break;
case LinuxEvent.UnmapNotify:
dirty = true;
@ -743,29 +806,41 @@ final class LinuxDisplay implements DisplayImplementation {
}
private void checkInput() {
long current_focus = nGetInputFocus(getDisplay());
focused = current_focus == getWindow();
if (parent == null)
return;
if (focused) {
focused_at_least_once = true;
acquireInput();
if (xembedded && !parent.isFocusOwner() && parent_focus_window_valid) {
setInputFocusUnsafe(parent_focus_window);
}
} else {
if (focused_at_least_once)
releaseInput();
if (parent != null && parent.isFocusOwner()) {
setInputFocusUnsafe();
if (parent.isFocusOwner()) {
if (xembedded) {
parent_focus_window = nGetInputFocus(getDisplay());
parent_focus_window_valid = true;
}
setInputFocusUnsafe(getWindow());
}
}
}
static native long nGetInputFocus(long display);
private static native void grabServer(long display);
private static native void ungrabServer(long display);
private static void setInputFocusUnsafe() {
setInputFocus(getDisplay(), getWindow(), CurrentTime);
private void setFocused(boolean got_focus, int focus_detail) {
if (focused == got_focus || focus_detail == NotifyDetailNone || focus_detail == NotifyPointer || focus_detail == NotifyPointerRoot)
return;
focused = got_focus;
if (focused) {
acquireInput();
} else {
releaseInput();
}
}
static native long nGetInputFocus(long display);
private void setInputFocusUnsafe(long window) {
setInputFocus(getDisplay(), window, CurrentTime);
try {
checkXError(getDisplay());
} catch (LWJGLException e) {
// Since we don't have any event timings for XSetInputFocus, a race condition might give a BadMatch, which we'll catch ang ignore
// Since we don't have any event timings for XSetInputFocus, a race condition might give a BadMatch, which we'll catch and ignore
LWJGLUtil.log("Got exception while trying to focus: " + e);
}
}
@ -921,10 +996,6 @@ final class LinuxDisplay implements DisplayImplementation {
}
}
/* public int isStateKeySet(int key) {
return Keyboard.STATE_UNKNOWN;
}
*/
private static native long nCreateCursor(long display, int width, int height, int xHotspot, int yHotspot, int numImages, IntBuffer images, int images_offset, IntBuffer delays, int delays_offset) throws LWJGLException;
private static long createBlankCursor() {

View file

@ -41,6 +41,8 @@ import java.nio.ByteBuffer;
* $Id: LinuxPeerInfo.java 2286 2006-03-23 19:32:21Z matzon $
*/
final class LinuxEvent {
public final static int FocusIn = 9;
public final static int FocusOut = 10;
public final static int KeyPress = 2;
public final static int KeyRelease = 3;
public final static int ButtonPress = 4;
@ -58,8 +60,21 @@ final class LinuxEvent {
}
private static native ByteBuffer createEventBuffer();
public final void copyFrom(LinuxEvent event) {
int pos = event_buffer.position();
int event_pos = event.event_buffer.position();
event_buffer.put(event.event_buffer);
event_buffer.position(pos);
event.event_buffer.position(event_pos);
}
public final static native int getPending(long display);
public final void sendEvent(long display, long window, boolean propagate, long event_mask) {
nSendEvent(event_buffer, display, window, propagate, event_mask);
}
private static native void nSendEvent(ByteBuffer event_buffer, long display, long window, boolean propagate, long event_mask);
public final boolean filterEvent(long window) {
return nFilterEvent(event_buffer, window);
}
@ -80,6 +95,23 @@ final class LinuxEvent {
}
private static native long nGetWindow(ByteBuffer event_buffer);
public final void setWindow(long window) {
nSetWindow(event_buffer, window);
}
private static native void nSetWindow(ByteBuffer event_buffer, long window);
/* Focus methods */
public final int getFocusMode() {
return nGetFocusMode(event_buffer);
}
private static native int nGetFocusMode(ByteBuffer event_buffer);
public final int getFocusDetail() {
return nGetFocusDetail(event_buffer);
}
private static native int nGetFocusDetail(ByteBuffer event_buffer);
/* ClientMessage methods */
public final long getClientMessageType() {

View file

@ -288,7 +288,7 @@ static Window createWindow(JNIEnv* env, Display *disp, int screen, jint window_m
return false;
cmap = XCreateColormap(disp, parent, vis_info->visual, AllocNone);
attribs.colormap = cmap;
attribs.event_mask = ExposureMask | /*FocusChangeMask | */VisibilityChangeMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
attribs.event_mask = ExposureMask | FocusChangeMask | VisibilityChangeMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
attribmask = CWColormap | CWEventMask;
if (isLegacyFullscreen(window_mode)) {
attribmask |= CWOverrideRedirect;
@ -334,14 +334,38 @@ static Window createWindow(JNIEnv* env, Display *disp, int screen, jint window_m
return win;
}
JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplay_grabServer(JNIEnv *env, jclass unused, jlong display) {
JNIEXPORT jlong JNICALL Java_org_lwjgl_opengl_LinuxDisplay_getParentWindow(JNIEnv *env, jclass unused, jlong display, jlong window_ptr) {
Display *disp = (Display *)(intptr_t)display;
XGrabServer(disp);
Window window = (Window)window_ptr;
Window root, parent;
Window *children;
unsigned int nchildren;
if (XQueryTree(disp, window, &root, &parent, &children, &nchildren) == 0) {
throwException(env, "XQueryTree failed");
return None;
}
if (children != NULL)
XFree(children);
return parent;
}
JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplay_ungrabServer(JNIEnv *env, jclass unused, jlong display) {
JNIEXPORT jboolean JNICALL Java_org_lwjgl_opengl_LinuxDisplay_hasProperty(JNIEnv *env, jclass unusued, jlong display, jlong window_ptr, jlong property_ptr) {
Display *disp = (Display *)(intptr_t)display;
XUngrabServer(disp);
Window window = (Window)window_ptr;
Atom property = (Atom)property_ptr;
int num_props;
Atom *properties = XListProperties(disp, window, &num_props);
if (properties == NULL)
return JNI_FALSE;
jboolean result = JNI_FALSE;
for (int i = 0; i < num_props; i++) {
if (properties[i] == property) {
result = JNI_TRUE;
break;
}
}
XFree(properties);
return result;
}
JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplay_setInputFocus(JNIEnv *env, jclass clazz, jlong display, jlong window_ptr, jlong time) {

View file

@ -53,6 +53,29 @@ JNIEXPORT jint JNICALL Java_org_lwjgl_opengl_LinuxEvent_getPending(JNIEnv *env,
return XPending(disp);
}
JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxEvent_nSetWindow(JNIEnv *env, jclass unused, jobject event_buffer, jlong window_ptr) {
XEvent *event = (XEvent *)(*env)->GetDirectBufferAddress(env, event_buffer);
Window window = (Window)window_ptr;
event->xany.window = window;
}
JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxEvent_nSendEvent(JNIEnv *env, jclass unused, jobject event_buffer, jlong display_ptr, jlong window_ptr, jboolean propagate, jlong eventmask) {
XEvent *event = (XEvent *)(*env)->GetDirectBufferAddress(env, event_buffer);
Display *disp = (Display *)(intptr_t)display_ptr;
Window window = (Window)window_ptr;
XSendEvent(disp, window, propagate == JNI_TRUE ? True : False, eventmask, event);
}
JNIEXPORT jint JNICALL Java_org_lwjgl_opengl_LinuxEvent_nGetFocusDetail(JNIEnv *env, jclass unused, jobject event_buffer) {
XEvent *event = (XEvent *)(*env)->GetDirectBufferAddress(env, event_buffer);
return event->xfocus.detail;
}
JNIEXPORT jint JNICALL Java_org_lwjgl_opengl_LinuxEvent_nGetFocusMode(JNIEnv *env, jclass unused, jobject event_buffer) {
XEvent *event = (XEvent *)(*env)->GetDirectBufferAddress(env, event_buffer);
return event->xfocus.mode;
}
JNIEXPORT jboolean JNICALL Java_org_lwjgl_opengl_LinuxEvent_nFilterEvent(JNIEnv *env, jclass unused, jobject event_buffer, jlong window_ptr) {
XEvent *event = (XEvent *)(*env)->GetDirectBufferAddress(env, event_buffer);
Window window = (Window)window_ptr;