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

Ignore XRRSetMonitor() errors and delete existing monitors with same name #693

Merged
merged 1 commit into from
May 21, 2024

Conversation

jknockel
Copy link
Contributor

Ignore server errors on XRRSetMonitor(). A similar fix appears in mutter [1].

The RandR protocol [2] states concerning RRSetMonitor that:

If 'name' matches an existing Monitor on the screen, the
existing one will be deleted as if RRDeleteMonitor were called.

Unfortunately, this behavior is not the case in practice, and if an existing monitor with the same name exists the server generates a BadValue error [3]. While this was fixed very recently in the Xorg xserver [3], it is unclear when these changes will make it to most distributions due to how long it has been since a formal Xorg xserver major release. Therefore, we must ignore this error to prevent muffin from aborting if a monitor with the same name already exists, a common condition upon restarting cinnamon or calling cinnamon --replace.

I used the low-level XSync() and XSetErrorHandler() calls because to my knowledge no MetaDisplay handles are available here and therefore APIs like meta_x11_error_trap_push() appear unavailable. However, if they were available these APIs would be a cleaner approach. The original mutter fix uses an API (mtk_x11_error_trap_push) that does not yet appear in muffin [1].

A word of warning to those that think that xcb may be the perfect tool for this job: a crashing bug was only recently fixed in libxcb [4] which caused all calls to xcb_randr_set_monitor() to crash or otherwise cause the calling program to behave in an undefined manner. While the fix is released in libxcb 1.17, this version is not even available in Ubuntu 24.04, for instance. Therefore, it may be some time before we can reliably call xcb_randr_set_monitor().

Unfortunately, the above is not sufficient for correct behavior. The aim of ignoring XRRSetMonitor() errors was merely to prevent muffin from aborting if a monitor with the same name as one we are setting already exists. Merely ignoring the problem is fine if the monitor outputs that we tried to set happen to be the ones already set on that monitor. However, if XRRSetMonitor() fails, then the existing monitor may have different outputs than the ones which we tried to set.

To work around this, we first try to delete any monitor with the new monitor's name using XRRDeleteMonitor(). This might fail if no such monitor exists, so ignore server errors from this call as well. Then call XRRSetMonitor() as before.

[1] https://gitlab.gnome.org/GNOME/mutter/-/commit/8d3696f39a0b3af725b7615f7e2ac74ce5e0bcbf
[2] https://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt
[3] https://gitlab.freedesktop.org/xorg/xserver/-/commit/146bb9b2c19fb75b7629b65d5871969b7fcef97a
[4] https://gitlab.freedesktop.org/xorg/lib/libxcb/-/commit/038636786ad1914f3daf3503ae9611f40dffbb8f

@jknockel
Copy link
Contributor Author

Thinking about this some more, another option may be to use XCB for the RRDeleteMonitor() call, since that becomes the risky operation, and then use traditional Xlib without any XSync() or XSetErrorHandler() for the RRSetMonitor() call, since that is no longer risky. I don't know if that is any cleaner though.

Another thing I forgot to mention is, why not just try to use a unique monitor name each time? This works too, but the downside is that these monitors never get deleted and you end up leaking them (e.g., they are persistently visible in xrandr --listactivemonitors).

@mtwebster
Copy link
Member

Hi, you should be safe to #include "core/display-private.h" in this file, which contains meta_get_display () to allow you to use the meta pop/push functions.

@mtwebster
Copy link
Member

I think it's ok as you have it - you're working around the current randr bug, and it should continue to work even if/when the xserver fix is released.

I'd rather keep it simple for now, especially since I assume this isn't a 100% repeatable bug in typical situations is it?

A somewhat similar fix appears in mutter [1].

The RandR protocol [2] states concerning RRSetMonitor that:

> If 'name' matches an existing Monitor on the screen, the
> existing one will be deleted as if RRDeleteMonitor were called.

Unfortunately, this behavior is not the case in practice, and if an
existing monitor with the same name exists the server generates a
BadValue error [3].  While this was fixed very recently in the Xorg
xserver [3], it is unclear when these changes will make it to most
distributions due to how long it has been since a formal Xorg xserver
major release.  Therefore, we must ourselves prevent muffin from
aborting if a monitor with the same name already exists, a common
condition upon restarting cinnamon or calling `cinnamon --replace`.

The mutter fix [1] solves the problem of preventing mutter from aborting
on the error, but the request still fails to set the monitor's outputs.
The mutter fix also uses an API which is not in muffin yet.

Therefore, we take the approach of using xcb to first delete any monitor
with the name of the one which we wish to set.  Note that this request
can also fail if no such monitor exists, so we explicitly ignore this
error.

A word of warning to those who may wish to use xcb to also replace the
SetMonitor call:  a crashing bug was only recently fixed in libxcb [4]
which caused all calls to xcb_randr_set_monitor() to crash or otherwise
cause the calling program to behave in an undefined manner.  While the
fix is released in libxcb 1.17, this version is not even available in
Ubuntu 24.04, for instance.  Therefore, it may be some time before we
can reliably call xcb_randr_set_monitor().

[1] https://gitlab.gnome.org/GNOME/mutter/-/commit/8d3696f39a0b3af725b7615f7e2ac74ce5e0bcbf
[2] https://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt
[3] https://gitlab.freedesktop.org/xorg/xserver/-/commit/146bb9b2c19fb75b7629b65d5871969b7fcef97a
[4] https://gitlab.freedesktop.org/xorg/lib/libxcb/-/commit/038636786ad1914f3daf3503ae9611f40dffbb8f
@jknockel
Copy link
Contributor Author

Hi, you should be safe to #include "core/display-private.h" in this file, which contains meta_get_display () to allow you to use the meta pop/push functions.

For some reason meta_get_display () returns NULL when I try calling it here, so I've gone with switching out to XCB for calling DeleteMonitor. The push/pop functions if working would be even simpler, but XCB seems still simpler than my previous approach.

I think it's ok as you have it - you're working around the current randr bug, and it should continue to work even if/when the xserver fix is released.

I'd rather keep it simple for now, especially since I assume this isn't a 100% repeatable bug in typical situations is it?

I believe that it is not repeatable in typical user setups which do not involve tiled monitors, but when plugged into my tiled monitor a fatal Xlib error is 100% repeatable on cinnamon --replace or Troubleshoot -> Restart Cinnamon.

@mtwebster mtwebster merged commit 199284c into linuxmint:master May 21, 2024
2 checks passed
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

Successfully merging this pull request may close these issues.

2 participants