Gdk_monitor_get_width_mm() failure (Wayland?)

Hi all,

I need to determine the physical dimensions (millimeters) of my display and have long been using gdk_monitor_get_{width,height}_mm() to retrieve these values. I am now running my code on a Wayland system for the first time (Fedora 33, gtk3-3.24.24, gdk-pixbuf2-2.40) and these functions no longer work, i.e., they always return 0. The sample code provided below demonstrates the problem. This code correctly reports the pixel dimensions of the monitor (i.e., number of pixels along the horizontal and vertical axes) but fails to report the sizes in mm of those axes.

This Fedora system is running inside a VMWare Fusion VM but I do not believe the VM environment is the cause of the problem as my code works fine under two other VMs (CentOS7 and Void) running xorg X11 without Wayland. I should also note that the Fedora VM’s XWayland server is somehow able to determine the physical size:

% xdpyinfo | grep milli
dimensions: 2269x1265 pixels (600x334 millimeters)

I am sure that xdpyinfo must be calling an Xlib function to obtain this info but I cannot do this if I want to preserve my gtk app’s portability (i.e., no dependence upon X)

Thanks, sample code follows! (Apologies for my Discourse-stupidity, I have no idea why there are strange font things happening in the code in this post. I could not figure out how to just attach a .c file to this post as a separate entity. Tips welcome.)

/*
   Compile with

   gcc monsize.c -o monsize \`pkg-config gtk+-3.0 --cflags --libs\`
*/

#include <stdio.h>
#include <gtk/gtk.h>

int
main(int argc, char **argv)
{
	GdkDisplay *dsp;
	GdkMonitor *mon;
	GdkRectangle rect;
	int nmons, i, wmm, hmm;

	if (gtk_init_check(&argc, &argv) == FALSE)
		exit(-1);

	if ((dsp= gdk_display_get_default()) == (GdkDisplay *) 0)
		exit(-1);
	nmons= gdk_display_get_n_monitors(dsp);
	for (i= 0; i < nmons; i++) {
		if ((mon= gdk_display_get_monitor(dsp, i)) == (GdkMonitor *) 0)
			exit(-1);
		gdk_monitor_get_geometry(mon, &rect);
		(void) printf("Monitor %1d pixel dimensions: %1dw x %1dh\n", i, rect.width, rect.height);
		wmm= gdk_monitor_get_width_mm(mon);
		hmm= gdk_monitor_get_height_mm(mon);
		(void) printf("Monitor %1d size: %1dmm x %1dmm\n", i, wmm, hmm);
	}

	exit(0);
}

Doing further web research just now I have discovered the GDK_BACKEND environment variable. If I set this to ‘x11’ then gdk_get_monitor_width_mm() reports a reasonable value. OK, that’s certainly a usable workaround but isn’t the whole point of gdk/gtk/etc. that they are supposed to provide platform independence with respect to X11 and Wayland? And isn’t the XWayland server actually running on top of Wayland anyway? This is thoroughly confusing, if anyone can provide a pointer to useful documentation of this issue it would be much appreciated! I am a long-ago Xlib programmer with several years of experience writing gtk3 apps running on Xorg servers, but this is my first time working on a system with Wayland.

Is there or is there not a platform-independent way for me to figure out my physical display size without knowing whether I am running on Wayland or an X server?

Thanks!

No, that’s not the point of GDK at all. GDK is a windowing system API that tries to be portable across platforms and windowing systems, but it’s only ever going to cover the requirements of GTK itself; it’s not meant to be a generic windowing system API covering all possible use cases. We actually tried to pare it down to the minimum common denominator precisely because otherwise it would break.

No, there isn’t.

At most, you can get the EDID blob using platform/windowing system specific API, and then parse the fields yourself; but you have to understand that there is no guarantee the display will give you a valid physical size because there’s no incentive whatsoever for hardware manufacturers to report a valid size. If you’re lucky, you might get a physical size, instead of getting 0×0 millimiters; but you might get 16×9mm, or the physical size for a monitor model that does not match your own, just as likely as you’d get the physical size that matches the glass rectangle of the monitor you care about. Additionally, physical sizes mean absolutely nothing for compound displays, or for projectors.

The difference between X11 and Wayland, in this regard, is that on X11 you have XRandR, which parses the EDID and tries to give you whatever it finds; when running under a Wayland display server, on the other hand, you have to rely on the display server to parse the EDID first (which is not always the case), and then report it through the wl_output interface. It’s entirely optional for the Wayland compositor to do so.

Thanks for the reply, Emmanuele!

Based on your suggestion re: wl_output it appears that gdk_wayland_monitor_get_wl_output() may help get me what I want (assuming that I can rely on the GDK macros to tell me when I am using Wayland). Unfortunately I have not been able to determine the internal structure of the struct wl_output object returned by that function. I see references to that struct throughout the /usr/include/wayland*h files but no definition anywhere. Can you tell me where I can find this, or point me to a code example using gdk_wayland_monitor_get_wl_output() to retrieve the physical size dimensions? I’ve been looking but unable to find such anywhere.

I do not understand how xdpyinfo can retrieve the physical size from XWayland (as it can clearly do in my case) if wayland itself does not know these values – isn’t XWayland getting this from the wayland compositor? Or is XWayland opening up some back-door connection to the monitor system like it’s a more traditional X server? How does GDK correctly return these values when I set GDK_BACKEND to x11? In that case I am still running wayland/XWayland.

I have noticed that these physical sizes are never accurate but I have found them close enough to be useful at least until now. I understand the position that the GDK API cannot be all things to all people, but being able to determine the physical dimensions of a single screen pixel is extremely useful when trying to render something on the display at a particular size (or at least reasonably close). I’m actually quite surprised that GTK does NOT require this internally.

