JavaFX Minimizing an undecorated scene

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); } } } 
+13
source share
2 answers

You can simply set the appropriate window style. It works in XP, but should be ok in Windows 7 32 bit. I think (but cannot verify) if you use 64 bits and then change to Ptr window functions, i.e. GetWindowLongPtr.

 import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.platform.win32.User32; import com.sun.jna.platform.win32.WinDef.HWND; import com.sun.jna.platform.win32.WinUser; import static com.sun.jna.platform.win32.WinUser.GWL_STYLE; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.TextArea; import javafx.scene.layout.VBox; import javafx.stage.Stage; public class JNATest extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) { TextArea ta = new TextArea("output\n"); VBox root = new VBox(5,ta); Scene scene = new Scene(root,800,200); stage.setTitle("Find this window"); stage.setScene(scene); stage.show(); //gets this window (stage) long lhwnd = com.sun.glass.ui.Window.getWindows().get(0).getNativeWindow(); Pointer lpVoid = new Pointer(lhwnd); //gets the foreground (focused) window final User32 user32 = User32.INSTANCE; char[] windowText = new char[512]; HWND hwnd = user32.GetForegroundWindow(); //see what the title is user32.GetWindowText(hwnd, windowText, 512); //user32.GetWindowText(new HWND(lpVoid), windowText, 512);//to use the hwnd from stage String text=(Native.toString(windowText)); //see if it the same pointer ta.appendText("HWND java:" + lpVoid + " HWND user32:"+hwnd+" text:"+text+"\n"); //change the window style if it the right title if (text.equals(stage.getTitle())){ //the style to change int WS_DLGFRAME = 0x00400000;//s/b long I think //not the same constant here?? ta.appendText("windows api:"+WS_DLGFRAME+" JNA: "+WinUser.SM_CXDLGFRAME); int oldStyle = user32.GetWindowLong(hwnd, GWL_STYLE); int newStyle = oldStyle & ~0x00400000; //bitwise not WS_DLGFRAME means remove the style newStyle = newStyle & ~0x00040000;//WS_THICKFRAME user32.SetWindowLong(hwnd, GWL_STYLE, newStyle); } } } 

My guess: you replace the last 3 lines with

  long oldStyleLong = user32.GetWindowLongPtr(hwnd, GWL_STYLE).longValue(); long newStyleLong = oldStyleLong & ~ 0x00400000l; user32.SetWindowLongPtr(hwnd, GWL_STYLE, new BaseTSD.LONG_PTR(newStyleLong)); 

for 64 bits. I think that I do not have these functions in my User32.dll, so I can not test it. There is a lot of extraneous code, mainly for testing or training. Remove unused lines when you figure out what you want to do.

ps. Do not add newStyle = newStyle & ~0x00020000;//WS_MINIMIZEBOX . This one of the JavaFX style flags is not used for unecorated. Therefore, minimization is not available. Maybe if you try to configure the stage without decoding and add (using |, not & ~) the flag of the minimize flag, you will get the same result. There are tools to find all style flags from any window.

Here's the simplest code that just modifies an undeclared stage using the HWND stage.

  public void start(Stage stage) { Scene scene = new Scene(new Pane(new Label("Hello World"))); stage.initStyle(StageStyle.UNDECORATED); stage.setTitle("Find this window"); stage.setScene(scene); stage.show(); long lhwnd = com.sun.glass.ui.Window.getWindows().get(0).getNativeWindow(); Pointer lpVoid = new Pointer(lhwnd); HWND hwnd = new HWND(lpVoid); final User32 user32 = User32.INSTANCE; int oldStyle = user32.GetWindowLong(hwnd, GWL_STYLE); System.out.println(Integer.toBinaryString(oldStyle)); int newStyle = oldStyle | 0x00020000;//WS_MINIMIZEBOX System.out.println(Integer.toBinaryString(newStyle)); user32.SetWindowLong(hwnd, GWL_STYLE, newStyle); } 

It prints style flags before and after, so you can see which styles are set.

+7
source

Didn't get your question right ... but here is the solution

 @FXML private void minimize() { Stage stage = (Stage) minimize.getScene().getWindow(); stage.setIconified(true); } 
-2
source

Source: https://habr.com/ru/post/1211396/


All Articles