-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
[WIP] Linux: Set X11 window's application name #8595
Conversation
…ined in Application just a placeholder saying "Avalonia Application", but now at least in Gnome top bar the place where the application name goes is no longer blank
You can test this PR using the following package version. |
return; | ||
} | ||
|
||
var classHintData = Marshal.PtrToStructure<XClassHint>(classHint); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's better to just cast it to a pointer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I'm not super familiar with native interop in C# yet, what exactly do you mean? If I try code like (XClassHint*)classHint
that doesn't compile with "Cannot declare a pointer to managed type". And the first result on StackOverflow said to just use Marshal.PtrToStructure
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(XClassHint*)classHint.ToPointer()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had to modify my struct to get that to compile to:
[StructLayout(LayoutKind.Sequential)]
public struct XClassHint
{
public void* ResName;
public void* ResClass;
}
fixed (void* textPtr = classTextData) | ||
{ | ||
classHintData.ResClass = new IntPtr(textPtr); | ||
classHintData.ResName = new IntPtr(textPtr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to https://tronche.com/gui/x/icccm/sec-4.html#WM_CLASS the instance name should be taken from RESOURCE_NAME
or match the executable file name.
Also note that human-readable application-title is supposed to be specified in the .desktop-file shipped with your application.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I re-read the documentation I was going off on: https://linux.die.net/man/3/xclasshint and sure enough it does also mention that WM_CLASS info. If I change the code here to use that value, then it is no longer user controllable with the "Name" property in the Application XAML, which is where the application name property is so people would be stuck with their .csproj name being the name showing up for their application in a user readable place. That's not exactly a kind of limitation I'd want to have. It would also be need to documented that the Name property in Application doesn't work on Linux.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading that documentation again it looks like maybe after all this existing code:
XChangeProperty(_x11.Display, _handle, _x11.Atoms.XA_WM_CLASS, _x11.Atoms.XA_STRING, 8,
PropertyMode.Replace, pdata, data.Length);
is incorrect as it provides only one string and not two? It seems in the documentation you linked the second string in WM_CLASS is the name of the application like "Emacs" and as Avalonia doesn't set that currently, seeing it be empty makes sense to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html and https://specifications.freedesktop.org/startup-notification-spec/startup-notification-0.1.txt
GNOME-recognized application name is supposed to come from an installed .desktop
file.
Which is why initial code uses a special property in X11PlatformOptions
. We could also fall back to Application.Current.Name
if it's not set, but I'd rather avoid using a human-readable name as a class name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I may be onto something with specifying null terminator separated data in WM_CLASS. If I have code like:
Encoding.ASCII.GetBytes($"first part\0{_platform.Options.WmClass}")
the "first part" shows up in the gnome top bar, and when I check the xproperties of the window:
$ xprop WM_CLASS
WM_CLASS(STRING) = "Avalonia Application", "first part"
Assuming the first part there is the internal class name, which is now what you said it should be.
Maybe that is the right way to do it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get that, but all other GUI frameworks I've ever used put something there in the top bar by default.
I just tested and it is true that having a .desktop file makes things work without my changes.
I then tested one more thing, if I undo my other changes but modify SetWmClass
to have the class string like this:
var data = Encoding.ASCII.GetBytes($"{wmClass}\0{wmClass}");
then it seems like the default name will be shown when running the program by itself but with an installed .desktop file the name from there gets used. If I replace all my changes in this PR with just that one line change, would that be acceptable? That way both the case of not having a .desktop file kind of works, and the case of having a .desktop file looks to work as expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here are WM_CLASS values for various apps from my machine built with different UI toolkits:
"jetbrains-rider", "jetbrains-rider"
"gnome-terminal-server", "Gnome-terminal"
"Telegram", "TelegramDesktop"
"Navigator", "Firefox-esr"
"google-chrome", "Google-chrome"
As you can see, none of them uses human-readable string for WM_CLASS values.
We need to investigate how those frameworks you are referring to are providing GNOME with a meaningful application name, because specifying it as WM_CLASS definitely isn't the way to do it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also note, that WM_CLASS can not be used for human-readable names because according to the Xlib docs the property has to be encoded with Host Portable Character Encoding which can only contain Latin-1 character set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect those are installed programs .desktop files? If so they don't really apply here as there you can do whatever because it looks like it just overwrites anything the program defines with what's in the .desktop file (#8595 (comment))
Godot engine works perfectly fine without any .desktop files and it looks like they absolutely don't care and instead set the second part to be what they want to show to the user:
WM_CLASS(STRING) = "Godot_Engine", "Godot Third-Person Shooter Demo"
Also at this point I'm not even suggesting anymore that Avalonia would put a random value there, I'd be perfectly happy by having Avalonia set both parts of the WM_CLASS and not just one, to the same value that is currently set.
You can use X11PlatformOptions.WmClass property to provide your custom value once #8597 is merged |
Thanks for making that PR, I think I can manage with the default new behaviour, but the option to override the WmClass if needed sounds nice (I didn't test that part). |
What does the pull request do?
Once fully complete fixes #7685
I'm opening this as a draft because I can't see a simple way to get the application's name to the X11Window class, so I'm hoping to get guidance as to what the project authors would do in this case. I didn't attempt any approach as I'd likely done something silly that would need to be refactored anyway.
What is the current behavior?
See the issue, but very briefly there's just a blank space where the application name should go.
What is the updated/expected behavior with this PR?
This adds code to set the application name.
How was the solution implemented (if it's not obvious)?
#7685 (comment)
Checklist
Breaking changes
Obsoletions / Deprecations
Fixed issues
Fixes #7685