Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support window decorations on linux #482

Closed
rkeen-siemens opened this issue Feb 18, 2022 · 13 comments
Closed

Support window decorations on linux #482

rkeen-siemens opened this issue Feb 18, 2022 · 13 comments
Milestone

Comments

@rkeen-siemens
Copy link

The window decorations (including the embedded menu bar and title bar options) would be great to have on linux as well as Windows.

@matthiasblaesing
Copy link

As I'm using NetBeans at work on Windows with menu integrated into title bar, I began to wonder how difficult it would be. As a quick experiment I came up with this:

  • Override FlatLaf#getSupportsWindowDecorations to always return true
  • in DemoFrame.java enforce menu entries for the title bar feature to be available
  • in FlatLafDemo.java set JFrame.setDefaultLookAndFeelDecorated

With that I get this window on Ubuntu Linux:

image

Window interactions work as expected, it does not match the configure GTK Theme, but that is to be expected by client side decorations. I don't understand why getSupportsWindowDecorations is so complicated, as the theme obviously can handle window decoration quite fine. Even more window decorations from LAF are only used if switched on by asking JFrame to actually use the support.

The biggest headache will probably be enabling the dynamic switch between OS window decorations and LAF window decorations as the JFrame does not "know" how it was created.

@DevCharly what do you think?

Patch with changes as described (please expand)
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java
index 4c92ac36..4d6d74a1 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java
@@ -183,14 +183,15 @@ public abstract class FlatLaf
 	 */
 	@Override
 	public boolean getSupportsWindowDecorations() {
-		if( SystemInfo.isProjector || SystemInfo.isWebswing || SystemInfo.isWinPE )
-			return false;
-
-		if( SystemInfo.isWindows_10_orLater &&
-			FlatNativeWindowBorder.isSupported() )
-		  return false;
-
-		return SystemInfo.isWindows_10_orLater;
+//		if( SystemInfo.isProjector || SystemInfo.isWebswing || SystemInfo.isWinPE )
+//			return false;
+//
+//		if( SystemInfo.isWindows_10_orLater &&
+//			FlatNativeWindowBorder.isSupported() )
+//		  return false;
+//
+//		return SystemInfo.isWindows_10_orLater;
+            return true;
 	}
 
 	@Override
diff --git a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java
index 678ed225..adfe174e 100644
--- a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java
+++ b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java
@@ -889,7 +889,7 @@ class DemoFrame
 		copyMenuItem.addActionListener( new DefaultEditorKit.CopyAction() );
 		pasteMenuItem.addActionListener( new DefaultEditorKit.PasteAction() );
 
