Have been trying to work with 'lwjgl' on linux and am running into an issue when I run my compiled code from the terminal. I am using the stable release of lwjgl 3.
I downloaded the lwjgl.jar from the website and run the command javac -cp lwjgl.jar: Main.java
which compiles the code fine. Then I run: java -cp lwjgl.jar: Main after and it throws this error;
[LWJGL] Failed to load a library. Possible solutions:
a) Set -Djava.library.path or -Dorg.lwjgl.librarypath to the directory that contains the shared libraries.
b) Add the JAR(s) containing the shared libraries to the classpath.
[LWJGL] Enable debug mode with -Dorg.lwjgl.util.Debug=true for better diagnostics.
Exception in thread "EndlessRunner" java.lang.UnsatisfiedLinkError: Failed to locate library: liblwjgl.so
at org.lwjgl.system.Library.loadSystemRelative(Library.java:100)
at org.lwjgl.system.Library.loadSystem(Library.java:71)
at org.lwjgl.system.Library.<clinit>(Library.java:43)
at org.lwjgl.system.MemoryAccess.<clinit>(MemoryAccess.java:17)
at org.lwjgl.system.Pointer.<clinit>(Pointer.java:22)
at org.lwjgl.glfw.GLFW.<clinit>(GLFW.java:562)
at Main.init(Main.java:31)
at Main.run(Main.java:78)
at java.lang.Thread.run(Thread.java:745)
I'm not sure if I missed some files that I needed too or if I'm going about this entirely the wrong way. Here is the code I am using, it's just some that I found online and I'm using as a test.
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryUtil.*;
import java.nio.ByteBuffer;
import org.lwjgl.glfw.GLFWVidMode;
public class Main implements Runnable{
private Thread thread;
public boolean running = true;
private long window;
private int width = 1200, height = 800;
public static void main(String args[]){
Main game = new Main();
game.start();
}
public void start(){
running = true;
thread = new Thread(this, "EndlessRunner");
thread.start();
}
public void init(){
// Initializes our window creator library - GLFW
// This basically means, if this glfwInit() doesn't run properlly
// print an error to the console
if(glfwInit() != true){
// Throw an error.
System.err.println("GLFW initialization failed!");
}
// Allows our window to be resizable
glfwWindowHint(GLFW_RESIZABLE, GL_TRUE);
// Creates our window. You'll need to declare private long window at the
// top of the class though.
// We pass the width and height of the game we want as well as the title for
// the window. The last 2 NULL parameters are for more advanced uses and you
// shouldn't worry about them right now.
window = glfwCreateWindow(width, height, "Endless Runner", NULL, NULL);
// This code performs the appropriate checks to ensure that the
// window was successfully created.
// If not then it prints an error to the console
if(window == NULL){
// Throw an Error
System.err.println("Could not create our Window!");
}
// creates a bytebuffer object 'vidmode' which then queries
// to see what the primary monitor is.
//ByteBuffer vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
// Sets the initial position of our game window.
glfwSetWindowPos(window, 100, 100);
// Sets the context of GLFW, this is vital for our program to work.
glfwMakeContextCurrent(window);
// finally shows our created window in all it's glory.
glfwShowWindow(window);
}
public void update(){
// Polls for any window events such as the window closing etc.
glfwPollEvents();
}
public void render(){
// Swaps out our buffers
glfwSwapBuffers(window);
}
#Override
public void run() {
// All our initialization code
init();
// Our main game loop
while(running){
update();
render();
// Checks to see if either the escape button or the
// red cross at the top were pressed.
// if so sets our boolean to false and closes the
// thread.
if(glfwWindowShouldClose(window) == true){
running = false;
}
}
}
}
Any help you guys can give would be very much appreciated.
Thanks.
I can only speak from using NetBeans 8 to run LWJGL 3, but I also got the same error. The problem I found had to do with the "native" jar files needing to be added to the "classpath" tab when setting up LWJGL initially. The reason for this is that it enables LWJGL to automatically find the native jars. Then under your VM settings you will want to set it to:
-Djava.library.path="Path to where you extracted JAR files"
Only include quotes if your path name includes any spaces
You can add lwjgl.jar to module path and lwjgl-native-\*.jar to classpath. Other too. For example: add lwjgl-opengl.jar to module path and lwjgl-opengl-native-\*.jar to classpath. That's work.
Related
I'm currently making a reading app in Java, and this is my main menu.
What I want is that when I press the bottom button after selecting a book another window with the book opens. What I did now is a function that will open the other window while closing the one I'm currently in to free a little bit of memory.
I first close the current window after retrieving the necessary information from it (the index of the book) like this:
btnOuvrirLeLivre.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
int index = list.getSelectionIndex();
LiseuseController controller = new LiseuseController(null, null);
parent.dispose();
controller.viewBookController(index);
}
});
(I'm using the MVC method for my project), parent is just the composite used to open the frame.
public void viewBookController(int index) {
Display displayBook = new Display();
Shell shellBook = new Shell(displayBook);
shellBook.setLayout(new GridLayout(1, false));
index += 250;
LiseuseView lecture = new LiseuseView(shellBook, SWT.NONE, index);
shellBook.pack();
shellBook.open();
while(!shellBook.isDisposed()) {
if(!displayBook.readAndDispatch())
displayBook.sleep();
}
displayBook.dispose();
}
The index is just the book's number in my database, everything should be fine but I get this error when I do this after pressing the button to open the book:
Exception in thread "main" org.eclipse.swt.SWTException: Invalid thread access
at org.eclipse.swt.SWT.error(SWT.java:4875)
at org.eclipse.swt.SWT.error(SWT.java:4790)
at org.eclipse.swt.SWT.error(SWT.java:4761)
at org.eclipse.swt.widgets.Display.checkDisplay(Display.java:824)
at org.eclipse.swt.widgets.Display.create(Display.java:887)
at org.eclipse.swt.graphics.Device.<init>(Device.java:126)
at org.eclipse.swt.widgets.Display.<init>(Display.java:563)
at org.eclipse.swt.widgets.Display.<init>(Display.java:554)
at Controller.LiseuseController.viewBookController(LiseuseController.java:133)
at View.LiseuseHome$4.widgetSelected(LiseuseHome.java:81)
at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:252)
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:89)
at org.eclipse.swt.widgets.Display.sendEvent(Display.java:4209)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1037)
at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:4026)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3626)
at Controller.LiseuseController.viewController(LiseuseController.java:126)
at Main.Main.main(Main.java:59)
(LiseuseController.java:133) contains "Display displayBook = new Display();"
And (LiseuseController.java:126) contains
if(!display.readAndDispatch())
display.sleep();
This is from the other function used to open the first window.
I don't really understand what is causing this error and I can't just put one in "visible" and the other one "invisible" like if I'm using Jframe because the 2 windows are on 2 different .java files.
Do you have any idea on how to fix this?
From the API documentation of org.eclipse.swt.widgets.Display
Applications which are built with SWT will almost always require only
a single display. In particular, some platforms which SWT supports
will not allow more than one active display. In other words, some
platforms do not support creating a new display if one already exists
that has not been sent the dispose() message.
You should therefore adapt your application to use only one instance of Display. Since the class LiceuseController already manages opening the initial window and the supplementary window(s), it seems fitting for it to manage an instance of Display for both uses.
This instance should be created at the application's start, maintained by the LiceuseController class and finally disposed when the application shuts down.
Another problem is that none of the methods in LiceuseController actually returns. As you can see from the stack trace, Display.readAndDispatch from LiseuseController.viewController is still active when you are creating the new window. I guess you also want to reopen the original window when the supplementary window is closed. Opening and closing windows in this manner will, however, endlessly increase your call stack until you end up with a StackOverflowException.
Instead, the LiceuseController should be able to create windows without outside interference. Therefore, instead of actively calling a method in the controller class to open another window, the listener should only tell the controller what window it should open next when the current window was closed.
An example could look like
public enum WindowType {
MAIN, BOOK, NONE
}
public class LiceuseController {
Display display;
WindowType nextToOpen = WindowType.MAIN;
public LiceuseController() {
display = new Display();
}
public void setNextToOpen(WindowType value) {
nextToOpen = value;
}
public void run() {
for (boolean run = true; run;) {
switch(nextToOpen) {
case MAIN:
viewController();
break;
case BOOK:
viewBookController();
break;
case NONE:
run = false;
break;
default:
throw new RuntimeException("unexpected enum constant");
}
}
/*
* Depending on how you want to manage the instance of this class,
* you could also extract this into a separate method.
*/
display.dispose();
}
private void viewController() {
// open main window using 'display'
}
private void viewBookController() {
// open book window using 'display'
}
}
so that a listener only needs to call LiceuseController.setNextToOpen and then close the current window. This will cause either viewController or viewBookController to return after which the loop will reenter and open the requested window. To shut down the application, call setNextToOpen with WindowType.NONE.
I am new to using LWJGL (and relatively so to Java in general) and as such, I was reading their page on getting started where they supplied an example Hello World program. I tried to run it myself and found that it errored with the following:
2020-03-18 10:20:02.145 java[19779:1119716] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '+[NSUndoManager(NSInternal) _endTopLevelGroupings] is only safe to invoke on the main thread.
I was confused as to what this meant because I didn't think the init() method was running in any thread other than main, and so I added a line to print which thread it was running in, and sure enough, it printed Thread[main,5,main].
What is going on here, and how can I fix it?
The full code is below for context.
import org.lwjgl.glfw.*;
import org.lwjgl.opengl.*;
import org.lwjgl.system.*;
import java.nio.*;
import static org.lwjgl.glfw.Callbacks.*;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryStack.*;
import static org.lwjgl.system.MemoryUtil.*;
public class HelloWorld {
// The window handle
private long window;
public void run() {
System.out.println("Hello LWJGL " + Version.getVersion() + "!");
init();
loop();
// Free the window callbacks and destroy the window
glfwFreeCallbacks(window);
glfwDestroyWindow(window);
// Terminate GLFW and free the error callback
glfwTerminate();
glfwSetErrorCallback(null).free();
}
private void init() {
// Setup an error callback. The default implementation
// will print the error message in System.err.
GLFWErrorCallback.createPrint(System.err).set();
// Initialize GLFW. Most GLFW functions will not work before doing this.
if ( !glfwInit() )
throw new IllegalStateException("Unable to initialize GLFW");
// Configure GLFW
glfwDefaultWindowHints(); // optional, the current window hints are already the default
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable
// This should be running on main?
System.out.println(Thread.currentThread());
// Create the window -- This is where the problem is
window = glfwCreateWindow(300, 300, "Hello World!", NULL, NULL);
if ( window == NULL )
throw new RuntimeException("Failed to create the GLFW window");
// Setup a key callback. It will be called every time a key is pressed, repeated or released.
glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> {
if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE )
glfwSetWindowShouldClose(window, true); // We will detect this in the rendering loop
});
// Get the thread stack and push a new frame
try ( MemoryStack stack = stackPush() ) {
IntBuffer pWidth = stack.mallocInt(1); // int*
IntBuffer pHeight = stack.mallocInt(1); // int*
// Get the window size passed to glfwCreateWindow
glfwGetWindowSize(window, pWidth, pHeight);
// Get the resolution of the primary monitor
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
// Center the window
glfwSetWindowPos(
window,
(vidmode.width() - pWidth.get(0)) / 2,
(vidmode.height() - pHeight.get(0)) / 2
);
} // the stack frame is popped automatically
// Make the OpenGL context current
glfwMakeContextCurrent(window);
// Enable v-sync
glfwSwapInterval(1);
// Make the window visible
glfwShowWindow(window);
}
private void loop() {
// This line is critical for LWJGL's interoperation with GLFW's
// OpenGL context, or any context that is managed externally.
// LWJGL detects the context that is current in the current thread,
// creates the GLCapabilities instance and makes the OpenGL
// bindings available for use.
GL.createCapabilities();
// Set the clear color
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
// Run the rendering loop until the user has attempted to close
// the window or has pressed the ESCAPE key.
while ( !glfwWindowShouldClose(window) ) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer
glfwSwapBuffers(window); // swap the color buffers
// Poll for window events. The key callback above will only be
// invoked during this call.
glfwPollEvents();
}
}
public static void main(String[] args) {
new HelloWorld().run();
}
}
There appears to be multiple issues with the example presented by the LWJGl3.org group.
1> The glfwInit() function call may need to be called a good deal earlier. this function needs to be called before other LWJGL function calls according to the library documentation.
1.1> Suggestion: place the call in the main()
public static void main(String[] args) {
glfwInit();
.....
1.2 OR- Suggestion: place it by itself as the first statement of the init() function
private void init() {
glfwInit();
....
1.3>OR- Suggestion:Create a class constructor and place the glfwInit(); function call in it so you are guaranteed that it is call early.
2> The import static declarations may need to be be altered
2.1> Suggestion: Try
import static org.lwjgl.glfw.GLFW.* ; // fires glfwInit;
//not helpful import static org.lwjgl.glfw.GLFW.glfwSetKeyCallback;
import static org.lwjgl.glfw.Callbacks.*;
import static org.lwjgl.glfw.GLFW.glfwSetKeyCallback;
//import static org.lwjgl.glfw.GLFWKeyCallback.*; //fails to do any thing
3> Give the "window" class variable a unique name in its declaration to avoid name collisions with the "window" associated with the Lambda function call --discussed later.
3.1 Suggestion:
// The window handle
private long windowid;
4> The init() function needs to have "local variable declarations;
int key;
int scancode;
int action ;
int mods;
5> The example as presented employs the new JRE8 feature Lambda function syntax: () -> trick. However Lambda functions are not "supported" for Eclipse 3.8.1 (ubuntu 16.04) already running on JRE8 but with Java SDK compliance set by default to 1.7. That required substantial child-birthing to move to Eclipse 2020-6R. Unfortunately, the effort is now in my Limbo like CPM86.
5.1> A "possible" work around may evolve from following Warmful Development's Setting up LWJGL 3 with Eclipse video at https://www.youtube.com/watch?v=VH9KAhjXVFM. The plan is to scan his series to see if a possible solution can evolve to my specific need.
I have three Run executable or batch file actions that invokes my launch scripts, tails into the log files respectively, and verifies that each service has started entirely. I would have used the native launch action in install4j but these services need to start in order - synchronously - and take up to 30 seconds per service.
After each action, I have a Run script action that verifies the return code and determines whether the rest of the scripts should execute as well. On the form itself, it shows the installed components and I would like to add an icon and change the color of each label after it successfully launches.
If this has confused you, please see the screen shot below to get a better under standing.
And as always, thanks for your support.
Chris
The trick is how to get the form environment of a screen from a "Run script" action. A screen that can contain form components is an instance of com.install4j.api.screens.FormPanelContainer and that class provides access to the com.install4j.api.formcomponents.FormEnvironment.
In a "Run script" action, you can do this:
import java.awt.EventQueue;
import java.awt.Color;
EventQueue.invokeLater(new Runnable() {
public void run() {
FormEnvironment formEnvironment =
((FormPanelContainer)context.getScreenById("screenId")).getFormEnvironment();
JComponent label = (JComponent)formEnvironment.getFormComponentById("componentId").
getConfigurationObject();
label.setForeground(Color.MAGENTA);
}
});
return true;
with the appropriate values for "screenId" and "componentId".
for a more reusable solution add
import java.awt.EventQueue;
import java.awt.Color;
public static void changeColor(final String screenId, final String componentId,
final Color color, final Context context)
{
EventQueue.invokeLater(new Runnable() {
public void run() {
FormEnvironment formEnvironment =
((FormPanelContainer)context.getScreenById(screenId)).getFormEnvironment();
JComponent label = (JComponent)formEnvironment.getFormComponentById(componentId).
getConfigurationObject();
label.setForeground(color);
}
});
}
to the static code on the "Installer->Custom Code & resources" step (install4j 6+) and call
changeColor("screenId", "componentId", java.awt.Color.GREEN, context);
in your "Run script" actions.
For setting an icon, you have to define the "Icon" property on one label component and use the "Initialization script" property to save it in the context and remove it from the label:
context.setVariable("checkIcon", configurationObject.getIcon());
configurationObject.setIcon(null);
Then after the call to label.setForeground(); in the above code snippets, you can call
label.setIcon((Icon)context.getVariable("checkIcon"));
I'm using Processing 2.0.3 on Windows 8. I tried to use the following code but I've no idea why my sketch couldn't run when I'm using the processing.video.* library:
import processing.video.*;
Capture cam;
void setup() {
size(200, 200);
cam = new Capture(this);
cam.start();
}
void draw() {
if (cam.available()) {
// Reads the new frame
cam.read();
}
image(cam, 0, 0);
}
I notice that the sketch will get stuck and will not open the sketch applet window at all if I call anything related to the Capture class. Calling println(Capture.list()); for example will cause the sketch to stuck at where ever that line was called.
What do I have to do to resolve this problem?
Update 12/21:
7u10 was recently released. Confirmed that:
The issue still persists
Thankfully, the workaround still functions!
Update 11/7:
And we have a workaround!
Leonid Romanov from Oracle on the openjdk.java.net mailing list provided some insight as to what's going on:
Well, although I'm not 100% sure yet, but it looks like when we enter full screen some other window becomes the first responder, hence the beep. Could you please try the following workaround: after calling setFullScreenWindow() on a frame, call setVisible(false) followed by setVisible(true). This, in theory, should restore the correct first responder.
The snippet of code that seems to work is simply:
dev.setFullScreenWindow(f);
f.setVisible(false);
f.setVisible(true);
I have updated the sample code with the ability to toggle this fix on and off; it is required every time a window enters fullscreen.
In the larger context of my more complex application, I am still running into keyboard focus issues on subcomponents within the fullscreen window, where a mouse click causes my window to lose focus. (I'm guessing it's going to the undesired first responder window referenced above.) I'll report back when I have more information about this case - I cannot reproduce it in the smaller sample yet.
Update 10/31:
Major update to the sample code:
Includes toggle between FullScreen exclusive and Lion-style FullScreen modes
Listens to the KeyboardFocusManager to display the hierarchy for the currently focused component
Uses both input maps and KeyListeners to try to capture input
Also did some more digging with coworkers to try to isolate issues:
On one front, we tried overriding some methods in RT.jar to see if there were problems with the way the screen device is being selected. Also tried were the entry points to the Toolkit.beep() functionality to see if the alert sounds were coming from the Java side - appears not.
On another front, it was clear that not even the native side is receiving keyboard events. A coworker attributes this to a switch from an AWTView to a NSWindow in 7u6.
A selection of existing Oracle bugs has been found, which you can look up here:
8000276 : [macosx] graphicsDevice.setFullScreenWindow(frame) crashes JVM
8000430 : [macosx] java.awt.FileDialog issues on macosx
7175707 : [macosx] PIT: 8 b43 Not running on AppKit thread issue again
Update 10/26:
Thanks to the comment by #maslovalex below regarding an Applet working on 7u5, I went back and painstakingly examined compatibility with the JDK versions for OSX:
10.7.1 with 7u4: Fullscreen Works!
10.7.1 with 7u5: Fullscreen Works!
10.7.5 with 7u5: Fullscreen Works!
10.7.5 with 7u6: Fullscreen Breaks :(
Combined with the other tests noted elsewhere, it's clear there was an issue introduced with 7u6 that remains in 7u7 and 7u9, and it affects both Lion 10.7 and Mountain Lion 10.8.
7u6 was a major milestone release, bringing full support of the JRE and JDK to Mac OS X and also including Java FX as part of the distribution. Further info is available in the Release Notes and the Roadmap. It's not tremendously surprising that such an issue could crop up as support shifts to Java FX.
The question becomes:
Will Oracle fix this in a near-term release of the JDK? (If you have links to existing bugs, please include them here.)
Is a workaround possible in the interim?
Other updates from today:
I incorporated the Apple extensions approach to fullscreen mode as an alternate path of exploration (updated sample code pending cleanup). The good news: input works! The bad news: there really don't seem to be any kiosking/isolation options.
I tried killing the Dock - directly or with an App - as I understand the Dock is responsible for Command-Tab app switching, Mission Control, and Launch Pad, only to find out that it's responsible for the handling of fullscreen apps as well! As such, the Java calls become non-functional and never return.
If there's a way to disable Command-Tab (and Mission Control and Launchpad and Spaces) without affecting the Dock's fullscreen handling, that would be extremely useful. Alternatively, one can try to remap certain keys such as Command, but that will affect the ability to use that modifier elsewhere in the program and the system itself (not exactly ideal, when you need to Command-C to copy some text).
I've had no luck with KeyListeners (I'm not receiving any callbacks), but I have a few more options to try.
Based on a coworker's suggestion, I tried ((sun.lwawt.macosx.LWCToolkit)Toolkit.getDefaultToolkit()).isApplicationActive() via reflection. The idea was that it:
is a native method with the comment "Returns true if the application (one of its windows) owns keyboard focus.". Calls to this method were added in CPlatformWindow.java in the past few months related to focus logic. If it returns false in your test code, it's probably part of the problem.
Unfortunately, everywhere I checked it, the method returned true. So even according to the low level system, my windows should have keyboard focus.
My previous optimism regarding the JAlbum fix has been dashed. The developer posted a response on their forum that explains how they simply removed proper fullscreen support on OS X while running Java 7. They have a bug into Oracle (and I'm hoping to get the bug number).
Update 10/25:
I have now also tried Java 7u9 on Lion 10.7.4 and have seen the exact same issue, so it's JDK- not OS-specific.
The core question has become whether you can embed in a fullscreen window core Swing Components that have default handling for keyboard input (JTextField/JTextArea or even editable combo boxes) and expect them to behave normally (without having to resort to rebuilding their basic key bindings manually). Also in question is whether other stalwarts of windowed layouts, such as using tab for focus traversal, should work.
The ideal goal would be to have the ability take a windowed Swing app with all of its buttons, tabs, fields, etc. and run it in fullscreen exclusive/kiosk mode with most functionality intact. (Previously, I have seen that Dialog pop ups or ComboBox drop downs do not function in fullscreen on Java 6 on OS X, but other Components behave fine.)
I'll be looking into the eawt FullScreen capabilities, which will be interesting if they support kiosk lock down options, such as eliminating Command-Tab application switching.
Original Question:
I have a Swing app that for years has supported FullScreen (exclusive) mode on Mac OS X up through Java 6. I've been doing compatibility testing with the latest Mountain Lion release (10.8.2 Supplemental) and Oracle's JDK 7 and noticed a glaring issue while in that mode: mouse movement and clicks work fine, but keyboard input is not delivered to the components.
(I've narrowed this down in a test case below to not being able to type into a simple JTextField while in fullscreen mode.)
One symptom is that each key press results in a system beep, as if the OS is disallowing keyboard events from being delivered to the application.
Separately, my application has an exit hook installed, and the Command-Q combo will trigger that hook - it's clear that the OS is listening to standard key combos.
I have tested this separately on three different Macs with various installs:
On Apple Java 6u35 and 6u37: both windowed and fullscreen modes receive input.
On Oracle Java 7u7 and 7u9: windowed mode works as expected while fullscreen has the symptoms above.
This may have been previously reported:
Java Graphics Full Screen Mode not Registering Keyboard Input.
However, that question is not specific about the Java version or platform.
Additional searching has turned up a separate fullscreen option introduced in Lion:
Fullscreen feature for Java Apps on OSX Lion.
I have yet to try using this approach, as keyboard input seems integral to the target use cases of FullScreen Exclusive mode, such as games.
There is some mention in the JavaDoc for this mode that input methods might be disabled. I tried to call the suggested Component.enableInputMethods(false), but it seemed to have no effect.
I'm somewhat optimistic that there's a solution to this issue based on an entry in the release notes of a Java app I came across (JAlbum). A stated fix for 10.10.6: "Keyboard support wasn't working when running the full screen slide show on Mac and Java 7"
My test case is below. It is lightly modified from the second example in this issue (which, unmodified, also exhibits my problem): How to handle events from keyboard and mouse in full screen exclusive mode in java?
In particular, it adds a button to toggle fullscreen.
import java.lang.reflect.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.beans.*;
/** #see https://stackoverflow.com/questions/13064607/ */
public class FullScreenTest extends JPanel {
private GraphicsDevice dev = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
private JFrame f = new JFrame("FullScreenTest");
private static final String EXIT = "Exit";
private Action exit = new AbstractAction(EXIT) {
#Override
public void actionPerformed(ActionEvent e) {
Object o = dev.getFullScreenWindow();
if(o != null) {
dev.setFullScreenWindow(null);
}
f.dispatchEvent(new WindowEvent(f, WindowEvent.WINDOW_CLOSING));
}
};
private JButton exitBTN = new JButton(exit);
private JTextField jtf = new JTextField("Uneditable in FullScreen with Java7u6+ on Mac OS X 10.7.3+");
private JLabel keystrokeLabel = new JLabel("(Last Modifier+Key Pressed in JTextField)");
private JLabel jtfFocusLabel = new JLabel("(JTextField Focus State)");
private JLabel focusLabel = new JLabel("(Focused Component Hierarchy)");
private JCheckBox useOSXFullScreenCB = new JCheckBox("Use Lion-Style FullScreen Mode");
private JCheckBox useWorkaroundCB = new JCheckBox("Use Visibility Workaround to Restore 1st Responder Window");
private static final String TOGGLE = "Toggle FullScreen (Command-T or Enter)";
private Action toggle = new AbstractAction(TOGGLE) {
#Override
public void actionPerformed(ActionEvent e) {
Object o = dev.getFullScreenWindow();
if(o == null) {
f.pack();
/**
* !! Neither of these calls seem to have any later effect.
* One exception: I have a report of a
* Mini going into an unrecoverable black screen without setVisible(true);
* May be only a Java 6 compatibility issue. !!
*/
//f.setVisible(true);
//f.setVisible(false);
if(!useOSXFullScreenCB.isSelected()) {
// No keyboard input after this call unless workaround is used
dev.setFullScreenWindow(f);
/**
* Workaround provided by Leonid Romanov at Oracle.
*/
if(useWorkaroundCB.isSelected()) {
f.setVisible(false);
f.setVisible(true);
//Not necessary to invoke later...
/*SwingUtilities.invokeLater(new Runnable() {
public void run() {
f.setVisible(false);
f.setVisible(true);
}
});*/
}
}
else {
toggleOSXFullscreen(f);
}
}
else {
dev.setFullScreenWindow(null);
f.pack();
f.setVisible(true);
}
isAppActive();
}
};
private JButton toggleBTN = new JButton(toggle);
public FullScreenTest() {
// -- Layout --
this.setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
exitBTN.setAlignmentX(JComponent.CENTER_ALIGNMENT);
exitBTN.setMaximumSize(new Dimension(Short.MAX_VALUE, 50));
this.add(exitBTN);
jtf.setAlignmentX(JComponent.CENTER_ALIGNMENT);
jtf.setMaximumSize(new Dimension(Short.MAX_VALUE, Short.MAX_VALUE));
this.add(jtf);
keystrokeLabel.setAlignmentX(JComponent.CENTER_ALIGNMENT);
keystrokeLabel.setMaximumSize(new Dimension(Short.MAX_VALUE, 50));
keystrokeLabel.setHorizontalAlignment(SwingConstants.CENTER);
keystrokeLabel.setForeground(Color.DARK_GRAY);
this.add(keystrokeLabel);
jtfFocusLabel.setAlignmentX(JComponent.CENTER_ALIGNMENT);
jtfFocusLabel.setMaximumSize(new Dimension(Short.MAX_VALUE, 50));
jtfFocusLabel.setHorizontalAlignment(SwingConstants.CENTER);
jtfFocusLabel.setForeground(Color.DARK_GRAY);
this.add(jtfFocusLabel);
focusLabel.setAlignmentX(JComponent.CENTER_ALIGNMENT);
focusLabel.setMaximumSize(new Dimension(Short.MAX_VALUE, 50));
focusLabel.setHorizontalAlignment(SwingConstants.CENTER);
focusLabel.setForeground(Color.DARK_GRAY);
this.add(focusLabel);
useOSXFullScreenCB.setAlignmentX(JComponent.CENTER_ALIGNMENT);
useOSXFullScreenCB.setMaximumSize(new Dimension(Short.MAX_VALUE, 50));
useOSXFullScreenCB.setHorizontalAlignment(SwingConstants.CENTER);
this.add(useOSXFullScreenCB);
useWorkaroundCB.setAlignmentX(JComponent.CENTER_ALIGNMENT);
useWorkaroundCB.setMaximumSize(new Dimension(Short.MAX_VALUE, 50));
useWorkaroundCB.setHorizontalAlignment(SwingConstants.CENTER);
this.add(useWorkaroundCB);
toggleBTN.setAlignmentX(JComponent.CENTER_ALIGNMENT);
toggleBTN.setMaximumSize(new Dimension(Short.MAX_VALUE, 50));
this.add(toggleBTN);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setResizable(false);
f.setUndecorated(true);
f.add(this);
f.pack();
enableOSXFullscreen(f);
// -- Listeners --
// Default BTN set to see how input maps respond in fullscreen
f.getRootPane().setDefaultButton(toggleBTN);
// Explicit input map test with Command-T toggle action from anywhere in the window
this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
KeyStroke.getKeyStroke(KeyEvent.VK_T, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
toggle.getValue(Action.NAME));
this.getActionMap().put(toggle.getValue(Action.NAME), toggle);
// KeyListener test
jtf.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
String ktext = "KeyPressed: "+e.getKeyModifiersText(e.getModifiers()) + "_"+ e.getKeyText(e.getKeyCode());
keystrokeLabel.setText(ktext);
System.out.println(ktext);
}
});
// FocusListener test
jtf.addFocusListener(new FocusListener() {
public void focusGained(FocusEvent fe) {
focused(fe);
}
public void focusLost(FocusEvent fe) {
focused(fe);
}
private void focused(FocusEvent fe) {
boolean allGood = jtf.hasFocus() && jtf.isEditable() && jtf.isEnabled();
jtfFocusLabel.setText("JTextField has focus (and is enabled/editable): " + allGood);
isAppActive();
}
});
// Keyboard Focus Manager
KeyboardFocusManager focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
focusManager.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
if (!("focusOwner".equals(e.getPropertyName()))) return;
Component comp = (Component)e.getNewValue();
if(comp == null) {
focusLabel.setText("(No Component Focused)");
return;
}
String label = comp.getClass().getName();
while(true) {
comp = comp.getParent();
if(comp == null) break;
label = comp.getClass().getSimpleName() + " -> " + label;
}
focusLabel.setText("Focus Hierarchy: " + label);
isAppActive();
}
});
}
/**
* Hint that this Window can enter fullscreen. Only need to call this once per Window.
* #param window
*/
#SuppressWarnings({"unchecked", "rawtypes"})
public static void enableOSXFullscreen(Window window) {
try {
Class util = Class.forName("com.apple.eawt.FullScreenUtilities");
Class params[] = new Class[]{Window.class, Boolean.TYPE};
Method method = util.getMethod("setWindowCanFullScreen", params);
method.invoke(util, window, true);
} catch (ClassNotFoundException e1) {
} catch (Exception e) {
System.out.println("Failed to enable Mac Fullscreen: "+e);
}
}
/**
* Toggle OSX fullscreen Window state. Must call enableOSXFullscreen first.
* Reflection version of: com.apple.eawt.Application.getApplication().requestToggleFullScreen(f);
* #param window
*/
#SuppressWarnings({"unchecked", "rawtypes"})
public static void toggleOSXFullscreen(Window window) {
try {
Class appClass = Class.forName("com.apple.eawt.Application");
Method method = appClass.getMethod("getApplication");
Object appInstance = method.invoke(appClass);
Class params[] = new Class[]{Window.class};
method = appClass.getMethod("requestToggleFullScreen", params);
method.invoke(appInstance, window);
} catch (ClassNotFoundException e1) {
} catch (Exception e) {
System.out.println("Failed to toggle Mac Fullscreen: "+e);
}
}
/**
* Quick check of the low-level window focus state based on Apple's Javadoc:
* "Returns true if the application (one of its windows) owns keyboard focus."
*/
#SuppressWarnings({"unchecked", "rawtypes"})
public static void isAppActive() {
try {
Class util = Class.forName("sun.lwawt.macosx.LWCToolkit");
Method method = util.getMethod("isApplicationActive");
Object obj = method.invoke(Toolkit.getDefaultToolkit());
System.out.println("AppActive: "+obj);
} catch (ClassNotFoundException e1) {
} catch (Exception e) {
System.out.println("Failed to check App: "+e);
}
}
public static void main(String[] args) {
System.out.println("Java Version: " + System.getProperty("java.version"));
System.out.println("OS Version: " + System.getProperty("os.version"));
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
FullScreenTest fst = new FullScreenTest();
if(!fst.dev.isFullScreenSupported()) {
System.out.println("FullScreen not supported on this graphics device. Exiting.");
System.exit(0);
}
fst.toggle.actionPerformed(null);
}
});
}
}
This is because the component to which you added the other has now lost focus, you can fix this by either:
calling requestFocus() on the component instance to which you add KeyBindings
or
alternatively use JComponent.WHEN_IN_FOCUSED_WINDOW with KeyBindings:
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_Q, 0),
"doSomething");
component.getActionMap().put("doSomething",
anAction);
Reference:
How to Use Key Bindings
Instead, use key bindings, as shown in this FullScreenTest. Also, consider a DocumentListener, shown here, for text components.
I think I finally found a solution, registering click listeners against to the JFrame itself. (This is a class which extends JFrame, hence all the "this" references.)
/**
* Toggles full screen mode. Requires a lot of references to the JFrame.
*/
public void setFullScreen(boolean fullScreen){
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice dev = env.getDefaultScreenDevice();//Gets the main screen
if(!fullScreen){//Checks if a full screen application isn't open
this.dispose();//Restarts the JFrame
this.setVisible(false);
this.setResizable(true);//Re-enables resize-ability.
this.setUndecorated(false);//Adds title bar back
this.setVisible(true);//Shows restarted JFrame
this.removeMouseListener(macWorkAround);
this.pack();
this.setExtendedState(this.getExtendedState()|JFrame.MAXIMIZED_BOTH);//Returns to maximized state
this.fullScreen = false;
}
else{
this.dispose();//Restarts the JFrame
this.setResizable(false);//Disables resizing else causes bugs
this.setUndecorated(true);//removes title bar
this.setVisible(true);//Makes it visible again
this.revalidate();
this.setSize(Toolkit.getDefaultToolkit().getScreenSize());
try{
dev.setFullScreenWindow(this);//Makes it full screen
if(System.getProperty("os.name").indexOf("Mac OS X") >= 0){
this.setVisible(false);
this.setVisible(true);
this.addMouseListener(macWorkAround);
}
this.repaint();
this.revalidate();
}
catch(Exception e){
dev.setFullScreenWindow(null);//Fall back behavior
}
this.requestFocus();
this.fullScreen = true;
}
}
private MouseAdapter macWorkAround = new MouseAdapter(){
public void mouseClicked(MouseEvent e){
MainGUI.this.setVisible(false);
MainGUI.this.setVisible(true);
}
};