1 /*
2  * Copyright (c) 2002-2010 LWJGL Project
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of 'LWJGL' nor the names of
17  *   its contributors may be used to endorse or promote products derived
18  *   from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 package org.lwjgl.opengl;
33 
34 /**
35  * This is the Display implementation interface. Display delegates
36  * to implementors of this interface. There is one DisplayImplementation
37  * for each supported platform.
38  * @author elias_naur
39  */
40 
41 import java.awt.Canvas;
42 import java.awt.event.FocusListener;
43 import java.awt.event.FocusEvent;
44 
45 import java.io.BufferedReader;
46 import java.io.IOException;
47 import java.io.InputStreamReader;
48 import java.nio.ByteOrder;
49 import java.nio.ByteBuffer;
50 import java.nio.FloatBuffer;
51 import java.nio.IntBuffer;
52 import java.lang.reflect.InvocationTargetException;
53 
54 import org.lwjgl.BufferUtils;
55 import org.lwjgl.LWJGLException;
56 import org.lwjgl.LWJGLUtil;
57 import org.lwjgl.MemoryUtil;
58 import org.lwjgl.opengl.XRandR.Screen;
59 import org.lwjgl.opengles.EGL;
60 
61 import java.security.AccessController;
62 import java.security.PrivilegedAction;
63 import java.util.ArrayList;
64 import java.util.List;
65 
66 final class LinuxDisplay implements DisplayImplementation {
67 	/* X11 constants */
68 	public static final int CurrentTime = 0;
69 	public static final int GrabSuccess = 0;
70 	public static final int AutoRepeatModeOff  = 0;
71 	public static final int AutoRepeatModeOn = 1;
72 	public static final int AutoRepeatModeDefault = 2;
73 	public static final int None = 0;
74 
75 	private static final int KeyPressMask = 1 << 0;
76 	private static final int KeyReleaseMask = 1 << 1;
77 	private static final int ButtonPressMask = 1 << 2;
78 	private static final int ButtonReleaseMask = 1 << 3;
79 
80 	private static final int NotifyAncestor = 0;
81 	private static final int NotifyNonlinear = 3;
82 	private static final int NotifyPointer = 5;
83 	private static final int NotifyPointerRoot = 6;
84 	private static final int NotifyDetailNone = 7;
85 
86 	private static final int SetModeInsert = 0;
87 	private static final int SaveSetRoot = 1;
88 	private static final int SaveSetUnmap = 1;
89 
90 	private static final int X_SetInputFocus = 42;
91 
92 	/** Window mode enum */
93 	private static final int FULLSCREEN_LEGACY = 1;
94 	private static final int FULLSCREEN_NETWM = 2;
95 	private static final int WINDOWED = 3;
96 
97 	/** Current window mode */
98 	private static int current_window_mode = WINDOWED;
99 
100 	/** Display mode switching API */
101 	private static final int XRANDR = 10;
102 	private static final int XF86VIDMODE = 11;
103 	private static final int NONE = 12;
104 
105 	/** Current X11 Display pointer */
106 	private static long display;
107 	private static long current_window;
108 	private static long saved_error_handler;
109 
110 	private static int display_connection_usage_count;
111 
112 	/** Event buffer */
113 	private final LinuxEvent event_buffer = new LinuxEvent();
114 	private final LinuxEvent tmp_event_buffer = new LinuxEvent();
115 
116 	/** Current mode swithcing API */
117 	private int current_displaymode_extension = NONE;
118 
119 	/** Atom used for the pointer warp messages */
120 	private long delete_atom;
121 
122 	private PeerInfo peer_info;
123 
124 	/** Saved gamma used to restore display settings */
125 	private ByteBuffer saved_gamma;
126 	private ByteBuffer current_gamma;
127 
128 	/** Saved mode to restore with */
129 	private DisplayMode saved_mode;
130 	private DisplayMode current_mode;
131 
132 
133 	private boolean keyboard_grabbed;
134 	private boolean pointer_grabbed;
135 	private boolean input_released;
136 	private boolean grab;
137 	private boolean focused;
138 	private boolean minimized;
139 	private boolean dirty;
140 	private boolean close_requested;
141 	private long current_cursor;
142 	private long blank_cursor;
143 	private boolean mouseInside = true;
144 	private boolean resizable;
145 	private boolean resized;
146 
147 	private int window_x;
148 	private int window_y;
149 	private int window_width;
150 	private int window_height;
151 
152 	private Canvas parent;
153 	private long parent_window;
154 	private static boolean xembedded;
155 	private long parent_proxy_focus_window;
156 	private boolean parent_focused;
157 	private boolean parent_focus_changed;
158 	private long last_window_focus = 0;
159 
160 	private LinuxKeyboard keyboard;
161 	private LinuxMouse mouse;
162 
163 	private String wm_class;
164 
165 	private final FocusListener focus_listener = new FocusListener() {
166 		public void focusGained(FocusEvent e) {
167 			synchronized (GlobalLock.lock) {
168 				parent_focused = true;
169 				parent_focus_changed = true;
170 			}
171 		}
172 		public void focusLost(FocusEvent e) {
173 			synchronized (GlobalLock.lock) {
174 				parent_focused = false;
175 				parent_focus_changed = true;
176 			}
177 		}
178 	};
179 
getCurrentGammaRamp()180 	private static ByteBuffer getCurrentGammaRamp() throws LWJGLException {
181 		lockAWT();
182 		try {
183 			incDisplay();
184 			try {
185 				if (isXF86VidModeSupported())
186 					return nGetCurrentGammaRamp(getDisplay(), getDefaultScreen());
187 				else
188 					return null;
189 			} finally {
190 				decDisplay();
191 			}
192 		} finally {
193 			unlockAWT();
194 		}
195 	}
nGetCurrentGammaRamp(long display, int screen)196 	private static native ByteBuffer nGetCurrentGammaRamp(long display, int screen) throws LWJGLException;
197 
getBestDisplayModeExtension()198 	private static int getBestDisplayModeExtension() {
199 		int result;
200 		if (isXrandrSupported()) {
201 			LWJGLUtil.log("Using Xrandr for display mode switching");
202 			result = XRANDR;
203 		} else if (isXF86VidModeSupported()) {
204 			LWJGLUtil.log("Using XF86VidMode for display mode switching");
205 			result = XF86VIDMODE;
206 		} else {
207 			LWJGLUtil.log("No display mode extensions available");
208 			result = NONE;
209 		}
210 		return result;
211 	}
212 
isXrandrSupported()213 	private static boolean isXrandrSupported() {
214 		if (Display.getPrivilegedBoolean("LWJGL_DISABLE_XRANDR"))
215 			return false;
216 		lockAWT();
217 		try {
218 			incDisplay();
219 			try {
220 				return nIsXrandrSupported(getDisplay());
221 			} finally {
222 				decDisplay();
223 			}
224 		} catch (LWJGLException e) {
225 			LWJGLUtil.log("Got exception while querying Xrandr support: " + e);
226 			return false;
227 		} finally {
228 			unlockAWT();
229 		}
230 	}
nIsXrandrSupported(long display)231 	private static native boolean nIsXrandrSupported(long display) throws LWJGLException;
232 
isXF86VidModeSupported()233 	private static boolean isXF86VidModeSupported() {
234 		lockAWT();
235 		try {
236 			incDisplay();
237 			try {
238 				return nIsXF86VidModeSupported(getDisplay());
239 			} finally {
240 				decDisplay();
241 			}
242 		} catch (LWJGLException e) {
243 			LWJGLUtil.log("Got exception while querying XF86VM support: " + e);
244 			return false;
245 		} finally {
246 			unlockAWT();
247 		}
248 	}
nIsXF86VidModeSupported(long display)249 	private static native boolean nIsXF86VidModeSupported(long display) throws LWJGLException;
250 
isNetWMFullscreenSupported()251 	private static boolean isNetWMFullscreenSupported() throws LWJGLException {
252 		if (Display.getPrivilegedBoolean("LWJGL_DISABLE_NETWM"))
253 			return false;
254 		lockAWT();
255 		try {
256 			incDisplay();
257 			try {
258 				return nIsNetWMFullscreenSupported(getDisplay(), getDefaultScreen());
259 			} finally {
260 				decDisplay();
261 			}
262 		} catch (LWJGLException e) {
263 			LWJGLUtil.log("Got exception while querying NetWM support: " + e);
264 			return false;
265 		} finally {
266 			unlockAWT();
267 		}
268 	}
nIsNetWMFullscreenSupported(long display, int screen)269 	private static native boolean nIsNetWMFullscreenSupported(long display, int screen) throws LWJGLException;
270 
271 	/* Since Xlib is not guaranteed to be thread safe, we need a way to synchronize LWJGL
272 	 * Xlib calls with AWT Xlib calls. Fortunately, JAWT implements Lock()/Unlock() to
273 	 * do just that.
274 	 */
lockAWT()275 	static void lockAWT() {
276 		try {
277 			nLockAWT();
278 		} catch (LWJGLException e) {
279 			LWJGLUtil.log("Caught exception while locking AWT: " + e);
280 		}
281 	}
nLockAWT()282 	private static native void nLockAWT() throws LWJGLException;
283 
unlockAWT()284 	static void unlockAWT() {
285 		try {
286 			nUnlockAWT();
287 		} catch (LWJGLException e) {
288 			LWJGLUtil.log("Caught exception while unlocking AWT: " + e);
289 		}
290 	}
nUnlockAWT()291 	private static native void nUnlockAWT() throws LWJGLException;
292 
293 	/**
294 	 * increment and decrement display usage.
295 	 */
incDisplay()296 	static void incDisplay() throws LWJGLException {
297 		if (display_connection_usage_count == 0) {
298 			try {
299 				// TODO: Can we know if we're on desktop or ES?
300 				GLContext.loadOpenGLLibrary();
301 				org.lwjgl.opengles.GLContext.loadOpenGLLibrary();
302 			} catch (Throwable t) {
303 			}
304 			saved_error_handler = setErrorHandler();
305 			display = openDisplay();
306 //			synchronize(display, true);
307 		}
308 		display_connection_usage_count++;
309 	}
callErrorHandler(long handler, long display, long error_ptr)310 	private static native int callErrorHandler(long handler, long display, long error_ptr);
setErrorHandler()311 	private static native long setErrorHandler();
resetErrorHandler(long handler)312 	private static native long resetErrorHandler(long handler);
synchronize(long display, boolean synchronize)313 	private static native void synchronize(long display, boolean synchronize);
314 
globalErrorHandler(long display, long event_ptr, long error_display, long serial, long error_code, long request_code, long minor_code)315 	private static int globalErrorHandler(long display, long event_ptr, long error_display, long serial, long error_code, long request_code, long minor_code) throws LWJGLException {
316 		if (xembedded && request_code == X_SetInputFocus) return 0; // ignore X error in xembeded mode to fix a browser issue when dragging or switching tabs
317 
318 		if (display == getDisplay()) {
319 			String error_msg = getErrorText(display, error_code);
320 			throw new LWJGLException("X Error - disp: 0x" + Long.toHexString(error_display) + " serial: " + serial + " error: " + error_msg + " request_code: " + request_code + " minor_code: " + minor_code);
321 		} else if (saved_error_handler != 0)
322 			return callErrorHandler(saved_error_handler, display, event_ptr);
323 		return 0;
324 	}
getErrorText(long display, long error_code)325 	private static native String getErrorText(long display, long error_code);
326 
decDisplay()327 	static void decDisplay() {
328 		/*
329 		 * Some drivers (at least some versions of the radeon dri driver)
330 		 * don't like it when the display is closed and later re-opened,
331 		 * so we'll just let the singleton display connection leak.
332 		 */
333 /*		display_connection_usage_count--;
334 		if (display_connection_usage_count < 0)
335 			throw new InternalError("display_connection_usage_count < 0: " + display_connection_usage_count);
336 		if (display_connection_usage_count == 0) {
337 			closeDisplay(display);
338 			resetErrorHandler(saved_error_handler);
339 			display = 0;
340 			GLContext.unloadOpenGLLibrary();
341 		}*/
342 	}
343 
openDisplay()344 	static native long openDisplay() throws LWJGLException;
closeDisplay(long display)345 	static native void closeDisplay(long display);
346 
getWindowMode(boolean fullscreen)347 	private int getWindowMode(boolean fullscreen) throws LWJGLException {
348 		if (fullscreen) {
349 			if (current_displaymode_extension == XRANDR && isNetWMFullscreenSupported()) {
350 				LWJGLUtil.log("Using NetWM for fullscreen window");
351 				return FULLSCREEN_NETWM;
352 			} else {
353 				LWJGLUtil.log("Using legacy mode for fullscreen window");
354 				return FULLSCREEN_LEGACY;
355 			}
356 		} else
357 			return WINDOWED;
358 	}
359 
getDisplay()360 	static long getDisplay() {
361 		if (display_connection_usage_count <= 0)
362 			throw new InternalError("display_connection_usage_count = " + display_connection_usage_count);
363 		return display;
364 	}
365 
getDefaultScreen()366 	static int getDefaultScreen() {
367 		return nGetDefaultScreen(getDisplay());
368 	}
nGetDefaultScreen(long display)369 	static native int nGetDefaultScreen(long display);
370 
getWindow()371 	static long getWindow() {
372 		return current_window;
373 	}
374 
ungrabKeyboard()375 	private void ungrabKeyboard() {
376 		if (keyboard_grabbed) {
377 			nUngrabKeyboard(getDisplay());
378 			keyboard_grabbed = false;
379 		}
380 	}
nUngrabKeyboard(long display)381 	static native int nUngrabKeyboard(long display);
382 
grabKeyboard()383 	private void grabKeyboard() {
384 		if (!keyboard_grabbed) {
385 			int res = nGrabKeyboard(getDisplay(), getWindow());
386 			if (res == GrabSuccess)
387 				keyboard_grabbed = true;
388 		}
389 	}
nGrabKeyboard(long display, long window)390 	static native int nGrabKeyboard(long display, long window);
391 
grabPointer()392 	private void grabPointer() {
393 		if (!pointer_grabbed) {
394 			int result = nGrabPointer(getDisplay(), getWindow(), None);
395 			if (result == GrabSuccess) {
396 				pointer_grabbed = true;
397 				// make sure we have a centered window
398 				if (isLegacyFullscreen()) {
399 					nSetViewPort(getDisplay(), getWindow(), getDefaultScreen());
400 				}
401 			}
402 		}
403 	}
nGrabPointer(long display, long window, long cursor)404 	static native int nGrabPointer(long display, long window, long cursor);
nSetViewPort(long display, long window, int screen)405 	private static native void nSetViewPort(long display, long window, int screen);
406 
ungrabPointer()407 	private void ungrabPointer() {
408 		if (pointer_grabbed) {
409 			pointer_grabbed = false;
410 			nUngrabPointer(getDisplay());
411 		}
412 	}
nUngrabPointer(long display)413 	static native int nUngrabPointer(long display);
414 
isFullscreen()415 	private static boolean isFullscreen() {
416 		return current_window_mode == FULLSCREEN_LEGACY || current_window_mode == FULLSCREEN_NETWM;
417 	}
418 
shouldGrab()419 	private boolean shouldGrab() {
420 		return !input_released && grab && mouse != null;
421 	}
422 
updatePointerGrab()423 	private void updatePointerGrab() {
424 		if (isFullscreen() || shouldGrab()) {
425 			grabPointer();
426 		} else {
427 			ungrabPointer();
428 		}
429 		updateCursor();
430 	}
431 
updateCursor()432 	private void updateCursor() {
433 		long cursor;
434 		if (shouldGrab()) {
435 			cursor = blank_cursor;
436 		} else {
437 			cursor = current_cursor;
438 		}
439 		nDefineCursor(getDisplay(), getWindow(), cursor);
440 	}
nDefineCursor(long display, long window, long cursor_handle)441 	private static native void nDefineCursor(long display, long window, long cursor_handle);
442 
isLegacyFullscreen()443 	private static boolean isLegacyFullscreen() {
444 		return current_window_mode == FULLSCREEN_LEGACY;
445 	}
446 
updateKeyboardGrab()447 	private void updateKeyboardGrab() {
448 		if (isLegacyFullscreen())
449 			grabKeyboard();
450 		else
451 			ungrabKeyboard();
452 	}
453 
createWindow(final DrawableLWJGL drawable, DisplayMode mode, Canvas parent, int x, int y)454 	public void createWindow(final DrawableLWJGL drawable, DisplayMode mode, Canvas parent, int x, int y) throws LWJGLException {
455 		lockAWT();
456 		try {
457 			incDisplay();
458 			try {
459 				if ( drawable instanceof DrawableGLES )
460 					peer_info = new LinuxDisplayPeerInfo();
461 
462 				ByteBuffer handle = peer_info.lockAndGetHandle();
463 				try {
464 					current_window_mode = getWindowMode(Display.isFullscreen());
465 
466 					// Try to enable Lecagy FullScreen Support in Compiz, else
467 					// we may have trouble with stuff overlapping our fullscreen window.
468 					if ( current_window_mode != WINDOWED )
469 						Compiz.setLegacyFullscreenSupport(true);
470 
471 					// Setting _MOTIF_WM_HINTS in fullscreen mode is problematic for certain window
472 					// managers. We do not set MWM_HINTS_DECORATIONS in fullscreen mode anymore,
473 					// unless org.lwjgl.opengl.Window.undecorated_fs has been specified.
474 					// See native/linux/org_lwjgl_opengl_Display.c, createWindow function.
475 					boolean undecorated = Display.getPrivilegedBoolean("org.lwjgl.opengl.Window.undecorated") || (current_window_mode != WINDOWED && Display.getPrivilegedBoolean("org.lwjgl.opengl.Window.undecorated_fs"));
476 
477 					this.parent = parent;
478 					parent_window = parent != null ? getHandle(parent) : getRootWindow(getDisplay(), getDefaultScreen());
479 					resizable = Display.isResizable();
480 					resized = false;
481 					window_x = x;
482 					window_y = y;
483 					window_width = mode.getWidth();
484 					window_height = mode.getHeight();
485 
486                                         // overwrite arguments x and y - superclass always uses 0,0 for fullscreen windows
487                                         // use the coordinates of XRandRs primary screen instead
488                                         // this is required to let the fullscreen window appear on the primary screen
489                                         if (mode.isFullscreenCapable()  && current_displaymode_extension == XRANDR) {
490                                             Screen primaryScreen = XRandR.DisplayModetoScreen(Display.getDisplayMode());
491                                             x = primaryScreen.xPos;
492                                             y = primaryScreen.yPos;
493                                         }
494 
495 					current_window = nCreateWindow(getDisplay(), getDefaultScreen(), handle, mode, current_window_mode, x, y, undecorated, parent_window, resizable);
496 
497 					// Set the WM_CLASS hint which is needed by some WM's e.g. Gnome Shell
498 					wm_class = Display.getPrivilegedString("LWJGL_WM_CLASS");
499 					if (wm_class == null) wm_class = Display.getTitle();
500 					setClassHint(Display.getTitle(), wm_class);
501 
502 					mapRaised(getDisplay(), current_window);
503 					xembedded = parent != null && isAncestorXEmbedded(parent_window);
504 					blank_cursor = createBlankCursor();
505 					current_cursor = None;
506 					focused = false;
507 					input_released = false;
508 					pointer_grabbed = false;
509 					keyboard_grabbed = false;
510 					close_requested = false;
511 					grab = false;
512 					minimized = false;
513 					dirty = true;
514 
515 					if ( drawable instanceof DrawableGLES )
516 						((DrawableGLES)drawable).initialize(current_window, getDisplay(), EGL.EGL_WINDOW_BIT, (org.lwjgl.opengles.PixelFormat)drawable.getPixelFormat());
517 
518 					if (parent != null) {
519 						parent.addFocusListener(focus_listener);
520 						parent_focused = parent.isFocusOwner();
521 						parent_focus_changed = true;
522 					}
523 				} finally {
524 					peer_info.unlock();
525 				}
526 			} catch (LWJGLException e) {
527 				decDisplay();
528 				throw e;
529 			}
530 		} finally {
531 			unlockAWT();
532 		}
533 	}
nCreateWindow(long display, int screen, ByteBuffer peer_info_handle, DisplayMode mode, int window_mode, int x, int y, boolean undecorated, long parent_handle, boolean resizable)534 	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, boolean resizable) throws LWJGLException;
getRootWindow(long display, int screen)535 	private static native long getRootWindow(long display, int screen);
hasProperty(long display, long window, long property)536 	private static native boolean hasProperty(long display, long window, long property);
getParentWindow(long display, long window)537 	private static native long getParentWindow(long display, long window) throws LWJGLException;
getChildCount(long display, long window)538 	private static native int getChildCount(long display, long window) throws LWJGLException;
mapRaised(long display, long window)539 	private static native void mapRaised(long display, long window);
reparentWindow(long display, long window, long parent, int x, int y)540 	private static native void reparentWindow(long display, long window, long parent, int x, int y);
nGetInputFocus(long display)541 	private static native long nGetInputFocus(long display) throws LWJGLException;
nSetInputFocus(long display, long window, long time)542 	private static native void nSetInputFocus(long display, long window, long time);
nSetWindowSize(long display, long window, int width, int height, boolean resizable)543 	private static native void nSetWindowSize(long display, long window, int width, int height, boolean resizable);
nGetX(long display, long window)544 	private static native int nGetX(long display, long window);
nGetY(long display, long window)545 	private static native int nGetY(long display, long window);
nGetWidth(long display, long window)546 	private static native int nGetWidth(long display, long window);
nGetHeight(long display, long window)547 	private static native int nGetHeight(long display, long window);
548 
isAncestorXEmbedded(long window)549 	private static boolean isAncestorXEmbedded(long window) throws LWJGLException {
550 		long xembed_atom = internAtom("_XEMBED_INFO", true);
551 		if (xembed_atom != None) {
552 			long w = window;
553 			while (w != None) {
554 				if (hasProperty(getDisplay(), w, xembed_atom))
555 					return true;
556 				w = getParentWindow(getDisplay(), w);
557 			}
558 		}
559 		return false;
560 	}
561 
getHandle(Canvas parent)562 	private static long getHandle(Canvas parent) throws LWJGLException {
563 		AWTCanvasImplementation awt_impl = AWTGLCanvas.createImplementation();
564 		LinuxPeerInfo parent_peer_info = (LinuxPeerInfo)awt_impl.createPeerInfo(parent, null, null);
565 		ByteBuffer parent_peer_info_handle = parent_peer_info.lockAndGetHandle();
566 		try {
567 			return parent_peer_info.getDrawable();
568 		} finally {
569 			parent_peer_info.unlock();
570 		}
571 	}
572 
updateInputGrab()573 	private void updateInputGrab() {
574 		updatePointerGrab();
575 		updateKeyboardGrab();
576 	}
577 
destroyWindow()578 	public void destroyWindow() {
579 		lockAWT();
580 		try {
581 			if (parent != null) {
582 				parent.removeFocusListener(focus_listener);
583 			}
584 			try {
585 				setNativeCursor(null);
586 			} catch (LWJGLException e) {
587 				LWJGLUtil.log("Failed to reset cursor: " + e.getMessage());
588 			}
589 			nDestroyCursor(getDisplay(), blank_cursor);
590 			blank_cursor = None;
591 			ungrabKeyboard();
592 			nDestroyWindow(getDisplay(), getWindow());
593 			decDisplay();
594 
595 			if ( current_window_mode != WINDOWED )
596 				Compiz.setLegacyFullscreenSupport(false);
597 		} finally {
598 			unlockAWT();
599 		}
600 	}
nDestroyWindow(long display, long window)601 	static native void nDestroyWindow(long display, long window);
602 
switchDisplayMode(DisplayMode mode)603 	public void switchDisplayMode(DisplayMode mode) throws LWJGLException {
604 		lockAWT();
605 		try {
606 			switchDisplayModeOnTmpDisplay(mode);
607 			current_mode = mode;
608 		} finally {
609 			unlockAWT();
610 		}
611 	}
612 
switchDisplayModeOnTmpDisplay(DisplayMode mode)613 	private void switchDisplayModeOnTmpDisplay(DisplayMode mode) throws LWJGLException {
614                 if (current_displaymode_extension == XRANDR) {
615                         // let Xrandr set the display mode
616                         XRandR.setConfiguration(false, XRandR.DisplayModetoScreen(mode));
617                 } else {
618                         incDisplay();
619                         try {
620                                 nSwitchDisplayMode(getDisplay(), getDefaultScreen(), current_displaymode_extension, mode);
621                         } finally {
622                                 decDisplay();
623                         }
624                 }
625 	}
nSwitchDisplayMode(long display, int screen, int extension, DisplayMode mode)626 	private static native void nSwitchDisplayMode(long display, int screen, int extension, DisplayMode mode) throws LWJGLException;
627 
internAtom(String atom_name, boolean only_if_exists)628 	private static long internAtom(String atom_name, boolean only_if_exists) throws LWJGLException {
629 		incDisplay();
630 		try {
631 			return nInternAtom(getDisplay(), atom_name, only_if_exists);
632 		} finally {
633 			decDisplay();
634 		}
635 	}
nInternAtom(long display, String atom_name, boolean only_if_exists)636 	static native long nInternAtom(long display, String atom_name, boolean only_if_exists);
637 
resetDisplayMode()638 	public void resetDisplayMode() {
639 		lockAWT();
640 		try {
641 			if( current_displaymode_extension == XRANDR )
642 			{
643 				AccessController.doPrivileged(new PrivilegedAction<Object>() {
644 					public Object run() {
645 						XRandR.restoreConfiguration();
646 						return null;
647 					}
648 				});
649 			}
650 			else
651 			{
652 				switchDisplayMode(saved_mode);
653 			}
654 			if (isXF86VidModeSupported())
655 				doSetGamma(saved_gamma);
656 
657 			Compiz.setLegacyFullscreenSupport(false);
658 		} catch (LWJGLException e) {
659 			LWJGLUtil.log("Caught exception while resetting mode: " + e);
660 		} finally {
661 			unlockAWT();
662 		}
663 	}
664 
getGammaRampLength()665 	public int getGammaRampLength() {
666 		if (!isXF86VidModeSupported())
667 			return 0;
668 		lockAWT();
669 		try {
670 			try {
671 				incDisplay();
672 				try {
673 					return nGetGammaRampLength(getDisplay(), getDefaultScreen());
674 				} catch (LWJGLException e) {
675 					LWJGLUtil.log("Got exception while querying gamma length: " + e);
676 					return 0;
677 				} finally {
678 					decDisplay();
679 				}
680 			} catch (LWJGLException e) {
681 				LWJGLUtil.log("Failed to get gamma ramp length: " + e);
682 				return 0;
683 			}
684 		} finally {
685 			unlockAWT();
686 		}
687 	}
nGetGammaRampLength(long display, int screen)688 	private static native int nGetGammaRampLength(long display, int screen) throws LWJGLException;
689 
setGammaRamp(FloatBuffer gammaRamp)690 	public void setGammaRamp(FloatBuffer gammaRamp) throws LWJGLException {
691 		if (!isXF86VidModeSupported())
692 			throw new LWJGLException("No gamma ramp support (Missing XF86VM extension)");
693 		doSetGamma(convertToNativeRamp(gammaRamp));
694 	}
695 
doSetGamma(ByteBuffer native_gamma)696 	private void doSetGamma(ByteBuffer native_gamma) throws LWJGLException {
697 		lockAWT();
698 		try {
699 			setGammaRampOnTmpDisplay(native_gamma);
700 			current_gamma = native_gamma;
701 		} finally {
702 			unlockAWT();
703 		}
704 	}
705 
setGammaRampOnTmpDisplay(ByteBuffer native_gamma)706 	private static void setGammaRampOnTmpDisplay(ByteBuffer native_gamma) throws LWJGLException {
707 		incDisplay();
708 		try {
709 			nSetGammaRamp(getDisplay(), getDefaultScreen(), native_gamma);
710 		} finally {
711 			decDisplay();
712 		}
713 	}
nSetGammaRamp(long display, int screen, ByteBuffer gammaRamp)714 	private static native void nSetGammaRamp(long display, int screen, ByteBuffer gammaRamp) throws LWJGLException;
715 
convertToNativeRamp(FloatBuffer ramp)716 	private static ByteBuffer convertToNativeRamp(FloatBuffer ramp) throws LWJGLException {
717 		return nConvertToNativeRamp(ramp, ramp.position(), ramp.remaining());
718 	}
nConvertToNativeRamp(FloatBuffer ramp, int offset, int length)719 	private static native ByteBuffer nConvertToNativeRamp(FloatBuffer ramp, int offset, int length) throws LWJGLException;
720 
getAdapter()721 	public String getAdapter() {
722 		return null;
723 	}
724 
getVersion()725 	public String getVersion() {
726 		return null;
727 	}
728 
init()729 	public DisplayMode init() throws LWJGLException {
730 		lockAWT();
731 		try {
732 			Compiz.init();
733 
734 			delete_atom = internAtom("WM_DELETE_WINDOW", false);
735 			current_displaymode_extension = getBestDisplayModeExtension();
736 			if (current_displaymode_extension == NONE)
737 				throw new LWJGLException("No display mode extension is available");
738 			DisplayMode[] modes = getAvailableDisplayModes();
739 			if (modes == null || modes.length == 0)
740 				throw new LWJGLException("No modes available");
741 			switch (current_displaymode_extension) {
742 				case XRANDR:
743 					saved_mode = AccessController.doPrivileged(new PrivilegedAction<DisplayMode>() {
744 						public DisplayMode run() {
745 							return XRandR.ScreentoDisplayMode(XRandR.getConfiguration());
746 						}
747 					});
748 					break;
749 				case XF86VIDMODE:
750 					saved_mode = modes[0];
751 					break;
752 				default:
753 					throw new LWJGLException("Unknown display mode extension: " + current_displaymode_extension);
754 			}
755 			current_mode = saved_mode;
756 			saved_gamma = getCurrentGammaRamp();
757 			current_gamma = saved_gamma;
758 			return saved_mode;
759 		} finally {
760 			unlockAWT();
761 		}
762 	}
763 
getCurrentXRandrMode()764 	private static DisplayMode getCurrentXRandrMode() throws LWJGLException {
765 		lockAWT();
766 		try {
767 			incDisplay();
768 			try {
769 				return nGetCurrentXRandrMode(getDisplay(), getDefaultScreen());
770 			} finally {
771 				decDisplay();
772 			}
773 		} finally {
774 			unlockAWT();
775 		}
776 	}
777 
778 	/** Assumes extension == XRANDR */
nGetCurrentXRandrMode(long display, int screen)779 	private static native DisplayMode nGetCurrentXRandrMode(long display, int screen) throws LWJGLException;
780 
setTitle(String title)781 	public void setTitle(String title) {
782 		lockAWT();
783 		try {
784 			final ByteBuffer titleText = MemoryUtil.encodeUTF8(title);
785 			nSetTitle(getDisplay(), getWindow(), MemoryUtil.getAddress(titleText), titleText.remaining() - 1);
786 		} finally {
787 			unlockAWT();
788 		}
789 	}
nSetTitle(long display, long window, long title, int len)790 	private static native void nSetTitle(long display, long window, long title, int len);
791 
792 	/** the WM_CLASS hint is needed by some WM's e.g. gnome shell */
setClassHint(String wm_name, String wm_class)793 	private void setClassHint(String wm_name, String wm_class) {
794 		lockAWT();
795 		try {
796 			final ByteBuffer nameText = MemoryUtil.encodeUTF8(wm_name);
797 			final ByteBuffer classText = MemoryUtil.encodeUTF8(wm_class);
798 
799 			nSetClassHint(getDisplay(), getWindow(), MemoryUtil.getAddress(nameText), MemoryUtil.getAddress(classText));
800 		} finally {
801 			unlockAWT();
802 		}
803 	}
nSetClassHint(long display, long window, long wm_name, long wm_class)804 	private static native void nSetClassHint(long display, long window, long wm_name, long wm_class);
805 
isCloseRequested()806 	public boolean isCloseRequested() {
807 		boolean result = close_requested;
808 		close_requested = false;
809 		return result;
810 	}
811 
isVisible()812 	public boolean isVisible() {
813 		return !minimized;
814 	}
815 
isActive()816 	public boolean isActive() {
817 		return focused || isLegacyFullscreen();
818 	}
819 
isDirty()820 	public boolean isDirty() {
821 		boolean result = dirty;
822 		dirty = false;
823 		return result;
824 	}
825 
createPeerInfo(PixelFormat pixel_format, ContextAttribs attribs)826 	public PeerInfo createPeerInfo(PixelFormat pixel_format, ContextAttribs attribs) throws LWJGLException {
827 		peer_info = new LinuxDisplayPeerInfo(pixel_format);
828 		return peer_info;
829 	}
830 
relayEventToParent(LinuxEvent event_buffer, int event_mask)831 	private void relayEventToParent(LinuxEvent event_buffer, int event_mask) {
832 		tmp_event_buffer.copyFrom(event_buffer);
833 		tmp_event_buffer.setWindow(parent_window);
834 		tmp_event_buffer.sendEvent(getDisplay(), parent_window, true, event_mask);
835 	}
836 
relayEventToParent(LinuxEvent event_buffer)837 	private void relayEventToParent(LinuxEvent event_buffer) {
838 		if (parent == null)
839 			return;
840 		switch (event_buffer.getType()) {
841 			case LinuxEvent.KeyPress:
842 				relayEventToParent(event_buffer, KeyPressMask);
843 				break;
844 			case LinuxEvent.KeyRelease:
845 				relayEventToParent(event_buffer, KeyPressMask);
846 				break;
847 			case LinuxEvent.ButtonPress:
848 				if (xembedded || !focused) relayEventToParent(event_buffer, KeyPressMask);
849 				break;
850 			case LinuxEvent.ButtonRelease:
851 				if (xembedded || !focused) relayEventToParent(event_buffer, KeyPressMask);
852 				break;
853 			default:
854 				break;
855 		}
856 	}
857 
processEvents()858 	private void processEvents() {
859 		while (LinuxEvent.getPending(getDisplay()) > 0) {
860 			event_buffer.nextEvent(getDisplay());
861 			long event_window = event_buffer.getWindow();
862 			relayEventToParent(event_buffer);
863 			if (event_window != getWindow() || event_buffer.filterEvent(event_window) ||
864 					(mouse != null && mouse.filterEvent(grab, shouldWarpPointer(), event_buffer)) ||
865 					 (keyboard != null && keyboard.filterEvent(event_buffer)))
866 				continue;
867 			switch (event_buffer.getType()) {
868 				case LinuxEvent.FocusIn:
869 					setFocused(true, event_buffer.getFocusDetail());
870 					break;
871 				case LinuxEvent.FocusOut:
872 					setFocused(false, event_buffer.getFocusDetail());
873 					break;
874 				case LinuxEvent.ClientMessage:
875 					if ((event_buffer.getClientFormat() == 32) && (event_buffer.getClientData(0) == delete_atom))
876 						close_requested = true;
877 					break;
878 				case LinuxEvent.MapNotify:
879 					dirty = true;
880 					minimized = false;
881 					break;
882 				case LinuxEvent.UnmapNotify:
883 					dirty = true;
884 					minimized = true;
885 					break;
886 				case LinuxEvent.Expose:
887 					dirty = true;
888 					break;
889 				case LinuxEvent.ConfigureNotify:
890 					int x = nGetX(getDisplay(), getWindow());
891 					int y = nGetY(getDisplay(), getWindow());
892 
893 					int width = nGetWidth(getDisplay(), getWindow());
894 					int height = nGetHeight(getDisplay(), getWindow());
895 
896 					window_x = x;
897 					window_y = y;
898 
899 					if (window_width != width || window_height != height) {
900 						resized = true;
901 						window_width = width;
902 						window_height = height;
903 					}
904 
905 					break;
906 				case LinuxEvent.EnterNotify:
907 					mouseInside = true;
908 					break;
909 				case LinuxEvent.LeaveNotify:
910 					mouseInside = false;
911 					break;
912 				default:
913 					break;
914 			}
915 		}
916 	}
917 
update()918 	public void update() {
919 		lockAWT();
920 		try {
921 			processEvents();
922 			checkInput();
923 		} finally {
924 			unlockAWT();
925 		}
926 	}
927 
reshape(int x, int y, int width, int height)928 	public void reshape(int x, int y, int width, int height) {
929 		lockAWT();
930 		try {
931 			nReshape(getDisplay(), getWindow(), x, y, width, height);
932 		} finally {
933 			unlockAWT();
934 		}
935 	}
nReshape(long display, long window, int x, int y, int width, int height)936 	private static native void nReshape(long display, long window, int x, int y, int width, int height);
937 
getAvailableDisplayModes()938 	public DisplayMode[] getAvailableDisplayModes() throws LWJGLException {
939 		lockAWT();
940 		try {
941                         incDisplay();
942                         if (current_displaymode_extension == XRANDR) {
943                                 // nGetAvailableDisplayModes cannot be trusted. Use it only for bitsPerPixel
944                                 DisplayMode[] nDisplayModes = nGetAvailableDisplayModes(getDisplay(), getDefaultScreen(), current_displaymode_extension);
945                                 int bpp = 24;
946                                 if (nDisplayModes.length > 0) {
947                                     bpp = nDisplayModes[0].getBitsPerPixel();
948                                 }
949                                 // get the resolutions and frequencys from XRandR
950                                 Screen[] resolutions = XRandR.getResolutions(XRandR.getScreenNames()[0]);
951                                 DisplayMode[] modes = new DisplayMode[resolutions.length];
952                                 for (int i = 0; i < modes.length; i++) {
953                                     modes[i] = new DisplayMode(resolutions[i].width, resolutions[i].height, bpp, resolutions[i].freq);
954                                 }
955                                 return modes;
956                         } else {
957                                 try {
958                                         DisplayMode[] modes = nGetAvailableDisplayModes(getDisplay(), getDefaultScreen(), current_displaymode_extension);
959                                         return modes;
960                                 } finally {
961                                         decDisplay();
962                                 }
963                         }
964 		} finally {
965 			unlockAWT();
966 		}
967 	}
nGetAvailableDisplayModes(long display, int screen, int extension)968 	private static native DisplayMode[] nGetAvailableDisplayModes(long display, int screen, int extension) throws LWJGLException;
969 
970 	/* Mouse */
hasWheel()971 	public boolean hasWheel() {
972 		return true;
973 	}
974 
getButtonCount()975 	public int getButtonCount() {
976 		return mouse.getButtonCount();
977 	}
978 
createMouse()979 	public void createMouse() throws LWJGLException {
980 		lockAWT();
981 		try {
982 			mouse = new LinuxMouse(getDisplay(), getWindow(), getWindow());
983 		} finally {
984 			unlockAWT();
985 		}
986 	}
987 
destroyMouse()988 	public void destroyMouse() {
989 		mouse = null;
990 		updateInputGrab();
991 	}
992 
pollMouse(IntBuffer coord_buffer, ByteBuffer buttons)993 	public void pollMouse(IntBuffer coord_buffer, ByteBuffer buttons) {
994 		lockAWT();
995 		try {
996 			mouse.poll(grab, coord_buffer, buttons);
997 		} finally {
998 			unlockAWT();
999 		}
1000 	}
1001 
readMouse(ByteBuffer buffer)1002 	public void readMouse(ByteBuffer buffer) {
1003 		lockAWT();
1004 		try {
1005 			mouse.read(buffer);
1006 		} finally {
1007 			unlockAWT();
1008 		}
1009 	}
1010 
setCursorPosition(int x, int y)1011 	public void setCursorPosition(int x, int y) {
1012 		lockAWT();
1013 		try {
1014 			mouse.setCursorPosition(x, y);
1015 		} finally {
1016 			unlockAWT();
1017 		}
1018 	}
1019 
checkInput()1020 	private void checkInput() {
1021 		if (parent == null) return;
1022 
1023 		if (xembedded) {
1024 			long current_focus_window = 0;
1025 
1026 			if (last_window_focus != current_focus_window || parent_focused != focused) {
1027 				if (isParentWindowActive(current_focus_window)) {
1028 					if (parent_focused) {
1029 						nSetInputFocus(getDisplay(), current_window, CurrentTime);
1030 						last_window_focus = current_window;
1031 						focused = true;
1032 					}
1033 					else {
1034 						// return focus to the parent proxy focus window
1035 						nSetInputFocus(getDisplay(), parent_proxy_focus_window, CurrentTime);
1036 						last_window_focus = parent_proxy_focus_window;
1037 						focused = false;
1038 					}
1039 				}
1040 				else {
1041 					last_window_focus = current_focus_window;
1042 					focused = false;
1043 				}
1044 			}
1045 		}
1046 		else {
1047 			if (parent_focus_changed && parent_focused) {
1048 				setInputFocusUnsafe(getWindow());
1049 				parent_focus_changed = false;
1050 			}
1051 		}
1052 	}
1053 
setInputFocusUnsafe(long window)1054 	private void setInputFocusUnsafe(long window) {
1055 		try {
1056 			nSetInputFocus(getDisplay(), window, CurrentTime);
1057 			nSync(getDisplay(), false);
1058 		} catch (LWJGLException e) {
1059 			// Since we don't have any event timings for XSetInputFocus, a race condition might give a BadMatch, which we'll catch and ignore
1060 			LWJGLUtil.log("Got exception while trying to focus: " + e);
1061 		}
1062 	}
1063 
nSync(long display, boolean throw_away_events)1064 	private static native void nSync(long display, boolean throw_away_events) throws LWJGLException;
1065 
1066 	/**
1067 	 * This method will check if the parent window is active when running
1068 	 * in xembed mode. Every xembed embedder window has a focus proxy
1069 	 * window that recieves all the input. This method will test whether
1070 	 * the provided window handle is the focus proxy, if so it will get its
1071 	 * parent window and then test whether this is an ancestor to our
1072 	 * current_window. If so then parent window is active.
1073 	 *
1074 	 * @param window - the window handle to test
1075 	 */
isParentWindowActive(long window)1076 	private boolean isParentWindowActive(long window) {
1077 		try {
1078 			// parent window already active as window is current_window
1079 			if (window == current_window) return true;
1080 
1081 			// xembed focus proxy will have no children
1082 			if (getChildCount(getDisplay(), window) != 0) return false;
1083 
1084 			// get parent, will be xembed embedder window and ancestor of current_window
1085 			long parent_window = getParentWindow(getDisplay(), window);
1086 
1087 			// parent must not be None
1088 			if (parent_window == None) return false;
1089 
1090 			// scroll current_window's ancestors to find parent_window
1091 			long w = current_window;
1092 
1093 			while (w != None) {
1094 				w = getParentWindow(getDisplay(), w);
1095 				if (w == parent_window) {
1096 					parent_proxy_focus_window = window; // save focus proxy window
1097 					return true;
1098 				}
1099 			}
1100 		} catch (LWJGLException e) {
1101 			LWJGLUtil.log("Failed to detect if parent window is active: " + e.getMessage());
1102 			return true; // on failure assume still active
1103 		}
1104 
1105 		return false; // failed to find an active parent window
1106 	}
1107 
setFocused(boolean got_focus, int focus_detail)1108 	private void setFocused(boolean got_focus, int focus_detail) {
1109 		if (focused == got_focus || focus_detail == NotifyDetailNone || focus_detail == NotifyPointer || focus_detail == NotifyPointerRoot || xembedded)
1110 			return;
1111 		focused = got_focus;
1112 
1113 		if (focused) {
1114 			acquireInput();
1115 		}
1116 		else {
1117 			releaseInput();
1118 		}
1119 	}
1120 
releaseInput()1121 	private void releaseInput() {
1122 		if (isLegacyFullscreen() || input_released)
1123 			return;
1124 		if ( keyboard != null )
1125 			keyboard.releaseAll();
1126 		input_released = true;
1127 		updateInputGrab();
1128 		if (current_window_mode == FULLSCREEN_NETWM) {
1129 			nIconifyWindow(getDisplay(), getWindow(), getDefaultScreen());
1130 			try {
1131 				if( current_displaymode_extension == XRANDR )
1132 				{
1133 					AccessController.doPrivileged(new PrivilegedAction<Object>() {
1134 						public Object run() {
1135 							XRandR.restoreConfiguration();
1136 							return null;
1137 						}
1138 					});
1139 				}
1140 				else
1141 				{
1142 					switchDisplayModeOnTmpDisplay(saved_mode);
1143 				}
1144 				setGammaRampOnTmpDisplay(saved_gamma);
1145 			} catch (LWJGLException e) {
1146 				LWJGLUtil.log("Failed to restore saved mode: " + e.getMessage());
1147 			}
1148 		}
1149 	}
nIconifyWindow(long display, long window, int screen)1150 	private static native void nIconifyWindow(long display, long window, int screen);
1151 
acquireInput()1152 	private void acquireInput() {
1153 		if (isLegacyFullscreen() || !input_released)
1154 			return;
1155 		input_released = false;
1156 		updateInputGrab();
1157 		if (current_window_mode == FULLSCREEN_NETWM) {
1158 			try {
1159 				switchDisplayModeOnTmpDisplay(current_mode);
1160 				setGammaRampOnTmpDisplay(current_gamma);
1161 			} catch (LWJGLException e) {
1162 				LWJGLUtil.log("Failed to restore mode: " + e.getMessage());
1163 			}
1164 		}
1165 	}
1166 
grabMouse(boolean new_grab)1167 	public void grabMouse(boolean new_grab) {
1168 		lockAWT();
1169 		try {
1170 			if (new_grab != grab) {
1171 				grab = new_grab;
1172 				updateInputGrab();
1173 				mouse.changeGrabbed(grab, shouldWarpPointer());
1174 			}
1175 		} finally {
1176 			unlockAWT();
1177 		}
1178 	}
1179 
shouldWarpPointer()1180 	private boolean shouldWarpPointer() {
1181 		return pointer_grabbed && shouldGrab();
1182 	}
1183 
getNativeCursorCapabilities()1184 	public int getNativeCursorCapabilities() {
1185 		lockAWT();
1186 		try {
1187 			incDisplay();
1188 			try {
1189 				return nGetNativeCursorCapabilities(getDisplay());
1190 			} finally {
1191 				decDisplay();
1192 			}
1193 		} catch (LWJGLException e) {
1194 			throw new RuntimeException(e);
1195 		} finally {
1196 			unlockAWT();
1197 		}
1198 	}
nGetNativeCursorCapabilities(long display)1199 	private static native int nGetNativeCursorCapabilities(long display) throws LWJGLException;
1200 
setNativeCursor(Object handle)1201 	public void setNativeCursor(Object handle) throws LWJGLException {
1202 		current_cursor = getCursorHandle(handle);
1203 		lockAWT();
1204 		try {
1205 			updateCursor();
1206 		} finally {
1207 			unlockAWT();
1208 		}
1209 	}
1210 
getMinCursorSize()1211 	public int getMinCursorSize() {
1212 		lockAWT();
1213 		try {
1214 			incDisplay();
1215 			try {
1216 				return nGetMinCursorSize(getDisplay(), getWindow());
1217 			} finally {
1218 				decDisplay();
1219 			}
1220 		} catch (LWJGLException e) {
1221 			LWJGLUtil.log("Exception occurred in getMinCursorSize: " + e);
1222 			return 0;
1223 		} finally {
1224 			unlockAWT();
1225 		}
1226 	}
nGetMinCursorSize(long display, long window)1227 	private static native int nGetMinCursorSize(long display, long window);
1228 
getMaxCursorSize()1229 	public int getMaxCursorSize() {
1230 		lockAWT();
1231 		try {
1232 			incDisplay();
1233 			try {
1234 				return nGetMaxCursorSize(getDisplay(), getWindow());
1235 			} finally {
1236 				decDisplay();
1237 			}
1238 		} catch (LWJGLException e) {
1239 			LWJGLUtil.log("Exception occurred in getMaxCursorSize: " + e);
1240 			return 0;
1241 		} finally {
1242 			unlockAWT();
1243 		}
1244 	}
nGetMaxCursorSize(long display, long window)1245 	private static native int nGetMaxCursorSize(long display, long window);
1246 
1247 	/* Keyboard */
createKeyboard()1248 	public void createKeyboard() throws LWJGLException {
1249 		lockAWT();
1250 		try {
1251 			keyboard = new LinuxKeyboard(getDisplay(), getWindow());
1252 		} finally {
1253 			unlockAWT();
1254 		}
1255 	}
1256 
destroyKeyboard()1257 	public void destroyKeyboard() {
1258 		lockAWT();
1259 		try {
1260 			keyboard.destroy(getDisplay());
1261 			keyboard = null;
1262 		} finally {
1263 			unlockAWT();
1264 		}
1265 	}
1266 
pollKeyboard(ByteBuffer keyDownBuffer)1267 	public void pollKeyboard(ByteBuffer keyDownBuffer) {
1268 		lockAWT();
1269 		try {
1270 			keyboard.poll(keyDownBuffer);
1271 		} finally {
1272 			unlockAWT();
1273 		}
1274 	}
1275 
readKeyboard(ByteBuffer buffer)1276 	public void readKeyboard(ByteBuffer buffer) {
1277 		lockAWT();
1278 		try {
1279 			keyboard.read(buffer);
1280 		} finally {
1281 			unlockAWT();
1282 		}
1283 	}
1284 
nCreateCursor(long display, int width, int height, int xHotspot, int yHotspot, int numImages, IntBuffer images, int images_offset, IntBuffer delays, int delays_offset)1285 	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;
1286 
createBlankCursor()1287 	private static long createBlankCursor() {
1288 		return nCreateBlankCursor(getDisplay(), getWindow());
1289 	}
nCreateBlankCursor(long display, long window)1290 	static native long nCreateBlankCursor(long display, long window);
1291 
createCursor(int width, int height, int xHotspot, int yHotspot, int numImages, IntBuffer images, IntBuffer delays)1292 	public Object createCursor(int width, int height, int xHotspot, int yHotspot, int numImages, IntBuffer images, IntBuffer delays) throws LWJGLException {
1293 		lockAWT();
1294 		try {
1295 			incDisplay();
1296 			try {
1297 				long cursor = nCreateCursor(getDisplay(), width, height, xHotspot, yHotspot, numImages, images, images.position(), delays, delays != null ? delays.position() : -1);
1298 				return cursor;
1299 			} catch (LWJGLException e) {
1300 				decDisplay();
1301 				throw e;
1302 			}
1303 		} finally {
1304 			unlockAWT();
1305 		}
1306 	}
1307 
getCursorHandle(Object cursor_handle)1308 	private static long getCursorHandle(Object cursor_handle) {
1309 		return cursor_handle != null ? (Long)cursor_handle : None;
1310 	}
1311 
destroyCursor(Object cursorHandle)1312 	public void destroyCursor(Object cursorHandle) {
1313 		lockAWT();
1314 		try {
1315 			nDestroyCursor(getDisplay(), getCursorHandle(cursorHandle));
1316 			decDisplay();
1317 		} finally {
1318 			unlockAWT();
1319 		}
1320 	}
nDestroyCursor(long display, long cursorHandle)1321 	static native void nDestroyCursor(long display, long cursorHandle);
1322 
getPbufferCapabilities()1323 	public int getPbufferCapabilities() {
1324 		lockAWT();
1325 		try {
1326 			incDisplay();
1327 			try {
1328 				return nGetPbufferCapabilities(getDisplay(), getDefaultScreen());
1329 			} finally {
1330 				decDisplay();
1331 			}
1332 		} catch (LWJGLException e) {
1333 			LWJGLUtil.log("Exception occurred in getPbufferCapabilities: " + e);
1334 			return 0;
1335 		} finally {
1336 			unlockAWT();
1337 		}
1338 	}
nGetPbufferCapabilities(long display, int screen)1339 	private static native int nGetPbufferCapabilities(long display, int screen);
1340 
isBufferLost(PeerInfo handle)1341 	public boolean isBufferLost(PeerInfo handle) {
1342 		return false;
1343 	}
1344 
createPbuffer(int width, int height, PixelFormat pixel_format, ContextAttribs attribs, IntBuffer pixelFormatCaps, IntBuffer pBufferAttribs)1345 	public PeerInfo createPbuffer(int width, int height, PixelFormat pixel_format, ContextAttribs attribs,
1346 			IntBuffer pixelFormatCaps,
1347 			IntBuffer pBufferAttribs) throws LWJGLException {
1348 		return new LinuxPbufferPeerInfo(width, height, pixel_format);
1349 	}
1350 
setPbufferAttrib(PeerInfo handle, int attrib, int value)1351 	public void setPbufferAttrib(PeerInfo handle, int attrib, int value) {
1352 		throw new UnsupportedOperationException();
1353 	}
1354 
bindTexImageToPbuffer(PeerInfo handle, int buffer)1355 	public void bindTexImageToPbuffer(PeerInfo handle, int buffer) {
1356 		throw new UnsupportedOperationException();
1357 	}
1358 
releaseTexImageFromPbuffer(PeerInfo handle, int buffer)1359 	public void releaseTexImageFromPbuffer(PeerInfo handle, int buffer) {
1360 		throw new UnsupportedOperationException();
1361 	}
1362 
1363 	/**
1364 	 * This method will convert icon bytebuffers into a single bytebuffer
1365 	 * as the icon format required by _NET_WM_ICON should be in a cardinal
1366 	 * 32 bit ARGB format i.e. all icons in a single buffer the data starting
1367 	 * with 32 bit width & height followed by the color data as 32bit ARGB.
1368 	 *
1369 	 * @param icons Array of icons in RGBA format
1370 	 */
convertIcons(ByteBuffer[] icons)1371 	private static ByteBuffer convertIcons(ByteBuffer[] icons) {
1372 
1373 		int bufferSize = 0;
1374 
1375 		// calculate size of bytebuffer
1376 		for ( ByteBuffer icon : icons ) {
1377 			int size = icon.limit() / 4;
1378 			int dimension = (int)Math.sqrt(size);
1379 			if ( dimension > 0 ) {
1380 				bufferSize += 2 * 4; // add 32 bit width & height, 4 bytes each
1381 				bufferSize += dimension * dimension * 4;
1382 			}
1383 		}
1384 
1385 		if (bufferSize == 0) return null;
1386 
1387 		ByteBuffer icon_argb = BufferUtils.createByteBuffer(bufferSize);//icon.capacity()+(2*4));
1388 		icon_argb.order(ByteOrder.BIG_ENDIAN);
1389 
1390 		for ( ByteBuffer icon : icons ) {
1391 			int size = icon.limit() / 4;
1392 			int dimension = (int)Math.sqrt(size);
1393 
1394 			icon_argb.putInt(dimension); // width
1395 			icon_argb.putInt(dimension); // height
1396 
1397 			for (int y = 0; y < dimension; y++) {
1398 				for (int x = 0; x < dimension; x++) {
1399 
1400 					byte r = icon.get((x*4)+(y*dimension*4));
1401 					byte g = icon.get((x*4)+(y*dimension*4)+1);
1402 					byte b = icon.get((x*4)+(y*dimension*4)+2);
1403 					byte a = icon.get((x*4)+(y*dimension*4)+3);
1404 
1405 					icon_argb.put(a);
1406 					icon_argb.put(r);
1407 					icon_argb.put(g);
1408 					icon_argb.put(b);
1409 				}
1410 			}
1411 		}
1412 
1413 		return icon_argb;
1414 	}
1415 
1416 	/**
1417 	 * Sets one or more icons for the Display.
1418 	 * <ul>
1419 	 * <li>On Windows you should supply at least one 16x16 icon and one 32x32.</li>
1420 	 * <li>Linux (and similar platforms) expect one 32x32 icon.</li>
1421 	 * <li>Mac OS X should be supplied one 128x128 icon</li>
1422 	 * </ul>
1423 	 * The implementation will use the supplied ByteBuffers with image data in RGBA and perform any conversions necessary for the specific platform.
1424 	 *
1425 	 * @param icons Array of icons in RGBA mode
1426 	 * @return number of icons used.
1427 	 */
setIcon(ByteBuffer[] icons)1428 	public int setIcon(ByteBuffer[] icons) {
1429 		lockAWT();
1430 		try {
1431 			incDisplay();
1432 			try {
1433 				// get icons as cardinal ARGB format
1434 				ByteBuffer icons_data = convertIcons(icons);
1435 				if (icons_data == null) return 0;
1436 				nSetWindowIcon(getDisplay(), getWindow(), icons_data, icons_data.capacity());//, icon_mask, icon_mask.capacity(), dimension, dimension);
1437 				return icons.length;
1438 			} finally {
1439 				decDisplay();
1440 			}
1441 		} catch (LWJGLException e) {
1442 			LWJGLUtil.log("Failed to set display icon: " + e);
1443 			return 0;
1444 		} finally {
1445 			unlockAWT();
1446 		}
1447 	}
1448 
nSetWindowIcon(long display, long window, ByteBuffer icons_data, int icons_size)1449 	private static native void nSetWindowIcon(long display, long window, ByteBuffer icons_data, int icons_size);
1450 
getX()1451 	public int getX() {
1452 		return window_x;
1453 	}
1454 
getY()1455 	public int getY() {
1456 		return window_y;
1457 	}
1458 
getWidth()1459 	public int getWidth() {
1460 		return window_width;
1461 	}
1462 
getHeight()1463 	public int getHeight() {
1464 		return window_height;
1465 	}
1466 
isInsideWindow()1467 	public boolean isInsideWindow() {
1468 		return mouseInside;
1469 	}
1470 
setResizable(boolean resizable)1471 	public void setResizable(boolean resizable) {
1472 		if (this.resizable == resizable) {
1473 			return;
1474 		}
1475 
1476 		this.resizable = resizable;
1477 		nSetWindowSize(getDisplay(), getWindow(), window_width, window_height, resizable);
1478 	}
1479 
wasResized()1480 	public boolean wasResized() {
1481 		if (resized) {
1482 			resized = false;
1483 			return true;
1484 		}
1485 
1486 		return false;
1487 	}
1488 
getPixelScaleFactor()1489 	public float getPixelScaleFactor() {
1490 		return 1f;
1491 	}
1492 
1493 	/**
1494 	 * Helper class for managing Compiz's workarounds. We need this to enable Legacy
1495 	 * Fullscreen Support in Compiz, else we'll have trouble with fullscreen windows
1496 	 * when Compiz effects are enabled.
1497 	 *
1498 	 * Implementation Note: This code is probably too much for an inner class, but
1499 	 * keeping it here until we're sure we cannot find a better solution.
1500 	 */
1501 	private static final class Compiz {
1502 
1503 		private static boolean applyFix;
1504 
1505 		private static Provider provider;
1506 
Compiz()1507 		private Compiz() {
1508 		}
1509 
init()1510 		static void init() {
1511 			if ( Display.getPrivilegedBoolean("org.lwjgl.opengl.Window.nocompiz_lfs") )
1512 				return;
1513 
1514 			AccessController.doPrivileged(new PrivilegedAction<Object>() {
1515 				public Object run() {
1516 					try {
1517 						// Check if Compiz is active
1518 						if ( !isProcessActive("compiz") )
1519 							return null;
1520 
1521 						provider = null;
1522 
1523 						String providerName = null;
1524 
1525 						// Check if Dbus is available
1526 						if ( isProcessActive("dbus-daemon") ) {
1527 							providerName = "Dbus";
1528 							provider = new Provider() {
1529 
1530 								private static final String KEY = "/org/freedesktop/compiz/workarounds/allscreens/legacy_fullscreen";
1531 
1532 								public boolean hasLegacyFullscreenSupport() throws LWJGLException {
1533 									final List output = Compiz.run(
1534 										"dbus-send", "--print-reply", "--type=method_call", "--dest=org.freedesktop.compiz", KEY, "org.freedesktop.compiz.get"
1535 									);
1536 
1537 									if ( output == null || output.size() < 2 )
1538 										throw new LWJGLException("Invalid Dbus reply.");
1539 
1540 									String line = (String)output.get(0);
1541 
1542 									if ( !line.startsWith("method return") )
1543 										throw new LWJGLException("Invalid Dbus reply.");
1544 
1545 									line = ((String)output.get(1)).trim(); // value
1546 									if ( !line.startsWith("boolean") || line.length() < 12)
1547 										throw new LWJGLException("Invalid Dbus reply.");
1548 
1549 									return "true".equalsIgnoreCase(line.substring("boolean".length() + 1));
1550 								}
1551 
1552 								public void setLegacyFullscreenSupport(final boolean state) throws LWJGLException {
1553 									if ( Compiz.run(
1554 										"dbus-send", "--type=method_call", "--dest=org.freedesktop.compiz", KEY, "org.freedesktop.compiz.set", "boolean:" + Boolean.toString(state)
1555 									) == null )
1556 										throw new LWJGLException("Failed to apply Compiz LFS workaround.");
1557 								}
1558 							};
1559 						} else {
1560 							try {
1561 								// Check if Gconf is available
1562 								Runtime.getRuntime().exec("gconftool");
1563 
1564 								providerName = "gconftool";
1565 								provider = new Provider() {
1566 
1567 									private static final String KEY = "/apps/compiz/plugins/workarounds/allscreens/options/legacy_fullscreen";
1568 
1569 									public boolean hasLegacyFullscreenSupport() throws LWJGLException {
1570 										final List output = Compiz.run(new String[] {
1571 											"gconftool", "-g", KEY
1572 										});
1573 
1574 										if ( output == null || output.size() == 0 )
1575 											throw new LWJGLException("Invalid gconftool reply.");
1576 
1577 										return Boolean.parseBoolean(((String)output.get(0)).trim());
1578 									}
1579 
1580 									public void setLegacyFullscreenSupport(final boolean state) throws LWJGLException {
1581 										if ( Compiz.run(new String[] {
1582 											"gconftool", "-s", KEY, "-s", Boolean.toString(state), "-t", "bool"
1583 										}) == null )
1584 											throw new LWJGLException("Failed to apply Compiz LFS workaround.");
1585 
1586 										if ( state ) {
1587 											try {
1588 												// gconftool will not apply the workaround immediately, sleep a bit
1589 												// to make sure it will be ok when we create the window.
1590 												Thread.sleep(200); // 100 is too low, 150 works, set to 200 to be safe.
1591 											} catch (InterruptedException e) {
1592 												e.printStackTrace();
1593 											}
1594 										}
1595 									}
1596 								};
1597 							} catch (IOException e) {
1598 								// Ignore
1599 							}
1600 						}
1601 
1602 						if ( provider != null && !provider.hasLegacyFullscreenSupport() ) { // No need to do anything if LFS is already enabled.
1603 							applyFix = true;
1604 							LWJGLUtil.log("Using " + providerName + " to apply Compiz LFS workaround.");
1605 						}
1606 					} catch (LWJGLException e) {
1607 						// Ignore
1608 					} finally {
1609 						return null;
1610 					}
1611 				}
1612 			});
1613 		}
1614 
setLegacyFullscreenSupport(final boolean enabled)1615 		static void setLegacyFullscreenSupport(final boolean enabled) {
1616 			if ( !applyFix )
1617 				return;
1618 
1619 			AccessController.doPrivileged(new PrivilegedAction<Object>() {
1620 				public Object run() {
1621 					try {
1622 						provider.setLegacyFullscreenSupport(enabled);
1623 					} catch (LWJGLException e) {
1624 						LWJGLUtil.log("Failed to change Compiz Legacy Fullscreen Support. Reason: " + e.getMessage());
1625 					}
1626 					return null;
1627 				}
1628 			});
1629 		}
1630 
run(final String... command)1631 		private static List<String> run(final String... command) throws LWJGLException {
1632 			final List<String> output = new ArrayList<String>();
1633 
1634 			try {
1635 				final Process p = Runtime.getRuntime().exec(command);
1636 				try {
1637 					final int exitValue = p.waitFor();
1638 					if ( exitValue != 0 )
1639 						return null;
1640 				} catch (InterruptedException e) {
1641 					throw new LWJGLException("Process interrupted.", e);
1642 				}
1643 
1644 				final BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
1645 
1646 				String line;
1647 				while ( (line = br.readLine()) != null )
1648 					output.add(line);
1649 
1650 				br.close();
1651 			} catch (final IOException e) {
1652 				throw new LWJGLException("Process failed.", e);
1653 			}
1654 
1655 			return output;
1656 		}
1657 
isProcessActive(final String processName)1658 		private static boolean isProcessActive(final String processName) throws LWJGLException {
1659 			final List<String> output = run(new String[] { "ps", "-C", processName });
1660 			if ( output == null )
1661 				return false;
1662 
1663 			for ( final String line : output ) {
1664 				if ( line.contains(processName) )
1665 					return true;
1666 			}
1667 
1668 			return false;
1669 		}
1670 
1671 		private interface Provider {
1672 
hasLegacyFullscreenSupport()1673 			boolean hasLegacyFullscreenSupport() throws LWJGLException;
1674 
setLegacyFullscreenSupport(boolean state)1675 			void setLegacyFullscreenSupport(boolean state) throws LWJGLException;
1676 
1677 		}
1678 	}
1679 
1680 }
1681