If there really is no way to reliably get this information without setting GDK_BACKEND to x11 (or calling gdk_set_allowed_backends(“x11”), which also seems to work for me), how long can I count on that workaround being available? Will GDK support the x11 backend now and forever, or is it going away at some point in the foreseeable future?

Thanks!

Roger

If you have weston installed, you can run weston-info from the command line to see the currently available wl_output objects. (Despite the name, it works outside of weston, it’s somewhat of the wayland equivalent of xdpyinfo. You can also try the standalone wayland-info tool if you don’t want to install weston, it is the same utility but separated from the weston code base)

For example on my system I see:

interface: 'wl_output', version: 2, name: 4
        x: 0, y: 0, scale: 1,
        physical_width: 260 mm, physical_height: 160 mm,

For a bit more technical details, the relevant part here is the geometry event in wl_output. You could write some Wayland code for this but that’s probably unnecessary, GDK should listen to that automatically and return the value with that function. (You can also verify this by running your program with environment variable WAYLAND_DEBUG=1 and looking for the geometry event) On my system these values are consistent between XWayland and Wayland, and the GDK functions return them correctly in both situations, so maybe the VM and Mutter together is causing some strange interaction? I’m not sure.

GTK has nothing to do with this: it just returns the data it gets from the compositor through the available interfaces. GTK does not parse the EDID blob itself, and won’t do that for the foreseeable future.

The Wayland client backend in GDK asks the compositor to retrieve the geometry of the output through the wl_output interface, and it gets the physical dimensions as well; those dimensions are available through the GdkMonitor API.

There is no requirement for a Wayland compositor to return those physical dimensions.

XWayland is an X11 server that does not render to a scanout buffer, but to a buffer provided by the Wayland compositor.

In any case, it’s entirely inconsequential; the Wayland compositor might not be sending the physical dimensions of the monitors to its clients even if the XWayland server is loading the EDID blob out of the monitors.

GTK 3 and 4 will support the X11 backend forever, since it’s part of the API. I don’t make promises for GTK5 or any future API, given that those don’t exist as of yet.

Of course, GTK can be compiled without the X11 backend, so you will need to do both compile and run time discovery, just like you’d do in order to check for the Windows or the macOS backend.

Thanks for the wayland-info pointer, Jason! My system says:

interface: ‘wl_output’, version: 2, name: 3
x: 0, y: 0, scale: 1,
physical_width: 0 mm, physical_height: 0 mm,
make: ‘unknown’, model: ‘unknown’,
subpixel_orientation: unknown, output_transform: normal,
mode:
width: 2365 px, height: 1321 px, refresh: 59.999 Hz,
flags: current preferred

I will interpret the above output to indicate that I am unlikely to have any success calling gdk_wayland_monitor_get_wl_output() to retrieve this information even if I could figure out how to decode whatever data structure that routine is returning.

You may well be correct about this problem being related to the VM environment, unfortunately this is the only Wayland system I have at the moment. I’d still really like to know how xdpyinfo (talking to XWayland) is coming up with this:

screen #0:
dimensions: 2365x1321 pixels (625x349 millimeters)
resolution: 96x96 dots per inch

All of the wayland architecture block diagrams I’ve seen (which admittedly are likely only crude generalizations) show XWayland communicating everything through the wayland compositor. Is xdpyinfo just making up this reported physical size based on pixel counts and some default (96dpi) resolution?

I’m thinking my strategy may be to just use the gdk_get_monitor_??_mm() values if they seem reasonable (i.e., non-zero and not resulting in some clearly bogus dpi) and otherwise fallback to a default dpi, perhaps with a screen resolution environment variable override for users who are extremely picky about the visual scaling.

Yes, it appears you are correct, normally XWayland would pass the information on from the wl_output, but it looks like the X server does try to guess if the values are zero: rrmonitor.cc#L77

if (crtc->numOutputs && crtc->outputs[0]->mmWidth && crtc->outputs[0]->mmHeight) {
    RROutputPtr output = crtc->outputs[0];
    geometry->mmWidth = output->mmWidth;
    geometry->mmHeight = output->mmHeight;
} else {
    geometry->mmWidth = floor ((geometry->box.x2 - geometry->box.x1) / DEFAULT_PIXELS_PER_MM + 0.5);
    geometry->mmHeight = floor ((geometry->box.y2 - geometry->box.y1) / DEFAULT_PIXELS_PER_MM + 0.5);
}

This seems to only be a problem because your VM device is reporting its size as 0mm x 0mm. As @ebassi says, the reporting of the device’s physical size is not going to always be accurate or available, even a physical monitor could have the wrong values in EDID. So yes, I would say the simplest thing to do here, if you are building some kind of GUI around this and accurate measurements are needed, is to keep using the measurements from GdkMonitor but only use those values as a default value, and allow the user to override them.

You could also try to call XRandr to get the raw values from the output yourself, or you could try to match the DRM device to a GdkMonitor and re-parse the EDID, but that will not strictly help in cases where the device is reporting 0mm x 0mm. It will only let you know when you’ll have to prompt the user for a real measurement when that happens, or to use your own heuristic to guess.

Thanks for digging into this further, Jason, you are truly generous in your assistance (not to mention knowledgeable in knowing where to look for the bodies)! Hopefully I will have a non-VM Wayland testbed at some point and will be able to better resolve whether or not this is largely a VM issue. In the meantime I’ve proceeded down the path of (i) using GdkMonitor returns as a starting point, (ii) overriding them with defaults when they are suspicious (apparently just as you have shown XWayland to be doing), and (iii) providing user-configurable environmental override in either case. This should meet my needs without complexifying things with more detailed queries to various underlying window systems that may be insufficient in any case.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.