I have an unencrypted JavaFX scene, as well as my own minimize, maximize, and close buttons. But, unfortunately, clicking on the taskbar icon in Windows 7 does not minimize the stage - compared to the framed behavior.
Is there a way to minimize the undeclared stage with pure Java code by clicking the taskbar icon? If not, how can I do this, for example, using JNA?
EDIT: Well, I tried to solve this problem with JNA, but without having executed any C / C ++ / JNA, I am a little at a loss to configure this. I would appreciate if someone would help me put the pieces together.
Here is my code:
public final class Utils { static { if (PlatformUtil.isWin7OrLater()) { Native.register("shell32"); Native.register("user32"); } } // Apparently, this is the event I am after public static final int WM_ACTIVATEAPP = 0x1C; public static void registerMinimizeHandler(Stage stage) { // Hacky way to get a pointer to JavaFX Window Pointer pointer = getWindowPointer(stage); WinDef.HWND hwnd = new WinDef.HWND(pointer); // Here my minimize/activate handler WinUser.WindowProc windowProc = new MinimizeHandler(stage); Pointer magicPointer = ... set this to point to windowProc? // This.. apparently, re-sets the WndProc? But how do I get the "magicPointer" that is "attached" to the windowProc? User32.INSTANCE.SetWindowLong(hwnd, User32.GWL_WNDPROC, magicPointer); } } private static class MinimizeHandler implements WinUser.WindowProc { private Stage stage; private MinimizeHandler(Stage stage) { this.stage = stage; } @Override public WinDef.LRESULT callback(WinDef.HWND hWnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam) { if (uMsg == WM_ACTIVATEAPP) { System.out.println("ACTIVATE"); } return User32.INSTANCE.DefWindowProc(hWnd, uMsg, wParam, lParam); } } private static Pointer getWindowPointer(Stage stage) { try { TKStage tkStage = stage.impl_getPeer(); Method getPlatformWindow = tkStage.getClass().getDeclaredMethod("getPlatformWindow" ); getPlatformWindow.setAccessible(true); Object platformWindow = getPlatformWindow.invoke(tkStage); Method getNativeHandle = platformWindow.getClass().getMethod( "getNativeHandle" ); getNativeHandle.setAccessible(true); Object nativeHandle = getNativeHandle.invoke(platformWindow); return new Pointer((Long) nativeHandle); } catch (Throwable e) { System.err.println("Error getting Window Pointer"); return null; } }
EDIT 2: In the end, I got further development with this, but as soon as I installed WNDPROC again, my undeclared window did not respond to any events. I offer a generosity of 100 reputation for a standalone example with a working solution. Windows (7+) works fine, I donβt even know how it behaves on other platforms.
EDIT 3: Well, I kind of gave up with this ... I set everything up correctly, and received the events, but I had problems figuring out the right event to listen to ..
Since we had an interest in the question, if someone wants to try to continue this, here is my last code (I hope it should "work" out of the box):
public final class Utils { static interface ExtUser32 extends StdCallLibrary, User32 { ExtUser32 INSTANCE = (ExtUser32) Native.loadLibrary( "user32", ExtUser32.class, W32APIOptions.DEFAULT_OPTIONS); WinDef.LRESULT CallWindowProcW( Pointer lpWndProc, Pointer hWnd, int msg, WinDef.WPARAM wParam, WinDef.LPARAM lParam); int SetWindowLong(HWND hWnd, int nIndex, com.sun.jna.Callback wndProc) throws LastErrorException; } // Some possible event types public static final int WM_ACTIVATE = 0x0006; public static final int WM_ACTIVATEAPP = 0x1C; public static final int WM_NCACTIVATE = 0x0086; public static void registerMinimizeHandler(Stage stage) { Pointer pointer = getWindowPointer(stage); WinDef.HWND hwnd = new WinDef.HWND(pointer); long old = ExtUser32.INSTANCE.GetWindowLong(hwnd, User32.GWL_WNDPROC); MinimizeHandler handler = new MinimizeHandler(stage, old); ExtUser32.INSTANCE.SetWindowLong(hwnd, User32.GWL_WNDPROC, handler); } private static Pointer getWindowPointer(Stage stage) { try { TKStage tkStage = stage.impl_getPeer(); Method getPlatformWindow = tkStage.getClass().getDeclaredMethod("getPlatformWindow" ); getPlatformWindow.setAccessible(true); Object platformWindow = getPlatformWindow.invoke(tkStage); Method getNativeHandle = platformWindow.getClass().getMethod( "getNativeHandle" ); getNativeHandle.setAccessible(true); Object nativeHandle = getNativeHandle.invoke(platformWindow); return new Pointer((Long) nativeHandle); } catch (Throwable e) { System.err.println("Error getting Window Pointer"); return null; } } private static class MinimizeHandler implements WinUser.WindowProc, StdCallLibrary.StdCallCallback { private Pointer mPrevWndProc32; private Stage stage; private MinimizeHandler(Stage stage, long oldPtr) { this.stage = stage; mPrevWndProc32 = new Pointer(oldPtr); // Set up an event pump to deliver messages for JavaFX to handle Thread thread = new Thread() { @Override public void run() { int result; WinUser.MSG msg = new WinUser.MSG(); while ((result = User32.INSTANCE.GetMessage(msg, null, 0, 0)) != 0) { if (result == -1) { System.err.println("error in get message"); break; } else { System.out.println("got message: " + result); User32.INSTANCE.TranslateMessage(msg); User32.INSTANCE.DispatchMessage(msg); } } } }; thread.start(); } @Override public WinDef.LRESULT callback(WinDef.HWND hWnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam) { if (uMsg == WM_ACTIVATEAPP) { // Window deactivated (wParam == 0)... Here where I got stuck and gave up, // this is probably not the best event to listen to.. the app // does indeed get iconified now by pressing the task-bar button, but it // instantly restores afterwards.. if (wParam.intValue() == 0) { stage.setIconified(true); } return new WinDef.LRESULT(0); } // Let JavaFX handle other events return ExtUser32.INSTANCE.CallWindowProcW( mPrevWndProc32, hWnd.getPointer(), uMsg, wParam, lParam); } } }