-		if( FlatLaf.supportsNativeWindowDecorations() ) {
+		if( true || FlatLaf.supportsNativeWindowDecorations() ) {
 			windowDecorationsCheckBoxMenuItem.setSelected( FlatLaf.isUseNativeWindowDecorations() );
 			menuBarEmbeddedCheckBoxMenuItem.setSelected( UIManager.getBoolean( "TitlePane.menuBarEmbedded" ) );
 			unifiedTitleBarMenuItem.setSelected( UIManager.getBoolean( "TitlePane.unifiedBackground" ) );
diff --git a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/FlatLafDemo.java b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/FlatLafDemo.java
index 8073ede9..116d314b 100644
--- a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/FlatLafDemo.java
+++ b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/FlatLafDemo.java
@@ -22,6 +22,7 @@ import com.formdev.flatlaf.FlatLaf;
 import com.formdev.flatlaf.extras.FlatInspector;
 import com.formdev.flatlaf.extras.FlatUIDefaultsInspector;
 import com.formdev.flatlaf.util.SystemInfo;
+import javax.swing.JFrame;
 
 /**
  * @author Karl Tauber
@@ -34,6 +35,7 @@ public class FlatLafDemo
 	static boolean screenshotsMode = Boolean.parseBoolean( System.getProperty( "flatlaf.demo.screenshotsMode" ) );
 
 	public static void main( String[] args ) {
+                JFrame.setDefaultLookAndFeelDecorated(true);
 		// macOS
 		if( SystemInfo.isMacOS ) {
 			// enable screen menu bar

@rkeen-siemens
Copy link
Author

Thanks, @matthiasblaesing. This looks great and seems to work well with the minor exception (in Gnome 3 on CentOS at least) that dragging to maximize/restore and snap to other windows and the edge of the screen does not seem to work. It would be nice to support the standard system interactions like these (including similar ones with other window managers). Other methods to maximize and snap (double click, max/min/restore buttons, keyboard) do seem to work, so it's still quite usable.

@DevCharly
Copy link
Collaborator

Tried this on Linux and it works better than expected.
The windows even have shadows 😄
Window resizing had some problems and unified window title bar did not work correctly, but these issues are now fixed.

I've changed FlatLaf.getSupportsWindowDecorations() to allow custom window decorations on Linux.

To enable them in your app use e.g.:

if( SystemInfo.isLinux ) {
    // enable custom window decorations
    JFrame.setDefaultLookAndFeelDecorated( true );
    JDialog.setDefaultLookAndFeelDecorated( true );
}

...with the minor exception (in Gnome 3 on CentOS at least) that dragging to maximize/restore and snap to other windows and the edge of the screen does not seem to work.

That's the downside of enabling custom window decorations.

It would be nice to support the standard system interactions like these (including similar ones with other window managers).

On Windows 10/11, there was the same problem and the only way to solve it was to implement some C++ code that interacts directly with Windows API, which was complex and time consuming.

Similar would be necessary for Linux using X11 API.
Currently, I don't have the time to do this.

Also there is OpenJDK Project Wakefield, which will support Wayland display server in the future. So it would be a waste of time to implement something now for X11, and later throw it away and implement same for Wayland...

@rkeen-siemens
Copy link
Author

@DevCharly, thanks for the quick update. When I entered this issue I was expecting an explanation of why this would be too difficult to implement, but I'm pleasantly surprised.

@rkeen-siemens
Copy link
Author

Siemens would like to see the drag and snap behavior implemented using the X11 API and would be willing to pay for its development (subject to timing and cost). @DevCharly (or other interested parties), do you have an estimate on when this could be released and what would be a reasonable amount to pay for the development and testing? If there's a better forum for this type of request, I'd be happy to repost this request there.

@matthiasblaesing
Copy link

If someone want to have a look at this. This is what I tried (feel free to use if it leads to improvements in FlatLaf):

  1. I mapped XUngrabPointer like this:
    interface X11Ext extends X11 {
	X11Ext INSTANCE = Native.load("X11", X11Ext.class);

	int XUngrabPointer(Display display, NativeLong time);
    }
  1. I managed to start a keyboard move like this:
    private static void move_resize(int x, int y) throws HeadlessException {
	System.out.printf("%d x %d%n", x, y);
	Display display = X11Ext.INSTANCE.XOpenDisplay(null);
	Window w = new X11Ext.Window(windowsId);
	X11Ext.XEvent event = new X11Ext.XEvent();
	event.type = X11Ext.ClientMessage;
	event.setType(XClientMessageEvent.class);
	event.xclient.type = X11Ext.ClientMessage;
	event.xclient.serial = new NativeLong(0);
	event.xclient.send_event = 1;
	event.xclient.message_type = X11Ext.INSTANCE.XInternAtom(display, "_NET_WM_MOVERESIZE", false);
	event.xclient.display = display;
	event.xclient.window = w;
	event.xclient.format = 32;
	event.xclient.data.setType(NativeLong[].class);
	event.xclient.data.l[0] = new NativeLong(x);
	event.xclient.data.l[1] = new NativeLong(y);
	event.xclient.data.l[2] = new NativeLong(10);
	event.xclient.data.l[3] = new NativeLong(0);
	event.xclient.data.l[4] = new NativeLong(0);
	System.out.println(X11Ext.INSTANCE.XUngrabPointer(display, new NativeLong(0)));
	System.out.println(X11Ext.INSTANCE.XUngrabKeyboard(display, new NativeLong(0)));
	System.out.println(X11Ext.INSTANCE.XSendEvent(display, X11Ext.INSTANCE.XDefaultRootWindow(display), 0, new NativeLong(SubstructureNotifyMask|X11.SubstructureRedirectMask), event));
	System.out.println(X11Ext.INSTANCE.XFlush(display));
	System.out.println(X11Ext.INSTANCE.XCloseDisplay(display));
	System.out.println("Done");
    }

Theoretically changing the third data element to 8 should make this a mouse move, that did not work for me though.

@DevCharly
Copy link
Collaborator

@rkeen-siemens thanks for the offer.
I'm willing to work on it.
Please contact me via Email: https://www.formdev.com/company/

Is the "drag and snap behavior" the only functionality that you to want?
I've compared native windows with FlatLaf decorated windows and there are some more differences:

  • resize:
    • with native windows you can click on the window shadow (outside of window) to resize the window
    • with FlatLaf decorated windows you have to click inside of the window border to resize
  • hold down Shift key while resizing to snap the window to the edges of the screen and other windows does not work
  • right-click on title bar does not show window popup menu

@matthiasblaesing many thanks for the code. I tried it.
Works for me even using 8 (using mouse screen coordinates for x and y).
Found out that _NET_WM_MOVERESIZE message can be also used for resizing to get snapping when holding down Shift key.
https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html#idm45381394031168

BTW is this API available on all Linux distros (that support X11)?

Any idea how to show that window popup menu via API?

image

Since the above code uses JNA and depending FlatLaf library on JNA is not an option (like to keep it dependency free), I want to translate the code into C/C++ and build a small .so library that is shipped inside of flatlaf.jar (similar to Windows DLLs inside of flatlaf.jar).

@DevCharly DevCharly reopened this Mar 16, 2022
@matthiasblaesing
Copy link

matthiasblaesing commented Mar 16, 2022

@DevCharly to my understanding implementors should check the atom list returned by querying the property _NET_SUPPORTED of the root window.

I'm not sure if going though direct X11 calls is the preferred way or if it would be better to use gdk. If I remember correctly, both OpenJFX and Swing/AWT are both build on top of gtk and gtk has support for wayland and X11. The drawback is, that you'd have to check if the function is called identically between the supported gtk versions. A runtime lookup of the function symbol might work correctly, if the function call names differ.

For the popup - sorry, no idea.

As reference this is the GDK function I referenced: https://docs.gtk.org/gdk3/method.Window.begin_move_drag.html
And yes, gtk4 changes this: https://docs.gtk.org/gdk4/method.Toplevel.begin_move.html

@rkeen-siemens
Copy link
Author

@DevCharly, thanks for the offer. I'm assuming we will want the other behaviors as well. I'll contact you via email in the next couple days with more details.

@medmedin2014
Copy link

I tested it on Manjaro KDE with X11 and it looks okay:

Screenshot_20220318_005332

The problems listed by @DevCharly are all present on KDE Plasma, and the additional one is that window shadow disappeared.

@rkeen-siemens
Copy link
Author

@DevCharly, I sent you an email last week with details on my earlier request for updates. Perhaps you have been busy, but I'm wondering if the message was lost in transit. I'll post my request here as well in case you didn't receive it or others are interested in the work.

Summary

Siemens would like to see the drag and snap behavior implemented when window decorations are enabled in Linux.

Already Available

The following are already available and should be maintained:

  • The previous implementation for Support window decorations on linux #482 provided the ability to enable window decorations including the embedded menu bar, and unified window title.
  • Keyboard actions to move, resize, max/minimize, tile, move to other workspaces, etc including support for snapping to other windows using the Shift key

Scope of Work

The following should be available as they are currently for native windows (i.e. with window decorations disabled and without the need to hold down a modifier key such as Super before performing the action).

Not Required

  • Support for resizing in the window shadow area
  • Support for snapping when dragging with the Shift key down

Assumptions

This assumes support is not OS or desktop environment specific (i.e. it should work in GNOME 3 on CentOS and KDE Plasma on Ubuntu). If there are any known issues in the existing implementation or new scope that are platform specific, please provide details of the limitations.

Timing and Delivery

We'd like to have this available in a publicly released version of FlatLaf no later than 30 June 2022. We are happy to test with pre-release versions or build from a branch if needed.

Ideally, this would be available in the version bundled in NetBeans 14 at the end of May. If that is not possible, we have our own fork of the NetBeans platform so as long as you can provide instructions for us to update it there that would be acceptable as well.

@PicoMitchell
Copy link

These custom window decorations on Linux are awesome! Thank you!

But I noticed I'm not getting any window shadows when running on Mint 20.3. Not sure what's different than the other distros where the window shadows work.

DevCharly added a commit that referenced this issue May 25, 2022
…atWindowsNativeWindowBorder` to `FlatNativeLibrary` to make it easier to add native libraries for other platforms (for issues #204 and #482)
DevCharly added a commit that referenced this issue Jul 21, 2022
…w menu (right-click on window title bar), if custom window decorations are enabled (issue #482)
DevCharly added a commit that referenced this issue Aug 21, 2022
…w menu (right-click on window title bar), if custom window decorations are enabled (issue #482)
DevCharly added a commit that referenced this issue Aug 21, 2022
…y to fix `UnsatisfiedLinkError: ... libjawt.so: cannot open shared object file ...` (issue #482)
@DevCharly
Copy link
Collaborator

Finally, a PR is now available to improve FlatLaf window decorations on Linux: #579 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants