Ignore XRRSetMonitor() errors and delete existing monitors with same name #693
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Ignore server errors on XRRSetMonitor(). A similar fix appears in mutter [1].
The RandR protocol [2] states concerning RRSetMonitor that:
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